diff --git a/desktop/app.js b/desktop/app.js index 1468a15ba..c1335deb2 100644 --- a/desktop/app.js +++ b/desktop/app.js @@ -18,7 +18,7 @@ const importNotes = require('./evernote-import'); const platform = require('./detect/platform'); const updater = require('./updater'); const { isDev } = require('./env'); -const spellcheck = require('./spellchecker'); +const contextMenu = require('./context-menu'); require('module').globalPaths.push(path.resolve(path.join(__dirname))); @@ -76,7 +76,7 @@ module.exports = function main() { mainWindow.loadUrl(url); } - spellcheck(mainWindow); + contextMenu(mainWindow); if ( 'test' !== process.env.NODE_ENV && diff --git a/desktop/context-menu/index.js b/desktop/context-menu/index.js new file mode 100644 index 000000000..b0bee4c4e --- /dev/null +++ b/desktop/context-menu/index.js @@ -0,0 +1,43 @@ +'use strict'; + +/** + * External dependencies + */ +const { Menu } = require('electron'); + +const { editorCommandSender } = require('../menus/utils'); + +module.exports = function (mainWindow) { + mainWindow.webContents.on('context-menu', (event, params) => { + const { editFlags } = params; + Menu.buildFromTemplate([ + { + id: 'selectAll', + label: 'Select All', + click: editorCommandSender({ action: 'selectAll' }), + enabled: editFlags.canSelectAll, + }, + { + id: 'cut', + label: 'Cut', + role: 'cut', + enabled: editFlags.canCut, + }, + { + id: 'copy', + label: 'Copy', + role: 'copy', + enabled: editFlags.canCopy, + }, + { + id: 'paste', + label: 'Paste', + role: 'paste', + enabled: editFlags.canPaste, + }, + { + type: 'separator', + }, + ]).popup({}); + }); +}; diff --git a/desktop/menus/edit-menu.js b/desktop/menus/edit-menu.js index fd4a14867..41e7fb1e6 100644 --- a/desktop/menus/edit-menu.js +++ b/desktop/menus/edit-menu.js @@ -1,4 +1,4 @@ -const { appCommandSender } = require('./utils'); +const { appCommandSender, editorCommandSender } = require('./utils'); const buildEditMenu = (settings, isAuthenticated) => { settings = settings || {}; @@ -9,11 +9,11 @@ const buildEditMenu = (settings, isAuthenticated) => { submenu: [ { label: '&Undo', - role: 'undo', + click: editorCommandSender({ action: 'undo' }), }, { label: '&Redo', - role: 'redo', + click: editorCommandSender({ action: 'redo' }), }, { type: 'separator', @@ -32,7 +32,7 @@ const buildEditMenu = (settings, isAuthenticated) => { }, { label: '&Select All', - role: 'selectall', + click: editorCommandSender({ action: 'selectAll' }), }, { type: 'separator' }, { diff --git a/desktop/menus/help-menu.js b/desktop/menus/help-menu.js index 1beed0bd9..bbb811eba 100644 --- a/desktop/menus/help-menu.js +++ b/desktop/menus/help-menu.js @@ -19,7 +19,10 @@ const buildHelpMenu = (mainWindow, isAuthenticated) => { { label: '&Keyboard Shortcuts', visible: isAuthenticated, - click: appCommandSender({ action: 'showDialog', dialog: 'KEYBINDINGS' }), + click: appCommandSender({ + action: 'showDialog', + dialog: 'KEYBINDINGS', + }), }, { type: 'separator' }, { diff --git a/desktop/menus/utils.js b/desktop/menus/utils.js index 27d73a99d..0e3116058 100644 --- a/desktop/menus/utils.js +++ b/desktop/menus/utils.js @@ -15,9 +15,17 @@ const buildRadioGroup = ({ action, propName, settings }) => { }; const appCommandSender = (arg) => { + return commandSender('appCommand', arg); +}; + +const editorCommandSender = (arg) => { + return commandSender('editorCommand', arg); +}; + +const commandSender = (commandName, arg) => { return (item, focusedWindow) => { if (focusedWindow) { - focusedWindow.webContents.send('appCommand', arg); + focusedWindow.webContents.send(commandName, arg); } }; }; @@ -25,4 +33,5 @@ const appCommandSender = (arg) => { module.exports = { buildRadioGroup, appCommandSender, + editorCommandSender, }; diff --git a/desktop/menus/view-menu.js b/desktop/menus/view-menu.js index 26cfbcdd6..2019013ea 100644 --- a/desktop/menus/view-menu.js +++ b/desktop/menus/view-menu.js @@ -40,7 +40,9 @@ const buildViewMenu = (settings, isAuthenticated) => { label: '&Reversed', type: 'checkbox', checked: settings.sortReversed, - click: appCommandSender({ action: 'toggleSortOrder' }), + click: appCommandSender({ + action: 'toggleSortOrder', + }), }, ]), }, @@ -96,7 +98,9 @@ const buildViewMenu = (settings, isAuthenticated) => { label: '&Sort Alphabetically', type: 'checkbox', checked: settings.sortTagsAlpha, - click: appCommandSender({ action: 'toggleSortTagsAlpha' }), + click: appCommandSender({ + action: 'toggleSortTagsAlpha', + }), }, ], }, diff --git a/desktop/preload.js b/desktop/preload.js index 8a477a19d..4114f9529 100644 --- a/desktop/preload.js +++ b/desktop/preload.js @@ -3,6 +3,7 @@ const { contextBridge, ipcRenderer, remote } = require('electron'); const validChannels = [ 'appCommand', 'clearCookies', + 'editorCommand', 'importNotes', 'noteImportChannel', 'setAutoHideMenuBar', diff --git a/desktop/spellchecker/index.js b/desktop/spellchecker/index.js deleted file mode 100644 index 8a6829ec6..000000000 --- a/desktop/spellchecker/index.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict'; - -/** - * External dependencies - */ -const { Menu, MenuItem } = require('electron'); - -module.exports = function (mainWindow) { - mainWindow.webContents.on('context-menu', (event, params) => { - const menu = new Menu(); - - const copy = new MenuItem({ label: 'Copy', role: 'copy' }); - - if (!params.isEditable) { - // If text is not editable, only permit the `Copy` action - menu.append(copy); - } else { - // Add each spelling suggestion - for (const suggestion of params.dictionarySuggestions) { - menu.append( - new MenuItem({ - label: suggestion, - click: () => mainWindow.webContents.replaceMisspelling(suggestion), - }) - ); - } - - // Allow users to add the misspelled word to the dictionary - if (params.misspelledWord) { - menu.append(new MenuItem({ type: 'separator' })); - menu.append( - new MenuItem({ - label: 'Add to Dictionary', - click: () => - mainWindow.webContents.session.addWordToSpellCheckerDictionary( - params.misspelledWord - ), - }) - ); - } - - // If text is editable, permit the Select All, Cut, Copy and Paste actions - const cut = new MenuItem({ label: 'Cut', role: 'cut' }); - const paste = new MenuItem({ label: 'Paste', role: 'paste' }); - const selectAll = new MenuItem({ - label: 'Select All', - role: 'selectAll', - }); - - const menuItems = [selectAll, cut, copy, paste]; - - if (params?.dictionarySuggestions?.length > 0) { - menu.append(new MenuItem({ type: 'separator' })); - } - - for (const item of menuItems) { - menu.append(item); - } - } - - menu.popup(); - }); -}; diff --git a/lib/global.d.ts b/lib/global.d.ts index 1785f302c..8e03924cd 100644 --- a/lib/global.d.ts +++ b/lib/global.d.ts @@ -11,6 +11,7 @@ declare global { confirmLogout(changes: string): 'logout' | 'reconsider' | 'export'; isMac: boolean; receive(command: 'appCommand', callback: (event: any) => any); + receive(command: 'editorCommand', callback: (event: any) => any); receive(command: 'noteImportChannel', callback: (event: any) => any); receive(command: 'wpLogin', callback: (event: any) => any); removeListener(command: 'noteImportChannel'); diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index 75b0dda50..4d62462cc 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -100,6 +100,7 @@ class NoteContentEditor extends Component { if (this.bootTimer) { clearTimeout(this.bootTimer); } + window.electron?.removeListener('editorCommand'); window.removeEventListener('keydown', this.handleKeys, true); } @@ -189,6 +190,20 @@ class NoteContentEditor extends Component { this.editor = editor; this.monaco = monaco; + window.electron?.receive('editorCommand', (command) => { + switch (command.action) { + case 'redo': + editor.trigger('', 'redo'); + return; + case 'selectAll': + editor.setSelection(editor.getModel().getFullModelRange()); + return; + case 'undo': + editor.trigger('', 'undo'); + return; + } + }); + const titleDecoration = (line: number) => ({ range: new monaco.Range(line, 1, line, 1), options: {