/* * Copyright (C) 2023-2024 Yomitan Authors * Copyright (C) 2016-2022 Yomichan 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 {ExtensionError} from '../core/extension-error.js'; export class API { /** * @param {import('../extension/web-extension.js').WebExtension} webExtension */ constructor(webExtension) { /** @type {import('../extension/web-extension.js').WebExtension} */ this._webExtension = webExtension; } /** * @param {import('api').ApiParam<'optionsGet', 'optionsContext'>} optionsContext * @returns {Promise<import('api').ApiReturn<'optionsGet'>>} */ optionsGet(optionsContext) { return this._invoke('optionsGet', {optionsContext}); } /** * @returns {Promise<import('api').ApiReturn<'optionsGetFull'>>} */ optionsGetFull() { return this._invoke('optionsGetFull', void 0); } /** * @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) { return this._invoke('termsFind', {text, details, optionsContext}); } /** * @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) { return this._invoke('parseText', {text, optionsContext, scanLength, useInternalParser, useMecabParser}); } /** * @param {import('api').ApiParam<'kanjiFind', 'text'>} text * @param {import('api').ApiParam<'kanjiFind', 'optionsContext'>} optionsContext * @returns {Promise<import('api').ApiReturn<'kanjiFind'>>} */ kanjiFind(text, optionsContext) { return this._invoke('kanjiFind', {text, optionsContext}); } /** * @returns {Promise<import('api').ApiReturn<'isAnkiConnected'>>} */ isAnkiConnected() { return this._invoke('isAnkiConnected', void 0); } /** * @returns {Promise<import('api').ApiReturn<'getAnkiConnectVersion'>>} */ getAnkiConnectVersion() { return this._invoke('getAnkiConnectVersion', void 0); } /** * @param {import('api').ApiParam<'addAnkiNote', 'note'>} note * @returns {Promise<import('api').ApiReturn<'addAnkiNote'>>} */ addAnkiNote(note) { return this._invoke('addAnkiNote', {note}); } /** * @param {import('api').ApiParam<'getAnkiNoteInfo', 'notes'>} notes * @param {import('api').ApiParam<'getAnkiNoteInfo', 'fetchAdditionalInfo'>} fetchAdditionalInfo * @returns {Promise<import('api').ApiReturn<'getAnkiNoteInfo'>>} */ getAnkiNoteInfo(notes, fetchAdditionalInfo) { return this._invoke('getAnkiNoteInfo', {notes, fetchAdditionalInfo}); } /** * @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) { return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails}); } /** * @param {import('api').ApiParam<'viewNotes', 'noteIds'>} noteIds * @param {import('api').ApiParam<'viewNotes', 'mode'>} mode * @param {import('api').ApiParam<'viewNotes', 'allowFallback'>} allowFallback * @returns {Promise<import('api').ApiReturn<'viewNotes'>>} */ viewNotes(noteIds, mode, allowFallback) { return this._invoke('viewNotes', {noteIds, mode, allowFallback}); } /** * @param {import('api').ApiParam<'suspendAnkiCardsForNote', 'noteId'>} noteId * @returns {Promise<import('api').ApiReturn<'suspendAnkiCardsForNote'>>} */ suspendAnkiCardsForNote(noteId) { return this._invoke('suspendAnkiCardsForNote', {noteId}); } /** * @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) { return this._invoke('getTermAudioInfoList', {source, term, reading}); } /** * @param {import('api').ApiParam<'commandExec', 'command'>} command * @param {import('api').ApiParam<'commandExec', 'params'>} [params] * @returns {Promise<import('api').ApiReturn<'commandExec'>>} */ commandExec(command, params) { return this._invoke('commandExec', {command, params}); } /** * @param {import('api').ApiParam<'sendMessageToFrame', 'frameId'>} frameId * @param {import('api').ApiParam<'sendMessageToFrame', 'message'>} message * @returns {Promise<import('api').ApiReturn<'sendMessageToFrame'>>} */ sendMessageToFrame(frameId, message) { return this._invoke('sendMessageToFrame', {frameId, message}); } /** * @param {import('api').ApiParam<'broadcastTab', 'message'>} message * @returns {Promise<import('api').ApiReturn<'broadcastTab'>>} */ broadcastTab(message) { return this._invoke('broadcastTab', {message}); } /** * @returns {Promise<import('api').ApiReturn<'frameInformationGet'>>} */ frameInformationGet() { return this._invoke('frameInformationGet', void 0); } /** * @param {import('api').ApiParam<'injectStylesheet', 'type'>} type * @param {import('api').ApiParam<'injectStylesheet', 'value'>} value * @returns {Promise<import('api').ApiReturn<'injectStylesheet'>>} */ injectStylesheet(type, value) { return this._invoke('injectStylesheet', {type, value}); } /** * @param {import('api').ApiParam<'getStylesheetContent', 'url'>} url * @returns {Promise<import('api').ApiReturn<'getStylesheetContent'>>} */ getStylesheetContent(url) { return this._invoke('getStylesheetContent', {url}); } /** * @returns {Promise<import('api').ApiReturn<'getEnvironmentInfo'>>} */ getEnvironmentInfo() { return this._invoke('getEnvironmentInfo', void 0); } /** * @returns {Promise<import('api').ApiReturn<'clipboardGet'>>} */ clipboardGet() { return this._invoke('clipboardGet', void 0); } /** * @returns {Promise<import('api').ApiReturn<'getZoom'>>} */ getZoom() { return this._invoke('getZoom', void 0); } /** * @returns {Promise<import('api').ApiReturn<'getDefaultAnkiFieldTemplates'>>} */ getDefaultAnkiFieldTemplates() { return this._invoke('getDefaultAnkiFieldTemplates', void 0); } /** * @returns {Promise<import('api').ApiReturn<'getDictionaryInfo'>>} */ getDictionaryInfo() { return this._invoke('getDictionaryInfo', void 0); } /** * @returns {Promise<import('api').ApiReturn<'purgeDatabase'>>} */ purgeDatabase() { return this._invoke('purgeDatabase', void 0); } /** * @param {import('api').ApiParam<'getMedia', 'targets'>} targets * @returns {Promise<import('api').ApiReturn<'getMedia'>>} */ getMedia(targets) { return this._invoke('getMedia', {targets}); } /** * @param {import('api').ApiParam<'logGenericErrorBackend', 'error'>} error * @param {import('api').ApiParam<'logGenericErrorBackend', 'level'>} level * @param {import('api').ApiParam<'logGenericErrorBackend', 'context'>} context * @returns {Promise<import('api').ApiReturn<'logGenericErrorBackend'>>} */ logGenericErrorBackend(error, level, context) { return this._invoke('logGenericErrorBackend', {error, level, context}); } /** * @returns {Promise<import('api').ApiReturn<'logIndicatorClear'>>} */ logIndicatorClear() { return this._invoke('logIndicatorClear', void 0); } /** * @param {import('api').ApiParam<'modifySettings', 'targets'>} targets * @param {import('api').ApiParam<'modifySettings', 'source'>} source * @returns {Promise<import('api').ApiReturn<'modifySettings'>>} */ modifySettings(targets, source) { return this._invoke('modifySettings', {targets, source}); } /** * @param {import('api').ApiParam<'getSettings', 'targets'>} targets * @returns {Promise<import('api').ApiReturn<'getSettings'>>} */ getSettings(targets) { return this._invoke('getSettings', {targets}); } /** * @param {import('api').ApiParam<'setAllSettings', 'value'>} value * @param {import('api').ApiParam<'setAllSettings', 'source'>} source * @returns {Promise<import('api').ApiReturn<'setAllSettings'>>} */ setAllSettings(value, source) { return this._invoke('setAllSettings', {value, source}); } /** * @param {import('api').ApiParams<'getOrCreateSearchPopup'>} details * @returns {Promise<import('api').ApiReturn<'getOrCreateSearchPopup'>>} */ getOrCreateSearchPopup(details) { return this._invoke('getOrCreateSearchPopup', details); } /** * @param {import('api').ApiParam<'isTabSearchPopup', 'tabId'>} tabId * @returns {Promise<import('api').ApiReturn<'isTabSearchPopup'>>} */ isTabSearchPopup(tabId) { return this._invoke('isTabSearchPopup', {tabId}); } /** * @param {import('api').ApiParam<'triggerDatabaseUpdated', 'type'>} type * @param {import('api').ApiParam<'triggerDatabaseUpdated', 'cause'>} cause * @returns {Promise<import('api').ApiReturn<'triggerDatabaseUpdated'>>} */ triggerDatabaseUpdated(type, cause) { return this._invoke('triggerDatabaseUpdated', {type, cause}); } /** * @returns {Promise<import('api').ApiReturn<'testMecab'>>} */ testMecab() { return this._invoke('testMecab', void 0); } /** * @param {import('api').ApiParam<'isTextLookupWorthy', 'text'>} text * @param {import('api').ApiParam<'isTextLookupWorthy', 'language'>} language * @returns {Promise<import('api').ApiReturn<'isTextLookupWorthy'>>} */ isTextLookupWorthy(text, language) { return this._invoke('isTextLookupWorthy', {text, language}); } /** * @param {import('api').ApiParam<'getTermFrequencies', 'termReadingList'>} termReadingList * @param {import('api').ApiParam<'getTermFrequencies', 'dictionaries'>} dictionaries * @returns {Promise<import('api').ApiReturn<'getTermFrequencies'>>} */ getTermFrequencies(termReadingList, dictionaries) { return this._invoke('getTermFrequencies', {termReadingList, dictionaries}); } /** * @param {import('api').ApiParam<'findAnkiNotes', 'query'>} query * @returns {Promise<import('api').ApiReturn<'findAnkiNotes'>>} */ findAnkiNotes(query) { return this._invoke('findAnkiNotes', {query}); } /** * @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}); } /** * @returns {Promise<import('api').ApiReturn<'getLanguageSummaries'>>} */ getLanguageSummaries() { return this._invoke('getLanguageSummaries', void 0); } // Utilities /** * @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) { /** @type {import('api').ApiMessage<TAction>} */ const data = {action, params}; return new Promise((resolve, reject) => { try { this._webExtension.sendMessage(data, (response) => { this._webExtension.getLastError(); if (response !== null && typeof response === 'object') { const {error} = /** @type {import('core').UnknownObject} */ (response); if (typeof error !== 'undefined') { reject(ExtensionError.deserialize(/** @type {import('core').SerializedError} */ (error))); } else { const {result} = /** @type {import('core').UnknownObject} */ (response); resolve(/** @type {import('api').ApiReturn<TAction>} */ (result)); } } else { const message = response === null ? 'Unexpected null response' : `Unexpected response of type ${typeof response}`; reject(new Error(`${message} (${JSON.stringify(data)})`)); } }); } catch (e) { reject(e); } }); } }