diff options
-rw-r--r-- | ext/js/accessibility/google-docs.js | 11 | ||||
-rw-r--r-- | ext/js/background/backend.js | 103 | ||||
-rw-r--r-- | ext/js/comm/api.js | 337 | ||||
-rw-r--r-- | ext/js/display/query-parser.js | 4 | ||||
-rw-r--r-- | types/ext/api.d.ts | 672 |
5 files changed, 514 insertions, 613 deletions
diff --git a/ext/js/accessibility/google-docs.js b/ext/js/accessibility/google-docs.js index 27841b6d..4bc398ff 100644 --- a/ext/js/accessibility/google-docs.js +++ b/ext/js/accessibility/google-docs.js @@ -24,10 +24,11 @@ self.googleDocsAccessibilitySetup = true; /** - * @template [TReturn=unknown] - * @param {string} action - * @param {import('core').SerializableObject} params - * @returns {Promise<TReturn>} + * @template {import('api').ApiNames} TAction + * @template {import('api').ApiParams<TAction>} TParams + * @param {TAction} action + * @param {TParams} params + * @returns {Promise<import('api').ApiReturn<TAction>>} */ const invokeApi = (action, params) => { return new Promise((resolve, reject) => { @@ -45,7 +46,7 @@ }; const optionsContext = {depth: 0, url: location.href}; - /** @type {import('api').OptionsGetResult} */ + /** @type {import('api').ApiReturn<'optionsGet'>} */ let options; try { options = await invokeApi('optionsGet', {optionsContext}); diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index c62685b2..f7cad1e6 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -22,7 +22,8 @@ import {AnkiConnect} from '../comm/anki-connect.js'; import {ClipboardMonitor} from '../comm/clipboard-monitor.js'; import {ClipboardReader} from '../comm/clipboard-reader.js'; import {Mecab} from '../comm/mecab.js'; -import {clone, deferPromise, invokeMessageHandler, isObject, log, promiseTimeout} from '../core.js'; +import {clone, deferPromise, isObject, log, promiseTimeout} from '../core.js'; +import {createApiMap, invokeApiMapHandler} from '../core/api-map.js'; import {ExtensionError} from '../core/extension-error.js'; import {readResponseJson} from '../core/json.js'; import {AnkiUtil} from '../data/anki-util.js'; @@ -146,8 +147,8 @@ export class Backend { this._permissionsUtil = new PermissionsUtil(); /* eslint-disable no-multi-spaces */ - /** @type {import('core').MessageHandlerMap} */ - this._messageHandlers = new Map(/** @type {import('core').MessageHandlerMapInit} */ ([ + /** @type {import('api').ApiMap} */ + this._apiMap = createApiMap([ ['requestBackendReadySignal', this._onApiRequestBackendReadySignal.bind(this)], ['optionsGet', this._onApiOptionsGet.bind(this)], ['optionsGetFull', this._onApiOptionsGetFull.bind(this)], @@ -190,7 +191,7 @@ export class Backend { ['findAnkiNotes', this._onApiFindAnkiNotes.bind(this)], ['loadExtensionScripts', this._onApiLoadExtensionScripts.bind(this)], ['openCrossFramePort', this._onApiOpenCrossFramePort.bind(this)] - ])); + ]); /* eslint-enable no-multi-spaces */ /** @type {Map<string, (params?: import('core').SerializableObject) => void>} */ @@ -369,7 +370,7 @@ export class Backend { }); } - /** @type {import('extension').ChromeRuntimeOnMessageCallback} */ + /** @type {import('extension').ChromeRuntimeOnMessageCallback<import('api').MessageAny>} */ _onMessageWrapper(message, sender, sendResponse) { if (this._isPrepared) { return this._onMessage(message, sender, sendResponse); @@ -392,15 +393,13 @@ export class Backend { } /** - * @param {{action: string, params?: import('core').SerializableObject}} message + * @param {import('api').MessageAny} message * @param {chrome.runtime.MessageSender} sender * @param {(response?: unknown) => void} callback * @returns {boolean} */ _onMessage({action, params}, sender, callback) { - const messageHandler = this._messageHandlers.get(action); - if (typeof messageHandler === 'undefined') { return false; } - return invokeMessageHandler(messageHandler, params, callback, sender); + return invokeApiMapHandler(this._apiMap, action, params, [sender], callback); } /** @@ -427,7 +426,7 @@ export class Backend { // Message handlers - /** @type {import('api').Handler<import('api').RequestBackendReadySignalDetails, import('api').RequestBackendReadySignalResult, true>} */ + /** @type {import('api').ApiHandler<'requestBackendReadySignal'>} */ _onApiRequestBackendReadySignal(_params, sender) { // tab ID isn't set in background (e.g. browser_action) const data = {action: 'Yomitan.backendReady', params: {}}; @@ -443,17 +442,17 @@ export class Backend { } } - /** @type {import('api').Handler<import('api').OptionsGetDetails, import('api').OptionsGetResult>} */ + /** @type {import('api').ApiHandler<'optionsGet'>} */ _onApiOptionsGet({optionsContext}) { return this._getProfileOptions(optionsContext, false); } - /** @type {import('api').Handler<import('api').OptionsGetFullDetails, import('api').OptionsGetFullResult>} */ + /** @type {import('api').ApiHandler<'optionsGetFull'>} */ _onApiOptionsGetFull() { return this._getOptionsFull(false); } - /** @type {import('api').Handler<import('api').KanjiFindDetails, import('api').KanjiFindResult>} */ + /** @type {import('api').ApiHandler<'kanjiFind'>} */ async _onApiKanjiFind({text, optionsContext}) { const options = this._getProfileOptions(optionsContext, false); const {general: {maxResults}} = options; @@ -463,7 +462,7 @@ export class Backend { return dictionaryEntries; } - /** @type {import('api').Handler<import('api').TermsFindDetails, import('api').TermsFindResult>} */ + /** @type {import('api').ApiHandler<'termsFind'>} */ async _onApiTermsFind({text, details, optionsContext}) { const options = this._getProfileOptions(optionsContext, false); const {general: {resultOutputMode: mode, maxResults}} = options; @@ -473,7 +472,7 @@ export class Backend { return {dictionaryEntries, originalTextLength}; } - /** @type {import('api').Handler<import('api').ParseTextDetails, import('api').ParseTextResult>} */ + /** @type {import('api').ApiHandler<'parseText'>} */ async _onApiParseText({text, optionsContext, scanLength, useInternalParser, useMecabParser}) { const [internalResults, mecabResults] = await Promise.all([ (useInternalParser ? this._textParseScanning(text, scanLength, optionsContext) : null), @@ -506,22 +505,22 @@ export class Backend { return results; } - /** @type {import('api').Handler<import('api').GetAnkiConnectVersionDetails, import('api').GetAnkiConnectVersionResult>} */ + /** @type {import('api').ApiHandler<'getAnkiConnectVersion'>} */ async _onApiGetAnkiConnectVersion() { return await this._anki.getVersion(); } - /** @type {import('api').Handler<import('api').IsAnkiConnectedDetails, import('api').IsAnkiConnectedResult>} */ + /** @type {import('api').ApiHandler<'isAnkiConnected'>} */ async _onApiIsAnkiConnected() { return await this._anki.isConnected(); } - /** @type {import('api').Handler<import('api').AddAnkiNoteDetails, import('api').AddAnkiNoteResult>} */ + /** @type {import('api').ApiHandler<'addAnkiNote'>} */ async _onApiAddAnkiNote({note}) { return await this._anki.addNote(note); } - /** @type {import('api').Handler<import('api').GetAnkiNoteInfoDetails, import('api').GetAnkiNoteInfoResult>} */ + /** @type {import('api').ApiHandler<'getAnkiNoteInfo'>} */ async _onApiGetAnkiNoteInfo({notes, fetchAdditionalInfo}) { /** @type {import('anki').NoteInfoWrapper[]} */ const results = []; @@ -558,7 +557,7 @@ export class Backend { return results; } - /** @type {import('api').Handler<import('api').InjectAnkiNoteMediaDetails, import('api').InjectAnkiNoteMediaResult>} */ + /** @type {import('api').ApiHandler<'injectAnkiNoteMedia'>} */ async _onApiInjectAnkiNoteMedia({timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails}) { return await this._injectAnkNoteMedia( this._anki, @@ -571,7 +570,7 @@ export class Backend { ); } - /** @type {import('api').Handler<import('api').NoteViewDetails, import('api').NoteViewResult>} */ + /** @type {import('api').ApiHandler<'noteView'>} */ async _onApiNoteView({noteId, mode, allowFallback}) { if (mode === 'edit') { try { @@ -590,7 +589,7 @@ export class Backend { return 'browse'; } - /** @type {import('api').Handler<import('api').SuspendAnkiCardsForNoteDetails, import('api').SuspendAnkiCardsForNoteResult>} */ + /** @type {import('api').ApiHandler<'suspendAnkiCardsForNote'>} */ async _onApiSuspendAnkiCardsForNote({noteId}) { const cardIds = await this._anki.findCardsForNote(noteId); const count = cardIds.length; @@ -601,17 +600,17 @@ export class Backend { return count; } - /** @type {import('api').Handler<import('api').CommandExecDetails, import('api').CommandExecResult>} */ + /** @type {import('api').ApiHandler<'commandExec'>} */ _onApiCommandExec({command, params}) { return this._runCommand(command, params); } - /** @type {import('api').Handler<import('api').GetTermAudioInfoListDetails, import('api').GetTermAudioInfoListResult>} */ + /** @type {import('api').ApiHandler<'getTermAudioInfoList'>} */ async _onApiGetTermAudioInfoList({source, term, reading}) { return await this._audioDownloader.getTermAudioInfoList(source, term, reading); } - /** @type {import('api').Handler<import('api').SendMessageToFrameDetails, import('api').SendMessageToFrameResult, true>} */ + /** @type {import('api').ApiHandler<'sendMessageToFrame'>} */ _onApiSendMessageToFrame({frameId: targetFrameId, action, params}, sender) { if (!sender) { return false; } const {tab} = sender; @@ -625,7 +624,7 @@ export class Backend { return true; } - /** @type {import('api').Handler<import('api').BroadcastTabDetails, import('api').BroadcastTabResult, true>} */ + /** @type {import('api').ApiHandler<'broadcastTab'>} */ _onApiBroadcastTab({action, params}, sender) { if (!sender) { return false; } const {tab} = sender; @@ -639,7 +638,7 @@ export class Backend { return true; } - /** @type {import('api').Handler<import('api').FrameInformationGetDetails, import('api').FrameInformationGetResult, true>} */ + /** @type {import('api').ApiHandler<'frameInformationGet'>} */ _onApiFrameInformationGet(_params, sender) { const tab = sender.tab; const tabId = tab ? tab.id : void 0; @@ -647,14 +646,14 @@ export class Backend { return Promise.resolve({tabId, frameId}); } - /** @type {import('api').Handler<import('api').InjectStylesheetDetails, import('api').InjectStylesheetResult, true>} */ + /** @type {import('api').ApiHandler<'injectStylesheet'>} */ async _onApiInjectStylesheet({type, value}, sender) { const {frameId, tab} = sender; if (typeof tab !== 'object' || tab === null || typeof tab.id !== 'number') { throw new Error('Invalid tab'); } return await this._scriptManager.injectStylesheet(type, value, tab.id, frameId, false); } - /** @type {import('api').Handler<import('api').GetStylesheetContentDetails, import('api').GetStylesheetContentResult>} */ + /** @type {import('api').ApiHandler<'getStylesheetContent'>} */ async _onApiGetStylesheetContent({url}) { if (!url.startsWith('/') || url.startsWith('//') || !url.endsWith('.css')) { throw new Error('Invalid URL'); @@ -662,22 +661,22 @@ export class Backend { return await this._fetchText(url); } - /** @type {import('api').Handler<import('api').GetEnvironmentInfoDetails, import('api').GetEnvironmentInfoResult>} */ + /** @type {import('api').ApiHandler<'getEnvironmentInfo'>} */ _onApiGetEnvironmentInfo() { return this._environment.getInfo(); } - /** @type {import('api').Handler<import('api').ClipboardGetDetails, import('api').ClipboardGetResult>} */ + /** @type {import('api').ApiHandler<'clipboardGet'>} */ async _onApiClipboardGet() { return this._clipboardReader.getText(false); } - /** @type {import('api').Handler<import('api').GetDisplayTemplatesHtmlDetails, import('api').GetDisplayTemplatesHtmlResult>} */ + /** @type {import('api').ApiHandler<'getDisplayTemplatesHtml'>} */ async _onApiGetDisplayTemplatesHtml() { return await this._fetchText('/display-templates.html'); } - /** @type {import('api').Handler<import('api').GetZoomDetails, import('api').GetZoomResult, true>} */ + /** @type {import('api').ApiHandler<'getZoom'>} */ _onApiGetZoom(_params, sender) { return new Promise((resolve, reject) => { if (!sender || !sender.tab) { @@ -707,45 +706,45 @@ export class Backend { }); } - /** @type {import('api').Handler<import('api').GetDefaultAnkiFieldTemplatesDetails, import('api').GetDefaultAnkiFieldTemplatesResult>} */ + /** @type {import('api').ApiHandler<'getDefaultAnkiFieldTemplates'>} */ _onApiGetDefaultAnkiFieldTemplates() { return /** @type {string} */ (this._defaultAnkiFieldTemplates); } - /** @type {import('api').Handler<import('api').GetDictionaryInfoDetails, import('api').GetDictionaryInfoResult>} */ + /** @type {import('api').ApiHandler<'getDictionaryInfo'>} */ async _onApiGetDictionaryInfo() { return await this._dictionaryDatabase.getDictionaryInfo(); } - /** @type {import('api').Handler<import('api').PurgeDatabaseDetails, import('api').PurgeDatabaseResult>} */ + /** @type {import('api').ApiHandler<'purgeDatabase'>} */ async _onApiPurgeDatabase() { await this._dictionaryDatabase.purge(); this._triggerDatabaseUpdated('dictionary', 'purge'); } - /** @type {import('api').Handler<import('api').GetMediaDetails, import('api').GetMediaResult>} */ + /** @type {import('api').ApiHandler<'getMedia'>} */ async _onApiGetMedia({targets}) { return await this._getNormalizedDictionaryDatabaseMedia(targets); } - /** @type {import('api').Handler<import('api').LogDetails, import('api').LogResult>} */ + /** @type {import('api').ApiHandler<'log'>} */ _onApiLog({error, level, context}) { log.log(ExtensionError.deserialize(error), level, context); } - /** @type {import('api').Handler<import('api').LogIndicatorClearDetails, import('api').LogIndicatorClearResult>} */ + /** @type {import('api').ApiHandler<'logIndicatorClear'>} */ _onApiLogIndicatorClear() { if (this._logErrorLevel === null) { return; } this._logErrorLevel = null; this._updateBadge(); } - /** @type {import('api').Handler<import('api').ModifySettingsDetails, import('api').ModifySettingsResult>} */ + /** @type {import('api').ApiHandler<'modifySettings'>} */ _onApiModifySettings({targets, source}) { return this._modifySettings(targets, source); } - /** @type {import('api').Handler<import('api').GetSettingsDetails, import('api').GetSettingsResult>} */ + /** @type {import('api').ApiHandler<'getSettings'>} */ _onApiGetSettings({targets}) { const results = []; for (const target of targets) { @@ -759,14 +758,14 @@ export class Backend { return results; } - /** @type {import('api').Handler<import('api').SetAllSettingsDetails, import('api').SetAllSettingsResult>} */ + /** @type {import('api').ApiHandler<'setAllSettings'>} */ async _onApiSetAllSettings({value, source}) { this._optionsUtil.validate(value); this._options = clone(value); await this._saveOptions(source); } - /** @type {import('api').Handler<import('api').GetOrCreateSearchPopupDetails, import('api').GetOrCreateSearchPopupResult>} */ + /** @type {import('api').ApiHandlerNoExtraArgs<'getOrCreateSearchPopup'>} */ async _onApiGetOrCreateSearchPopup({focus = false, text}) { const {tab, created} = await this._getOrCreateSearchPopupWrapper(); if (focus === true || (focus === 'ifCreated' && created)) { @@ -782,19 +781,19 @@ export class Backend { return {tabId: typeof id === 'number' ? id : null, windowId: tab.windowId}; } - /** @type {import('api').Handler<import('api').IsTabSearchPopupDetails, import('api').IsTabSearchPopupResult>} */ + /** @type {import('api').ApiHandler<'isTabSearchPopup'>} */ async _onApiIsTabSearchPopup({tabId}) { const baseUrl = chrome.runtime.getURL('/search.html'); const tab = typeof tabId === 'number' ? await this._checkTabUrl(tabId, (url) => url !== null && url.startsWith(baseUrl)) : null; return (tab !== null); } - /** @type {import('api').Handler<import('api').TriggerDatabaseUpdatedDetails, import('api').TriggerDatabaseUpdatedResult>} */ + /** @type {import('api').ApiHandler<'triggerDatabaseUpdated'>} */ _onApiTriggerDatabaseUpdated({type, cause}) { this._triggerDatabaseUpdated(type, cause); } - /** @type {import('api').Handler<import('api').TestMecabDetails, import('api').TestMecabResult>} */ + /** @type {import('api').ApiHandler<'testMecab'>} */ async _onApiTestMecab() { if (!this._mecab.isEnabled()) { throw new Error('MeCab not enabled'); @@ -831,22 +830,22 @@ export class Backend { return true; } - /** @type {import('api').Handler<import('api').TextHasJapaneseCharactersDetails, import('api').TextHasJapaneseCharactersResult>} */ + /** @type {import('api').ApiHandler<'textHasJapaneseCharacters'>} */ _onApiTextHasJapaneseCharacters({text}) { return this._japaneseUtil.isStringPartiallyJapanese(text); } - /** @type {import('api').Handler<import('api').GetTermFrequenciesDetails, import('api').GetTermFrequenciesResult>} */ + /** @type {import('api').ApiHandler<'getTermFrequencies'>} */ async _onApiGetTermFrequencies({termReadingList, dictionaries}) { return await this._translator.getTermFrequencies(termReadingList, dictionaries); } - /** @type {import('api').Handler<import('api').FindAnkiNotesDetails, import('api').FindAnkiNotesResult>} */ + /** @type {import('api').ApiHandler<'findAnkiNotes'>} */ async _onApiFindAnkiNotes({query}) { return await this._anki.findNotes(query); } - /** @type {import('api').Handler<import('api').LoadExtensionScriptsDetails, import('api').LoadExtensionScriptsResult, true>} */ + /** @type {import('api').ApiHandler<'loadExtensionScripts'>} */ async _onApiLoadExtensionScripts({files}, sender) { if (!sender || !sender.tab) { throw new Error('Invalid sender'); } const tabId = sender.tab.id; @@ -857,7 +856,7 @@ export class Backend { } } - /** @type {import('api').Handler<import('api').OpenCrossFramePortDetails, import('api').OpenCrossFramePortResult, true>} */ + /** @type {import('api').ApiHandler<'openCrossFramePort'>} */ _onApiOpenCrossFramePort({targetTabId, targetFrameId}, sender) { const sourceTabId = (sender && sender.tab ? sender.tab.id : null); if (typeof sourceTabId !== 'number') { @@ -2062,7 +2061,7 @@ export class Backend { * @param {?import('api').InjectAnkiNoteMediaScreenshotDetails} screenshotDetails * @param {?import('api').InjectAnkiNoteMediaClipboardDetails} clipboardDetails * @param {import('api').InjectAnkiNoteMediaDictionaryMediaDetails[]} dictionaryMediaDetails - * @returns {Promise<import('api').InjectAnkiNoteMediaResult>} + * @returns {Promise<import('api').ApiReturn<'injectAnkiNoteMedia'>>} */ async _injectAnkNoteMedia(ankiConnect, timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails) { let screenshotFileName = null; diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index de19650d..f2d4d545 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -28,395 +28,344 @@ export class API { } /** - * @param {import('api').OptionsGetDetails['optionsContext']} optionsContext - * @returns {Promise<import('api').OptionsGetResult>} + * @param {import('api').ApiParam<'optionsGet', 'optionsContext'>} optionsContext + * @returns {Promise<import('api').ApiReturn<'optionsGet'>>} */ optionsGet(optionsContext) { - /** @type {import('api').OptionsGetDetails} */ - const details = {optionsContext}; - return this._invoke('optionsGet', details); + return this._invoke('optionsGet', {optionsContext}); } /** - * @returns {Promise<import('api').OptionsGetFullResult>} + * @returns {Promise<import('api').ApiReturn<'optionsGetFull'>>} */ optionsGetFull() { - return this._invoke('optionsGetFull'); + return this._invoke('optionsGetFull', void 0); } /** - * @param {import('api').TermsFindDetails['text']} text - * @param {import('api').TermsFindDetails['details']} details - * @param {import('api').TermsFindDetails['optionsContext']} optionsContext - * @returns {Promise<import('api').TermsFindResult>} + * @param {import('api').ApiParam<'termsFind', 'text'>} text + * @param {import('api').ApiParam<'termsFind', 'details'>} details + * @param {import('api').ApiParam<'termsFind', 'optionsContext'>} optionsContext + * @returns {Promise<import('api').ApiReturn<'termsFind'>>} */ termsFind(text, details, optionsContext) { - /** @type {import('api').TermsFindDetails} */ - const details2 = {text, details, optionsContext}; - return this._invoke('termsFind', details2); + return this._invoke('termsFind', {text, details, optionsContext}); } /** - * @param {import('api').ParseTextDetails['text']} text - * @param {import('api').ParseTextDetails['optionsContext']} optionsContext - * @param {import('api').ParseTextDetails['scanLength']} scanLength - * @param {import('api').ParseTextDetails['useInternalParser']} useInternalParser - * @param {import('api').ParseTextDetails['useMecabParser']} useMecabParser - * @returns {Promise<import('api').ParseTextResult>} + * @param {import('api').ApiParam<'parseText', 'text'>} text + * @param {import('api').ApiParam<'parseText', 'optionsContext'>} optionsContext + * @param {import('api').ApiParam<'parseText', 'scanLength'>} scanLength + * @param {import('api').ApiParam<'parseText', 'useInternalParser'>} useInternalParser + * @param {import('api').ApiParam<'parseText', 'useMecabParser'>} useMecabParser + * @returns {Promise<import('api').ApiReturn<'parseText'>>} */ parseText(text, optionsContext, scanLength, useInternalParser, useMecabParser) { - /** @type {import('api').ParseTextDetails} */ - const details = {text, optionsContext, scanLength, useInternalParser, useMecabParser}; - return this._invoke('parseText', details); + return this._invoke('parseText', {text, optionsContext, scanLength, useInternalParser, useMecabParser}); } /** - * @param {import('api').KanjiFindDetails['text']} text - * @param {import('api').KanjiFindDetails['optionsContext']} optionsContext - * @returns {Promise<import('api').KanjiFindResult>} + * @param {import('api').ApiParam<'kanjiFind', 'text'>} text + * @param {import('api').ApiParam<'kanjiFind', 'optionsContext'>} optionsContext + * @returns {Promise<import('api').ApiReturn<'kanjiFind'>>} */ kanjiFind(text, optionsContext) { - /** @type {import('api').KanjiFindDetails} */ - const details = {text, optionsContext}; - return this._invoke('kanjiFind', details); + return this._invoke('kanjiFind', {text, optionsContext}); } /** - * @returns {Promise<import('api').IsAnkiConnectedResult>} + * @returns {Promise<import('api').ApiReturn<'isAnkiConnected'>>} */ isAnkiConnected() { - return this._invoke('isAnkiConnected'); + return this._invoke('isAnkiConnected', void 0); } /** - * @returns {Promise<import('api').GetAnkiConnectVersionResult>} + * @returns {Promise<import('api').ApiReturn<'getAnkiConnectVersion'>>} */ getAnkiConnectVersion() { - return this._invoke('getAnkiConnectVersion'); + return this._invoke('getAnkiConnectVersion', void 0); } /** - * @param {import('api').AddAnkiNoteDetails['note']} note - * @returns {Promise<import('api').AddAnkiNoteResult>} + * @param {import('api').ApiParam<'addAnkiNote', 'note'>} note + * @returns {Promise<import('api').ApiReturn<'addAnkiNote'>>} */ addAnkiNote(note) { - /** @type {import('api').AddAnkiNoteDetails} */ - const details = {note}; - return this._invoke('addAnkiNote', details); + return this._invoke('addAnkiNote', {note}); } /** - * @param {import('api').GetAnkiNoteInfoDetails['notes']} notes - * @param {import('api').GetAnkiNoteInfoDetails['fetchAdditionalInfo']} fetchAdditionalInfo - * @returns {Promise<import('api').GetAnkiNoteInfoResult>} + * @param {import('api').ApiParam<'getAnkiNoteInfo', 'notes'>} notes + * @param {import('api').ApiParam<'getAnkiNoteInfo', 'fetchAdditionalInfo'>} fetchAdditionalInfo + * @returns {Promise<import('api').ApiReturn<'getAnkiNoteInfo'>>} */ getAnkiNoteInfo(notes, fetchAdditionalInfo) { - /** @type {import('api').GetAnkiNoteInfoDetails} */ - const details = {notes, fetchAdditionalInfo}; - return this._invoke('getAnkiNoteInfo', details); + return this._invoke('getAnkiNoteInfo', {notes, fetchAdditionalInfo}); } /** - * @param {import('api').InjectAnkiNoteMediaDetails['timestamp']} timestamp - * @param {import('api').InjectAnkiNoteMediaDetails['definitionDetails']} definitionDetails - * @param {import('api').InjectAnkiNoteMediaDetails['audioDetails']} audioDetails - * @param {import('api').InjectAnkiNoteMediaDetails['screenshotDetails']} screenshotDetails - * @param {import('api').InjectAnkiNoteMediaDetails['clipboardDetails']} clipboardDetails - * @param {import('api').InjectAnkiNoteMediaDetails['dictionaryMediaDetails']} dictionaryMediaDetails - * @returns {Promise<import('api').InjectAnkiNoteMediaResult>} + * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'timestamp'>} timestamp + * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'definitionDetails'>} definitionDetails + * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'audioDetails'>} audioDetails + * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'screenshotDetails'>} screenshotDetails + * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'clipboardDetails'>} clipboardDetails + * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'dictionaryMediaDetails'>} dictionaryMediaDetails + * @returns {Promise<import('api').ApiReturn<'injectAnkiNoteMedia'>>} */ injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails) { - /** @type {import('api').InjectAnkiNoteMediaDetails} */ - const details = {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails}; - return this._invoke('injectAnkiNoteMedia', details); + return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails}); } /** - * @param {import('api').NoteViewDetails['noteId']} noteId - * @param {import('api').NoteViewDetails['mode']} mode - * @param {import('api').NoteViewDetails['allowFallback']} allowFallback - * @returns {Promise<import('api').NoteViewResult>} + * @param {import('api').ApiParam<'noteView', 'noteId'>} noteId + * @param {import('api').ApiParam<'noteView', 'mode'>} mode + * @param {import('api').ApiParam<'noteView', 'allowFallback'>} allowFallback + * @returns {Promise<import('api').ApiReturn<'noteView'>>} */ noteView(noteId, mode, allowFallback) { - /** @type {import('api').NoteViewDetails} */ - const details = {noteId, mode, allowFallback}; - return this._invoke('noteView', details); + return this._invoke('noteView', {noteId, mode, allowFallback}); } /** - * @param {import('api').SuspendAnkiCardsForNoteDetails['noteId']} noteId - * @returns {Promise<import('api').SuspendAnkiCardsForNoteResult>} + * @param {import('api').ApiParam<'suspendAnkiCardsForNote', 'noteId'>} noteId + * @returns {Promise<import('api').ApiReturn<'suspendAnkiCardsForNote'>>} */ suspendAnkiCardsForNote(noteId) { - /** @type {import('api').SuspendAnkiCardsForNoteDetails} */ - const details = {noteId}; - return this._invoke('suspendAnkiCardsForNote', details); + return this._invoke('suspendAnkiCardsForNote', {noteId}); } /** - * @param {import('api').GetTermAudioInfoListDetails['source']} source - * @param {import('api').GetTermAudioInfoListDetails['term']} term - * @param {import('api').GetTermAudioInfoListDetails['reading']} reading - * @returns {Promise<import('api').GetTermAudioInfoListResult>} + * @param {import('api').ApiParam<'getTermAudioInfoList', 'source'>} source + * @param {import('api').ApiParam<'getTermAudioInfoList', 'term'>} term + * @param {import('api').ApiParam<'getTermAudioInfoList', 'reading'>} reading + * @returns {Promise<import('api').ApiReturn<'getTermAudioInfoList'>>} */ getTermAudioInfoList(source, term, reading) { - /** @type {import('api').GetTermAudioInfoListDetails} */ - const details = {source, term, reading}; - return this._invoke('getTermAudioInfoList', details); + return this._invoke('getTermAudioInfoList', {source, term, reading}); } /** - * @param {import('api').CommandExecDetails['command']} command - * @param {import('api').CommandExecDetails['params']} [params] - * @returns {Promise<import('api').CommandExecResult>} + * @param {import('api').ApiParam<'commandExec', 'command'>} command + * @param {import('api').ApiParam<'commandExec', 'params'>} [params] + * @returns {Promise<import('api').ApiReturn<'commandExec'>>} */ commandExec(command, params) { - /** @type {import('api').CommandExecDetails} */ - const details = {command, params}; - return this._invoke('commandExec', details); + return this._invoke('commandExec', {command, params}); } /** - * @param {import('api').SendMessageToFrameDetails['frameId']} frameId - * @param {import('api').SendMessageToFrameDetails['action']} action - * @param {import('api').SendMessageToFrameDetails['params']} [params] - * @returns {Promise<import('api').SendMessageToFrameResult>} + * @param {import('api').ApiParam<'sendMessageToFrame', 'frameId'>} frameId + * @param {import('api').ApiParam<'sendMessageToFrame', 'action'>} action + * @param {import('api').ApiParam<'sendMessageToFrame', 'params'>} [params] + * @returns {Promise<import('api').ApiReturn<'sendMessageToFrame'>>} */ sendMessageToFrame(frameId, action, params) { - /** @type {import('api').SendMessageToFrameDetails} */ - const details = {frameId, action, params}; - return this._invoke('sendMessageToFrame', details); + return this._invoke('sendMessageToFrame', {frameId, action, params}); } /** - * @param {import('api').BroadcastTabDetails['action']} action - * @param {import('api').BroadcastTabDetails['params']} params - * @returns {Promise<import('api').BroadcastTabResult>} + * @param {import('api').ApiParam<'broadcastTab', 'action'>} action + * @param {import('api').ApiParam<'broadcastTab', 'params'>} params + * @returns {Promise<import('api').ApiReturn<'broadcastTab'>>} */ broadcastTab(action, params) { - /** @type {import('api').BroadcastTabDetails} */ - const details = {action, params}; - return this._invoke('broadcastTab', details); + return this._invoke('broadcastTab', {action, params}); } /** - * @returns {Promise<import('api').FrameInformationGetResult>} + * @returns {Promise<import('api').ApiReturn<'frameInformationGet'>>} */ frameInformationGet() { - return this._invoke('frameInformationGet'); + return this._invoke('frameInformationGet', void 0); } /** - * @param {import('api').InjectStylesheetDetails['type']} type - * @param {import('api').InjectStylesheetDetails['value']} value - * @returns {Promise<import('api').InjectStylesheetResult>} + * @param {import('api').ApiParam<'injectStylesheet', 'type'>} type + * @param {import('api').ApiParam<'injectStylesheet', 'value'>} value + * @returns {Promise<import('api').ApiReturn<'injectStylesheet'>>} */ injectStylesheet(type, value) { - /** @type {import('api').InjectStylesheetDetails} */ - const details = {type, value}; - return this._invoke('injectStylesheet', details); + return this._invoke('injectStylesheet', {type, value}); } /** - * @param {import('api').GetStylesheetContentDetails['url']} url - * @returns {Promise<import('api').GetStylesheetContentResult>} + * @param {import('api').ApiParam<'getStylesheetContent', 'url'>} url + * @returns {Promise<import('api').ApiReturn<'getStylesheetContent'>>} */ getStylesheetContent(url) { - /** @type {import('api').GetStylesheetContentDetails} */ - const details = {url}; - return this._invoke('getStylesheetContent', details); + return this._invoke('getStylesheetContent', {url}); } /** - * @returns {Promise<import('api').GetEnvironmentInfoResult>} + * @returns {Promise<import('api').ApiReturn<'getEnvironmentInfo'>>} */ getEnvironmentInfo() { - return this._invoke('getEnvironmentInfo'); + return this._invoke('getEnvironmentInfo', void 0); } /** - * @returns {Promise<import('api').ClipboardGetResult>} + * @returns {Promise<import('api').ApiReturn<'clipboardGet'>>} */ clipboardGet() { - return this._invoke('clipboardGet'); + return this._invoke('clipboardGet', void 0); } /** - * @returns {Promise<import('api').GetDisplayTemplatesHtmlResult>} + * @returns {Promise<import('api').ApiReturn<'getDisplayTemplatesHtml'>>} */ getDisplayTemplatesHtml() { - return this._invoke('getDisplayTemplatesHtml'); + return this._invoke('getDisplayTemplatesHtml', void 0); } /** - * @returns {Promise<import('api').GetZoomResult>} + * @returns {Promise<import('api').ApiReturn<'getZoom'>>} */ getZoom() { - return this._invoke('getZoom'); + return this._invoke('getZoom', void 0); } /** - * @returns {Promise<import('api').GetDefaultAnkiFieldTemplatesResult>} + * @returns {Promise<import('api').ApiReturn<'getDefaultAnkiFieldTemplates'>>} */ getDefaultAnkiFieldTemplates() { - return this._invoke('getDefaultAnkiFieldTemplates'); + return this._invoke('getDefaultAnkiFieldTemplates', void 0); } /** - * @returns {Promise<import('api').GetDictionaryInfoResult>} + * @returns {Promise<import('api').ApiReturn<'getDictionaryInfo'>>} */ getDictionaryInfo() { - return this._invoke('getDictionaryInfo'); + return this._invoke('getDictionaryInfo', void 0); } /** - * @returns {Promise<import('api').PurgeDatabaseResult>} + * @returns {Promise<import('api').ApiReturn<'purgeDatabase'>>} */ purgeDatabase() { - return this._invoke('purgeDatabase'); + return this._invoke('purgeDatabase', void 0); } /** - * @param {import('api').GetMediaDetails['targets']} targets - * @returns {Promise<import('api').GetMediaResult>} + * @param {import('api').ApiParam<'getMedia', 'targets'>} targets + * @returns {Promise<import('api').ApiReturn<'getMedia'>>} */ getMedia(targets) { - /** @type {import('api').GetMediaDetails} */ - const details = {targets}; - return this._invoke('getMedia', details); + return this._invoke('getMedia', {targets}); } /** - * @param {import('api').LogDetails['error']} error - * @param {import('api').LogDetails['level']} level - * @param {import('api').LogDetails['context']} context - * @returns {Promise<import('api').LogResult>} + * @param {import('api').ApiParam<'log', 'error'>} error + * @param {import('api').ApiParam<'log', 'level'>} level + * @param {import('api').ApiParam<'log', 'context'>} context + * @returns {Promise<import('api').ApiReturn<'log'>>} */ log(error, level, context) { - /** @type {import('api').LogDetails} */ - const details = {error, level, context}; - return this._invoke('log', details); + return this._invoke('log', {error, level, context}); } /** - * @returns {Promise<import('api').LogIndicatorClearResult>} + * @returns {Promise<import('api').ApiReturn<'logIndicatorClear'>>} */ logIndicatorClear() { - return this._invoke('logIndicatorClear'); + return this._invoke('logIndicatorClear', void 0); } /** - * @param {import('api').ModifySettingsDetails['targets']} targets - * @param {import('api').ModifySettingsDetails['source']} source - * @returns {Promise<import('api').ModifySettingsResult>} + * @param {import('api').ApiParam<'modifySettings', 'targets'>} targets + * @param {import('api').ApiParam<'modifySettings', 'source'>} source + * @returns {Promise<import('api').ApiReturn<'modifySettings'>>} */ modifySettings(targets, source) { - const details = {targets, source}; - return this._invoke('modifySettings', details); + return this._invoke('modifySettings', {targets, source}); } /** - * @param {import('api').GetSettingsDetails['targets']} targets - * @returns {Promise<import('api').GetSettingsResult>} + * @param {import('api').ApiParam<'getSettings', 'targets'>} targets + * @returns {Promise<import('api').ApiReturn<'getSettings'>>} */ getSettings(targets) { - /** @type {import('api').GetSettingsDetails} */ - const details = {targets}; - return this._invoke('getSettings', details); + return this._invoke('getSettings', {targets}); } /** - * @param {import('api').SetAllSettingsDetails['value']} value - * @param {import('api').SetAllSettingsDetails['source']} source - * @returns {Promise<import('api').SetAllSettingsResult>} + * @param {import('api').ApiParam<'setAllSettings', 'value'>} value + * @param {import('api').ApiParam<'setAllSettings', 'source'>} source + * @returns {Promise<import('api').ApiReturn<'setAllSettings'>>} */ setAllSettings(value, source) { - /** @type {import('api').SetAllSettingsDetails} */ - const details = {value, source}; - return this._invoke('setAllSettings', details); + return this._invoke('setAllSettings', {value, source}); } /** - * @param {import('api').GetOrCreateSearchPopupDetails} details - * @returns {Promise<import('api').GetOrCreateSearchPopupResult>} + * @param {import('api').ApiParams<'getOrCreateSearchPopup'>} details + * @returns {Promise<import('api').ApiReturn<'getOrCreateSearchPopup'>>} */ getOrCreateSearchPopup(details) { return this._invoke('getOrCreateSearchPopup', details); } /** - * @param {import('api').IsTabSearchPopupDetails['tabId']} tabId - * @returns {Promise<import('api').IsTabSearchPopupResult>} + * @param {import('api').ApiParam<'isTabSearchPopup', 'tabId'>} tabId + * @returns {Promise<import('api').ApiReturn<'isTabSearchPopup'>>} */ isTabSearchPopup(tabId) { - /** @type {import('api').IsTabSearchPopupDetails} */ - const details = {tabId}; - return this._invoke('isTabSearchPopup', details); + return this._invoke('isTabSearchPopup', {tabId}); } /** - * @param {import('api').TriggerDatabaseUpdatedDetails['type']} type - * @param {import('api').TriggerDatabaseUpdatedDetails['cause']} cause - * @returns {Promise<import('api').TriggerDatabaseUpdatedResult>} + * @param {import('api').ApiParam<'triggerDatabaseUpdated', 'type'>} type + * @param {import('api').ApiParam<'triggerDatabaseUpdated', 'cause'>} cause + * @returns {Promise<import('api').ApiReturn<'triggerDatabaseUpdated'>>} */ triggerDatabaseUpdated(type, cause) { - /** @type {import('api').TriggerDatabaseUpdatedDetails} */ - const details = {type, cause}; - return this._invoke('triggerDatabaseUpdated', details); + return this._invoke('triggerDatabaseUpdated', {type, cause}); } /** - * @returns {Promise<import('api').TestMecabResult>} + * @returns {Promise<import('api').ApiReturn<'testMecab'>>} */ testMecab() { - return this._invoke('testMecab'); + return this._invoke('testMecab', void 0); } /** - * @param {import('api').TextHasJapaneseCharactersDetails['text']} text - * @returns {Promise<import('api').TextHasJapaneseCharactersResult>} + * @param {import('api').ApiParam<'textHasJapaneseCharacters', 'text'>} text + * @returns {Promise<import('api').ApiReturn<'textHasJapaneseCharacters'>>} */ textHasJapaneseCharacters(text) { - /** @type {import('api').TextHasJapaneseCharactersDetails} */ - const details = {text}; - return this._invoke('textHasJapaneseCharacters', details); + return this._invoke('textHasJapaneseCharacters', {text}); } /** - * @param {import('api').GetTermFrequenciesDetails['termReadingList']} termReadingList - * @param {import('api').GetTermFrequenciesDetails['dictionaries']} dictionaries - * @returns {Promise<import('api').GetTermFrequenciesResult>} + * @param {import('api').ApiParam<'getTermFrequencies', 'termReadingList'>} termReadingList + * @param {import('api').ApiParam<'getTermFrequencies', 'dictionaries'>} dictionaries + * @returns {Promise<import('api').ApiReturn<'getTermFrequencies'>>} */ getTermFrequencies(termReadingList, dictionaries) { - /** @type {import('api').GetTermFrequenciesDetails} */ - const details = {termReadingList, dictionaries}; - return this._invoke('getTermFrequencies', details); + return this._invoke('getTermFrequencies', {termReadingList, dictionaries}); } /** - * @param {import('api').FindAnkiNotesDetails['query']} query - * @returns {Promise<import('api').FindAnkiNotesResult>} + * @param {import('api').ApiParam<'findAnkiNotes', 'query'>} query + * @returns {Promise<import('api').ApiReturn<'findAnkiNotes'>>} */ findAnkiNotes(query) { - /** @type {import('api').FindAnkiNotesDetails} */ - const details = {query}; - return this._invoke('findAnkiNotes', details); + return this._invoke('findAnkiNotes', {query}); } /** - * @param {import('api').LoadExtensionScriptsDetails['files']} files - * @returns {Promise<import('api').LoadExtensionScriptsResult>} + * @param {import('api').ApiParam<'loadExtensionScripts', 'files'>} files + * @returns {Promise<import('api').ApiReturn<'loadExtensionScripts'>>} */ loadExtensionScripts(files) { - /** @type {import('api').LoadExtensionScriptsDetails} */ - const details = {files}; - return this._invoke('loadExtensionScripts', details); + return this._invoke('loadExtensionScripts', {files}); } /** - * @param {import('api').OpenCrossFramePortDetails['targetTabId']} targetTabId - * @param {import('api').OpenCrossFramePortDetails['targetFrameId']} targetFrameId - * @returns {Promise<import('api').OpenCrossFramePortResult>} + * @param {import('api').ApiParam<'openCrossFramePort', 'targetTabId'>} targetTabId + * @param {import('api').ApiParam<'openCrossFramePort', 'targetFrameId'>} targetFrameId + * @returns {Promise<import('api').ApiReturn<'openCrossFramePort'>>} */ openCrossFramePort(targetTabId, targetFrameId) { return this._invoke('openCrossFramePort', {targetTabId, targetFrameId}); @@ -425,12 +374,14 @@ export class API { // Utilities /** - * @template [TReturn=unknown] - * @param {string} action - * @param {import('core').SerializableObject} [params] - * @returns {Promise<TReturn>} + * @template {import('api').ApiNames} TAction + * @template {import('api').ApiParams<TAction>} TParams + * @param {TAction} action + * @param {TParams} params + * @returns {Promise<import('api').ApiReturn<TAction>>} */ - _invoke(action, params = {}) { + _invoke(action, params) { + /** @type {import('api').MessageAny} */ const data = {action, params}; return new Promise((resolve, reject) => { try { diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js index 3e59098f..0e7e1e1a 100644 --- a/ext/js/display/query-parser.js +++ b/ext/js/display/query-parser.js @@ -48,7 +48,7 @@ export class QueryParser extends EventDispatcher { this._useInternalParser = true; /** @type {boolean} */ this._useMecabParser = false; - /** @type {import('api').ParseTextResult} */ + /** @type {import('api').ParseTextResultItem[]} */ this._parseResults = []; /** @type {HTMLElement} */ this._queryParser = querySelectorNotNull(document, '#query-parser-content'); @@ -252,7 +252,7 @@ export class QueryParser extends EventDispatcher { /** * @param {HTMLSelectElement} select - * @param {import('api').ParseTextResult} parseResults + * @param {import('api').ParseTextResultItem[]} parseResults * @param {?string} selectedParser */ _updateParserModeSelect(select, parseResults, selectedParser) { diff --git a/types/ext/api.d.ts b/types/ext/api.d.ts index 93004447..4e01de02 100644 --- a/types/ext/api.d.ts +++ b/types/ext/api.d.ts @@ -31,58 +31,23 @@ import type * as Settings from './settings'; import type * as SettingsModifications from './settings-modifications'; import type * as Translation from './translation'; import type * as Translator from './translator'; - -// Generic - -export type Handler<TDetails = unknown, TResult = unknown, THasSender extends boolean = false> = ( - details: TDetails, - sender: (THasSender extends true ? chrome.runtime.MessageSender : void) -) => (TResult | Promise<TResult>); - -// optionsGet - -export type OptionsGetDetails = { - optionsContext: Settings.OptionsContext; -}; - -export type OptionsGetResult = Settings.ProfileOptions; - -// optionsGetFull - -export type OptionsGetFullDetails = Record<string, never>; - -export type OptionsGetFullResult = Settings.Options; - -// termsFind +import type { + ApiMap as BaseApiMap, + ApiMapInit as BaseApiMapInit, + ApiHandler as BaseApiHandler, + ApiParams as BaseApiParams, + ApiReturn as BaseApiReturn, + ApiNames as BaseApiNames, + ApiParam as BaseApiParam, + ApiParamNames as BaseApiParamNames, + ApiParamsAny as BaseApiParamsAny, +} from './api-map'; export type FindTermsDetails = { matchType?: Translation.FindTermsMatchType; deinflect?: boolean; }; -export type TermsFindDetails = { - text: string; - details: FindTermsDetails; - optionsContext: Settings.OptionsContext; -}; - -export type TermsFindResult = { - dictionaryEntries: Dictionary.TermDictionaryEntry[]; - originalTextLength: number; -}; - -// parseText - -export type ParseTextDetails = { - text: string; - optionsContext: Settings.OptionsContext; - scanLength: number; - useInternalParser: boolean; - useMecabParser: boolean; -}; - -export type ParseTextResult = ParseTextResultItem[]; - export type ParseTextResultItem = { id: string; source: 'scanning-parser' | 'mecab'; @@ -97,55 +62,6 @@ export type ParseTextSegment = { export type ParseTextLine = ParseTextSegment[]; -// kanjiFind - -export type KanjiFindDetails = { - text: string; - optionsContext: Settings.OptionsContext; -}; - -export type KanjiFindResult = Dictionary.KanjiDictionaryEntry[]; - -// isAnkiConnected - -export type IsAnkiConnectedDetails = Record<string, never>; - -export type IsAnkiConnectedResult = boolean; - -// getAnkiConnectVersion - -export type GetAnkiConnectVersionDetails = Record<string, never>; - -export type GetAnkiConnectVersionResult = number | null; - -// addAnkiNote - -export type AddAnkiNoteDetails = { - note: Anki.Note; -}; - -export type AddAnkiNoteResult = Anki.NoteId | null; - -// getAnkiNoteInfo - -export type GetAnkiNoteInfoDetails = { - notes: Anki.Note[]; - fetchAdditionalInfo: boolean; -}; - -export type GetAnkiNoteInfoResult = Anki.NoteInfoWrapper[]; - -// injectAnkiNoteMedia - -export type InjectAnkiNoteMediaDetails = { - timestamp: number; - definitionDetails: InjectAnkiNoteMediaDefinitionDetails; - audioDetails: InjectAnkiNoteMediaAudioDetails | null; - screenshotDetails: InjectAnkiNoteMediaScreenshotDetails | null; - clipboardDetails: InjectAnkiNoteMediaClipboardDetails | null; - dictionaryMediaDetails: InjectAnkiNoteMediaDictionaryMediaDetails[]; -}; - export type InjectAnkiNoteMediaTermDefinitionDetails = { type: 'term'; term: string; @@ -178,286 +94,320 @@ export type InjectAnkiNoteMediaDictionaryMediaDetails = { path: string; }; -export type InjectAnkiNoteMediaResult = { - screenshotFileName: string | null; - clipboardImageFileName: string | null; - clipboardText: string | null; - audioFileName: string | null; - dictionaryMedia: InjectAnkiNoteDictionaryMediaResult[]; - errors: Core.SerializedError[]; -}; - export type InjectAnkiNoteDictionaryMediaResult = { dictionary: string; path: string; fileName: string | null; }; -// noteView - -export type NoteViewDetails = { - noteId: Anki.NoteId; - mode: Settings.AnkiNoteGuiMode; - allowFallback: boolean; -}; - -export type NoteViewResult = Settings.AnkiNoteGuiMode; - -// suspendAnkiCardsForNote - -export type SuspendAnkiCardsForNoteDetails = { - noteId: Anki.NoteId; -}; - -export type SuspendAnkiCardsForNoteResult = number; - -// getTermAudioInfoList - -export type GetTermAudioInfoListDetails = { - source: Audio.AudioSourceInfo; - term: string; - reading: string; -}; - -export type GetTermAudioInfoListResult = AudioDownloader.Info[]; - -// commandExec - -export type CommandExecDetails = { - command: string; - params?: Core.SerializableObject; -}; - -export type CommandExecResult = boolean; - -// sendMessageToFrame - -export type SendMessageToFrameDetails = { - frameId: number; - action: string; - params?: Core.SerializableObject; -}; - -export type SendMessageToFrameResult = boolean; - -// broadcastTab - -export type BroadcastTabDetails = { - action: string; - params?: Core.SerializableObject; -}; - -export type BroadcastTabResult = boolean; - -// frameInformationGet - -export type FrameInformationGetDetails = Record<string, never>; - -export type FrameInformationGetResult = Extension.ContentOrigin; - -// injectStylesheet - -export type InjectStylesheetDetails = { - type: 'file' | 'code'; - value: string; -}; - -export type InjectStylesheetResult = void; - -// getStylesheetContent - -export type GetStylesheetContentDetails = { - url: string; -}; - -export type GetStylesheetContentResult = string; - -// getEnvironmentInfo - -export type GetEnvironmentInfoDetails = Record<string, never>; - -export type GetEnvironmentInfoResult = Environment.Info; - -// clipboardGet - -export type ClipboardGetDetails = Record<string, never>; - -export type ClipboardGetResult = string; - -// getDisplayTemplatesHtml - -export type GetDisplayTemplatesHtmlDetails = Record<string, never>; - -export type GetDisplayTemplatesHtmlResult = string; - -// getZoom - -export type GetZoomDetails = Record<string, never>; - -export type GetZoomResult = { - zoomFactor: number; -}; - -// getDefaultAnkiFieldTemplates - -export type GetDefaultAnkiFieldTemplatesDetails = Record<string, never>; - -export type GetDefaultAnkiFieldTemplatesResult = string; - -// getDictionaryInfo - -export type GetDictionaryInfoDetails = Record<string, never>; - -export type GetDictionaryInfoResult = DictionaryImporter.Summary[]; - -// purgeDatabase - -export type PurgeDatabaseDetails = Record<string, never>; - -export type PurgeDatabaseResult = void; - -// getMedia - -export type GetMediaDetails = { - targets: GetMediaDetailsTarget[]; -}; - export type GetMediaDetailsTarget = { path: string; dictionary: string; }; -export type GetMediaResult = DictionaryDatabase.MediaDataStringContent[]; - -// log - -export type LogDetails = { - error: Core.SerializedError; - level: Log.LogLevel; - context: Log.LogContext | undefined; -}; - -export type LogResult = void; - -// logIndicatorClear - -export type LogIndicatorClearDetails = Record<string, never>; - -export type LogIndicatorClearResult = void; - -// modifySettings - -export type ModifySettingsDetails = { - targets: SettingsModifications.ScopedModification[]; - source: string; -}; - -export type ModifySettingsResult = Core.Response<SettingsModifications.ModificationResult>[]; - -// getSettings - -export type GetSettingsDetails = { - targets: SettingsModifications.ScopedRead[]; -}; - -export type GetSettingsResult = Core.Response<SettingsModifications.ModificationResult>[]; - -// setAllSettings - -export type SetAllSettingsDetails = { - value: Settings.Options; - source: string; -}; - -export type SetAllSettingsResult = void; - -// getOrCreateSearchPopup - -export type GetOrCreateSearchPopupDetails = { - focus?: boolean | 'ifCreated'; - text?: string; -}; - -export type GetOrCreateSearchPopupResult = { - tabId: number | null; - windowId: number; -}; - -// isTabSearchPopup - -export type IsTabSearchPopupDetails = { - tabId: number; -}; - -export type IsTabSearchPopupResult = boolean; - -// triggerDatabaseUpdated - -export type TriggerDatabaseUpdatedDetails = { - type: Backend.DatabaseUpdateType; - cause: Backend.DatabaseUpdateCause; -}; - -export type TriggerDatabaseUpdatedResult = void; - -// testMecab - -export type TestMecabDetails = Record<string, never>; - -export type TestMecabResult = true; - -// textHasJapaneseCharacters - -export type TextHasJapaneseCharactersDetails = { - text: string; -}; - -export type TextHasJapaneseCharactersResult = boolean; - -// getTermFrequencies - -export type GetTermFrequenciesDetails = { - termReadingList: GetTermFrequenciesDetailsTermReadingListItem[]; - dictionaries: string[]; -}; - export type GetTermFrequenciesDetailsTermReadingListItem = { term: string; reading: string | null; }; -export type GetTermFrequenciesResult = Translator.TermFrequencySimple[]; - -// findAnkiNotes - -export type FindAnkiNotesDetails = { - query: string; -}; - -export type FindAnkiNotesResult = Anki.NoteId[]; - -// loadExtensionScripts - -export type LoadExtensionScriptsDetails = { - files: string[]; -}; - -export type LoadExtensionScriptsResult = void; - -// openCrossFramePort - -export type OpenCrossFramePortDetails = { - targetTabId: number; - targetFrameId: number; -}; - -export type OpenCrossFramePortResult = { - targetTabId: number; - targetFrameId: number; +type ApiSurface = { + optionsGet: { + params: { + optionsContext: Settings.OptionsContext; + }; + return: Settings.ProfileOptions; + }; + optionsGetFull: { + params: void; + return: Settings.Options; + }; + termsFind: { + params: { + text: string; + details: FindTermsDetails; + optionsContext: Settings.OptionsContext; + }; + return: { + dictionaryEntries: Dictionary.TermDictionaryEntry[]; + originalTextLength: number; + }; + }; + parseText: { + params: { + text: string; + optionsContext: Settings.OptionsContext; + scanLength: number; + useInternalParser: boolean; + useMecabParser: boolean; + }; + return: ParseTextResultItem[]; + }; + kanjiFind: { + params: { + text: string; + optionsContext: Settings.OptionsContext; + }; + return: Dictionary.KanjiDictionaryEntry[]; + }; + isAnkiConnected: { + params: void; + return: boolean; + }; + getAnkiConnectVersion: { + params: void; + return: number | null; + }; + addAnkiNote: { + params: { + note: Anki.Note; + }; + return: Anki.NoteId | null; + }; + getAnkiNoteInfo: { + params: { + notes: Anki.Note[]; + fetchAdditionalInfo: boolean; + }; + return: Anki.NoteInfoWrapper[]; + }; + injectAnkiNoteMedia: { + params: { + timestamp: number; + definitionDetails: InjectAnkiNoteMediaDefinitionDetails; + audioDetails: InjectAnkiNoteMediaAudioDetails | null; + screenshotDetails: InjectAnkiNoteMediaScreenshotDetails | null; + clipboardDetails: InjectAnkiNoteMediaClipboardDetails | null; + dictionaryMediaDetails: InjectAnkiNoteMediaDictionaryMediaDetails[]; + }; + return: { + screenshotFileName: string | null; + clipboardImageFileName: string | null; + clipboardText: string | null; + audioFileName: string | null; + dictionaryMedia: InjectAnkiNoteDictionaryMediaResult[]; + errors: Core.SerializedError[]; + }; + }; + noteView: { + params: { + noteId: Anki.NoteId; + mode: Settings.AnkiNoteGuiMode; + allowFallback: boolean; + }; + return: Settings.AnkiNoteGuiMode; + }; + suspendAnkiCardsForNote: { + params: { + noteId: Anki.NoteId; + }; + return: number; + }; + getTermAudioInfoList: { + params: { + source: Audio.AudioSourceInfo; + term: string; + reading: string; + }; + return: AudioDownloader.Info[]; + }; + commandExec: { + params: { + command: string; + params?: Core.SerializableObject; + }; + return: boolean; + }; + sendMessageToFrame: { + params: { + frameId: number; + action: string; + params?: Core.SerializableObject; + }; + return: boolean; + }; + broadcastTab: { + params: { + action: string; + params?: Core.SerializableObject; + }; + return: boolean; + }; + frameInformationGet: { + params: void; + return: Extension.ContentOrigin; + }; + injectStylesheet: { + params: { + type: 'file' | 'code'; + value: string; + }; + return: void; + }; + getStylesheetContent: { + params: { + url: string; + }; + return: string; + }; + getEnvironmentInfo: { + params: void; + return: Environment.Info; + }; + clipboardGet: { + params: void; + return: string; + }; + getDisplayTemplatesHtml: { + params: void; + return: string; + }; + getZoom: { + params: void; + return: { + zoomFactor: number; + }; + }; + getDefaultAnkiFieldTemplates: { + params: void; + return: string; + }; + getDictionaryInfo: { + params: void; + return: DictionaryImporter.Summary[]; + }; + purgeDatabase: { + params: void; + return: void; + }; + getMedia: { + params: { + targets: GetMediaDetailsTarget[]; + }; + return: DictionaryDatabase.MediaDataStringContent[]; + }; + log: { + params: { + error: Core.SerializedError; + level: Log.LogLevel; + context: Log.LogContext | undefined; + }; + return: void; + }; + logIndicatorClear: { + params: void; + return: void; + }; + modifySettings: { + params: { + targets: SettingsModifications.ScopedModification[]; + source: string; + }; + return: Core.Response<SettingsModifications.ModificationResult>[]; + }; + getSettings: { + params: { + targets: SettingsModifications.ScopedRead[]; + }; + return: Core.Response<SettingsModifications.ModificationResult>[]; + }; + setAllSettings: { + params: { + value: Settings.Options; + source: string; + }; + return: void; + }; + getOrCreateSearchPopup: { + params: { + focus?: boolean | 'ifCreated'; + text?: string; + }; + return: { + tabId: number | null; + windowId: number; + }; + }; + isTabSearchPopup: { + params: { + tabId: number; + }; + return: boolean; + }; + triggerDatabaseUpdated: { + params: { + type: Backend.DatabaseUpdateType; + cause: Backend.DatabaseUpdateCause; + }; + return: void; + }; + testMecab: { + params: void; + return: true; + }; + textHasJapaneseCharacters: { + params: { + text: string; + }; + return: boolean; + }; + getTermFrequencies: { + params: { + termReadingList: GetTermFrequenciesDetailsTermReadingListItem[]; + dictionaries: string[]; + }; + return: Translator.TermFrequencySimple[]; + }; + findAnkiNotes: { + params: { + query: string; + }; + return: Anki.NoteId[]; + }; + loadExtensionScripts: { + params: { + files: string[]; + }; + return: void; + }; + openCrossFramePort: { + params: { + targetTabId: number; + targetFrameId: number; + }; + return: { + targetTabId: number; + targetFrameId: number; + }; + }; + requestBackendReadySignal: { + params: void; + return: boolean; + }; +}; + +type ApiExtraArgs = [sender: chrome.runtime.MessageSender]; + +export type ApiNames = BaseApiNames<ApiSurface>; + +export type ApiMap = BaseApiMap<ApiSurface, ApiExtraArgs>; + +export type ApiMapInit = BaseApiMapInit<ApiSurface, ApiExtraArgs>; + +export type ApiHandler<TName extends ApiNames> = BaseApiHandler<ApiSurface[TName], ApiExtraArgs>; + +export type ApiHandlerNoExtraArgs<TName extends ApiNames> = BaseApiHandler<ApiSurface[TName], []>; + +export type ApiParams<TName extends ApiNames> = BaseApiParams<ApiSurface[TName]>; + +export type ApiParam<TName extends ApiNames, TParamName extends BaseApiParamNames<ApiSurface[TName]>> = BaseApiParam<ApiSurface[TName], TParamName>; + +export type ApiReturn<TName extends ApiNames> = BaseApiReturn<ApiSurface[TName]>; + +export type ApiParamsAny = BaseApiParamsAny<ApiSurface>; + +export type MessageAny = Message<ApiNames>; + +type Message<TName extends ApiNames> = { + action: TName; + params: ApiParams<TName>; }; - -// requestBackendReadySignal - -export type RequestBackendReadySignalDetails = Record<string, never>; - -export type RequestBackendReadySignalResult = boolean; |