aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Maa <jmaa@berkeley.edu>2024-06-10 10:59:54 -0700
committerGitHub <noreply@github.com>2024-06-10 17:59:54 +0000
commite24075e1a91ab8b58bb5836cf7abcefae5cbf8c3 (patch)
tree57881c2de6a9d75185a390741d404d61d5c9ce69
parent8eea3661714c64857aa32a8662f7cca6674dd3a4 (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.json3
-rw-r--r--ext/data/schemas/options-schema.json5
-rw-r--r--ext/js/app/frontend.js13
-rw-r--r--ext/js/background/backend.js15
-rw-r--r--ext/js/data/options-util.js11
-rw-r--r--ext/js/language/text-scanner.js11
-rw-r--r--ext/settings.html8
-rw-r--r--test/options-util.test.js3
-rw-r--r--types/ext/application.d.ts4
-rw-r--r--types/ext/settings.d.ts1
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;