From 71c5d2e1fb28f13fea35b1be076909f463826f55 Mon Sep 17 00:00:00 2001 From: Nautatava Navlakha Date: Tue, 14 Apr 2020 07:07:30 +0000 Subject: [PATCH] Add built-in support for spellchecking (#94) Co-authored-by: Sindre Sorhus --- fixture-menu.js | 6 ++++- fixture.js | 2 +- index.d.ts | 50 ++++++++++++++++++++++++++++++++------- index.js | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- readme.md | 25 +++++++++++++++++--- 6 files changed, 133 insertions(+), 14 deletions(-) diff --git a/fixture-menu.js b/fixture-menu.js index a3b023c..7bb09c9 100644 --- a/fixture-menu.js +++ b/fixture-menu.js @@ -33,5 +33,9 @@ contextMenu({ (async () => { await app.whenReady(); - await (new BrowserWindow()).loadFile(path.join(__dirname, 'fixture.html')); + await (new BrowserWindow({ + webPreferences: { + spellcheck: true + } + })).loadFile(path.join(__dirname, 'fixture.html')); })(); diff --git a/fixture.js b/fixture.js index 97b4f73..6cd4754 100644 --- a/fixture.js +++ b/fixture.js @@ -45,7 +45,7 @@ contextMenu({ await (new BrowserWindow({ webPreferences: { - nodeIntegration: true + spellcheck: true } })).loadFile(path.join(__dirname, 'fixture.html')); })(); diff --git a/index.d.ts b/index.d.ts index b8f2a71..e10adb6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -12,11 +12,25 @@ import { declare namespace contextMenu { interface Labels { /** - The placeholder `{selection}` will be replaced by the currently selected text. + @default 'Correct Automatically' + */ + readonly correctAutomatically?: string; + + /** + @default 'Learn Spelling' + */ + readonly learnSpelling?: string; + /** + The placeholder `{selection}` will be replaced by the currently selected text. @default 'Look Up “{selection}”' */ readonly lookUpSelection?: string; + + /** + @default 'Search with Google' + */ + readonly searchWithGoogle?: string; /** @default 'Cut' @@ -80,7 +94,10 @@ declare namespace contextMenu { interface Actions { readonly separator: () => MenuItemConstructorOptions; + readonly correctAutomatically: (options: ActionOptions) => MenuItemConstructorOptions; + readonly learnSpelling: (options: ActionOptions) => MenuItemConstructorOptions; readonly lookUpSelection: (options: ActionOptions) => MenuItemConstructorOptions; + readonly searchWithGoogle: (options: ActionOptions) => MenuItemConstructorOptions; readonly cut: (options: ActionOptions) => MenuItemConstructorOptions; readonly copy: (options: ActionOptions) => MenuItemConstructorOptions; readonly paste: (options: ActionOptions) => MenuItemConstructorOptions; @@ -129,6 +146,13 @@ declare namespace contextMenu { */ readonly showLookUpSelection?: boolean; + /** + Show the `Search with Google` menu item when right-clicking text on macOS. + + @default true + */ + readonly showSearchWithGoogle?: boolean; + /** Show the `Copy Image` menu item when right-clicking on an image. @@ -219,13 +243,16 @@ declare namespace contextMenu { The following options are ignored when `menu` is used: - `showLookUpSelection` + - `showSearchWithGoogle` - `showCopyImage` - `showCopyImageAddress` - `showSaveImageAs` - `showInspectElement` - `showServices` + + To get spellchecking, “Correct Automatically”, and “Learn Spelling” in the menu, please enable the `spellcheck` preference in browser window: `new BrowserWindow({webPreferences: {spellcheck: true}})` - @default [defaultActions.cut(), defaultActions.copy(), defaultActions.paste(), defaultActions.separator(), defaultActions.saveImage(), defaultActions.saveImageAs(), defaultActions.copyLink(), defaultActions.copyImage(), defaultActions.copyImageAddress(), defaultActions.separator(), defaultActions.copyLink(), defaultActions.separator(), defaultActions.inspect()] + @default [...dictionarySuggestions, defaultActions.separator(), defaultActions.correctAutomatically(), defaultActions.separator(), defaultActions.learnSpelling(), defaultActions.separator(), defaultActions.lookUpSelection(), defaultActions.separator(),defaultActions.searchWithGoogle(), defaultActions.cut(), defaultActions.copy(), defaultActions.paste(), defaultActions.separator(), defaultActions.saveImage(), defaultActions.saveImageAs(), defaultActions.copyLink(), defaultActions.copyImage(), defaultActions.copyImageAddress(), defaultActions.separator(), defaultActions.copyLink(), defaultActions.separator(), defaultActions.inspect()] */ readonly menu?: ( defaultActions: Actions, @@ -246,17 +273,24 @@ import {app, BrowserWindow} from 'electron'; import contextMenu = require('electron-context-menu'); contextMenu({ - prepend: (params, browserWindow) => [{ - label: 'Rainbow', - // Only show it when right-clicking images - visible: params.mediaType === 'image' - }] + prepend: (defaultActions, params, browserWindow) => [ + { + label: 'Rainbow', + // Only show it when right-clicking images + visible: params.mediaType === 'image' + } + ] }); let mainWindow; (async () => { await app.whenReady(); - mainWindow = new BrowserWindow(); + + mainWindow = new BrowserWindow({ + webPreferences: { + spellcheck: true + } + }); }); ``` */ diff --git a/index.js b/index.js index bfac64c..93f41ed 100644 --- a/index.js +++ b/index.js @@ -41,6 +41,24 @@ const create = (win, options) => { const defaultActions = { separator: () => ({type: 'separator'}), + correctAutomatically: decorateMenuItem({ + id: 'correctAutomatically', + label: 'Correct Spelling Automatically', + visible: props.isEditable && hasText && props.misspelledWord && props.dictionarySuggestions.length > 0, + click() { + const target = webContents(win); + target.insertText(props.dictionarySuggestions[0]); + } + }), + learnSpelling: decorateMenuItem({ + id: 'learnSpelling', + label: 'Learn Spelling', + visible: props.isEditable && hasText && props.misspelledWord, + click() { + const target = webContents(win); + target.session.addWordToSpellCheckerDictionary(props.misspelledWord); + } + }), lookUpSelection: decorateMenuItem({ id: 'lookUpSelection', label: 'Look Up “{selection}”', @@ -51,6 +69,16 @@ const create = (win, options) => { } } }), + searchWithGoogle: decorateMenuItem({ + id: 'searchWithGoogle', + label: 'Search with Google', + visible: hasText, + click() { + const url = new URL('https://www.google.com/search'); + url.searchParams.set('q', props.selectionText); + electron.shell.openExternal(url.toString()); + } + }), cut: decorateMenuItem({ id: 'cut', label: 'Cut', @@ -173,10 +201,44 @@ const create = (win, options) => { const shouldShowInspectElement = typeof options.showInspectElement === 'boolean' ? options.showInspectElement : isDev; + function word(suggestion) { + return { + id: 'dictionarySuggestions', + label: suggestion, + visible: props.isEditable && hasText && props.misspelledWord, + click(menuItem) { + const target = webContents(win); + target.insertText(menuItem.label); + } + }; + } + + let dictionarySuggestions = []; + if (hasText && props.misspelledWord && props.dictionarySuggestions.length > 0) { + dictionarySuggestions = props.dictionarySuggestions.map(word); + } else { + dictionarySuggestions.push( + { + id: 'dictionarySuggestions', + label: 'No Guesses Found', + visible: hasText && props.misspelledWord, + enabled: false + } + ); + } + let menuTemplate = [ + defaultActions.separator(), + ...dictionarySuggestions, + defaultActions.separator(), + defaultActions.correctAutomatically(), + defaultActions.separator(), + defaultActions.learnSpelling(), defaultActions.separator(), options.showLookUpSelection !== false && defaultActions.lookUpSelection(), defaultActions.separator(), + options.showSearchWithGoogle !== false && defaultActions.searchWithGoogle(), + defaultActions.separator(), defaultActions.cut(), defaultActions.copy(), defaultActions.paste(), diff --git a/package.json b/package.json index fbbf97a..cea2415 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "devDependencies": { "@types/node": "^12.0.10", "ava": "^2.1.0", - "electron": "^7.1.1", + "electron": "^8.0.0", "tsd": "^0.10.0", "xo": "^0.25.3" }, diff --git a/readme.md b/readme.md index 5e7121a..c576f7a 100644 --- a/readme.md +++ b/readme.md @@ -43,7 +43,12 @@ contextMenu({ let mainWindow; (async () => { await app.whenReady(); - mainWindow = new BrowserWindow(); + + mainWindow = new BrowserWindow( + webPreferences: { + spellcheck: true + } + ); })(); ``` @@ -90,6 +95,13 @@ Default: `true` Show the `Look Up {selection}` menu item when right-clicking text on macOS. +#### showSearchWithGoogle + +Type: `boolean`\ +Default: `true` + +Show the `Search with Google` menu item when right-clicking text on macOS. + #### showCopyImage Type: `boolean`\ @@ -184,6 +196,8 @@ Even though you include an action, it will still only be shown/enabled when appr `MenuItem` labels may contain the the placeholder `{selection}` which will be replaced by the currently selected text as described in [`options.labels`](#labels). +To get spellchecking, “Correct Automatically”, and “Learn Spelling” in the menu, please enable the `spellcheck` preference in browser window: `new BrowserWindow({webPreferences: {spellcheck: true}})` + The following options are ignored when `menu` is used: - `showLookUpSelection` @@ -192,11 +206,16 @@ The following options are ignored when `menu` is used: - `showSaveImageAs` - `showInspectElement` - `showServices` - +- `showSearchWithGoogle` + Default actions: +- `spellCheck` +- `correctAutomatically` +- `learnSpelling` - `separator` - `lookUpSelection` +- `searchWithGoogle` - `cut` - `copy` - `paste` @@ -208,7 +227,7 @@ Default actions: - `inspect` - `services` -Example: +Example for actions: ```js {