summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2023-11-27 17:43:53 -0500
committertoasted-nutbread <toasted-nutbread@users.noreply.github.com>2023-11-27 17:43:53 -0500
commit29317da4ea237557e1805a834913dad73c51ed8a (patch)
treee19ff6014774b42a95c22dbc7874b51d7bc3bf66
parenta96be7d413c8f1714f0d1b44b3987f2eb87b22d8 (diff)
Update offscreen
-rw-r--r--ext/js/background/offscreen-proxy.js137
-rw-r--r--ext/js/background/offscreen.js121
-rw-r--r--ext/js/language/translator.js4
-rw-r--r--types/ext/dictionary-database.d.ts2
-rw-r--r--types/ext/offscreen.d.ts115
-rw-r--r--types/ext/translator.d.ts12
6 files changed, 298 insertions, 93 deletions
diff --git a/ext/js/background/offscreen-proxy.js b/ext/js/background/offscreen-proxy.js
index 0fb2f269..757d78d5 100644
--- a/ext/js/background/offscreen-proxy.js
+++ b/ext/js/background/offscreen-proxy.js
@@ -18,13 +18,17 @@
import {isObject} from '../core.js';
import {ArrayBufferUtil} from '../data/sandbox/array-buffer-util.js';
+import {ExtensionError} from '../core/extension-error.js';
export class OffscreenProxy {
constructor() {
+ /** @type {?Promise<void>} */
this._creatingOffscreen = null;
}
- // https://developer.chrome.com/docs/extensions/reference/offscreen/
+ /**
+ * @see https://developer.chrome.com/docs/extensions/reference/offscreen/
+ */
async prepare() {
if (await this._hasOffscreenDocument()) {
return;
@@ -36,20 +40,30 @@ export class OffscreenProxy {
this._creatingOffscreen = chrome.offscreen.createDocument({
url: 'offscreen.html',
- reasons: ['CLIPBOARD'],
+ reasons: [
+ /** @type {chrome.offscreen.Reason} */ ('CLIPBOARD')
+ ],
justification: 'Access to the clipboard'
});
await this._creatingOffscreen;
this._creatingOffscreen = null;
}
+ /**
+ * @returns {Promise<boolean>}
+ */
async _hasOffscreenDocument() {
const offscreenUrl = chrome.runtime.getURL('offscreen.html');
- if (!chrome.runtime.getContexts) { // chrome version <116
+ // @ts-ignore - API not defined yet
+ if (!chrome.runtime.getContexts) { // chrome version below 116
+ // Clients: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/clients
+ // @ts-ignore - Types not set up for service workers yet
const matchedClients = await clients.matchAll();
+ // @ts-ignore - Types not set up for service workers yet
return await matchedClients.some((client) => client.url === offscreenUrl);
}
+ // @ts-ignore - API not defined yet
const contexts = await chrome.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
documentUrls: [offscreenUrl]
@@ -57,103 +71,152 @@ export class OffscreenProxy {
return !!contexts.length;
}
- sendMessagePromise(...args) {
+ /**
+ * @template {import('offscreen').MessageType} TMessageType
+ * @param {import('offscreen').Message<TMessageType>} message
+ * @returns {Promise<import('offscreen').MessageReturn<TMessageType>>}
+ */
+ sendMessagePromise(message) {
return new Promise((resolve, reject) => {
- const callback = (response) => {
+ chrome.runtime.sendMessage(message, (response) => {
try {
resolve(this._getMessageResponseResult(response));
} catch (error) {
reject(error);
}
- };
-
- chrome.runtime.sendMessage(...args, callback);
+ });
});
}
+ /**
+ * @template [TReturn=unknown]
+ * @param {import('core').Response<TReturn>} response
+ * @returns {TReturn}
+ * @throws {Error}
+ */
_getMessageResponseResult(response) {
- let error = chrome.runtime.lastError;
+ const error = chrome.runtime.lastError;
if (error) {
throw new Error(error.message);
}
if (!isObject(response)) {
throw new Error('Offscreen document did not respond');
}
- error = response.error;
- if (error) {
- throw deserializeError(error);
+ const error2 = response.error;
+ if (error2) {
+ throw ExtensionError.deserialize(error2);
}
return response.result;
}
}
export class DictionaryDatabaseProxy {
+ /**
+ * @param {OffscreenProxy} offscreen
+ */
constructor(offscreen) {
+ /** @type {OffscreenProxy} */
this._offscreen = offscreen;
}
- prepare() {
- return this._offscreen.sendMessagePromise({action: 'databasePrepareOffscreen'});
+ /**
+ * @returns {Promise<void>}
+ */
+ async prepare() {
+ await this._offscreen.sendMessagePromise({action: 'databasePrepareOffscreen'});
}
- getDictionaryInfo() {
+ /**
+ * @returns {Promise<import('dictionary-importer').Summary[]>}
+ */
+ async getDictionaryInfo() {
return this._offscreen.sendMessagePromise({action: 'getDictionaryInfoOffscreen'});
}
- purge() {
- return this._offscreen.sendMessagePromise({action: 'databasePurgeOffscreen'});
+ /**
+ * @returns {Promise<boolean>}
+ */
+ async purge() {
+ return await this._offscreen.sendMessagePromise({action: 'databasePurgeOffscreen'});
}
+ /**
+ * @param {import('dictionary-database').MediaRequest[]} targets
+ * @returns {Promise<import('dictionary-database').Media[]>}
+ */
async getMedia(targets) {
- const serializedMedia = await this._offscreen.sendMessagePromise({action: 'databaseGetMediaOffscreen', params: {targets}});
+ const serializedMedia = /** @type {import('dictionary-database').Media<string>[]} */ (await this._offscreen.sendMessagePromise({action: 'databaseGetMediaOffscreen', params: {targets}}));
const media = serializedMedia.map((m) => ({...m, content: ArrayBufferUtil.base64ToArrayBuffer(m.content)}));
return media;
}
}
export class TranslatorProxy {
+ /**
+ * @param {OffscreenProxy} offscreen
+ */
constructor(offscreen) {
+ /** @type {OffscreenProxy} */
this._offscreen = offscreen;
}
- prepare(deinflectionReasons) {
- return this._offscreen.sendMessagePromise({action: 'translatorPrepareOffscreen', params: {deinflectionReasons}});
+ /**
+ * @param {import('deinflector').ReasonsRaw} deinflectionReasons
+ */
+ async prepare(deinflectionReasons) {
+ await this._offscreen.sendMessagePromise({action: 'translatorPrepareOffscreen', params: {deinflectionReasons}});
}
- async findKanji(text, findKanjiOptions) {
- const enabledDictionaryMapList = [...findKanjiOptions.enabledDictionaryMap];
- const modifiedKanjiOptions = {
- ...findKanjiOptions,
+ /**
+ * @param {string} text
+ * @param {import('translation').FindKanjiOptions} options
+ * @returns {Promise<import('dictionary').KanjiDictionaryEntry[]>}
+ */
+ async findKanji(text, options) {
+ const enabledDictionaryMapList = [...options.enabledDictionaryMap];
+ /** @type {import('offscreen').FindKanjiOptionsOffscreen} */
+ const modifiedOptions = {
+ ...options,
enabledDictionaryMap: enabledDictionaryMapList
};
- return this._offscreen.sendMessagePromise({action: 'findKanjiOffscreen', params: {text, findKanjiOptions: modifiedKanjiOptions}});
+ return this._offscreen.sendMessagePromise({action: 'findKanjiOffscreen', params: {text, options: modifiedOptions}});
}
- async findTerms(mode, text, findTermsOptions) {
- const {enabledDictionaryMap, excludeDictionaryDefinitions, textReplacements} = findTermsOptions;
+ /**
+ * @param {import('translator').FindTermsMode} mode
+ * @param {string} text
+ * @param {import('translation').FindTermsOptions} options
+ * @returns {Promise<import('translator').FindTermsResult>}
+ */
+ async findTerms(mode, text, options) {
+ const {enabledDictionaryMap, excludeDictionaryDefinitions, textReplacements} = options;
const enabledDictionaryMapList = [...enabledDictionaryMap];
const excludeDictionaryDefinitionsList = excludeDictionaryDefinitions ? [...excludeDictionaryDefinitions] : null;
const textReplacementsSerialized = textReplacements.map((group) => {
- if (!group) {
- return group;
- }
- return group.map((opt) => ({...opt, pattern: opt.pattern.toString()}));
+ return group !== null ? group.map((opt) => ({...opt, pattern: opt.pattern.toString()})) : null;
});
- const modifiedFindTermsOptions = {
- ...findTermsOptions,
+ /** @type {import('offscreen').FindTermsOptionsOffscreen} */
+ const modifiedOptions = {
+ ...options,
enabledDictionaryMap: enabledDictionaryMapList,
excludeDictionaryDefinitions: excludeDictionaryDefinitionsList,
textReplacements: textReplacementsSerialized
};
- return this._offscreen.sendMessagePromise({action: 'findTermsOffscreen', params: {mode, text, findTermsOptions: modifiedFindTermsOptions}});
+ return this._offscreen.sendMessagePromise({action: 'findTermsOffscreen', params: {mode, text, options: modifiedOptions}});
}
+ /**
+ * @param {import('translator').TermReadingList} termReadingList
+ * @param {string[]} dictionaries
+ * @returns {Promise<import('translator').TermFrequencySimple[]>}
+ */
async getTermFrequencies(termReadingList, dictionaries) {
return this._offscreen.sendMessagePromise({action: 'getTermFrequenciesOffscreen', params: {termReadingList, dictionaries}});
}
- clearDatabaseCaches() {
- return this._offscreen.sendMessagePromise({action: 'clearDatabaseCachesOffscreen'});
+ /** */
+ async clearDatabaseCaches() {
+ await this._offscreen.sendMessagePromise({action: 'clearDatabaseCachesOffscreen'});
}
}
@@ -173,7 +236,7 @@ export class ClipboardReaderProxy {
set browser(value) {
if (this._browser === value) { return; }
this._browser = value;
- this._offscreen.sendMessagePromise({action: 'clipboardSetBrowserOffsecreen', params: {value}});
+ this._offscreen.sendMessagePromise({action: 'clipboardSetBrowserOffscreen', params: {value}});
}
/**
diff --git a/ext/js/background/offscreen.js b/ext/js/background/offscreen.js
index 6302aa84..1b68887b 100644
--- a/ext/js/background/offscreen.js
+++ b/ext/js/background/offscreen.js
@@ -23,7 +23,6 @@ import {ArrayBufferUtil} from '../data/sandbox/array-buffer-util.js';
import {DictionaryDatabase} from '../language/dictionary-database.js';
import {JapaneseUtil} from '../language/sandbox/japanese-util.js';
import {Translator} from '../language/translator.js';
-import {yomitan} from '../yomitan.js';
/**
* This class controls the core logic of the extension, including API calls
@@ -34,12 +33,16 @@ export class Offscreen {
* Creates a new instance.
*/
constructor() {
+ /** @type {JapaneseUtil} */
this._japaneseUtil = new JapaneseUtil(wanakana);
+ /** @type {DictionaryDatabase} */
this._dictionaryDatabase = new DictionaryDatabase();
+ /** @type {Translator} */
this._translator = new Translator({
japaneseUtil: this._japaneseUtil,
database: this._dictionaryDatabase
});
+ /** @type {ClipboardReader} */
this._clipboardReader = new ClipboardReader({
// eslint-disable-next-line no-undef
document: (typeof document === 'object' && document !== null ? document : null),
@@ -47,42 +50,45 @@ export class Offscreen {
richContentPasteTargetSelector: '#clipboard-rich-content-paste-target'
});
+ /** @type {import('offscreen').MessageHandlerMap} */
this._messageHandlers = new Map([
- ['clipboardGetTextOffscreen', {async: true, contentScript: true, handler: this._getTextHandler.bind(this)}],
- ['clipboardGetImageOffscreen', {async: true, contentScript: true, handler: this._getImageHandler.bind(this)}],
- ['clipboardSetBrowserOffsecreen', {async: false, contentScript: true, handler: this._setClipboardBrowser.bind(this)}],
- ['databasePrepareOffscreen', {async: true, contentScript: true, handler: this._prepareDatabaseHandler.bind(this)}],
- ['getDictionaryInfoOffscreen', {async: true, contentScript: true, handler: this._getDictionaryInfoHandler.bind(this)}],
- ['databasePurgeOffscreen', {async: true, contentScript: true, handler: this._purgeDatabaseHandler.bind(this)}],
- ['databaseGetMediaOffscreen', {async: true, contentScript: true, handler: this._getMediaHandler.bind(this)}],
- ['translatorPrepareOffscreen', {async: false, contentScript: true, handler: this._prepareTranslatorHandler.bind(this)}],
- ['findKanjiOffscreen', {async: true, contentScript: true, handler: this._findKanjiHandler.bind(this)}],
- ['findTermsOffscreen', {async: true, contentScript: true, handler: this._findTermsHandler.bind(this)}],
- ['getTermFrequenciesOffscreen', {async: true, contentScript: true, handler: this._getTermFrequenciesHandler.bind(this)}],
- ['clearDatabaseCachesOffscreen', {async: false, contentScript: true, handler: this._clearDatabaseCachesHandler.bind(this)}]
+ ['clipboardGetTextOffscreen', {async: true, handler: this._getTextHandler.bind(this)}],
+ ['clipboardGetImageOffscreen', {async: true, handler: this._getImageHandler.bind(this)}],
+ ['clipboardSetBrowserOffscreen', {async: false, handler: this._setClipboardBrowser.bind(this)}],
+ ['databasePrepareOffscreen', {async: true, handler: this._prepareDatabaseHandler.bind(this)}],
+ ['getDictionaryInfoOffscreen', {async: true, handler: this._getDictionaryInfoHandler.bind(this)}],
+ ['databasePurgeOffscreen', {async: true, handler: this._purgeDatabaseHandler.bind(this)}],
+ ['databaseGetMediaOffscreen', {async: true, handler: this._getMediaHandler.bind(this)}],
+ ['translatorPrepareOffscreen', {async: false, handler: this._prepareTranslatorHandler.bind(this)}],
+ ['findKanjiOffscreen', {async: true, handler: this._findKanjiHandler.bind(this)}],
+ ['findTermsOffscreen', {async: true, handler: this._findTermsHandler.bind(this)}],
+ ['getTermFrequenciesOffscreen', {async: true, handler: this._getTermFrequenciesHandler.bind(this)}],
+ ['clearDatabaseCachesOffscreen', {async: false, handler: this._clearDatabaseCachesHandler.bind(this)}]
]);
const onMessage = this._onMessage.bind(this);
chrome.runtime.onMessage.addListener(onMessage);
+ /** @type {?Promise<void>} */
this._prepareDatabasePromise = null;
}
- _getTextHandler({useRichText}) {
- return this._clipboardReader.getText(useRichText);
+ /** @type {import('offscreen').MessageHandler<'clipboardGetTextOffscreen', true>} */
+ async _getTextHandler({useRichText}) {
+ return await this._clipboardReader.getText(useRichText);
}
- _getImageHandler() {
- return this._clipboardReader.getImage();
+ /** @type {import('offscreen').MessageHandler<'clipboardGetImageOffscreen', true>} */
+ async _getImageHandler() {
+ return await this._clipboardReader.getImage();
}
- /**
- * @param {{value: import('environment').Browser}} details
- */
+ /** @type {import('offscreen').MessageHandler<'clipboardSetBrowserOffscreen', false>} */
_setClipboardBrowser({value}) {
this._clipboardReader.browser = value;
}
+ /** @type {import('offscreen').MessageHandler<'databasePrepareOffscreen', true>} */
_prepareDatabaseHandler() {
if (this._prepareDatabasePromise !== null) {
return this._prepareDatabasePromise;
@@ -91,70 +97,79 @@ export class Offscreen {
return this._prepareDatabasePromise;
}
- _getDictionaryInfoHandler() {
- return this._dictionaryDatabase.getDictionaryInfo();
+ /** @type {import('offscreen').MessageHandler<'getDictionaryInfoOffscreen', true>} */
+ async _getDictionaryInfoHandler() {
+ return await this._dictionaryDatabase.getDictionaryInfo();
}
- _purgeDatabaseHandler() {
- return this._dictionaryDatabase.purge();
+ /** @type {import('offscreen').MessageHandler<'databasePurgeOffscreen', true>} */
+ async _purgeDatabaseHandler() {
+ return await this._dictionaryDatabase.purge();
}
+ /** @type {import('offscreen').MessageHandler<'databaseGetMediaOffscreen', true>} */
async _getMediaHandler({targets}) {
const media = await this._dictionaryDatabase.getMedia(targets);
const serializedMedia = media.map((m) => ({...m, content: ArrayBufferUtil.arrayBufferToBase64(m.content)}));
return serializedMedia;
}
+ /** @type {import('offscreen').MessageHandler<'translatorPrepareOffscreen', false>} */
_prepareTranslatorHandler({deinflectionReasons}) {
- return this._translator.prepare(deinflectionReasons);
+ this._translator.prepare(deinflectionReasons);
}
- _findKanjiHandler({text, findKanjiOptions}) {
- findKanjiOptions.enabledDictionaryMap = new Map(findKanjiOptions.enabledDictionaryMap);
- return this._translator.findKanji(text, findKanjiOptions);
+ /** @type {import('offscreen').MessageHandler<'findKanjiOffscreen', true>} */
+ async _findKanjiHandler({text, options}) {
+ /** @type {import('translation').FindKanjiOptions} */
+ const modifiedOptions = {
+ ...options,
+ enabledDictionaryMap: new Map(options.enabledDictionaryMap)
+ };
+ return await this._translator.findKanji(text, modifiedOptions);
}
- _findTermsHandler({mode, text, findTermsOptions}) {
- findTermsOptions.enabledDictionaryMap = new Map(findTermsOptions.enabledDictionaryMap);
- if (findTermsOptions.excludeDictionaryDefinitions) {
- findTermsOptions.excludeDictionaryDefinitions = new Set(findTermsOptions.excludeDictionaryDefinitions);
- }
- findTermsOptions.textReplacements = findTermsOptions.textReplacements.map((group) => {
- if (!group) {
- return group;
- }
+ /** @type {import('offscreen').MessageHandler<'findTermsOffscreen', true>} */
+ _findTermsHandler({mode, text, options}) {
+ const enabledDictionaryMap = new Map(options.enabledDictionaryMap);
+ const excludeDictionaryDefinitions = (
+ options.excludeDictionaryDefinitions !== null ?
+ new Set(options.excludeDictionaryDefinitions) :
+ null
+ );
+ const textReplacements = options.textReplacements.map((group) => {
+ if (group === null) { return null; }
return group.map((opt) => {
- const [, pattern, flags] = opt.pattern.match(/\/(.*?)\/([a-z]*)?$/i); // https://stackoverflow.com/a/33642463
+ // https://stackoverflow.com/a/33642463
+ const match = opt.pattern.match(/\/(.*?)\/([a-z]*)?$/i);
+ const [, pattern, flags] = match !== null ? match : ['', '', ''];
return {...opt, pattern: new RegExp(pattern, flags ?? '')};
});
});
- return this._translator.findTerms(mode, text, findTermsOptions);
+ /** @type {import('translation').FindTermsOptions} */
+ const modifiedOptions = {
+ ...options,
+ enabledDictionaryMap,
+ excludeDictionaryDefinitions,
+ textReplacements
+ };
+ return this._translator.findTerms(mode, text, modifiedOptions);
}
+ /** @type {import('offscreen').MessageHandler<'getTermFrequenciesOffscreen', true>} */
_getTermFrequenciesHandler({termReadingList, dictionaries}) {
return this._translator.getTermFrequencies(termReadingList, dictionaries);
}
+ /** @type {import('offscreen').MessageHandler<'clearDatabaseCachesOffscreen', false>} */
_clearDatabaseCachesHandler() {
- return this._translator.clearDatabaseCaches();
+ this._translator.clearDatabaseCaches();
}
+ /** @type {import('extension').ChromeRuntimeOnMessageCallback} */
_onMessage({action, params}, sender, callback) {
const messageHandler = this._messageHandlers.get(action);
if (typeof messageHandler === 'undefined') { return false; }
- this._validatePrivilegedMessageSender(sender);
-
return invokeMessageHandler(messageHandler, params, callback, sender);
}
-
- _validatePrivilegedMessageSender(sender) {
- let {url} = sender;
- if (typeof url === 'string' && yomitan.isExtensionUrl(url)) { return; }
- const {tab} = url;
- if (typeof tab === 'object' && tab !== null) {
- ({url} = tab);
- if (typeof url === 'string' && yomitan.isExtensionUrl(url)) { return; }
- }
- throw new Error('Invalid message sender');
- }
}
diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js
index c21b16b1..aa1b71dd 100644
--- a/ext/js/language/translator.js
+++ b/ext/js/language/translator.js
@@ -157,7 +157,7 @@ export class Translator {
/**
* Gets a list of frequency information for a given list of term-reading pairs
* and a list of dictionaries.
- * @param {{term: string, reading: string|null}[]} termReadingList An array of `{term, reading}` pairs. If reading is null,
+ * @param {import('translator').TermReadingList} termReadingList An array of `{term, reading}` pairs. If reading is null,
* the reading won't be compared.
* @param {string[]} dictionaries An array of dictionary names.
* @returns {Promise<import('translator').TermFrequencySimple[]>} An array of term frequencies.
@@ -203,7 +203,7 @@ export class Translator {
* @param {Map<string, import('translation').FindTermDictionary>} enabledDictionaryMap
* @param {import('translation').FindTermsOptions} options
* @param {TranslatorTagAggregator} tagAggregator
- * @returns {Promise<{dictionaryEntries: import('dictionary').TermDictionaryEntry[], originalTextLength: number}>}
+ * @returns {Promise<import('translator').FindTermsResult>}
*/
async _findTermsInternal(text, enabledDictionaryMap, options, tagAggregator) {
if (options.removeNonJapaneseCharacters) {
diff --git a/types/ext/dictionary-database.d.ts b/types/ext/dictionary-database.d.ts
index 2e0f854b..06a246e8 100644
--- a/types/ext/dictionary-database.d.ts
+++ b/types/ext/dictionary-database.d.ts
@@ -35,7 +35,7 @@ export interface MediaDataBase<TContentType = unknown> {
export interface MediaDataArrayBufferContent extends MediaDataBase<ArrayBuffer> {}
export interface MediaDataStringContent extends MediaDataBase<string> {}
-export type Media = {index: number} & MediaDataBase<ArrayBuffer>;
+export type Media<T extends (ArrayBuffer | string) = ArrayBuffer> = {index: number} & MediaDataBase<T>;
export type DatabaseTermEntry = {
expression: string;
diff --git a/types/ext/offscreen.d.ts b/types/ext/offscreen.d.ts
new file mode 100644
index 00000000..7dd64d1e
--- /dev/null
+++ b/types/ext/offscreen.d.ts
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 Yomitan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import type * as Core from './core';
+import type * as Deinflector from './deinflector';
+import type * as Dictionary from './dictionary';
+import type * as DictionaryDatabase from './dictionary-database';
+import type * as DictionaryImporter from './dictionary-importer';
+import type * as Environment from './environment';
+import type * as Translation from './translation';
+import type * as Translator from './translator';
+
+export type MessageAny2 = Message<keyof MessageDetailsMap>;
+
+export type Message<T extends MessageType> = (
+ MessageDetailsMap[T] extends undefined ?
+ {action: T} :
+ {action: T, params: MessageDetailsMap[T]}
+);
+
+export type MessageReturn<T extends MessageType> = MessageReturnMap[T];
+
+type MessageDetailsMap = {
+ databasePrepareOffscreen: undefined;
+ getDictionaryInfoOffscreen: undefined;
+ databasePurgeOffscreen: undefined;
+ databaseGetMediaOffscreen: {
+ targets: DictionaryDatabase.MediaRequest[];
+ };
+ translatorPrepareOffscreen: {
+ deinflectionReasons: Deinflector.ReasonsRaw;
+ };
+ findKanjiOffscreen: {
+ text: string;
+ options: FindKanjiOptionsOffscreen;
+ };
+ findTermsOffscreen: {
+ mode: Translator.FindTermsMode;
+ text: string;
+ options: FindTermsOptionsOffscreen;
+ };
+ getTermFrequenciesOffscreen: {
+ termReadingList: Translator.TermReadingList;
+ dictionaries: string[];
+ };
+ clearDatabaseCachesOffscreen: undefined;
+ clipboardSetBrowserOffscreen: {
+ value: Environment.Browser | null;
+ };
+ clipboardGetTextOffscreen: {
+ useRichText: boolean;
+ };
+ clipboardGetImageOffscreen: undefined;
+};
+
+type MessageReturnMap = {
+ databasePrepareOffscreen: void;
+ getDictionaryInfoOffscreen: DictionaryImporter.Summary[];
+ databasePurgeOffscreen: boolean;
+ databaseGetMediaOffscreen: DictionaryDatabase.Media<string>[];
+ translatorPrepareOffscreen: void;
+ findKanjiOffscreen: Dictionary.KanjiDictionaryEntry[];
+ findTermsOffscreen: Translator.FindTermsResult;
+ getTermFrequenciesOffscreen: Translator.TermFrequencySimple[];
+ clearDatabaseCachesOffscreen: void;
+ clipboardSetBrowserOffscreen: void;
+ clipboardGetTextOffscreen: string;
+ clipboardGetImageOffscreen: string | null;
+};
+
+export type MessageType = keyof MessageDetailsMap;
+
+export type FindKanjiOptionsOffscreen = Omit<Translation.FindKanjiOptions, 'enabledDictionaryMap'> & {
+ enabledDictionaryMap: [
+ key: string,
+ options: Translation.FindKanjiDictionary,
+ ][];
+};
+
+
+export type FindTermsOptionsOffscreen = Omit<Translation.FindTermsOptions, 'enabledDictionaryMap' | 'excludeDictionaryDefinitions' | 'textReplacements'> & {
+ enabledDictionaryMap: [
+ key: string,
+ options: Translation.FindTermDictionary,
+ ][];
+ excludeDictionaryDefinitions: string[] | null;
+ textReplacements: (FindTermsTextReplacementOffscreen[] | null)[];
+};
+
+export type FindTermsTextReplacementOffscreen = Omit<Translation.FindTermsTextReplacement, 'pattern'> & {
+ pattern: string;
+};
+
+export type MessageHandler<
+ TMessage extends MessageType,
+ TIsAsync extends boolean,
+> = (
+ details: MessageDetailsMap[TMessage],
+) => (TIsAsync extends true ? Promise<MessageReturn<TMessage>> : MessageReturn<TMessage>);
+
+export type MessageHandlerMap = Map<MessageType, Core.MessageHandlerDetails>;
diff --git a/types/ext/translator.d.ts b/types/ext/translator.d.ts
index f17b3bf6..e7d45295 100644
--- a/types/ext/translator.d.ts
+++ b/types/ext/translator.d.ts
@@ -81,3 +81,15 @@ export type SequenceQuery = {
};
export type FindTermsMode = 'simple' | 'group' | 'merge' | 'split';
+
+export type TermReadingItem = {
+ term: string;
+ reading: string | null;
+};
+
+export type TermReadingList = TermReadingItem[];
+
+export type FindTermsResult = {
+ dictionaryEntries: Dictionary.TermDictionaryEntry[];
+ originalTextLength: number;
+};