diff --git a/entrypoints/content.ts b/entrypoints/content.ts index e171dd4..e764a95 100644 --- a/entrypoints/content.ts +++ b/entrypoints/content.ts @@ -1,6 +1,6 @@ import {Config} from "./utils/model"; import {cssInject} from "./main/css"; -import {handler} from "./main/trans"; +import {handleTranslation} from "./main/trans"; import {cache} from "./utils/cache"; import {constants} from "@/entrypoints/utils/constant"; @@ -34,7 +34,7 @@ export default defineContentScript({ window.addEventListener('keydown', event => { if (config.hotkey === event.key) { screen.hotkeyPressed = true; - handler(config, screen.mouseX, screen.mouseY, 25) + handleTranslation(config, screen.mouseX, screen.mouseY) } }) @@ -43,7 +43,7 @@ export default defineContentScript({ screen.mouseX = event.clientX; screen.mouseY = event.clientY; if (screen.hotkeyPressed) { - handler(config, screen.mouseX, screen.mouseY, 25, true) + handleTranslation(config, screen.mouseX, screen.mouseY, 50) } }); @@ -52,7 +52,7 @@ export default defineContentScript({ if (event.touches.length === 3) { let centerX = (event.touches[0].clientX + event.touches[1].clientX + event.touches[2].clientX) / 3; let centerY = (event.touches[0].clientY + event.touches[1].clientY + event.touches[2].clientY) / 3; - handler(config, centerX, centerY) + handleTranslation(config, centerX, centerY) } }); @@ -62,8 +62,8 @@ export default defineContentScript({ // 通过双击事件获取鼠标位置 let mouseX = event.clientX; let mouseY = event.clientY; - // 调用 handler 函数进行翻译 - handler(config, mouseX, mouseY); + // 调用 handleTranslation 函数进行翻译 + handleTranslation(config, mouseX, mouseY); } }); @@ -79,7 +79,7 @@ export default defineContentScript({ timer = setTimeout(() => { let mouseX = event.clientX; let mouseY = event.clientY; - handler(config, mouseX, mouseY); + handleTranslation(config, mouseX, mouseY); }, 500) as unknown as number; } }); @@ -104,7 +104,7 @@ export default defineContentScript({ if (event.button === 1) { let mouseX = event.clientX; let mouseY = event.clientY; - handler(config, mouseX, mouseY); + handleTranslation(config, mouseX, mouseY); } } }); diff --git a/entrypoints/main/common.ts b/entrypoints/main/common.ts deleted file mode 100644 index c48cb37..0000000 --- a/entrypoints/main/common.ts +++ /dev/null @@ -1,39 +0,0 @@ -// 面向切面编程,后置函数编写 -import {html} from 'js-beautify'; - -// 格式化翻译后文本 -export function beautyHTML(text: string): string { - // 1、js-beautify格式化代码 - // 2、正则表达式匹配 < a> 则将其替换为 (因为 js-beautify 只能将 < a> 格式化为 < a>) - // return html(text).replace(/<\s+/g, "<") - text = replaceSensitiveWords(text); - - return html(text) -} - -// 替换 svg 标签中的一些大小写敏感的词(html 不区分大小写,但 svg 标签区分大小写) -function replaceSensitiveWords(text: string): string { - return text.replace(/viewbox|preserveaspectratio|clippathunits|gradienttransform|patterncontentunits|lineargradient|clippath/gi, (match) => { - switch (match.toLowerCase()) { - case 'viewbox': - return 'viewBox'; - case 'preserveaspectratio': - return 'preserveAspectRatio'; - case 'clippathunits': - return 'clipPathUnits'; - case 'gradienttransform': - return 'gradientTransform'; - case 'patterncontentunits': - return 'patternContentUnits'; - case 'lineargradient': - return 'linearGradient'; - case 'clippath': - return 'clipPath'; - default: - return match; - } - }); -} - - - diff --git a/entrypoints/main/compatible.ts b/entrypoints/main/compat.ts similarity index 100% rename from entrypoints/main/compatible.ts rename to entrypoints/main/compat.ts diff --git a/entrypoints/main/css.ts b/entrypoints/main/css.ts index 9e3c988..fcdab10 100644 --- a/entrypoints/main/css.ts +++ b/entrypoints/main/css.ts @@ -99,7 +99,7 @@ const css = ` } /*双语模式,block 强制换行、inline-block 自适应换行*/ -.fluent-bilingual-style{ +.fluent-read-bilingual{ display: block; word-break: break-word; margin: 8px 0 !important; diff --git a/entrypoints/main/dom.ts b/entrypoints/main/dom.ts index dedd491..dfed962 100644 --- a/entrypoints/main/dom.ts +++ b/entrypoints/main/dom.ts @@ -1,6 +1,7 @@ import {Config} from "@/entrypoints/utils/model"; -import {btnTransThrottle} from "@/entrypoints/main/trans"; -import {getMainDomain, selectCompatFn} from "@/entrypoints/main/compatible"; +import {getMainDomain, selectCompatFn} from "@/entrypoints/main/compat"; +import {html} from 'js-beautify'; +import {handleBtnTranslation} from "@/entrypoints/main/trans"; // 当遇到这些 tag 时直接翻译 const directSet = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', "li"]); @@ -33,12 +34,12 @@ export function grabNode(config: Config, node: any): any { // 3、button 按钮适配 if (curTag === 'button' || (curTag === 'span' && node.parentNode && node.parentNode.tagName.toLowerCase() === 'button')) { // 翻译按钮内部而不是整个按钮,避免按钮失去响应式点击事件 - if (node.textContent.trim() !== '') btnTransThrottle(config, node) + if (node.textContent.trim() !== '') handleBtnTranslation(config, node) return false; } // 4、特殊适配,根据域名进行特殊处理 - let fn = selectCompatFn[getMainDomain(url)]; + let fn = selectCompatFn[getMainDomain(url.host)]; if (fn && fn(node)) return node; // 4、如果遇到 span,则首先该节点就符合翻译条件 @@ -98,3 +99,32 @@ export function LLMStandardHTML(node: any) { }); return text; } + +export function beautyHTML(text: string): string { + text = replaceSensitiveWords(text); + return html(text) +} + +// 替换 svg 标签中的一些大小写敏感的词(html 不区分大小写,但 svg 标签区分大小写) +function replaceSensitiveWords(text: string): string { + return text.replace(/viewbox|preserveaspectratio|clippathunits|gradienttransform|patterncontentunits|lineargradient|clippath/gi, (match) => { + switch (match.toLowerCase()) { + case 'viewbox': + return 'viewBox'; + case 'preserveaspectratio': + return 'preserveAspectRatio'; + case 'clippathunits': + return 'clipPathUnits'; + case 'gradienttransform': + return 'gradientTransform'; + case 'patterncontentunits': + return 'patternContentUnits'; + case 'lineargradient': + return 'linearGradient'; + case 'clippath': + return 'clipPath'; + default: + return match; + } + }); +} diff --git a/entrypoints/main/trans.ts b/entrypoints/main/trans.ts index b8e6a70..cd027b8 100644 --- a/entrypoints/main/trans.ts +++ b/entrypoints/main/trans.ts @@ -1,285 +1,209 @@ -import {checkConfig, hasClassName, skipNode} from "./check"; +import {checkConfig, hasClassName, skipNode} from "../utils/check"; import {Config} from "../utils/model"; -import {getMainDomain, replaceCompatFn} from "./compatible"; import {cache} from "../utils/cache"; -import {detectlang} from "./detectlang"; import {options, services} from "../utils/option"; -import {insertFailedTip, insertLoadingSpinner} from "./icon"; -import {beautyHTML} from "./common"; -import {throttle} from "@/entrypoints/utils/tip"; +import {insertFailedTip, insertLoadingSpinner} from "../utils/icon"; import {styles} from "@/entrypoints/utils/constant"; import {smashTruncationStyle} from "@/entrypoints/main/css"; -import {grabNode, LLMStandardHTML} from "@/entrypoints/main/dom"; - -// todo 需重构、分离 +import {beautyHTML, grabNode, LLMStandardHTML} from "@/entrypoints/main/dom"; +import {detectlang, throttle} from "@/entrypoints/utils/common"; let hoverTimer: any; // 鼠标悬停计时器 -let htmlSet = new Set(); // 去重 +let htmlSet = new Set(); // 防抖 const url = new URL(location.href.split('?')[0]); -export function handler(config: Config, mouseX: number, mouseY: number, time: number = 0, slide = false) { - - // 检查配置项是否正确 +export function handleTranslation(config: Config, mouseX: number, mouseY: number, delayTime: number = 0) { + // 检查配置 if (!checkConfig(config)) return; - clearTimeout(hoverTimer); // 清除计时器 + clearTimeout(hoverTimer); hoverTimer = setTimeout(() => { - // 获取起始节点 - let node = grabNode(config, document.elementFromPoint(mouseX, mouseY)); // 获取最终需要翻译的节点 - // console.log("翻译节点:", node); + let node = grabNode(config, document.elementFromPoint(mouseX, mouseY)); - // 跳过不需要翻译的节点 + // 判断是否跳过节点 if (skipNode(node)) return; - // 去重判断 + // 防抖 let nodeOuterHTML = node.outerHTML; - if (htmlSet.has(nodeOuterHTML)) { - // console.log('重复节点', node); - return; - } + if (htmlSet.has(nodeOuterHTML)) return; htmlSet.add(nodeOuterHTML); - // TODO 2024.5.18 下述代码需重构 - // 判断是否为双语对照模式,如果是则走双语翻译 - if (config.style === styles.bilingualComparison) { - // 如果已经翻译过,则移除译文 - let bilingualNode = hasClassName(node, 'fluent-bilingual-tag'); - if (bilingualNode) { - if (slide) { - htmlSet.delete(nodeOuterHTML); - return; - } - let spinner = insertLoadingSpinner(bilingualNode, true); - setTimeout(() => { - spinner.remove(); - bilingualNode.remove(); - htmlSet.delete(nodeOuterHTML); - }, 250); - return; - } - // 判断缓存 - let cached = cache.localGet(config, node.innerText); - if (cached) { - let spinner = insertLoadingSpinner(node, true); - if (!cached || origin === cached) { - htmlSet.delete(nodeOuterHTML); - return; - } - // 250 ms 后移除 spinner - setTimeout(() => { - spinner.remove(); - htmlSet.delete(nodeOuterHTML); - bilingualAppendChild(config, node, cached); // 追加至原文后面 - }, 250); - - return; - } - - bilingualTranslate(config, node, nodeOuterHTML) - return; + // 根据翻译模式进行翻译 + if (config.style === styles.bilingualTranslation) { + handleBilingualTranslation(config, node, delayTime > 0); // 根据 delayTime 可判断是否为滑动翻译 + } else { + handleSingleTranslation(config, node, delayTime > 0); } + }, delayTime); +} - // else 为仅译文模式 - - // 检测缓存 - let outerHTMLCache = cache.localGet(config, node.outerHTML); - if (outerHTMLCache) { - if (slide) { - htmlSet.delete(nodeOuterHTML); - return; - } - // console.log("缓存命中:", outerHTMLCache); - let spinner = insertLoadingSpinner(node, true); - setTimeout(() => { // 延迟 remove 转圈动画与替换文本 - spinner.remove(); - htmlSet.delete(nodeOuterHTML); - let compatFn = replaceCompatFn[getMainDomain(url.host)]; // 兼容函数 - if (compatFn) { - compatFn(node, outerHTMLCache); // 兼容函数 - } else { - node.outerHTML = outerHTMLCache; - } - cache.set(htmlSet, outerHTMLCache, 250); - }, 250); +// 双语翻译 +function handleBilingualTranslation(config: Config, node: any, slide: boolean) { + let nodeOuterHTML = node.outerHTML; + // 如果已经翻译过,250ms 后删除翻译结果 + let bilingualNode = hasClassName(node, 'fluent-read-bilingual'); + if (bilingualNode) { + if (slide) { + htmlSet.delete(nodeOuterHTML); return; } - // console.log("无缓存:", node.outerHTML); - // 无缓存,正常翻译 - translate(config, node); - }, time); -} - -// 双语模式追加翻译结果+控制样式 -function bilingualAppendChild(config: Config, node: any, text: string) { - - let newNode = document.createElement("span"); - - newNode.classList.add("fluent-bilingual-style", "fluent-bilingual-tag") // 样式、标记 - newNode.classList.add(options.styles[config.display].class); // 控制译文显示样式 - - newNode.innerHTML = text; + let spinner = insertLoadingSpinner(bilingualNode, true); + setTimeout(() => { + spinner.remove(); + bilingualNode.remove(); + htmlSet.delete(nodeOuterHTML); + }, 250); + return; + } - // 移除特定样式,这些样式会影响翻译结果的显示 - smashTruncationStyle(node); + // 检查是否有缓存 + let cached = cache.localGet(config, node.innerText); + if (cached) { + let spinner = insertLoadingSpinner(node, true); + setTimeout(() => { + spinner.remove(); + htmlSet.delete(nodeOuterHTML); + bilingualAppendChild(config, node, cached); + }, 250); + return; + } - node.appendChild(newNode); + // 翻译 + bilingualTranslate(config, node, nodeOuterHTML); } - -// 按钮翻译节流 -export const btnTransThrottle = throttle(btnTrans, 250); - -function btnTrans(config: Config, child: any) { - if (services.isMachine(config.service)) { - // 机器翻译 innerHTML - let rs = cache.localGet(config, child.innerHTML); - if (rs) { - child.innerHTML = rs; - return; - } - // background.ts - browser.runtime.sendMessage({context: document.title, origin: child.innerHTML}) - .then((text: string) => { - cache.localSetDual(config, child.innerHTML, text) - child.innerHTML = text - }).catch(error => console.error('调用失败:', error)) - } else { - // LLM 翻译 textContent - let rs = cache.localGet(config, child.textContent); - if (rs) { - child.textContent = rs; +function handleSingleTranslation(config: Config, node: any, slide: boolean) { + let nodeOuterHTML = node.outerHTML; + let outerHTMLCache = cache.localGet(config, node.outerHTML); + if (outerHTMLCache) { + if (slide) { + htmlSet.delete(nodeOuterHTML); return; } - // background.ts - browser.runtime.sendMessage({context: document.title, origin: child.textContent}) - .then((text: string) => { - cache.localSetDual(config, child.textContent, text) - child.textContent = text - }).catch(error => console.error('调用失败:', error)) + let spinner = insertLoadingSpinner(node, true); + setTimeout(() => { + spinner.remove(); + htmlSet.delete(nodeOuterHTML); + + // 创建临时节点 span,获取 outerHTMLCache innerHTML + let tmpNode = document.createElement("span"); + tmpNode.innerHTML = outerHTMLCache; + let elementNode = tmpNode.firstChild as HTMLElement; + + // 替换节点内容 + node.innerHTML = elementNode.innerHTML; + + cache.set(htmlSet, outerHTMLCache, 250); + }, 250); + return; } -} + singleTranslate(config, node); +} function bilingualTranslate(config: Config, node: any, nodeOuterHTML: any) { - // 正则表达式去除所有空格后再检查语言类型 if (detectlang(node.textContent.replace(/[\s\u3000]/g, '')) === config.to) return; - // 待翻译文本 - let origin = node.innerText - // 插入转圈动画 + + let origin = node.innerText; let spinner = insertLoadingSpinner(node); - // 超时控制 let timeout = setTimeout(() => { insertFailedTip(config, node, "timeout", spinner); }, 45000); - // 调用翻译服务(正在翻译ing...),允许失败重试 3 次、间隔 500ms - const translating = (failCount = 0) => { - - config.count++ && storage.setItem('local:config', JSON.stringify(config)); // 翻译次数 +1,更新配置 + config.count++ && storage.setItem('local:config', JSON.stringify(config)); + // 正在翻译...允许失败重试 3 次 + const translating = (failCount = 0) => { browser.runtime.sendMessage({context: document.title, origin: origin}) .then((text: string) => { - clearTimeout(timeout) // 取消超时 - spinner.remove() // 移除 spinner - setTimeout(() => { - bilingualAppendChild(config, node, text); - htmlSet.delete(nodeOuterHTML); - }, 150); - - // 相同也不应该隐藏,否则可能会造成用户误解 - // if (!text || origin === text) return; - + clearTimeout(timeout); + spinner.remove(); + htmlSet.delete(nodeOuterHTML); + bilingualAppendChild(config, node, text); cache.localSet(config, origin, text); }) .catch(error => { clearTimeout(timeout); - if (failCount < 3) { // 如果失败次数小于3次,重新尝试 - // 延迟 500ms 后重试 + if (failCount < 3) { setTimeout(() => { translating(failCount + 1); - }, 500); - } else { // 达到3次失败后,显示失败提示 + }, 1000); + } else { insertFailedTip(config, node, error.toString() || "", spinner); } }); } - translating(); // 开始翻译 + translating(); } -export function translate(config: Config, node: any) { - - // console.log("翻译节点:", node) - - // 正则表达式去除所有空格后再检查语言类型 - // 如果源语言与目标语言相同,则不翻译 +export function singleTranslate(config: Config, node: any) { if (detectlang(node.textContent.replace(/[\s\u3000]/g, '')) === config.to) return; - // origin 是待翻译文本;机器翻译 origin = outerHTML;LLM 翻译 origin = llmGetText(node) let origin = services.isMachine(config.service) ? node.outerHTML : LLMStandardHTML(node); - - let spinner = insertLoadingSpinner(node); // 插入转圈动画 + let spinner = insertLoadingSpinner(node); let timeout = setTimeout(() => { insertFailedTip(config, node, "timeout", spinner); }, 45000); - config.count++; // 翻译次数 +1 - storage.setItem('local:config', JSON.stringify(config)); // 更新配置 + config.count++ && storage.setItem('local:config', JSON.stringify(config)); - // 调用翻译服务(正在翻译ing...),允许失败重试 3 次、间隔 500ms + // 正在翻译...允许失败重试 3 次 const translating = (failCount = 0) => { browser.runtime.sendMessage({context: document.title, origin: origin}) .then((text: string) => { - clearTimeout(timeout) // 取消超时 - spinner.remove() // 移除 spinner - - // 格式化 html 翻译结果 - text = beautyHTML(text) + clearTimeout(timeout); + spinner.remove(); - // console.log("翻译前的句子:", origin); - // console.log("翻译后的句子:", text); + text = beautyHTML(text); if (!text || origin === text) return; - let oldOuterHtml = node.outerHTML // 保存旧的 outerHTML - - let newOuterHtml = text - if (services.isMachine(config.service)) { - // 1、机器翻译 - if (!node.parentNode) return; - let compatFn = replaceCompatFn[getMainDomain(url.host)]; - if (compatFn) { - oldOuterHtml = node.outerHTML; - compatFn(node, text); - newOuterHtml = node.outerHTML; - } else { - node.outerHTML = text; - } - } else { - // 2、LLM 翻译 - node.innerHTML = text; - newOuterHtml = node.outerHTML; - } + let oldOuterHtml = node.outerHTML; + node.innerHTML = text; + let newOuterHtml = node.outerHTML; - cache.localSetDual(config, oldOuterHtml, newOuterHtml); // 设置缓存 - // 延迟删除 newOuterHtml,立即删除 oldOuterHtml + // 缓存翻译结果 + cache.localSetDual(config, oldOuterHtml, newOuterHtml); cache.set(htmlSet, newOuterHtml, 250); htmlSet.delete(oldOuterHtml); }) .catch(error => { clearTimeout(timeout); - if (failCount < 3) { // 如果失败次数小于3次,重新尝试 - // 延迟 500ms 后重试 + if (failCount < 3) { setTimeout(() => { translating(failCount + 1); - }, 500); - } else { // 达到3次失败后,显示失败提示 + }, 1000); + } else { insertFailedTip(config, node, error.toString() || "", spinner); } }); } - - translating(); // 开始翻译 + translating(); } +export const handleBtnTranslation = throttle((config: Config, node: any) => { + let origin = node.innerText; + let rs = cache.localGet(config, origin); + if (rs) { + node.innerText = rs; + return; + } + + browser.runtime.sendMessage({context: document.title, origin: origin}) + .then((text: string) => { + cache.localSetDual(config, origin, text); + node.innerText = text; + }).catch(error => console.error('调用失败:', error)) +}, 250) + + +function bilingualAppendChild(config: Config, node: any, text: string) { + let newNode = document.createElement("span"); + newNode.classList.add("fluent-read-bilingual"); + newNode.classList.add(options.styles[config.display].class); + newNode.innerHTML = text; + smashTruncationStyle(node); + node.appendChild(newNode); +} \ No newline at end of file diff --git a/entrypoints/utils/cache.ts b/entrypoints/utils/cache.ts index 7e23805..01753fa 100644 --- a/entrypoints/utils/cache.ts +++ b/entrypoints/utils/cache.ts @@ -9,21 +9,21 @@ function buildKey(config: Config, message: string) { let service = config.service let model = config.model[service] === customModelString ? config.customModel[service] : config.model[service] // 前缀_服务_模型_目标语言_消息 - return prefix + service + "_" + model + "_" + config.to + "_" + message + return [prefix, config.style, service, model, config.to, message].join('_') } export const cache = { - set(set: Set, key: any, time: number) { + set(set: Set, key: any, expire: number) { set.add(key); - if (time >= 0) setTimeout(() => set.delete(key), time); + if (expire >= 0) setTimeout(() => set.delete(key), expire); }, // local 系列为特化的缓存方法,用于操作翻译缓存 - localSet(config: Config, origin: string, result: string) { - localStorage.setItem(buildKey(config, origin), result) + localSet(config: Config, key: string, value: string) { + localStorage.setItem(buildKey(config, key), value) }, - localSetDual(config: Config, origin: string, result: string) { - this.localSet(config, result, origin) - this.localSet(config, origin, result) + localSetDual(config: Config, key: string, value: string) { + this.localSet(config, value, key) + this.localSet(config, key, value) }, localGet(config: Config, origin: string) { return localStorage.getItem(buildKey(config, origin)) diff --git a/entrypoints/main/check.ts b/entrypoints/utils/check.ts similarity index 95% rename from entrypoints/main/check.ts rename to entrypoints/utils/check.ts index 0c522e2..8ae3fd8 100644 --- a/entrypoints/main/check.ts +++ b/entrypoints/utils/check.ts @@ -1,6 +1,6 @@ -import {customModelString, services} from "../utils/option"; -import {Config} from "../utils/model"; -import {sendErrorMessage} from "../utils/tip"; +import {customModelString, services} from "./option"; +import {Config} from "./model"; +import {sendErrorMessage} from "./tip"; // 检查翻译前配置 export function checkConfig(config: Config): boolean { diff --git a/entrypoints/main/detectlang.ts b/entrypoints/utils/common.ts similarity index 56% rename from entrypoints/main/detectlang.ts rename to entrypoints/utils/common.ts index 79e272f..c733137 100644 --- a/entrypoints/main/detectlang.ts +++ b/entrypoints/utils/common.ts @@ -1,5 +1,17 @@ +// 防抖限流函数,可传递参数 import {franc} from "franc-min"; +export function throttle(fn: (...args: any[]) => void, interval: number) { + let last = 0; // 维护上次执行的时间 + return function(this: any, ...args: any[]) { + const now = Date.now(); + if (now - last >= interval) { + last = now; + fn.apply(this, args); // 使用 apply 来传递参数数组 + } + }; +} + // 输出标准的语言类型,franc 只返回最可信的结果,francAll 返回所有结果并包含确信度 export function detectlang(origin: any): string { let find = franc(origin, {minLength: 0}); diff --git a/entrypoints/utils/constant.ts b/entrypoints/utils/constant.ts index c17ff81..3fd6687 100644 --- a/entrypoints/utils/constant.ts +++ b/entrypoints/utils/constant.ts @@ -33,7 +33,7 @@ export const constants = { export const styles = { // 仅译文 - translationOnly: 0, + singleTranslation: 0, // 双语对照模式 - bilingualComparison: 1, + bilingualTranslation: 1, } \ No newline at end of file diff --git a/entrypoints/main/icon.ts b/entrypoints/utils/icon.ts similarity index 97% rename from entrypoints/main/icon.ts rename to entrypoints/utils/icon.ts index 04cf808..0bbce6a 100644 --- a/entrypoints/main/icon.ts +++ b/entrypoints/utils/icon.ts @@ -1,8 +1,8 @@ // 失败时展示的图标 -import {sendErrorMessage} from "../utils/tip"; -import {Config} from "../utils/model"; +import {sendErrorMessage} from "./tip"; +import {Config} from "./model"; import 'element-plus/es/components/message/style/css' -import {translate} from "./trans"; +import {singleTranslate} from "@/entrypoints/main/trans"; const icon = { retry: ` @@ -31,7 +31,7 @@ export function insertFailedTip(config: Config, node: any, errMsg: string, spinn event.stopPropagation(); // 阻止事件继续向上传播 wrapper.remove(); // 移除错误提示元素,重新翻译 - translate(config, node); + singleTranslate(config, node); }); // 添加失败标记 failure diff --git a/entrypoints/utils/option.ts b/entrypoints/utils/option.ts index 07d6445..fdbbc9c 100644 --- a/entrypoints/utils/option.ts +++ b/entrypoints/utils/option.ts @@ -258,9 +258,10 @@ export const options = { }, { value: 1, - label: '弱化', - class: 'fluent-display-dimmed', + label: '加粗', + class: 'fluent-display-bold', }, + { value: 2, label: '实线下划线', @@ -271,10 +272,11 @@ export const options = { label: '虚线下划线', class: 'fluent-display-dot-underline', }, + { value: 4, - label: '学习效果', - class: 'fluent-display-learning-mode', + label: '弱化', + class: 'fluent-display-dimmed', }, { value: 5, @@ -288,19 +290,20 @@ export const options = { }, { value: 7, - label: '马克笔', - class: 'fluent-display-marker', + label: '学习效果', + class: 'fluent-display-learning-mode', }, { value: 8, - label: '引用', - class: 'fluent-display-quote', + label: '马克笔', + class: 'fluent-display-marker', }, { value: 9, - label: '加粗', - class: 'fluent-display-bold', + label: '引用', + class: 'fluent-display-quote', }, + ], } diff --git a/entrypoints/utils/tip.ts b/entrypoints/utils/tip.ts index 93f74fa..ae22e5b 100644 --- a/entrypoints/utils/tip.ts +++ b/entrypoints/utils/tip.ts @@ -1,4 +1,5 @@ import {ElMessage} from "element-plus"; +import {throttle} from "@/entrypoints/utils/common"; const prefix = "流畅阅读:"; @@ -10,18 +11,6 @@ function _sendSuccessMessage(message: string) { ElMessage({message: prefix + message, type: 'success'}); } -// 修改防抖限流函数(允许传递参数) -export function throttle(fn: (...args: any[]) => void, interval: number) { - let last = 0; // 维护上次执行的时间 - return function(this: any, ...args: any[]) { // 使用 any 放宽 this 类型 - const now = Date.now(); - if (now - last >= interval) { - last = now; - fn.apply(this, args); // 使用 apply 来传递参数数组 - } - }; -} - // 使用防抖函数包装,1s 内只能发送一次消息 export const sendErrorMessage = throttle(_sendErrorMessage, 1000); export const sendSuccessMessage = throttle(_sendSuccessMessage, 1000); diff --git a/package.json b/package.json index 5fef9f3..5519373 100644 --- a/package.json +++ b/package.json @@ -17,16 +17,16 @@ "dependencies": { "@element-plus/icons-vue": "^2.3.1", "crypto-js": "^4.2.0", - "element-plus": "^2.6.2", + "element-plus": "^2.7.3", "franc-min": "^6.2.0", "js-beautify": "^1.15.1", - "vue": "^3.4.0" + "vue": "^3.4.27" }, "devDependencies": { - "@vitejs/plugin-vue": "^5.0.1", - "typescript": "^5.3.3", - "vite": "^5.2.7", - "vue-tsc": "^2.0.6", - "wxt": "^0.17.0" + "@vitejs/plugin-vue": "^5.0.4", + "typescript": "^5.4.5", + "vite": "^5.2.11", + "vue-tsc": "^2.0.19", + "wxt": "^0.18.3" } }