jupyterlab-desktop icon indicating copy to clipboard operation
jupyterlab-desktop copied to clipboard

Copy, cut and paste with context menu

Open krassowski opened this issue 2 years ago • 2 comments

Problem

First, thanks for the awesome work on reviving this! I tested on Ubuntu and it works!

There is the Shift + Right Click for Browser Menu entry, but Shift + Right Click does not do anything for me. This means that some actions (copy, paste, cut, copy link address) are not accessible now.

Screenshot from 2021-09-15 17-31-31

Proposed Solution

  • Work with upstream JupterLab to add full self-contained functionality of copy, paste, and cut (which are already available in Text Editor but not in Notebook) and maybe additional actions (copy link) too
  • Disable the Shift + Right Click for Browser Menu entry in JupyterLab App as it is misleading (this should be possible with the menu schema files).

We should also add:

  • Copy Link URL
  • Copy Image
  • and maybe Save image?

krassowski avatar Sep 15 '21 17:09 krassowski

Thank you for opening your first issue in this project! Engagement like this is essential for open source projects! :hugs:
If you haven't done so already, check out Jupyter's Code of Conduct. Also, please try to follow the issue template as it helps other other community members to contribute more effectively. welcome You can meet the other Jovyans by joining our Discourse forum. There is also an intro thread there where you can stop by and say Hi! :wave:
Welcome to the Jupyter community! :tada:

welcome[bot] avatar Sep 15 '21 17:09 welcome[bot]

I explored four approaches:

  • A) using webContents API (e.g. webContents.copy) for both interacting with clipboard and with the page, and communicating via remotes
  • B) using Electron.clipboard directly for interacting with clipboard, Web APIs like Selection and DOM API for interacting with the page, and communicating via remotes
  • C) using Clipboard API on client side only, no communication with server
  • D) adding a fallback context menu with Electron-native implementation of commonly needed actions

The code for A and B would rougly look as follows:

Code

In desktop-extension

app.commands.addCommand(CommandIDs.copySelection, {
    label: 'Copy Text',
    execute: () => {
        const selection = window.getSelection();
        asyncRemoteRenderer.runRemoteMethod(IAppRemoteInterface.writeToClipboard, selection.toString()).catch(console.warn);
    },
    isVisible: () => {
        const selection = window.getSelection();
        return selection.toString() !== '';
    },
});

and server side (in main/app):

asyncRemoteMain.registerRemoteMethod(IAppRemoteInterface.writeToClipboard,
    (data: string): Promise<void> => {
        // approach A:
        // clipboard.writeText(data);
        // approach B:
        this._window.webContents.copy();
        return Promise.resolve();
    });

Problems with approaches A and B:

  • isVisible() gets called too frequently leading to performance degradation (rendering it unusable);
  • handling of CodeEditor in Notebook cells is difficult because the custom context menu takes focus away from the editor hence the text is no longer selected; this necessitates writing custom code as for the Copy command of File Editor in the core JupyterLab but also aware of multiple cells (which would be a slight adjustment)

Problems with A:

  • in order to handle CodeEditors issue requires some clever focus management/closing the JupyterLab context menu first, ensuring focus is back on selected element and only then executing the action. This is doable but will require a major API change upstream to implement it cleanly. This might be error-prone.

Problems with B:

  • in order to handle CodeEditors issue requires getting the selected text directly from the editor
  • requires re-implementing a bit of logic to handle rich-MIME copying; this is already somewhat implemented in JupyterLab core and we would be duplicating it here if using approach B
  • pasting is difficult (especially into contentEditable/CodeMirror editor) because we need to manage DOM nodes/events manually; this is not an issue with approach A.
  • "Copy link URL" and "Copy Image" might not be trivial to implement well (but still feasible!)

Problems with C:

  • it needs to be implemented directly in JupyterLab
    • JupyterLab postponed implementation waiting for wider browser support for Clipboard API
    • it might need to be implemented as an opt-in because some browsers do not allow to read from clipboard in web context
  • actions such as "Copy link URL" and "Copy Image" will need to be implemented (note: we already have "Copy Output to Clipboard" implemented in https://github.com/jupyterlab/jupyterlab/pull/10282)

Problems with D:

  • we end up with two menus again, this is detrimental to UX (though at least consistent with the web version)

I think that long term we should pursue approach C by implementing relevant code in JupyterLab core (4.0 or 4.1?). In the meantime we can adopt approach D.

krassowski avatar Nov 20 '21 15:11 krassowski