feat: Extensive refactoring and modification to support any number of interfaces

This commit is contained in:
Gabe
2025-09-24 23:24:00 +08:00
parent 779c9fc850
commit 2a46939aa5
65 changed files with 2054 additions and 1947 deletions

View File

@@ -6,7 +6,7 @@ const parseMSToken = (token) => {
try {
return JSON.parse(atob(token.split(".")[1])).exp;
} catch (err) {
kissLog(err, "parseMSToken");
kissLog("parseMSToken", err);
}
return 0;
};

View File

@@ -131,17 +131,15 @@ const queueMap = new Map();
/**
* 获取批处理实例
* @param {*} translator
* @returns
*/
export const getBatchQueue = (args, opts) => {
const { translator, from, to } = args;
const key = `${translator}_${from}_${to}`;
export const getBatchQueue = (args) => {
const { from, to, apiSetting } = args;
const key = `${apiSetting.apiSlug}_${from}_${to}`;
if (queueMap.has(key)) {
return queueMap.get(key);
}
const queue = BatchQueue(args, opts);
const queue = BatchQueue(args, apiSetting);
queueMap.set(key, queue);
return queue;
};

View File

@@ -8,7 +8,7 @@ function _browser() {
try {
return require("webextension-polyfill");
} catch (err) {
// kissLog(err, "browser");
// kissLog("browser", err);
}
}

View File

@@ -43,7 +43,7 @@ export const getHttpCache = async (input, init) => {
return await parseResponse(res);
}
} catch (err) {
kissLog(err, "get cache");
kissLog("get cache", err);
}
return null;
};
@@ -54,7 +54,12 @@ export const getHttpCache = async (input, init) => {
* @param {*} init
* @param {*} data
*/
export const putHttpCache = async (input, init, data) => {
export const putHttpCache = async (
input,
init,
data,
maxAge = DEFAULT_CACHE_TIMEOUT // todo: 从设置里面读取最大缓存时间
) => {
try {
const req = await newCacheReq(input, init);
const cache = await caches.open(CACHE_NAME);
@@ -62,13 +67,13 @@ export const putHttpCache = async (input, init, data) => {
status: 200,
headers: {
"Content-Type": "application/json",
"Cache-Control": `max-age=${DEFAULT_CACHE_TIMEOUT}`,
"Cache-Control": `max-age=${maxAge}`,
},
});
// res.headers.set("Cache-Control", `max-age=${DEFAULT_CACHE_TIMEOUT}`);
// res.headers.set("Cache-Control", `max-age=${maxAge}`);
await cache.put(req, res);
} catch (err) {
kissLog(err, "put cache");
kissLog("put cache", err);
}
};

View File

@@ -57,7 +57,7 @@ export const fetchPatcher = async (input, init = {}, opts) => {
try {
timeout = (await getSettingWithDefault()).httpTimeout;
} catch (err) {
kissLog(err, "getSettingWithDefault");
kissLog("getSettingWithDefault", err);
}
}
if (!timeout) {

View File

@@ -28,7 +28,7 @@ export const tryClearCaches = async () => {
try {
caches.delete(CACHE_NAME);
} catch (err) {
kissLog(err, "clean caches");
kissLog("clean caches", err);
}
};
@@ -48,7 +48,7 @@ export const tryDetectLang = async (
try {
lang = await langdetectMap[langDetector](q);
} catch (err) {
kissLog(err, "detect lang remote");
kissLog("detect lang remote", err);
}
}
@@ -57,7 +57,7 @@ export const tryDetectLang = async (
const res = await browser?.i18n?.detectLanguage(q);
lang = res?.languages?.[0]?.language;
} catch (err) {
kissLog(err, "detect lang local");
kissLog("detect lang local", err);
}
}

View File

@@ -1,8 +1,8 @@
import {
DEFAULT_INPUT_RULE,
DEFAULT_TRANS_APIS,
DEFAULT_INPUT_SHORTCUT,
OPT_LANGS_LIST,
DEFAULT_API_SETTING,
} from "../config";
import { genEventName, removeEndchar, matchInputStr, sleep } from "./utils";
import { stepShortcutRegister } from "./shortcut";
@@ -87,7 +87,7 @@ export default function inputTranslate({
inputRule: {
transOpen,
triggerShortcut,
translator,
apiSlug,
fromLang,
toLang,
triggerCount,
@@ -100,7 +100,8 @@ export default function inputTranslate({
return;
}
const apiSetting = transApis?.[translator] || DEFAULT_TRANS_APIS[translator];
const apiSetting =
transApis.find((api) => api.apiSlug === apiSlug) || DEFAULT_API_SETTING;
if (triggerShortcut.length === 0) {
triggerShortcut = DEFAULT_INPUT_SHORTCUT;
triggerCount = 1;
@@ -156,7 +157,7 @@ export default function inputTranslate({
addLoading(node, loadingId);
const [trText, isSame] = await apiTranslate({
translator,
apiSlug,
text,
fromLang,
toLang,
@@ -188,7 +189,7 @@ export default function inputTranslate({
collapseToEnd(node);
}
} catch (err) {
kissLog(err, "translate input");
kissLog("translate input", err);
} finally {
removeLoading(node, loadingId);
}

View File

@@ -1,12 +1,126 @@
/**
* 日志函数
* @param {*} msg
* @param {*} type
*/
export const kissLog = (msg, type) => {
let prefix = `[KISS-Translator]`;
if (type) {
prefix += `[${type}]`;
}
console.log(`${prefix} ${msg}`);
// 定义日志级别
export const LogLevel = {
DEBUG: { value: 0, name: "DEBUG", color: "#6495ED" }, // 宝蓝色
INFO: { value: 1, name: "INFO", color: "#4CAF50" }, // 绿色
WARN: { value: 2, name: "WARN", color: "#FFC107" }, // 琥珀色
ERROR: { value: 3, name: "ERROR", color: "#F44336" }, // 红色
SILENT: { value: 4, name: "SILENT" }, // 特殊级别,用于关闭所有日志
};
class Logger {
/**
* @param {object} [options={}] 配置选项
* @param {LogLevel} [options.level=LogLevel.INFO] 要显示的最低日志级别
* @param {string} [options.prefix='App'] 日志前缀,用于区分模块
*/
constructor(options = {}) {
this.config = {
level: options.level || LogLevel.INFO,
prefix: options.prefix || "KISS-Translator",
};
}
/**
* 动态设置日志级别
* @param {LogLevel} level - 新的日志级别
*/
setLevel(level) {
if (level && typeof level.value === "number") {
this.config.level = level;
console.log(`[${this.config.prefix}] Log level set to ${level.name}`);
}
}
/**
* 核心日志记录方法
* @private
* @param {LogLevel} level - 当前消息的日志级别
* @param {...any} args - 要记录的多个参数,可以是任何类型
*/
_log(level, ...args) {
// 如果当前级别低于配置的最低级别,则不打印
if (level.value < this.config.level.value) {
return;
}
const timestamp = new Date().toISOString();
const prefixStr = `[${this.config.prefix}]`;
const levelStr = `[${level.name}]`;
// 判断是否在浏览器环境并且浏览器支持 console 样式
const isBrowser =
typeof window !== "undefined" && typeof window.document !== "undefined";
if (isBrowser) {
// 在浏览器中使用颜色高亮
const consoleMethod = this._getConsoleMethod(level);
consoleMethod(
`%c${timestamp} %c${prefixStr} %c${levelStr}`,
"color: gray; font-weight: lighter;", // 时间戳样式
"color: #7c57e0; font-weight: bold;", // 前缀样式 (紫色)
`color: ${level.color}; font-weight: bold;`, // 日志级别样式
...args
);
} else {
// 在 Node.js 或不支持样式的环境中,输出纯文本
const consoleMethod = this._getConsoleMethod(level);
consoleMethod(timestamp, prefixStr, levelStr, ...args);
}
}
/**
* 根据日志级别获取对应的 console 方法
* @private
*/
_getConsoleMethod(level) {
switch (level) {
case LogLevel.ERROR:
return console.error;
case LogLevel.WARN:
return console.warn;
case LogLevel.INFO:
return console.info;
default:
return console.log;
}
}
/**
* 记录 DEBUG 级别的日志
* @param {...any} args
*/
debug(...args) {
this._log(LogLevel.DEBUG, ...args);
}
/**
* 记录 INFO 级别的日志
* @param {...any} args
*/
info(...args) {
this._log(LogLevel.INFO, ...args);
}
/**
* 记录 WARN 级别的日志
* @param {...any} args
*/
warn(...args) {
this._log(LogLevel.WARN, ...args);
}
/**
* 记录 ERROR 级别的日志
* @param {...any} args
*/
error(...args) {
this._log(LogLevel.ERROR, ...args);
}
}
const isDevelopment =
typeof process === "undefined" || process.env.NODE_ENV !== "development";
const defaultLevel = isDevelopment ? LogLevel.DEBUG : LogLevel.INFO;
export const logger = new Logger({ level: defaultLevel });
export const kissLog = logger.info.bind(logger);

View File

@@ -70,7 +70,7 @@ class TaskPool {
const res = await fn(args);
resolve(res);
} catch (err) {
kissLog(err, "task");
kissLog("task pool", err);
if (retry < this.#maxRetry) {
setTimeout(() => {
this.#pool.unshift({ ...task, retry: retry + 1 }); // unshift 保证重试任务优先

View File

@@ -2,12 +2,12 @@ import { matchValue, type, isMatch } from "./utils";
import {
GLOBAL_KEY,
REMAIN_KEY,
OPT_TRANS_ALL,
OPT_STYLE_ALL,
OPT_LANGS_FROM,
OPT_LANGS_TO,
// OPT_TIMING_ALL,
GLOBLA_RULE,
DEFAULT_API_TYPE,
} from "../config";
import { loadOrFetchSubRules } from "./subRules";
import { getRulesWithDefault, setRules } from "./storage";
@@ -50,7 +50,7 @@ export const matchRule = async (
rules.splice(-1, 0, ...subRules);
}
} catch (err) {
kissLog(err, "load injectRules");
kissLog("load injectRules", err);
}
}
@@ -86,7 +86,7 @@ export const matchRule = async (
});
[
"translator",
"apiSlug",
"fromLang",
"toLang",
"transOpen",
@@ -158,7 +158,7 @@ export const checkRules = (rules) => {
parentStyle,
injectJs,
injectCss,
translator,
apiSlug,
fromLang,
toLang,
textStyle,
@@ -193,7 +193,7 @@ export const checkRules = (rules) => {
injectCss: type(injectCss) === "string" ? injectCss : "",
bgColor: type(bgColor) === "string" ? bgColor : "",
textDiyStyle: type(textDiyStyle) === "string" ? textDiyStyle : "",
translator: matchValue([GLOBAL_KEY, ...OPT_TRANS_ALL], translator),
apiSlug: apiSlug?.trim() || DEFAULT_API_TYPE,
fromLang: matchValue([GLOBAL_KEY, ...fromLangs], fromLang),
toLang: matchValue([GLOBAL_KEY, ...toLangs], toLang),
textStyle: matchValue([GLOBAL_KEY, ...OPT_STYLE_ALL], textStyle),

View File

@@ -33,7 +33,7 @@ export class ShadowRootMonitor {
try {
monitorInstance.callback(shadowRoot);
} catch (error) {
kissLog(error, "Error in ShadowRootMonitor callback");
kissLog("Error in ShadowRootMonitor callback", error);
}
}
return shadowRoot;

View File

@@ -1,112 +1,106 @@
import { isSameSet } from "./utils";
/**
* 键盘快捷键监听
* @param {*} fn
* @param {*} target
* @param {*} timeout
* @returns
* 键盘快捷键监听
* @param {(pressedKeys: Set<string>, event: KeyboardEvent) => void} onKeyDown - Keydown 回调
* @param {(pressedKeys: Set<string>, event: KeyboardEvent) => void} onKeyUp - Keyup 回调
* @param {EventTarget} target - 监听的目标元素
* @returns {() => void} - 用于注销监听的函数
*/
export const shortcutListener = (fn, target = document, timeout = 3000) => {
const allkeys = new Set();
const curkeys = new Set();
let timer = null;
export const shortcutListener = (
onKeyDown = () => {},
onKeyUp = () => {},
target = document
) => {
const pressedKeys = new Set();
const handleKeydown = (e) => {
timer && clearTimeout(timer);
timer = setTimeout(() => {
allkeys.clear();
curkeys.clear();
clearTimeout(timer);
timer = null;
}, timeout);
if (e.code) {
allkeys.add(e.code);
curkeys.add(e.code);
fn([...curkeys], [...allkeys]);
}
const handleKeyDown = (e) => {
if (pressedKeys.has(e.code)) return;
pressedKeys.add(e.code);
onKeyDown(new Set(pressedKeys), e);
};
const handleKeyup = (e) => {
curkeys.delete(e.code);
if (curkeys.size === 0) {
fn([...curkeys], [...allkeys]);
allkeys.clear();
}
const handleKeyUp = (e) => {
// onKeyUp 应该在 key 从集合中移除前触发,以便判断组合键
onKeyUp(new Set(pressedKeys), e);
pressedKeys.delete(e.code);
};
target.addEventListener("keydown", handleKeydown, true);
target.addEventListener("keyup", handleKeyup, true);
target.addEventListener("keydown", handleKeyDown);
target.addEventListener("keyup", handleKeyUp);
return () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
target.removeEventListener("keydown", handleKeydown);
target.removeEventListener("keyup", handleKeyup);
target.removeEventListener("keydown", handleKeyDown);
target.removeEventListener("keyup", handleKeyUp);
pressedKeys.clear();
};
};
/**
* 注册键盘快捷键
* @param {*} targetKeys
* @param {*} fn
* @param {*} target
* @returns
* @param {string[]} targetKeys - 目标快捷键数组
* @param {() => void} fn - 匹配成功后执行的回调
* @param {EventTarget} target - 监听目标
* @returns {() => void} - 注销函数
*/
export const shortcutRegister = (targetKeys = [], fn, target = document) => {
return shortcutListener((curkeys) => {
if (
targetKeys.length > 0 &&
isSameSet(new Set(targetKeys), new Set(curkeys))
) {
if (targetKeys.length === 0) return () => {};
const targetKeySet = new Set(targetKeys);
const onKeyDown = (pressedKeys, event) => {
if (targetKeySet.size > 0 && isSameSet(targetKeySet, pressedKeys)) {
event.preventDefault();
event.stopPropagation();
fn();
}
}, target);
};
const onKeyUp = () => {};
return shortcutListener(onKeyDown, onKeyUp, target);
};
/**
* 高阶函数:为目标函数增加计次和超时重置功能
* @param {() => void} fn - 需要被包装的函数
* @param {number} step - 需要触发的次数
* @param {number} timeout - 超时毫秒数
* @returns {() => void} - 包装后的新函数
*/
const withStepCounter = (fn, step, timeout) => {
let count = 0;
let timer = null;
return () => {
timer && clearTimeout(timer);
timer = setTimeout(() => {
count = 0;
}, timeout);
count++;
if (count === step) {
count = 0;
clearTimeout(timer);
fn();
}
};
};
/**
* 注册连续快捷键
* @param {*} targetKeys
* @param {*} fn
* @param {*} step
* @param {*} timeout
* @param {*} target
* @returns
* @param {string[]} targetKeys - 目标快捷键数组
* @param {() => void} fn - 成功回调
* @param {number} step - 连续触发次数
* @param {number} timeout - 每次触发的间隔超时
* @param {EventTarget} target - 监听目标
* @returns {() => void} - 注销函数
*/
export const stepShortcutRegister = (
targetKeys = [],
fn,
step = 3,
step = 2,
timeout = 500,
target = document
) => {
let count = 0;
let pre = Date.now();
let timer;
return shortcutListener((curkeys, allkeys) => {
timer && clearTimeout(timer);
timer = setTimeout(() => {
clearTimeout(timer);
count = 0;
}, timeout);
if (targetKeys.length > 0 && curkeys.length === 0) {
const now = Date.now();
if (
(count === 0 || now - pre < timeout) &&
isSameSet(new Set(targetKeys), new Set(allkeys))
) {
count++;
if (count === step) {
count = 0;
fn();
}
} else {
count = 0;
}
pre = now;
}
}, target);
const steppedFn = withStepCounter(fn, step, timeout);
return shortcutRegister(targetKeys, steppedFn, target);
};

View File

@@ -15,6 +15,7 @@ import {
import { isExt, isGm } from "./client";
import { browser } from "./browser";
import { kissLog } from "./log";
import { debounce } from "./utils";
async function set(key, val) {
if (isExt) {
@@ -90,7 +91,7 @@ export const getSettingWithDefault = async () => ({
...((await getSetting()) || {}),
});
export const setSetting = (val) => setObj(STOKEY_SETTING, val);
export const updateSetting = (obj) => putObj(STOKEY_SETTING, obj);
export const putSetting = (obj) => putObj(STOKEY_SETTING, obj);
/**
* 规则列表
@@ -122,14 +123,20 @@ export const setSubRules = (url, val) =>
export const getFab = () => getObj(STOKEY_FAB);
export const getFabWithDefault = async () => (await getFab()) || {};
export const setFab = (obj) => setObj(STOKEY_FAB, obj);
export const updateFab = (obj) => putObj(STOKEY_FAB, obj);
export const putFab = (obj) => putObj(STOKEY_FAB, obj);
/**
* 数据同步
*/
export const getSync = () => getObj(STOKEY_SYNC);
export const getSyncWithDefault = async () => (await getSync()) || DEFAULT_SYNC;
export const updateSync = (obj) => putObj(STOKEY_SYNC, obj);
export const putSync = (obj) => putObj(STOKEY_SYNC, obj);
export const putSyncMeta = async (key) => {
const { syncMeta = {} } = await getSyncWithDefault();
syncMeta[key] = { ...(syncMeta[key] || {}), updateAt: Date.now() };
await putSync({ syncMeta });
};
export const debounceSyncMeta = debounce(putSyncMeta, 300);
/**
* ms auth
@@ -156,6 +163,6 @@ export const tryInitDefaultData = async () => {
BUILTIN_RULES
);
} catch (err) {
kissLog(err, "init default");
kissLog("init default", err);
}
};

View File

@@ -1,7 +1,7 @@
import { GLOBAL_KEY } from "../config";
import {
getSyncWithDefault,
updateSync,
putSync,
setSubRules,
getSubRules,
} from "./storage";
@@ -17,7 +17,7 @@ import { kissLog } from "./log";
const updateSyncDataCache = async (url) => {
const { dataCaches = {} } = await getSyncWithDefault();
dataCaches[url] = Date.now();
await updateSync({ dataCaches });
await putSync({ dataCaches });
};
/**
@@ -47,7 +47,7 @@ export const syncAllSubRules = async (subrulesList) => {
await syncSubRules(subrules.url);
await updateSyncDataCache(subrules.url);
} catch (err) {
kissLog(err, `sync subrule error: ${subrules.url}`);
kissLog(`sync subrule error: ${subrules.url}`, err);
}
}
};
@@ -65,10 +65,10 @@ export const trySyncAllSubRules = async ({ subrulesList }) => {
if (now - subRulesSyncAt > interval) {
// 同步订阅规则
await syncAllSubRules(subrulesList);
await updateSync({ subRulesSyncAt: now });
await putSync({ subRulesSyncAt: now });
}
} catch (err) {
kissLog(err, "try sync all subrules");
kissLog("try sync all subrules", err);
}
};

View File

@@ -9,7 +9,7 @@ import {
} from "../config";
import {
getSyncWithDefault,
updateSync,
putSync,
getSettingWithDefault,
getRulesWithDefault,
getWordsWithDefault,
@@ -61,7 +61,7 @@ const syncByWorker = async (data, { syncUrl, syncKey }) => {
return await apiSyncData(`${syncUrl}/sync`, syncKey, data);
};
const syncData = async (key, valueFn) => {
export const syncData = async (key, value) => {
const {
syncType,
syncUrl,
@@ -70,13 +70,14 @@ const syncData = async (key, valueFn) => {
syncMeta = {},
} = await getSyncWithDefault();
if (!syncUrl || !syncKey || (syncType === OPT_SYNCTYPE_WEBDAV && !syncUser)) {
return;
throw new Error("sync args err");
}
let { updateAt = 0, syncAt = 0 } = syncMeta[key] || {};
syncAt === 0 && (updateAt = 0);
if (syncAt === 0) {
updateAt = 0; // 没有同步过,更新时间置零
}
const value = await valueFn();
const data = {
key,
value: JSON.stringify(value),
@@ -93,13 +94,20 @@ const syncData = async (key, valueFn) => {
? await syncByWebdav(data, args)
: await syncByWorker(data, args);
if (!res) {
throw new Error("sync data got err", key);
}
const newVal = JSON.parse(res.value);
const isNew = res.updateAt > updateAt;
syncMeta[key] = {
updateAt: res.updateAt,
syncAt: Date.now(),
};
await updateSync({ syncMeta });
await putSync({ syncMeta });
return { value: JSON.parse(res.value), isNew: res.updateAt > updateAt };
return { value: newVal, isNew };
};
/**
@@ -107,7 +115,8 @@ const syncData = async (key, valueFn) => {
* @returns
*/
const syncSetting = async () => {
const res = await syncData(KV_SETTING_KEY, getSettingWithDefault);
const value = await getSettingWithDefault();
const res = await syncData(KV_SETTING_KEY, value);
if (res?.isNew) {
await setSetting(res.value);
}
@@ -117,7 +126,7 @@ export const trySyncSetting = async () => {
try {
await syncSetting();
} catch (err) {
kissLog(err, "sync setting");
kissLog("sync setting", err.message);
}
};
@@ -126,7 +135,8 @@ export const trySyncSetting = async () => {
* @returns
*/
const syncRules = async () => {
const res = await syncData(KV_RULES_KEY, getRulesWithDefault);
const value = await getRulesWithDefault();
const res = await syncData(KV_RULES_KEY, value);
if (res?.isNew) {
await setRules(res.value);
}
@@ -136,7 +146,7 @@ export const trySyncRules = async () => {
try {
await syncRules();
} catch (err) {
kissLog(err, "sync user rules");
kissLog("sync user rules", err.message);
}
};
@@ -145,7 +155,8 @@ export const trySyncRules = async () => {
* @returns
*/
const syncWords = async () => {
const res = await syncData(KV_WORDS_KEY, getWordsWithDefault);
const value = await getWordsWithDefault();
const res = await syncData(KV_WORDS_KEY, value);
if (res?.isNew) {
await setWords(res.value);
}
@@ -155,7 +166,7 @@ export const trySyncWords = async () => {
try {
await syncWords();
} catch (err) {
kissLog(err, "sync fav words");
kissLog("sync fav words", err.message);
}
};

View File

@@ -7,9 +7,9 @@ import {
OPT_STYLE_FUZZY,
GLOBLA_RULE,
DEFAULT_SETTING,
DEFAULT_TRANS_APIS,
DEFAULT__MOUSEHOVER_KEY,
DEFAULT_MOUSEHOVER_KEY,
OPT_STYLE_NONE,
DEFAULT_API_SETTING,
} from "../config";
import interpreter from "./interpreter";
import { ShadowRootMonitor } from "./shadowroot";
@@ -356,7 +356,7 @@ export class Translator {
this.#startObserveShadowRoot(shadowRoot);
});
} catch (err) {
kissLog(err, "findAllShadowRoots");
kissLog("findAllShadowRoots", err);
}
}
}
@@ -419,7 +419,7 @@ export class Translator {
termPatterns.push(`(${key})`);
this.#termValues.push(value);
} catch (err) {
kissLog(err, `Invalid RegExp for term: "${key}"`);
kissLog(`Invalid RegExp for term: "${key}"`, err);
}
}
}
@@ -556,7 +556,7 @@ export class Translator {
}
}
} catch (err) {
kissLog(err, "无法访问某个 shadowRoot");
kissLog("无法访问某个 shadowRoot", err);
}
// const end = performance.now();
// const duration = end - start;
@@ -839,7 +839,7 @@ export class Translator {
nodes,
});
} catch (err) {
kissLog(err, "transStartHook");
kissLog("transStartHook", err);
}
}
@@ -913,14 +913,14 @@ export class Translator {
innerNode: inner,
});
} catch (err) {
kissLog(err, "transEndHook");
kissLog("transEndHook", err);
}
}
} catch (err) {
// inner.textContent = `[失败]...`;
// todo: 失败重试按钮
wrapper.remove();
kissLog(err, "translateNodeGroup");
kissLog("translateNodeGroup", err);
}
}
@@ -1037,16 +1037,13 @@ export class Translator {
// 发起翻译请求
#translateFetch(text) {
const { translator, fromLang, toLang } = this.#rule;
// const apiSetting = this.#setting.transApis[translator];
const apiSetting = {
...DEFAULT_TRANS_APIS[translator],
...(this.#setting.transApis[translator] || {}),
};
const { apiSlug, fromLang, toLang } = this.#rule;
const apiSetting =
this.#setting.transApis.find((api) => api.apiSlug === apiSlug) ||
DEFAULT_API_SETTING;
return apiTranslate({
text,
translator,
fromLang,
toLang,
apiSetting,
@@ -1150,11 +1147,11 @@ export class Translator {
return;
}
const { translator, fromLang, toLang, hasRichText, textStyle, transOnly } =
const { apiSlug, fromLang, toLang, hasRichText, textStyle, transOnly } =
this.#rule;
const needsRefresh =
appliedRule.translator !== translator ||
appliedRule.apiSlug !== apiSlug ||
appliedRule.fromLang !== fromLang ||
appliedRule.toLang !== toLang ||
appliedRule.hasRichText !== hasRichText;
@@ -1162,7 +1159,7 @@ export class Translator {
// 需要重新翻译
if (needsRefresh) {
Object.assign(appliedRule, {
translator,
apiSlug,
fromLang,
toLang,
hasRichText,
@@ -1207,7 +1204,7 @@ export class Translator {
document.addEventListener("mousemove", this.#boundMouseMoveHandler);
let { mouseHoverKey } = this.#setting.mouseHoverSetting;
if (mouseHoverKey.length === 0) {
mouseHoverKey = DEFAULT__MOUSEHOVER_KEY;
mouseHoverKey = DEFAULT_MOUSEHOVER_KEY;
}
this.#removeKeydownHandler = shortcutRegister(
mouseHoverKey,
@@ -1273,7 +1270,7 @@ export class Translator {
document.title = trText || title;
})
.catch((err) => {
kissLog(err, "tanslate title");
kissLog("tanslate title", err);
});
}
}