Copy to cliboard

Hi! I’m wondering if anybody uses copy to clipboard functionality. Every time I call copyText I’m getting error Plugin Error: Plugin xd.plugin is not permitted to make changes from the background. Return a Promise to continue execution asynchronously.. The code I use is:

public async copyUrl(): Promise<void> {
  try {
    await clipboard.copyText(this.shareUrl);
    this.shareSucceed = true;
  } catch (ex) {
    this.shareError = ex;
  }
}

Any ideas what’s wrong?

Checked other topics it seems that background problem is pretty common for different not background operations: Using Jimp to write images and Changing `selection.items` without "not permitted to make changes from the background" error.

Promises have to be returned to continue execution asynchronously as the error message suggest.s However, in this case, copyText is a synchronous method. You don’t have to await or wrap it around with a promise block.

Really error doesn’t depend on if I await for result on not. If I just call copyText in a sync manner error is the same:

Plugin Error: Plugin io.sympli.xd.plugin is not permitted to make changes from the background. Return a Promise to continue execution asynchronously.
    at convertPluginErrorToString (plugins/PluginErrorUtil.js:1:198)
    at internalFormatPluginError (plugins/PluginErrorUtil.js:1:1073)
    at internalReportPluginError (plugins/PluginErrorUtil.js:1:1180)
    at Object.reportPluginError (plugins/PluginErrorUtil.js:1:1612)
    at Object.checkAllowedToEdit (plugins/ScenegraphGuard.js:1:1435)
    at Object.<anonymous> (plugins/ClipboardWrapper.js:1:194)
    at t.copyUrl (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:501323)

Where is copyUrl() being called from? I don’t see anything below it in your call stack. You have to be getting called from XD (in response to a user action) and return the Promise back to that caller. If you’re not acting in response to a user action, you won’t be able to modify the clipboard.

1 Like

copyUrl is a click handler so it’s called on button click. @stevekwak, said that copyText is synchronous method so I don’t need to return promise back. Is it correct?
Just curious why clipboard wrapper calls checkAllowedToEdit? I’m not editing anything, I’m just trying to copy some text.
Full stack is:

Plugin Error: Plugin xd.plugin is not permitted to make changes from the background. Return a Promise to continue execution asynchronously.
    at convertPluginErrorToString (plugins/PluginErrorUtil.js:1:198)
    at internalFormatPluginError (plugins/PluginErrorUtil.js:1:1073)
    at internalReportPluginError (plugins/PluginErrorUtil.js:1:1180)
    at Object.reportPluginError (plugins/PluginErrorUtil.js:1:1612)
    at Object.checkAllowedToEdit (plugins/ScenegraphGuard.js:1:1435)
    at Object.<anonymous> (plugins/ClipboardWrapper.js:1:194)
    at t.copyUrl (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:501319)
    at Object.handleEvent (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:505516)
    at Object.handleEvent (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:202654)
    at Object.handleEvent (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:224395)
    at So (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:173569)
    at C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:179549
    at b.<anonymous> (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:275734)
    at e.invokeTask (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\polyfills.js:1:7850)
    at Object.onInvokeTask (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\main.js:1:141548)
    at e.invokeTask (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\polyfills.js:1:7771)
    at t.runTask (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\polyfills.js:1:3032)
    at t.invokeTask [as invoke] (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\polyfills.js:1:8933)
    at m (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\polyfills.js:1:22379)
    at b.k (C:\Users\avgre\AppData\Local\Packages\Adobe.CC.XD.Prerelease_adky2gkssdxte\LocalState\develop\xd.plugin\src\polyfills.js:1:22609)
    at uxp://uxp-internal/home/ubuntu/jenkins/workspace/Torq/torq-native/release-3.1.0/build/modules_gen/domjs/src/js/domjs_scripts.js:322:893
    at j (uxp://uxp-internal/home/ubuntu/jenkins/workspace/Torq/torq-native/release-3.1.0/build/modules_gen/domjs/src/js/domjs_scripts.js:322:785)
    at g (uxp://uxp-internal/home/ubuntu/jenkins/workspace/Torq/torq-native/release-3.1.0/build/modules_gen/domjs/src/js/domjs_scripts.js:322:390)
    at f (uxp://uxp-internal/home/ubuntu/jenkins/workspace/Torq/torq-native/release-3.1.0/build/modules_gen/domjs/src/js/domjs_scripts.js:322:192)
    at k (uxp://uxp-internal/home/ubuntu/jenkins/workspace/Torq/torq-native/release-3.1.0/build/modules_gen/domjs/src/js/domjs_scripts.js:322:1097)
    at l (uxp://uxp-internal/home/ubuntu/jenkins/workspace/Torq/torq-native/release-3.1.0/build/modules_gen/domjs/src/js/domjs_scripts.js:322:1468)
    at b.value (uxp://uxp-internal/home/ubuntu/jenkins/workspace/Torq/torq-native/release-3.1.0/build/modules_gen/domjs/src/js/domjs_scripts.js:82:7361)

It’s hard to debug without looking at your code. Would you be able to share your plugin with us? Please feel free to send me the file at kwak@adobe.com

With everything simplified, this should work:

const clipboard = require("clipboard");

function test() {
    clipboard.copyText("hello world");
}

module.exports = {
    commands: {
        test
    }
};

When plugin is run, “hello world” text will be copied to the clipboard

1 Like

@stevekwak, could you please try this one:

function helloHandlerFunction(selection) {
    const clipboard = require("clipboard");
    
    if (dialog == null) {
        //  create the dialog
        dialog = document.createElement("dialog");

        //  create the form element
        //  the form element has default styling and spacing
        let form = document.createElement("form");
        dialog.appendChild(form);
        //  don't forget to set your desired width
        form.style.width = 200;

        //  create a footer to hold your form submit and cancel buttons
        let footer = document.createElement("footer");
        form.appendChild(footer);

        let copyButton = document.createElement("button");
        copyButton.uxpVariant = "cta";
        copyButton.textContent = "Copy";
        copyButton.onclick = (e) => { clipboard.copyText("hello world"); };
        footer.appendChild(copyButton);
    }
    
    document.body.appendChild(dialog).showModal();
}

You need to return that because .showModal() returns a promise:

return document.body.appendChild(dialog).showModal();
1 Like

Thanks, that works for dialogs. What should be returned in case of panels?

function showCopyPanel() {
    const clipboard = require("clipboard");

    panel = document.createElement("panel");

    let form = document.createElement("form");
    panel.appendChild(form);
    form.style.width = 200;
    let footer = document.createElement("footer");
    form.appendChild(footer);

    let copyButton = document.createElement("button");
    copyButton.uxpVariant = "cta";
    copyButton.textContent = "Copy";
    copyButton.onclick = (e) => { clipboard.copyText("hello world"); };
    footer.appendChild(copyButton);

    return document.body.appendChild(panel);
}

and show event just call it:

show(event) {
  return showCopyPanel();
}

You need to attach the created panel to the event.node object. Take a look at this example and let me know if you have any questions.

Did I get it right, you mean something like following:

function showCopyPanel(container) {
    const clipboard = require("clipboard");
    panel = document.createElement("panel");
    let form = document.createElement("form");
    panel.appendChild(form);
    form.style.width = 200;
    let footer = document.createElement("footer");
    form.appendChild(footer);

    let copyButton = document.createElement("button");
    copyButton.uxpVariant = "cta";
    copyButton.textContent = "Copy";
    copyButton.onclick = (e) => { clipboard.copyText("hello world"); };
    footer.appendChild(copyButton);

    return container.appendChild(panel);
}

and show event

show(event) {
  return showCopyPanel(event.node);
}

The result is exactly the same :frowning: If you don’t mind could you please share code snippet with tested working example. Maybe it’s platform issue? I’m testing it on windows 10.

Wrap the above like so:

copyButton.onclick = (e) => {
    require("application").editDocument( () => clipboard.copyText("hello world"); );
}

Not sure why copy text requires document editing but it works! Thank you very much!