diff options
| author | James Maa <jmaa@berkeley.edu> | 2024-06-10 10:59:54 -0700 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-10 17:59:54 +0000 | 
| commit | e24075e1a91ab8b58bb5836cf7abcefae5cbf8c3 (patch) | |
| tree | 57881c2de6a9d75185a390741d404d61d5c9ce69 | |
| parent | 8eea3661714c64857aa32a8662f7cca6674dd3a4 (diff) | |
Add context menu interface for Yomitan (#1028)
* --wip-- [skip ci]
* Draft
* Remove weird code
* Use existing API instead of dulpicating
* Small improvements
* remove console.log
* remove console.log
* Add setting for contextMenu
* Fix test
* Address comments
* Add option-util upgrade
* fix option-utils
| -rw-r--r-- | dev/data/manifest-variants.json | 3 | ||||
| -rw-r--r-- | ext/data/schemas/options-schema.json | 5 | ||||
| -rw-r--r-- | ext/js/app/frontend.js | 13 | ||||
| -rw-r--r-- | ext/js/background/backend.js | 15 | ||||
| -rw-r--r-- | ext/js/data/options-util.js | 11 | ||||
| -rw-r--r-- | ext/js/language/text-scanner.js | 11 | ||||
| -rw-r--r-- | ext/settings.html | 8 | ||||
| -rw-r--r-- | test/options-util.test.js | 3 | ||||
| -rw-r--r-- | types/ext/application.d.ts | 4 | ||||
| -rw-r--r-- | types/ext/settings.d.ts | 1 | 
10 files changed, 66 insertions, 8 deletions
| diff --git a/dev/data/manifest-variants.json b/dev/data/manifest-variants.json index 93841f46..3c299965 100644 --- a/dev/data/manifest-variants.json +++ b/dev/data/manifest-variants.json @@ -64,7 +64,8 @@              "unlimitedStorage",              "declarativeNetRequest",              "scripting", -            "offscreen" +            "offscreen", +            "contextMenus"          ],          "optional_permissions": [              "clipboardRead", diff --git a/ext/data/schemas/options-schema.json b/ext/data/schemas/options-schema.json index 5db4c087..1bbdf183 100644 --- a/ext/data/schemas/options-schema.json +++ b/ext/data/schemas/options-schema.json @@ -99,6 +99,7 @@                                      "popupScaleRelativeToPageZoom",                                      "popupScaleRelativeToVisualViewport",                                      "showGuide", +                                    "enableContextMenuScanSelected",                                      "compactTags",                                      "glossaryLayoutMode",                                      "mainDictionary", @@ -206,6 +207,10 @@                                          "type": "boolean",                                          "default": true                                      }, +                                    "enableContextMenuScanSelected": { +                                        "type": "boolean", +                                        "default": true +                                    },                                      "compactTags": {                                          "type": "boolean",                                          "default": false diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index 39176475..bdb8cfc5 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -115,6 +115,7 @@ export class Frontend {              ['frontendRequestReadyBroadcast',   this._onMessageRequestFrontendReadyBroadcast.bind(this)],              ['frontendSetAllVisibleOverride',   this._onApiSetAllVisibleOverride.bind(this)],              ['frontendClearAllVisibleOverride', this._onApiClearAllVisibleOverride.bind(this)], +            ['frontendScanSelectedText',        this._onApiScanSelectedText.bind(this)],          ]);          this._hotkeyHandler.registerActions([ @@ -263,6 +264,13 @@ export class Frontend {      /**       * @returns {void}       */ +    _onApiScanSelectedText() { +        void this._scanSelectedText(false, true, true); +    } + +    /** +     * @returns {void} +     */      _onActionScanTextAtSelection() {          void this._scanSelectedText(false, false);      } @@ -934,13 +942,14 @@ export class Frontend {      /**       * @param {boolean} allowEmptyRange       * @param {boolean} disallowExpandSelection +     * @param {boolean} showEmpty show empty popup if no results are found       * @returns {Promise<boolean>}       */ -    async _scanSelectedText(allowEmptyRange, disallowExpandSelection) { +    async _scanSelectedText(allowEmptyRange, disallowExpandSelection, showEmpty = false) {          const range = this._getFirstSelectionRange(allowEmptyRange);          if (range === null) { return false; }          const source = disallowExpandSelection ? TextSourceRange.createLazy(range) : TextSourceRange.create(range); -        await this._textScanner.search(source, {focus: true, restoreSelection: true}); +        await this._textScanner.search(source, {focus: true, restoreSelection: true}, showEmpty);          return true;      } diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index a04566c9..88912b70 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -1310,6 +1310,21 @@ export class Backend {              this._clipboardMonitor.stop();          } +        if (options.general.enableContextMenuScanSelected) { +            chrome.contextMenus.create({ +                id: 'yomitan_lookup', +                title: 'Lookup in Yomitan', +                contexts: ['selection'], +            }); +            chrome.contextMenus.onClicked.addListener((info) => { +                if (info.selectionText) { +                    this._sendMessageAllTabsIgnoreResponse({action: 'frontendScanSelectedText'}); +                } +            }); +        } else { +            chrome.contextMenus.remove('yomitan_lookup', () => this._checkLastError(chrome.runtime.lastError)); +        } +          void this._accessibilityController.update(this._getOptionsFull(false));          this._sendMessageAllTabsIgnoreResponse({action: 'applicationOptionsUpdated', params: {source}}); diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index 5ef30adb..c6bdf025 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -546,6 +546,7 @@ export class OptionsUtil {              this._updateVersion36,              this._updateVersion37,              this._updateVersion38, +            this._updateVersion39,          ];          /* eslint-enable @typescript-eslint/unbound-method */          if (typeof targetVersion === 'number' && targetVersion < result.length) { @@ -1314,6 +1315,16 @@ export class OptionsUtil {      }      /** +     *  - Add new setting enableContextMenuScanSelected +     *  @type {import('options-util').UpdateFunction} +     */ +    async _updateVersion39(options) { +        for (const profile of options.profiles) { +            profile.options.general.enableContextMenuScanSelected = true; +        } +    } + +    /**       * @param {string} url       * @returns {Promise<chrome.tabs.Tab>}       */ diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index aba44644..fdc33400 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -411,10 +411,11 @@ export class TextScanner extends EventDispatcher {      /**       * @param {import('text-source').TextSource} textSource       * @param {import('text-scanner').InputInfoDetail} [inputDetail] +     * @param {boolean} showEmpty       */ -    async search(textSource, inputDetail) { +    async search(textSource, inputDetail, showEmpty = false) {          const inputInfo = this._createInputInfo(null, 'script', 'script', true, [], [], inputDetail); -        await this._search(textSource, this._searchTerms, this._searchKanji, inputInfo); +        await this._search(textSource, this._searchTerms, this._searchKanji, inputInfo, showEmpty);      }      // Private @@ -437,8 +438,9 @@ export class TextScanner extends EventDispatcher {       * @param {boolean} searchTerms       * @param {boolean} searchKanji       * @param {import('text-scanner').InputInfo} inputInfo +     * @param {boolean} showEmpty shows a "No results found" popup if no results are found       */ -    async _search(textSource, searchTerms, searchKanji, inputInfo) { +    async _search(textSource, searchTerms, searchKanji, inputInfo, showEmpty = false) {          try {              const inputInfoDetail = inputInfo.detail;              const selectionRestoreInfo = ( @@ -465,7 +467,8 @@ export class TextScanner extends EventDispatcher {              const result = await this._findDictionaryEntries(textSource, searchTerms, searchKanji, optionsContext);              if (result !== null) {                  ({dictionaryEntries, sentence, type} = result); -            } else if (textSource !== null && textSource instanceof TextSourceElement && await this._isTextLookupWorthy(textSource.fullContent)) { +            } else if (showEmpty || (textSource !== null && textSource instanceof TextSourceElement && await this._isTextLookupWorthy(textSource.fullContent))) { +                // Shows a "No results found" message                  dictionaryEntries = [];                  sentence = {text: '', offset: 0};              } diff --git a/ext/settings.html b/ext/settings.html index 956e2664..0ea4653a 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -148,6 +148,14 @@                      <label class="toggle"><input type="checkbox" data-setting="general.showGuide"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>                  </div>              </div></div> +            <div class="settings-item advanced-only"><div class="settings-item-inner"> +                <div class="settings-item-left"> +                    <div class="settings-item-label">Show "Lookup in Yomitan" in right-click menu</div> +                </div> +                <div class="settings-item-right"> +                    <label class="toggle"><input type="checkbox" data-setting="general.enableContextMenuScanSelected"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label> +                </div> +            </div></div>              <div class="settings-item advanced-only"><div class="settings-item-inner settings-item-inner-wrappable">                  <div class="settings-item-left">                      <div class="settings-item-label">Maximum number of results</div> diff --git a/test/options-util.test.js b/test/options-util.test.js index af4daef9..54bae173 100644 --- a/test/options-util.test.js +++ b/test/options-util.test.js @@ -259,6 +259,7 @@ function createProfileOptionsUpdatedTestData1() {              popupScaleRelativeToPageZoom: false,              popupScaleRelativeToVisualViewport: true,              showGuide: true, +            enableContextMenuScanSelected: true,              compactTags: false,              glossaryLayoutMode: 'default',              mainDictionary: '', @@ -605,7 +606,7 @@ function createOptionsUpdatedTestData1() {              },          ],          profileCurrent: 0, -        version: 38, +        version: 39,          global: {              database: {                  prefixWildcardsSupported: false, diff --git a/types/ext/application.d.ts b/types/ext/application.d.ts index 8d80894d..8dc05da3 100644 --- a/types/ext/application.d.ts +++ b/types/ext/application.d.ts @@ -105,6 +105,10 @@ export type ApiSurface = {          };          return: void;      }; +    frontendScanSelectedText: { +        params: void; +        return: void; +    };      frameEndpointReady: {          params: FrameEndpointReadyDetails;          return: void; diff --git a/types/ext/settings.d.ts b/types/ext/settings.d.ts index b69679b0..80fb8a1f 100644 --- a/types/ext/settings.d.ts +++ b/types/ext/settings.d.ts @@ -119,6 +119,7 @@ export type GeneralOptions = {      popupScaleRelativeToPageZoom: boolean;      popupScaleRelativeToVisualViewport: boolean;      showGuide: boolean; +    enableContextMenuScanSelected: boolean;      compactTags: boolean;      glossaryLayoutMode: GlossaryLayoutMode;      mainDictionary: string; |