Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71bbd2e54a | ||
|
|
3083d8e147 | ||
|
|
e74883e9c2 | ||
|
|
0816a9d167 | ||
|
|
4b3e91fa84 | ||
|
|
0973a0b60e | ||
|
|
de5f61126d | ||
|
|
0c20ca761f | ||
|
|
4bce56207e | ||
|
|
dca54e0033 | ||
|
|
309646bf1d | ||
|
|
18b9961b39 | ||
|
|
1e51ff17f2 | ||
|
|
63b5f707e2 | ||
|
|
30efb6ee7a | ||
|
|
61b017618a | ||
|
|
1e0397adc9 | ||
|
|
48b34bf95f | ||
|
|
d5fc69e210 | ||
|
|
59f9dd697f | ||
|
|
c9d72323f1 | ||
|
|
e87f7f3abe | ||
|
|
82ebbcb6d6 | ||
|
|
2db11070c5 | ||
|
|
5efd2517e7 | ||
|
|
c0ba654678 | ||
|
|
546a5a549b | ||
|
|
cbf02c34e3 | ||
|
|
74a7258f10 | ||
|
|
1006c044bc | ||
|
|
ef4ea719f3 | ||
|
|
8b34afe69f | ||
|
|
01292af298 | ||
|
|
cff8b2fe39 | ||
|
|
2cb20b5cc0 | ||
|
|
8f2aed18fe | ||
|
|
d85831cc9a | ||
|
|
55dc3a5556 | ||
|
|
591afe08bd | ||
|
|
748f2002ab | ||
|
|
d2d18a2384 | ||
|
|
35f4fa6aa7 | ||
|
|
66fc2d22ed | ||
|
|
16cf9ee1ed | ||
|
|
d9d97bf14c | ||
|
|
dc811bd3c7 | ||
|
|
b939d1849a |
2
.env
2
.env
@@ -2,7 +2,7 @@ GENERATE_SOURCEMAP=false
|
||||
|
||||
REACT_APP_NAME=KISS Translator
|
||||
REACT_APP_NAME_CN=简约翻译
|
||||
REACT_APP_VERSION=1.7.15
|
||||
REACT_APP_VERSION=1.8.2
|
||||
|
||||
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ A simple, open source [bilingual translation extension & Greasemonkey script](ht
|
||||
- [x] WebDAV
|
||||
- [x] Custom translation rules
|
||||
- [x] Rule subscription/rule sharing
|
||||
- [x] Customized terminology
|
||||
- [x] Custom translation style
|
||||
- [x] Custom shortcut keys
|
||||
- `Alt+Q` Toggle Translation
|
||||
@@ -84,3 +85,7 @@ pnpm build
|
||||
## Discussion
|
||||
|
||||
- Join [Telegram Group](https://t.me/+RRCu_4oNwrM2NmFl)
|
||||
|
||||
## Appreciate
|
||||
|
||||

|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
- [x] WebDAV
|
||||
- [x] 自定义翻译规则
|
||||
- [x] 规则订阅/规则分享
|
||||
- [x] 自定义专业术语
|
||||
- [x] 自定义译文样式
|
||||
- [x] 自定义快捷键
|
||||
- `Alt+Q` 开启翻译
|
||||
- `Alt+Q` 启停翻译
|
||||
- `Alt+C` 切换样式
|
||||
- `Alt+K` 打开设置弹窗
|
||||
- `Alt+S` 打开翻译弹窗/翻译选中文字
|
||||
@@ -84,3 +85,7 @@ pnpm build
|
||||
## 交流
|
||||
|
||||
- 加入 [Telegram 群](https://t.me/+RRCu_4oNwrM2NmFl)
|
||||
|
||||
## 赞赏
|
||||
|
||||

|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kiss-translator",
|
||||
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
||||
"version": "1.7.15",
|
||||
"version": "1.8.2",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
"message": "A simple bilingual translation extension & Greasemonkey script"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "Toggle Translate (Alt+Q)"
|
||||
"message": "Toggle Translate"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "Toggle Style (Alt+C)"
|
||||
"message": "Toggle Style"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "Open Options (Alt+O)"
|
||||
"message": "Open Options"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "Translate Popup/Selected (Alt+S)"
|
||||
"message": "Translate Popup/Selected"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
"message": "一个简约的双语对照翻译扩展 & 油猴脚本"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "开启翻译 (Alt+Q)"
|
||||
"message": "启停翻译"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "切换样式 (Alt+C)"
|
||||
"message": "切换样式"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "打开设置 (Alt+O)"
|
||||
"message": "打开设置"
|
||||
},
|
||||
"open_tranbox": {
|
||||
"message": "翻译弹窗/选中文字 (Alt+S)"
|
||||
"message": "翻译弹窗/选中文字"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,45 @@
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root">
|
||||
<p>You need to enable <code>JavaScript</code> to run <span>this app.</span></p>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<div id="content">
|
||||
<p>You need to enable JavaScript to run <span>this app.</span></p>
|
||||
The <span>embargo</span> has just lifted to confirm that AmpereOne is
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "1.7.15",
|
||||
"version": "1.8.2",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
@@ -28,6 +28,12 @@
|
||||
},
|
||||
"description": "__MSG_toggle_translate__"
|
||||
},
|
||||
"openTranbox": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+S"
|
||||
},
|
||||
"description": "__MSG_open_tranbox__"
|
||||
},
|
||||
"toggleStyle": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+C"
|
||||
@@ -35,9 +41,6 @@
|
||||
"description": "__MSG_toggle_style__"
|
||||
},
|
||||
"openOptions": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+O"
|
||||
},
|
||||
"description": "__MSG_open_options__"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "1.7.15",
|
||||
"version": "1.8.2",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
@@ -29,6 +29,12 @@
|
||||
},
|
||||
"description": "__MSG_toggle_translate__"
|
||||
},
|
||||
"openTranbox": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+S"
|
||||
},
|
||||
"description": "__MSG_open_tranbox__"
|
||||
},
|
||||
"toggleStyle": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+C"
|
||||
@@ -36,9 +42,6 @@
|
||||
"description": "__MSG_toggle_style__"
|
||||
},
|
||||
"openOptions": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+O"
|
||||
},
|
||||
"description": "__MSG_open_options__"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import queryString from "query-string";
|
||||
import { getBdauth, setBdauth } from "../libs/storage";
|
||||
import { URL_BAIDU_WEB, URL_BAIDU_TRAN } from "../config";
|
||||
import {
|
||||
URL_BAIDU_WEB,
|
||||
URL_BAIDU_TRANSAPI_V2,
|
||||
URL_BAIDU_TRANSAPI,
|
||||
} from "../config";
|
||||
import { fetchApi } from "../libs/fetch";
|
||||
|
||||
/* eslint-disable */
|
||||
@@ -203,7 +207,12 @@ const _bdAuth = () => {
|
||||
|
||||
const bdAuth = _bdAuth();
|
||||
|
||||
export const genBaidu = async ({ text, from, to }) => {
|
||||
/**
|
||||
* 失效作废
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export const genBaiduV2 = async ({ text, from, to }) => {
|
||||
const { token, gtk } = await bdAuth();
|
||||
const sign = getSign(text, gtk);
|
||||
const data = {
|
||||
@@ -217,7 +226,7 @@ export const genBaidu = async ({ text, from, to }) => {
|
||||
ts: Date.now(),
|
||||
};
|
||||
|
||||
const input = `${URL_BAIDU_TRAN}?from=${from}&to=${to}`;
|
||||
const input = `${URL_BAIDU_TRANSAPI_V2}?from=${from}&to=${to}`;
|
||||
const init = {
|
||||
headers: {
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
@@ -228,3 +237,22 @@ export const genBaidu = async ({ text, from, to }) => {
|
||||
|
||||
return [input, init];
|
||||
};
|
||||
|
||||
export const genBaidu = async ({ text, from, to }) => {
|
||||
const data = {
|
||||
from,
|
||||
to,
|
||||
query: text,
|
||||
source: "txt",
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
},
|
||||
method: "POST",
|
||||
body: queryString.stringify(data),
|
||||
};
|
||||
|
||||
return [URL_BAIDU_TRANSAPI, init];
|
||||
};
|
||||
|
||||
@@ -125,11 +125,14 @@ export const apiTranslate = async ({
|
||||
return [trText, isSame];
|
||||
}
|
||||
|
||||
// 版本号一/二位升级,旧缓存失效
|
||||
const [v1, v2] = process.env.REACT_APP_VERSION.split(".");
|
||||
const cacheOpts = {
|
||||
translator,
|
||||
text,
|
||||
fromLang,
|
||||
toLang,
|
||||
version: [v1, v2].join("."),
|
||||
};
|
||||
|
||||
const transOpts = {
|
||||
@@ -155,7 +158,9 @@ export const apiTranslate = async ({
|
||||
isSame = to === res.src;
|
||||
break;
|
||||
case OPT_TRANS_MICROSOFT:
|
||||
trText = res[0].translations.map((item) => item.text).join(" ");
|
||||
trText = res
|
||||
.map((item) => item.translations.map((item) => item.text).join(" "))
|
||||
.join(" ");
|
||||
isSame = text === trText;
|
||||
break;
|
||||
case OPT_TRANS_DEEPL:
|
||||
@@ -171,19 +176,28 @@ export const apiTranslate = async ({
|
||||
isSame = to === res.source_lang;
|
||||
break;
|
||||
case OPT_TRANS_BAIDU:
|
||||
trText = res.trans_result?.data.map((item) => item.dst).join(" ");
|
||||
isSame = res.trans_result?.to === res.trans_result?.from;
|
||||
// trText = res.trans_result?.data.map((item) => item.dst).join(" ");
|
||||
// isSame = res.trans_result?.to === res.trans_result?.from;
|
||||
if (res.type === 1) {
|
||||
trText = Object.keys(JSON.parse(res.result).content[0].mean[0].cont)[0];
|
||||
isSame = to === res.from;
|
||||
} else if (res.type === 2) {
|
||||
trText = res.data.map((item) => item.dst).join(" ");
|
||||
isSame = to === res.from;
|
||||
}
|
||||
break;
|
||||
case OPT_TRANS_TENCENT:
|
||||
trText = res.auto_translation;
|
||||
isSame = text === trText;
|
||||
break;
|
||||
case OPT_TRANS_OPENAI:
|
||||
trText = res?.choices?.[0].message.content;
|
||||
trText = res?.choices?.map((item) => item.message.content).join(" ");
|
||||
isSame = text === trText;
|
||||
break;
|
||||
case OPT_TRANS_GEMINI:
|
||||
trText = res?.candidates?.[0].content.parts[0].text;
|
||||
trText = res?.candidates
|
||||
?.map((item) => item.content.parts.map((item) => item.text).join(" "))
|
||||
.join(" ");
|
||||
isSame = text === trText;
|
||||
break;
|
||||
case OPT_TRANS_CLOUDFLAREAI:
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
MSG_TRANS_TOGGLE_STYLE,
|
||||
MSG_OPEN_TRANBOX,
|
||||
MSG_CONTEXT_MENUS,
|
||||
MSG_COMMAND_SHORTCUTS,
|
||||
CMD_TOGGLE_TRANSLATE,
|
||||
CMD_TOGGLE_STYLE,
|
||||
CMD_OPEN_OPTIONS,
|
||||
@@ -27,39 +28,51 @@ globalThis.ContextType = "BACKGROUND";
|
||||
/**
|
||||
* 添加右键菜单
|
||||
*/
|
||||
function addContextMenus() {
|
||||
browser.contextMenus.create({
|
||||
id: CMD_TOGGLE_TRANSLATE,
|
||||
title: browser.i18n.getMessage("toggle_translate"),
|
||||
contexts: ["all"],
|
||||
});
|
||||
browser.contextMenus.create({
|
||||
id: CMD_TOGGLE_STYLE,
|
||||
title: browser.i18n.getMessage("toggle_style"),
|
||||
contexts: ["all"],
|
||||
});
|
||||
browser.contextMenus.create({
|
||||
id: CMD_OPEN_TRANBOX,
|
||||
title: browser.i18n.getMessage("open_tranbox"),
|
||||
contexts: ["all"],
|
||||
});
|
||||
browser.contextMenus.create({
|
||||
id: "options_separator",
|
||||
type: "separator",
|
||||
contexts: ["all"],
|
||||
});
|
||||
browser.contextMenus.create({
|
||||
id: CMD_OPEN_OPTIONS,
|
||||
title: browser.i18n.getMessage("open_options"),
|
||||
contexts: ["all"],
|
||||
});
|
||||
}
|
||||
async function addContextMenus(contextMenuType = 1) {
|
||||
// 添加前先删除,避免重复ID的错误
|
||||
try {
|
||||
await browser.contextMenus.removeAll();
|
||||
} catch (err) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除右键菜单
|
||||
*/
|
||||
function removeContextMenus() {
|
||||
browser.contextMenus.removeAll();
|
||||
switch (contextMenuType) {
|
||||
case 1:
|
||||
browser.contextMenus.create({
|
||||
id: CMD_TOGGLE_TRANSLATE,
|
||||
title: browser.i18n.getMessage("toggle_translate"),
|
||||
contexts: ["page", "selection"],
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
browser.contextMenus.create({
|
||||
id: CMD_TOGGLE_TRANSLATE,
|
||||
title: browser.i18n.getMessage("toggle_translate"),
|
||||
contexts: ["page", "selection"],
|
||||
});
|
||||
browser.contextMenus.create({
|
||||
id: CMD_TOGGLE_STYLE,
|
||||
title: browser.i18n.getMessage("toggle_style"),
|
||||
contexts: ["page", "selection"],
|
||||
});
|
||||
browser.contextMenus.create({
|
||||
id: CMD_OPEN_TRANBOX,
|
||||
title: browser.i18n.getMessage("open_tranbox"),
|
||||
contexts: ["page", "selection"],
|
||||
});
|
||||
browser.contextMenus.create({
|
||||
id: "options_separator",
|
||||
type: "separator",
|
||||
contexts: ["page", "selection"],
|
||||
});
|
||||
browser.contextMenus.create({
|
||||
id: CMD_OPEN_OPTIONS,
|
||||
title: browser.i18n.getMessage("open_options"),
|
||||
contexts: ["page", "selection"],
|
||||
});
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,11 +92,8 @@ browser.runtime.onStartup.addListener(async () => {
|
||||
// 同步数据
|
||||
await trySyncSettingAndRules();
|
||||
|
||||
const {
|
||||
clearCache,
|
||||
contextMenus = true,
|
||||
subrulesList,
|
||||
} = await getSettingWithDefault();
|
||||
const { clearCache, contextMenuType, subrulesList } =
|
||||
await getSettingWithDefault();
|
||||
|
||||
// 清除缓存
|
||||
if (clearCache) {
|
||||
@@ -91,9 +101,8 @@ browser.runtime.onStartup.addListener(async () => {
|
||||
}
|
||||
|
||||
// 右键菜单
|
||||
if (!contextMenus) {
|
||||
removeContextMenus();
|
||||
}
|
||||
// firefox重启后菜单会消失,故重复添加
|
||||
addContextMenus(contextMenuType);
|
||||
|
||||
// 同步订阅规则
|
||||
trySyncAllSubRules({ subrulesList });
|
||||
@@ -131,12 +140,18 @@ browser.runtime.onMessage.addListener(
|
||||
saveRule(args);
|
||||
break;
|
||||
case MSG_CONTEXT_MENUS:
|
||||
const { contextMenus } = args;
|
||||
if (contextMenus) {
|
||||
addContextMenus();
|
||||
} else {
|
||||
removeContextMenus();
|
||||
}
|
||||
const { contextMenuType } = args;
|
||||
addContextMenus(contextMenuType);
|
||||
break;
|
||||
case MSG_COMMAND_SHORTCUTS:
|
||||
browser.commands
|
||||
.getAll()
|
||||
.then((commands) => {
|
||||
sendResponse({ data: commands });
|
||||
})
|
||||
.catch((error) => {
|
||||
sendResponse({ error: error.message });
|
||||
});
|
||||
break;
|
||||
default:
|
||||
sendResponse({ error: `message action is unavailable: ${action}` });
|
||||
@@ -154,6 +169,9 @@ browser.commands.onCommand.addListener((command) => {
|
||||
case CMD_TOGGLE_TRANSLATE:
|
||||
sendTabMsg(MSG_TRANS_TOGGLE);
|
||||
break;
|
||||
case CMD_OPEN_TRANBOX:
|
||||
sendTabMsg(MSG_OPEN_TRANBOX);
|
||||
break;
|
||||
case CMD_TOGGLE_STYLE:
|
||||
sendTabMsg(MSG_TRANS_TOGGLE_STYLE);
|
||||
break;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { touchTapListener } from "./libs/touch";
|
||||
import { debounce, genEventName } from "./libs/utils";
|
||||
import { handlePing, injectScript } from "./libs/gm";
|
||||
import { browser } from "./libs/browser";
|
||||
import { runWebfix } from "./libs/webfix";
|
||||
import { matchFixer } from "./libs/webfix";
|
||||
import { matchRule } from "./libs/rules";
|
||||
import { trySyncAllSubRules } from "./libs/subRules";
|
||||
import { isInBlacklist } from "./libs/blacklist";
|
||||
@@ -112,10 +112,6 @@ function runIframe(setting) {
|
||||
*/
|
||||
async function showFab(translator) {
|
||||
const fab = await getFabWithDefault();
|
||||
if (fab.isHide) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $action = document.createElement("div");
|
||||
$action.setAttribute("id", APP_LCNAME);
|
||||
document.body.parentElement.appendChild($action);
|
||||
@@ -144,7 +140,7 @@ async function showFab(translator) {
|
||||
* @returns
|
||||
*/
|
||||
function showTransbox({
|
||||
contextMenus = true,
|
||||
contextMenuType,
|
||||
tranboxSetting = DEFAULT_TRANBOX_SETTING,
|
||||
transApis,
|
||||
}) {
|
||||
@@ -169,7 +165,7 @@ function showTransbox({
|
||||
<React.StrictMode>
|
||||
<CacheProvider value={cache}>
|
||||
<Slection
|
||||
contextMenus={contextMenus}
|
||||
contextMenuType={contextMenuType}
|
||||
tranboxSetting={tranboxSetting}
|
||||
transApis={transApis}
|
||||
/>
|
||||
@@ -256,11 +252,11 @@ export async function run(isUserscript = false) {
|
||||
}
|
||||
|
||||
// 不规范网页修复
|
||||
await runWebfix(setting);
|
||||
const fixerSetting = await matchFixer(href, setting);
|
||||
|
||||
// 翻译网页
|
||||
const rule = await matchRule(href, setting);
|
||||
const translator = new Translator(rule, setting);
|
||||
const translator = new Translator(rule, setting, fixerSetting);
|
||||
|
||||
// 监听消息
|
||||
windowListener(rule);
|
||||
|
||||
@@ -112,8 +112,8 @@ export const I18N = {
|
||||
en: customApiHelpEN,
|
||||
},
|
||||
translate_alt: {
|
||||
zh: `翻译 (Alt+Q)`,
|
||||
en: `Translate (Alt+Q)`,
|
||||
zh: `翻译`,
|
||||
en: `Translate`,
|
||||
},
|
||||
basic_setting: {
|
||||
zh: `基本设置`,
|
||||
@@ -183,13 +183,17 @@ export const I18N = {
|
||||
zh: `翻译服务`,
|
||||
en: `Translate Service`,
|
||||
},
|
||||
mouseover_translation: {
|
||||
zh: `鼠标悬停翻译`,
|
||||
en: `Mouseover translation`,
|
||||
translate_timing: {
|
||||
zh: `翻译时机`,
|
||||
en: `Translate Timing`,
|
||||
},
|
||||
mk_disable: {
|
||||
zh: `禁用`,
|
||||
en: `Disable`,
|
||||
zh: `滚动加载(建议)`,
|
||||
en: `Rolling Loading (Suggested)`,
|
||||
},
|
||||
mk_pageopen: {
|
||||
zh: `页面打开`,
|
||||
en: `Page Open`,
|
||||
},
|
||||
mk_mouseover: {
|
||||
zh: `鼠标悬停`,
|
||||
@@ -228,8 +232,8 @@ export const I18N = {
|
||||
en: `Text Style`,
|
||||
},
|
||||
text_style_alt: {
|
||||
zh: `文字样式 (Alt+C)`,
|
||||
en: `Text Style (Alt+C)`,
|
||||
zh: `文字样式`,
|
||||
en: `Text Style`,
|
||||
},
|
||||
bg_color: {
|
||||
zh: `样式颜色`,
|
||||
@@ -356,23 +360,23 @@ export const I18N = {
|
||||
en: `Follow the syntax of "CSS"`,
|
||||
},
|
||||
setting: {
|
||||
zh: `设置 (Alt+O)`,
|
||||
en: `Setting (Alt+O)`,
|
||||
zh: `设置`,
|
||||
en: `Setting`,
|
||||
},
|
||||
pattern: {
|
||||
zh: `匹配网址`,
|
||||
en: `URL pattern`,
|
||||
},
|
||||
pattern_helper: {
|
||||
zh: `1、支持星号(*)通配符。2、多个URL用英文逗号“,”分隔。`,
|
||||
en: `1. The asterisk (*) wildcard is supported. 2. Multiple URLs separated by English commas ",".`,
|
||||
zh: `1、支持星号(*)通配符。2、多个URL用换行或英文逗号“,”分隔。`,
|
||||
en: `1. Supports the asterisk (*) wildcard character. 2. Separate multiple URLs with newlines or English commas ",".`,
|
||||
},
|
||||
selector_helper: {
|
||||
zh: `1、遵循CSS选择器语法。2、留空表示采用全局设置。3、多个CSS选择器之间用“;”隔开。4、“shadow root”选择器和内部选择器用“>>>”隔开。`,
|
||||
en: `1. Follow CSS selector syntax. 2. Leave blank to adopt the global setting. 3. Separate multiple CSS selectors with ";". 4. The "shadow root" selector and the internal selector are separated by ">>>".`,
|
||||
},
|
||||
translate_switch: {
|
||||
zh: `开启翻译`,
|
||||
zh: `启停翻译`,
|
||||
en: `Translate Switch`,
|
||||
},
|
||||
default_enabled: {
|
||||
@@ -387,6 +391,22 @@ export const I18N = {
|
||||
zh: `选择器`,
|
||||
en: `Selector`,
|
||||
},
|
||||
keep_selector: {
|
||||
zh: `保留元素选择器`,
|
||||
en: `Keep unchanged selector`,
|
||||
},
|
||||
keep_selector_helper: {
|
||||
zh: `1、遵循CSS选择器语法。2、留空表示采用全局设置。3、子元素选择器用“>>>”隔开。`,
|
||||
en: `1. Follow CSS selector syntax. 2. Leave blank to adopt the global setting. 3.Sub-element selectors are separated by ">>>".`,
|
||||
},
|
||||
terms: {
|
||||
zh: `专业术语`,
|
||||
en: `Terms`,
|
||||
},
|
||||
terms_helper: {
|
||||
zh: `0、支持正则表达式匹配。1、多条术语用换行或分号“;”隔开。2、术语和译文用英文逗号“,”隔开。3、没有译文视为不翻译术语。4、留空表示采用全局设置。`,
|
||||
en: `0. Supports regular expression matching. 1. Separate multiple terms with newlines or semicolons ";". 2. Terms and translations are separated by English commas ",". 3. If there is no translation, the term will be deemed not to be translated. 4. Leave blank to adopt the global setting.`,
|
||||
},
|
||||
root_selector: {
|
||||
zh: `根选择器`,
|
||||
en: `Root Selector`,
|
||||
@@ -540,7 +560,7 @@ export const I18N = {
|
||||
en: `Shortcuts Setting`,
|
||||
},
|
||||
toggle_translate_shortcut: {
|
||||
zh: `"开启翻译"快捷键`,
|
||||
zh: `"启停翻译"快捷键`,
|
||||
en: `"Toggle Translate" Shortcut`,
|
||||
},
|
||||
toggle_style_shortcut: {
|
||||
@@ -703,8 +723,28 @@ export const I18N = {
|
||||
zh: `此功能依赖准确的语言检测,建议启用远程语言检测。`,
|
||||
en: `This feature relies on accurate language detection. It is recommended to enable remote language detection.`,
|
||||
},
|
||||
add_context_menus: {
|
||||
zh: `添加右键菜单`,
|
||||
en: `Add Context Menus`,
|
||||
context_menus: {
|
||||
zh: `右键菜单`,
|
||||
en: `Context Menus`,
|
||||
},
|
||||
hide_context_menus: {
|
||||
zh: `隐藏右键菜单`,
|
||||
en: `Hide Context Menus`,
|
||||
},
|
||||
simple_context_menus: {
|
||||
zh: `简单右键菜单`,
|
||||
en: `Simple_context_menus Context Menus`,
|
||||
},
|
||||
secondary_context_menus: {
|
||||
zh: `二级右键菜单`,
|
||||
en: `Secondary Context Menus`,
|
||||
},
|
||||
mulkeys_help: {
|
||||
zh: `支持用换行或英文逗号“,”分隔多个KEY轮询调用。`,
|
||||
en: `Supports multiple KEY polling calls separated by newlines or English commas ",".`,
|
||||
},
|
||||
translate_page_title: {
|
||||
zh: `是否同时翻译页面标题`,
|
||||
en: `Translate Page Title`,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
DEFAULT_SELECTOR,
|
||||
DEFAULT_KEEP_SELECTOR,
|
||||
GLOBAL_KEY,
|
||||
REMAIN_KEY,
|
||||
SHADOW_KEY,
|
||||
@@ -64,6 +65,7 @@ export const MSG_TRANS_GETRULE = "trans_getrule";
|
||||
export const MSG_TRANS_PUTRULE = "trans_putrule";
|
||||
export const MSG_TRANS_CURRULE = "trans_currule";
|
||||
export const MSG_CONTEXT_MENUS = "context_menus";
|
||||
export const MSG_COMMAND_SHORTCUTS = "command_shortcuts";
|
||||
|
||||
export const THEME_LIGHT = "light";
|
||||
export const THEME_DARK = "dark";
|
||||
@@ -82,7 +84,8 @@ export const URL_MICROSOFT_TRAN =
|
||||
export const URL_MICROSOFT_AUTH = "https://edge.microsoft.com/translate/auth";
|
||||
export const URL_BAIDU_LANGDETECT = "https://fanyi.baidu.com/langdetect";
|
||||
export const URL_BAIDU_WEB = "https://fanyi.baidu.com/";
|
||||
export const URL_BAIDU_TRAN = "https://fanyi.baidu.com/v2transapi";
|
||||
export const URL_BAIDU_TRANSAPI = "https://fanyi.baidu.com/transapi";
|
||||
export const URL_BAIDU_TRANSAPI_V2 = "https://fanyi.baidu.com/v2transapi";
|
||||
export const URL_DEEPLFREE_TRAN = "https://www2.deepl.com/jsonrpc";
|
||||
export const URL_TENCENT_TRANSMART = "https://transmart.qq.com/api/imt";
|
||||
|
||||
@@ -100,11 +103,11 @@ export const OPT_TRANS_CUSTOMIZE = "Custom";
|
||||
export const OPT_TRANS_ALL = [
|
||||
OPT_TRANS_GOOGLE,
|
||||
OPT_TRANS_MICROSOFT,
|
||||
OPT_TRANS_BAIDU,
|
||||
OPT_TRANS_TENCENT,
|
||||
OPT_TRANS_DEEPL,
|
||||
OPT_TRANS_DEEPLFREE,
|
||||
OPT_TRANS_DEEPLX,
|
||||
OPT_TRANS_BAIDU,
|
||||
OPT_TRANS_TENCENT,
|
||||
OPT_TRANS_OPENAI,
|
||||
OPT_TRANS_GEMINI,
|
||||
OPT_TRANS_CLOUDFLAREAI,
|
||||
@@ -295,13 +298,15 @@ export const OPT_STYLE_USE_COLOR = [
|
||||
OPT_STYLE_BLOCKQUOTE,
|
||||
];
|
||||
|
||||
export const OPT_MOUSEKEY_DISABLE = "mk_disable";
|
||||
export const OPT_MOUSEKEY_DISABLE = "mk_disable"; // 滚动加载翻译
|
||||
export const OPT_MOUSEKEY_PAGEOPEN = "mk_pageopen"; // 直接翻译到底
|
||||
export const OPT_MOUSEKEY_MOUSEOVER = "mk_mouseover";
|
||||
export const OPT_MOUSEKEY_CONTROL = "mk_ctrlKey";
|
||||
export const OPT_MOUSEKEY_SHIFT = "mk_shiftKey";
|
||||
export const OPT_MOUSEKEY_ALT = "mk_altKey";
|
||||
export const OPT_MOUSEKEY_ALL = [
|
||||
OPT_MOUSEKEY_DISABLE,
|
||||
OPT_MOUSEKEY_PAGEOPEN,
|
||||
OPT_MOUSEKEY_MOUSEOVER,
|
||||
OPT_MOUSEKEY_CONTROL,
|
||||
OPT_MOUSEKEY_SHIFT,
|
||||
@@ -321,6 +326,8 @@ export const DEFAULT_COLOR = "#209CEE"; // 默认高亮背景色/线条颜色
|
||||
export const GLOBLA_RULE = {
|
||||
pattern: "*",
|
||||
selector: DEFAULT_SELECTOR,
|
||||
keepSelector: DEFAULT_KEEP_SELECTOR,
|
||||
terms: "",
|
||||
translator: OPT_TRANS_MICROSOFT,
|
||||
fromLang: "auto",
|
||||
toLang: "zh-CN",
|
||||
@@ -445,11 +452,13 @@ export const DEFAULT_SETTING = {
|
||||
injectRules: true, // 是否注入订阅规则
|
||||
injectWebfix: true, // 是否注入修复补丁
|
||||
detectRemote: false, // 是否使用远程语言检测
|
||||
contextMenus: true, // 是否添加右键菜单
|
||||
contextMenus: true, // 是否添加右键菜单(作废)
|
||||
contextMenuType: 1, // 右键菜单类型(0不显示,1简单菜单,2多级菜单)
|
||||
transTitle: false, // 是否同时翻译页面标题
|
||||
subrulesList: DEFAULT_SUBRULES_LIST, // 订阅列表
|
||||
owSubrule: DEFAULT_OW_RULE, // 覆写订阅规则
|
||||
transApis: DEFAULT_TRANS_APIS, // 翻译接口
|
||||
mouseKey: OPT_MOUSEKEY_DISABLE, // 鼠标悬停翻译
|
||||
mouseKey: OPT_MOUSEKEY_DISABLE, // 翻译时机/鼠标悬停翻译
|
||||
shortcuts: DEFAULT_SHORTCUTS, // 快捷键
|
||||
inputRule: DEFAULT_INPUT_RULE, // 输入框设置
|
||||
tranboxSetting: DEFAULT_TRANBOX_SETTING, // 划词翻译设置
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const DEFAULT_SELECTOR = `:is(li, p, h1, h2, h3, h4, h5, h6, dd, blockquote)`;
|
||||
export const DEFAULT_KEEP_SELECTOR = `code, img, svg`;
|
||||
|
||||
export const GLOBAL_KEY = "*";
|
||||
export const REMAIN_KEY = "-";
|
||||
@@ -8,6 +9,8 @@ export const SHADOW_KEY = ">>>";
|
||||
export const DEFAULT_RULE = {
|
||||
pattern: "",
|
||||
selector: "",
|
||||
keepSelector: "",
|
||||
terms: "",
|
||||
translator: GLOBAL_KEY,
|
||||
fromLang: GLOBAL_KEY,
|
||||
toLang: GLOBAL_KEY,
|
||||
@@ -41,65 +44,155 @@ export const DEFAULT_OW_RULE = {
|
||||
};
|
||||
|
||||
const RULES_MAP = {
|
||||
"www.google.com/search": `h3, .IsZvec, .VwiC3b`,
|
||||
"news.google.com": `[role="link"], .DY5T1d, .ifw3f, ${DEFAULT_SELECTOR}`,
|
||||
"www.foxnews.com": `h1, h2, .title, .sidebar [data-type="Title"], .article-content ${DEFAULT_SELECTOR}; [data-spotim-module="conversation"]>div >>> [data-spot-im-class="message-text"] p, [data-spot-im-class="message-text"]`,
|
||||
"bearblog.dev, www.theverge.com, www.tampermonkey.net/documentation.php": `${DEFAULT_SELECTOR}`,
|
||||
"themessenger.com": `.leading-tight, .leading-tighter, .my-2 p, .font-body p, article ${DEFAULT_SELECTOR}`,
|
||||
"www.telegraph.co.uk, go.dev/doc/": `article ${DEFAULT_SELECTOR}`,
|
||||
"www.theguardian.com": `.show-underline, .dcr-hup5wm div, .dcr-7vl6y8 div, .dcr-12evv1c, figcaption, article ${DEFAULT_SELECTOR}, [data-cy="mostviewed-footer"] h4`,
|
||||
"www.semafor.com": `${DEFAULT_SELECTOR}, .styles_intro__IYj__, [class*="styles_description"]`,
|
||||
"www.noemamag.com": `.splash__title, .single-card__title, .single-card__type, .single-card__topic, .highlighted-content__title, .single-card__author, article ${DEFAULT_SELECTOR}, .quote__text, .wp-caption-text div`,
|
||||
"restofworld.org": `${DEFAULT_SELECTOR}, .recirc-story__headline, .recirc-story__dek`,
|
||||
"www.axios.com": `.h7, ${DEFAULT_SELECTOR}`,
|
||||
"www.newyorker.com": `.summary-item__hed, .summary-item__dek, .summary-collection-grid__dek, .dqtvfu, .rubric__link, .caption, article ${DEFAULT_SELECTOR}, .HEhan ${DEFAULT_SELECTOR}, .ContributorBioBio-fBolsO`,
|
||||
"time.com": `h1, h3, .summary, .video-title, #article-body ${DEFAULT_SELECTOR}, .image-wrap-container .credit.body-caption, .media-heading`,
|
||||
"www.dw.com": `.ts-teaser-title a, .news-title a, .title a, .teaser-description a, .hbudab h3, .hbudab p, figcaption ,article ${DEFAULT_SELECTOR}`,
|
||||
"www.bbc.com": `h1, h2, .media__link, .media__summary, article ${DEFAULT_SELECTOR}, .ssrcss-y7krbn-Stack, .ssrcss-17zglt8-PromoHeadline, .ssrcss-18cjaf3-Headline, .gs-c-promo-heading__title, .gs-c-promo-summary, .media__content h3, .article__intro`,
|
||||
"www.chinadaily.com.cn": `h1, .tMain [shape="rect"], .cMain [shape="rect"], .photo_art [shape="rect"], .mai_r [shape="rect"], .lisBox li, #Content ${DEFAULT_SELECTOR}`,
|
||||
"www.facebook.com": `[role="main"] [dir="auto"]`,
|
||||
"www.reddit.com": `[slot="title"], [slot="text-body"] ${DEFAULT_SELECTOR}, #-post-rtjson-content p`,
|
||||
"www.quora.com": `.qu-wordBreak--break-word`,
|
||||
"edition.cnn.com": `.container__title, .container__headline, .headline__text, .image__caption, [data-type="Title"], .article__content ${DEFAULT_SELECTOR}`,
|
||||
"www.reuters.com": `#main-content [data-testid="Heading"], #main-content [data-testid="Body"], .article-body__content__17Yit ${DEFAULT_SELECTOR}`,
|
||||
"www.bloomberg.com": `[data-component="headline"], [data-component="related-item-headline"], [data-component="title"], article ${DEFAULT_SELECTOR}`,
|
||||
"deno.land, docs.github.com": `main ${DEFAULT_SELECTOR}`,
|
||||
"doc.rust-lang.org": `.content ${DEFAULT_SELECTOR}`,
|
||||
"www.indiehackers.com": `h1, h3, .content ${DEFAULT_SELECTOR}, .feed-item__title-link`,
|
||||
"platform.openai.com/docs": `.docs-body ${DEFAULT_SELECTOR}`,
|
||||
"en.wikipedia.org": `h1, .mw-parser-output ${DEFAULT_SELECTOR}`,
|
||||
"stackoverflow.com": `h1, .s-prose p, .comment-body .comment-copy`,
|
||||
"www.npmjs.com/package, developer.chrome.com/docs, medium.com, developers.cloudflare.com, react.dev, create-react-app.dev, pytorch.org": `article ${DEFAULT_SELECTOR}`,
|
||||
"news.ycombinator.com": `.title, .commtext`,
|
||||
"github.com": `.markdown-body ${DEFAULT_SELECTOR}, .repo-description p, .Layout-sidebar .f4, .container-lg .py-4 .f5, .container-lg .my-4 .f5, .Box-row .pr-4, .Box-row article .mt-1, [itemprop="description"], .markdown-title, bdi, .ws-pre-wrap, .status-meta, span.status-meta, .col-10.color-fg-muted, .TimelineItem-body, .pinned-item-list-item-content .color-fg-muted, .markdown-body td, .markdown-body th`,
|
||||
"twitter.com": `[data-testid="tweetText"]`,
|
||||
"m.youtube.com": `.slim-video-information-title .yt-core-attributed-string, .media-item-headline .yt-core-attributed-string, .comment-text .yt-core-attributed-string, .typography-body-2b .yt-core-attributed-string, #ytp-caption-window-container .ytp-caption-segment`,
|
||||
"www.youtube.com": `h1, #video-title, #content-text, #title, yt-attributed-string>span>span, #ytp-caption-window-container .ytp-caption-segment`,
|
||||
"bard.google.com": `.query-content ${DEFAULT_SELECTOR}, message-content ${DEFAULT_SELECTOR}`,
|
||||
"www.bing.com": `.b_algoSlug, .rwrl_padref; .cib-serp-main >>> .ac-textBlock ${DEFAULT_SELECTOR}, .text-message-content div`,
|
||||
"www.phoronix.com": `article ${DEFAULT_SELECTOR}`,
|
||||
"wx2.qq.com": `.js_message_plain`,
|
||||
"app.slack.com/client/": `.p-rich_text_section, .c-message_attachment__text, .p-rich_text_list li`,
|
||||
"discord.com/channels/": `div[id^=message-content]`,
|
||||
"t.me/s/": `.js-message_text ${DEFAULT_SELECTOR}`,
|
||||
"web.telegram.org/k/": `.message, .bot-commands-list-element-description, .reply-markup-button-text`,
|
||||
"web.telegram.org/a/": `.message, .text-content, .bot-commands-list-element-description, .reply-markup-button-text`,
|
||||
"chromereleases.googleblog.com": `.title, .publishdate, p, i, .header-desc, .header-title, .text`,
|
||||
"www.instagram.com/": `h1, article span[dir=auto] > span[dir=auto], ._ab1y`,
|
||||
"www.instagram.com/p/,www.instagram.com/reels/": `h1, div[class='x9f619 xjbqb8w x78zum5 x168nmei x13lgxp2 x5pf9jr xo71vjh x1uhb9sk x1plvlek xryxfnj x1c4vz4f x2lah0s xdt5ytf xqjyukv x1cy8zhl x1oa3qoh x1nhvcw1'] > span[class='x1lliihq x1plvlek xryxfnj x1n2onr6 x193iq5w xeuugli x1fj9vlw x13faqbe x1vvkbs x1s928wv xhkezso x1gmr53x x1cpjm7i x1fgarty x1943h6x x1i0vuye xvs91rp xo1l8bm x5n08af x10wh9bi x1wdrske x8viiok x18hxmgj'], span[class='x193iq5w xeuugli x1fj9vlw x13faqbe x1vvkbs xt0psk2 x1i0vuye xvs91rp xo1l8bm x5n08af x10wh9bi x1wdrske x8viiok x18hxmgj']`,
|
||||
"mail.google.com": `${DEFAULT_SELECTOR}, h2[data-thread-perm-id], span[data-thread-id], div[data-message-id] div[class=''], .messageBody, #views`,
|
||||
"web.whatsapp.com": `.copyable-text > span`,
|
||||
"chat.openai.com": `div[data-message-author-role] > div ${DEFAULT_SELECTOR}`,
|
||||
"forum.ru-board.com": `.tit, .dats, span.post ${DEFAULT_SELECTOR}`,
|
||||
"education.github.com": `${DEFAULT_SELECTOR}, a, summary, span.Button-content`,
|
||||
"blogs.windows.com": `${DEFAULT_SELECTOR}, .c-uhf-nav-link, figcaption`,
|
||||
"developer.apple.com/documentation/": `#main ${DEFAULT_SELECTOR}, #main .abstract .content, #main .abstract.content, #main .link span`,
|
||||
"www.google.com/search": [`h3, .IsZvec, .VwiC3b`],
|
||||
"news.google.com": [`[role="link"], .DY5T1d, .ifw3f, ${DEFAULT_SELECTOR}`],
|
||||
"www.foxnews.com": [
|
||||
`h1, h2, .title, .sidebar [data-type="Title"], .article-content ${DEFAULT_SELECTOR}; [data-spotim-module="conversation"]>div >>> [data-spot-im-class="message-text"] p, [data-spot-im-class="message-text"]`,
|
||||
],
|
||||
"bearblog.dev, www.theverge.com, www.tampermonkey.net/documentation.php": [
|
||||
`${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"themessenger.com": [
|
||||
`.leading-tight, .leading-tighter, .my-2 p, .font-body p, article ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"www.telegraph.co.uk, go.dev/doc/": [`article ${DEFAULT_SELECTOR}`],
|
||||
"www.theguardian.com": [
|
||||
`.show-underline, .dcr-hup5wm div, .dcr-7vl6y8 div, .dcr-12evv1c, figcaption, article ${DEFAULT_SELECTOR}, [data-cy="mostviewed-footer"] h4`,
|
||||
],
|
||||
"www.semafor.com": [
|
||||
`${DEFAULT_SELECTOR}, .styles_intro__IYj__, [class*="styles_description"]`,
|
||||
],
|
||||
"www.noemamag.com": [
|
||||
`.splash__title, .single-card__title, .single-card__type, .single-card__topic, .highlighted-content__title, .single-card__author, article ${DEFAULT_SELECTOR}, .quote__text, .wp-caption-text div`,
|
||||
],
|
||||
"restofworld.org": [
|
||||
`${DEFAULT_SELECTOR}, .recirc-story__headline, .recirc-story__dek`,
|
||||
],
|
||||
"www.axios.com": [`.h7, ${DEFAULT_SELECTOR}`],
|
||||
"www.newyorker.com": [
|
||||
`.summary-item__hed, .summary-item__dek, .summary-collection-grid__dek, .dqtvfu, .rubric__link, .caption, article ${DEFAULT_SELECTOR}, .HEhan ${DEFAULT_SELECTOR}, .ContributorBioBio-fBolsO, .BaseText-ewhhUZ`,
|
||||
],
|
||||
"time.com": [
|
||||
`h1, h3, .summary, .video-title, #article-body ${DEFAULT_SELECTOR}, .image-wrap-container .credit.body-caption, .media-heading`,
|
||||
],
|
||||
"www.dw.com": [
|
||||
`.ts-teaser-title a, .news-title a, .title a, .teaser-description a, .hbudab h3, .hbudab p, figcaption ,article ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"www.bbc.com": [
|
||||
`h1, h2, .media__link, .media__summary, article ${DEFAULT_SELECTOR}, .ssrcss-y7krbn-Stack, .ssrcss-17zglt8-PromoHeadline, .ssrcss-18cjaf3-Headline, .gs-c-promo-heading__title, .gs-c-promo-summary, .media__content h3, .article__intro, .lx-c-summary-points>li`,
|
||||
],
|
||||
"www.chinadaily.com.cn": [
|
||||
`h1, .tMain [shape="rect"], .cMain [shape="rect"], .photo_art [shape="rect"], .mai_r [shape="rect"], .lisBox li, #Content ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"www.facebook.com": [`[role="main"] [dir="auto"]`],
|
||||
"www.reddit.com": [
|
||||
`div:is(.tbIApBd2DM_drfZQJjIum, ._1zPvgKHteTOub9dKkvrOl4,.ULWj94BYSOqoJDetxgcnU),a:is([class^="_334yl59"],[class^="_2GrMpxD"]),h1,h2,h3,h4,h5,h6,p,button`,
|
||||
],
|
||||
"www.quora.com": [`.qu-wordBreak--break-word`],
|
||||
"edition.cnn.com": [
|
||||
`.container__title, .container__headline, .headline__text, .image__caption, [data-type="Title"], .article__content ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"www.reuters.com": [
|
||||
`#main-content [data-testid="Heading"], #main-content [data-testid="Body"], .article-body__content__17Yit ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"www.bloomberg.com": [
|
||||
`[data-component="headline"], [data-component="related-item-headline"], [data-component="title"], article ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"deno.land, docs.github.com": [`main ${DEFAULT_SELECTOR}`, `code, img, svg`],
|
||||
"doc.rust-lang.org": [`.content ${DEFAULT_SELECTOR}`, `code, img, svg`],
|
||||
"www.indiehackers.com": [
|
||||
`h1, h3, .content ${DEFAULT_SELECTOR}, .feed-item__title-link`,
|
||||
],
|
||||
"platform.openai.com/docs": [
|
||||
`.docs-body ${DEFAULT_SELECTOR}`,
|
||||
`code, img, svg`,
|
||||
],
|
||||
"en.wikipedia.org": [
|
||||
`h1, .mw-parser-output ${DEFAULT_SELECTOR}`,
|
||||
`.mwe-math-element`,
|
||||
],
|
||||
"stackoverflow.com": [
|
||||
`h1, .s-prose p, .comment-body .comment-copy`,
|
||||
`code, img, svg`,
|
||||
],
|
||||
"www.npmjs.com/package, developer.chrome.com/docs, medium.com, developers.cloudflare.com, react.dev, create-react-app.dev, pytorch.org":
|
||||
[`article ${DEFAULT_SELECTOR}`],
|
||||
"news.ycombinator.com": [`.title, .commtext`],
|
||||
"github.com": [
|
||||
`.markdown-body ${DEFAULT_SELECTOR}, .repo-description p, .Layout-sidebar .f4, .container-lg .py-4 .f5, .container-lg .my-4 .f5, .Box-row .pr-4, .Box-row article .mt-1, [itemprop="description"], .markdown-title, bdi, .ws-pre-wrap, .status-meta, span.status-meta, .col-10.color-fg-muted, .TimelineItem-body, .pinned-item-list-item-content .color-fg-muted, .markdown-body td, .markdown-body th`,
|
||||
`code, img, svg`,
|
||||
],
|
||||
"twitter.com": [
|
||||
`[data-testid="tweetText"], [data-testid="birdwatch-pivot"]>div.css-1rynq56`,
|
||||
`img, a, .r-18u37iz, .css-175oi2r`,
|
||||
],
|
||||
"m.youtube.com": [
|
||||
`.slim-video-information-title .yt-core-attributed-string, .media-item-headline .yt-core-attributed-string, .comment-text .yt-core-attributed-string, .typography-body-2b .yt-core-attributed-string, #ytp-caption-window-container .ytp-caption-segment`,
|
||||
],
|
||||
"www.youtube.com": [
|
||||
`h1, #video-title, #content-text, #title, yt-attributed-string>span>span, #ytp-caption-window-container .ytp-caption-segment`,
|
||||
],
|
||||
"bard.google.com": [
|
||||
`.query-content ${DEFAULT_SELECTOR}, message-content ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"www.bing.com": [
|
||||
`.b_algoSlug, .rwrl_padref; .cib-serp-main >>> .ac-textBlock ${DEFAULT_SELECTOR}, .text-message-content div`,
|
||||
],
|
||||
"www.phoronix.com": [`article ${DEFAULT_SELECTOR}`],
|
||||
"wx2.qq.com": [`.js_message_plain`],
|
||||
"app.slack.com/client/": [
|
||||
`.p-rich_text_section, .c-message_attachment__text, .p-rich_text_list li`,
|
||||
],
|
||||
"discord.com/channels/": [
|
||||
`li[id^=chat-messages] div[id^=message-content], div[class^=headerText], div[class^=name_], section[aria-label='Search Results'] div[id^=message-content]`,
|
||||
],
|
||||
"t.me/s/": [`.js-message_text ${DEFAULT_SELECTOR}`],
|
||||
"web.telegram.org/k/": [
|
||||
`.message, .bot-commands-list-element-description, .reply-markup-button-text`,
|
||||
],
|
||||
"web.telegram.org/a/": [
|
||||
`.message, .text-content, .bot-commands-list-element-description, .reply-markup-button-text`,
|
||||
],
|
||||
"chromereleases.googleblog.com": [
|
||||
`.title, .publishdate, p, i, .header-desc, .header-title, .text`,
|
||||
],
|
||||
"www.instagram.com/": [`h1, article span[dir=auto] > span[dir=auto], ._ab1y`],
|
||||
"www.instagram.com/p/,www.instagram.com/reels/": [
|
||||
`h1, div[class='x9f619 xjbqb8w x78zum5 x168nmei x13lgxp2 x5pf9jr xo71vjh x1uhb9sk x1plvlek xryxfnj x1c4vz4f x2lah0s xdt5ytf xqjyukv x1cy8zhl x1oa3qoh x1nhvcw1'] > span[class='x1lliihq x1plvlek xryxfnj x1n2onr6 x193iq5w xeuugli x1fj9vlw x13faqbe x1vvkbs x1s928wv xhkezso x1gmr53x x1cpjm7i x1fgarty x1943h6x x1i0vuye xvs91rp xo1l8bm x5n08af x10wh9bi x1wdrske x8viiok x18hxmgj'], span[class='x193iq5w xeuugli x1fj9vlw x13faqbe x1vvkbs xt0psk2 x1i0vuye xvs91rp xo1l8bm x5n08af x10wh9bi x1wdrske x8viiok x18hxmgj']`,
|
||||
],
|
||||
"mail.google.com": [
|
||||
`${DEFAULT_SELECTOR}, h2[data-thread-perm-id], span[data-thread-id], div[data-message-id] div[class=''], .messageBody, #views`,
|
||||
],
|
||||
"web.whatsapp.com": [`.copyable-text > span`],
|
||||
"chat.openai.com": [
|
||||
`div[data-message-author-role] > div ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"forum.ru-board.com": [`.tit, .dats, span.post, .lgf ${DEFAULT_SELECTOR}`],
|
||||
"education.github.com": [
|
||||
`${DEFAULT_SELECTOR}, a, summary, span.Button-content`,
|
||||
],
|
||||
"blogs.windows.com": [`${DEFAULT_SELECTOR}, .c-uhf-nav-link, figcaption`],
|
||||
"developer.apple.com/documentation/": [
|
||||
`#main ${DEFAULT_SELECTOR}, #main .abstract .content, #main .abstract.content, #main .link span`,
|
||||
`code, img, svg`,
|
||||
],
|
||||
"greasyfork.org": [
|
||||
`h2, .script-link, .script-description, #additional-info ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"www.fmkorea.com": [`#container ${DEFAULT_SELECTOR}`],
|
||||
"forum.arduino.cc": [
|
||||
`.top-row>.title, .featured-topic>.title, .link-top-line>.title, .category-description, .topic-excerpt, .fancy-title, .cooked ${DEFAULT_SELECTOR}`,
|
||||
],
|
||||
"docs.arduino.cc": [`[class^="tutorial-module--left"] ${DEFAULT_SELECTOR}`],
|
||||
"www.historydefined.net": [`.wp-element-caption, ${DEFAULT_SELECTOR}`],
|
||||
};
|
||||
|
||||
export const BUILTIN_RULES = Object.entries(RULES_MAP)
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.map(([pattern, selector]) => ({
|
||||
.map(([pattern, [selector, keepSelector = "", terms = ""]]) => ({
|
||||
...DEFAULT_RULE,
|
||||
pattern,
|
||||
selector,
|
||||
keepSelector,
|
||||
terms,
|
||||
}));
|
||||
|
||||
@@ -23,6 +23,12 @@ export function useTranslate(q, rule, setting) {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
if (!q.replace(/\[(\d+)\]/g, "").trim()) {
|
||||
setText(q);
|
||||
setSamelang(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const deLang = await tryDetectLang(q, setting.detectRemote);
|
||||
const disableLangs = setting.disableLangs || [];
|
||||
if (
|
||||
|
||||
@@ -10,4 +10,4 @@ import { DEFAULT_BLACKLIST } from "../config";
|
||||
export const isInBlacklist = (
|
||||
href,
|
||||
{ blacklist = DEFAULT_BLACKLIST.join(",\n") }
|
||||
) => blacklist.split(",").some((url) => isMatch(href, url.trim()));
|
||||
) => blacklist.split(/\n|,/).some((url) => isMatch(href, url.trim()));
|
||||
|
||||
@@ -21,6 +21,26 @@ import { msAuth } from "./auth";
|
||||
import { genDeeplFree } from "../apis/deepl";
|
||||
import { genBaidu } from "../apis/baidu";
|
||||
|
||||
const keyMap = new Map();
|
||||
|
||||
// 轮询key
|
||||
const keyPick = (translator, key = "") => {
|
||||
const keys = key
|
||||
.split(/\n|,/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (keys.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const preIndex = keyMap.get(translator) ?? -1;
|
||||
const curIndex = (preIndex + 1) % keys.length;
|
||||
keyMap.set(translator, curIndex);
|
||||
|
||||
return keys[curIndex];
|
||||
};
|
||||
|
||||
/**
|
||||
* 构造缓存 request
|
||||
* @param {*} request
|
||||
@@ -257,6 +277,17 @@ const genCustom = ({ text, from, to, url, key }) => {
|
||||
*/
|
||||
export const newTransReq = ({ translator, text, from, to }, apiSetting) => {
|
||||
const args = { text, from, to, ...apiSetting };
|
||||
|
||||
switch (translator) {
|
||||
case OPT_TRANS_DEEPL:
|
||||
case OPT_TRANS_OPENAI:
|
||||
case OPT_TRANS_GEMINI:
|
||||
case OPT_TRANS_CLOUDFLAREAI:
|
||||
args.key = keyPick(translator, args.key);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
switch (translator) {
|
||||
case OPT_TRANS_GOOGLE:
|
||||
return genGoogle(args);
|
||||
|
||||
@@ -66,6 +66,8 @@ export const matchRule = async (
|
||||
}
|
||||
|
||||
rule.selector = rule.selector?.trim() || globalRule.selector;
|
||||
rule.keepSelector = rule.keepSelector?.trim() || globalRule.keepSelector;
|
||||
rule.terms = rule.terms?.trim() || globalRule.terms;
|
||||
if (rule.textStyle === GLOBAL_KEY) {
|
||||
rule.textStyle = globalRule.textStyle;
|
||||
rule.bgColor = globalRule.bgColor;
|
||||
@@ -112,6 +114,8 @@ export const checkRules = (rules) => {
|
||||
({
|
||||
pattern,
|
||||
selector,
|
||||
keepSelector,
|
||||
terms,
|
||||
translator,
|
||||
fromLang,
|
||||
toLang,
|
||||
@@ -122,6 +126,8 @@ export const checkRules = (rules) => {
|
||||
}) => ({
|
||||
pattern: pattern.trim(),
|
||||
selector: type(selector) === "string" ? selector : "",
|
||||
keepSelector: type(keepSelector) === "string" ? keepSelector : "",
|
||||
terms: type(terms) === "string" ? terms : "",
|
||||
bgColor: type(bgColor) === "string" ? bgColor : "",
|
||||
textDiyStyle: type(textDiyStyle) === "string" ? textDiyStyle : "",
|
||||
translator: matchValue([GLOBAL_KEY, ...OPT_TRANS_ALL], translator),
|
||||
|
||||
@@ -36,8 +36,8 @@ export const shortcutListener = (fn, target = document, timeout = 3000) => {
|
||||
}
|
||||
};
|
||||
|
||||
target.addEventListener("keydown", handleKeydown);
|
||||
target.addEventListener("keyup", handleKeyup);
|
||||
target.addEventListener("keydown", handleKeydown, true);
|
||||
target.addEventListener("keyup", handleKeyup, true);
|
||||
return () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
|
||||
@@ -42,7 +42,7 @@ export const syncSubRules = async (url) => {
|
||||
* @returns
|
||||
*/
|
||||
export const syncAllSubRules = async (subrulesList) => {
|
||||
for (let subrules of subrulesList) {
|
||||
for (const subrules of subrulesList) {
|
||||
try {
|
||||
await syncSubRules(subrules.url);
|
||||
await updateSyncDataCache(subrules.url);
|
||||
|
||||
@@ -8,11 +8,15 @@ import {
|
||||
OPT_STYLE_FUZZY,
|
||||
SHADOW_KEY,
|
||||
OPT_MOUSEKEY_DISABLE,
|
||||
OPT_MOUSEKEY_PAGEOPEN,
|
||||
OPT_MOUSEKEY_MOUSEOVER,
|
||||
DEFAULT_TRANS_APIS,
|
||||
} from "../config";
|
||||
import Content from "../views/Content";
|
||||
import { updateFetchPool, clearFetchPool } from "./fetch";
|
||||
import { debounce, genEventName } from "./utils";
|
||||
import { runFixer } from "./webfix";
|
||||
import { apiTranslate } from "../apis";
|
||||
|
||||
/**
|
||||
* 翻译类
|
||||
@@ -20,6 +24,7 @@ import { debounce, genEventName } from "./utils";
|
||||
export class Translator {
|
||||
_rule = {};
|
||||
_setting = {};
|
||||
_fixerSetting = null;
|
||||
_rootNodes = new Set();
|
||||
_tranNodes = new Map();
|
||||
_skipNodeNames = [
|
||||
@@ -40,6 +45,9 @@ export class Translator {
|
||||
];
|
||||
_eventName = genEventName();
|
||||
_mouseoverNode = null;
|
||||
_keepSelector = [null, null];
|
||||
_terms = [];
|
||||
_docTitle = "";
|
||||
|
||||
// 显示
|
||||
_interseObserver = new IntersectionObserver(
|
||||
@@ -91,13 +99,22 @@ export class Translator {
|
||||
};
|
||||
};
|
||||
|
||||
constructor(rule, setting) {
|
||||
constructor(rule, setting, fixerSetting) {
|
||||
const { fetchInterval, fetchLimit } = setting;
|
||||
updateFetchPool(fetchInterval, fetchLimit);
|
||||
this._overrideAttachShadow();
|
||||
|
||||
this._setting = setting;
|
||||
this._rule = rule;
|
||||
this._fixerSetting = fixerSetting;
|
||||
|
||||
this._keepSelector = (rule.keepSelector || "")
|
||||
.split(SHADOW_KEY)
|
||||
.map((item) => item.trim());
|
||||
this._terms = (rule.terms || "")
|
||||
.split(/\n|;/)
|
||||
.map((item) => item.split(",").map((item) => item.trim()))
|
||||
.filter(([term]) => Boolean(term));
|
||||
|
||||
if (rule.transOpen === "true") {
|
||||
this._register();
|
||||
@@ -155,6 +172,20 @@ export class Translator {
|
||||
this.rule = { ...this.rule, textStyle };
|
||||
};
|
||||
|
||||
translateText = async (text) => {
|
||||
const { translator, fromLang, toLang } = this._rule;
|
||||
const apiSetting =
|
||||
this._setting.transApis?.[translator] || DEFAULT_TRANS_APIS[translator];
|
||||
const [trText] = await apiTranslate({
|
||||
text,
|
||||
translator,
|
||||
fromLang,
|
||||
toLang,
|
||||
apiSetting,
|
||||
});
|
||||
return trText;
|
||||
};
|
||||
|
||||
_querySelectorAll = (selector, node) => {
|
||||
try {
|
||||
return Array.from(node.querySelectorAll(selector));
|
||||
@@ -235,6 +266,11 @@ export class Translator {
|
||||
return;
|
||||
}
|
||||
|
||||
// webfix
|
||||
if (this._fixerSetting) {
|
||||
runFixer(this._fixerSetting);
|
||||
}
|
||||
|
||||
// 搜索节点
|
||||
this._queryNodes();
|
||||
|
||||
@@ -255,6 +291,11 @@ export class Translator {
|
||||
this._tranNodes.forEach((_, node) => {
|
||||
this._interseObserver.observe(node);
|
||||
});
|
||||
} else if (this._setting.mouseKey === OPT_MOUSEKEY_PAGEOPEN) {
|
||||
// 全文直接翻译
|
||||
this._tranNodes.forEach((_, node) => {
|
||||
this._render(node);
|
||||
});
|
||||
} else {
|
||||
// 监听鼠标悬停
|
||||
window.addEventListener("keydown", this._handleKeydown);
|
||||
@@ -263,6 +304,15 @@ export class Translator {
|
||||
node.addEventListener("mouseleave", this._handleMouseout);
|
||||
});
|
||||
}
|
||||
|
||||
// 翻译页面标题
|
||||
if (this._setting.transTitle && !this._docTitle) {
|
||||
const title = document.title;
|
||||
this._docTitle = title;
|
||||
this.translateText(title).then((trText) => {
|
||||
document.title = `${trText} | ${title}`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_handleMouseover = (e) => {
|
||||
@@ -310,6 +360,12 @@ export class Translator {
|
||||
};
|
||||
|
||||
_unRegister = () => {
|
||||
// 恢复页面标题
|
||||
if (this._docTitle) {
|
||||
document.title = this._docTitle;
|
||||
this._docTitle = "";
|
||||
}
|
||||
|
||||
// 解除节点变化监听
|
||||
this._mutaObserver.disconnect();
|
||||
|
||||
@@ -326,6 +382,10 @@ export class Translator {
|
||||
// 移除已插入元素
|
||||
node.querySelector(APP_LCNAME)?.remove();
|
||||
});
|
||||
} else if (this._setting.mouseKey === OPT_MOUSEKEY_PAGEOPEN) {
|
||||
this._tranNodes.forEach((_, node) => {
|
||||
node.querySelector(APP_LCNAME)?.remove();
|
||||
});
|
||||
} else {
|
||||
// 移除鼠标悬停监听
|
||||
window.removeEventListener("keydown", this._handleKeydown);
|
||||
@@ -352,6 +412,11 @@ export class Translator {
|
||||
}
|
||||
}, 500);
|
||||
|
||||
_invalidLength = (q) =>
|
||||
!q ||
|
||||
q.length < (this._setting.minLength ?? TRANS_MIN_LENGTH) ||
|
||||
q.length > (this._setting.maxLength ?? TRANS_MAX_LENGTH);
|
||||
|
||||
_render = (el) => {
|
||||
let traEl = el.querySelector(APP_LCNAME);
|
||||
|
||||
@@ -371,19 +436,52 @@ export class Translator {
|
||||
traEl.remove();
|
||||
}
|
||||
|
||||
const q = el.innerText.trim();
|
||||
let q = el.innerText.trim();
|
||||
this._tranNodes.set(el, q);
|
||||
const keeps = [];
|
||||
|
||||
// 保留元素
|
||||
const [matchSelector, subSelector] = this._keepSelector;
|
||||
if (matchSelector || subSelector) {
|
||||
let text = "";
|
||||
el.childNodes.forEach((child) => {
|
||||
if (
|
||||
child.nodeType === 1 &&
|
||||
((matchSelector && child.matches(matchSelector)) ||
|
||||
(subSelector && child.querySelector(subSelector)))
|
||||
) {
|
||||
if (child.nodeName === "IMG") {
|
||||
child.style.cssText += `width: ${child.width}px;`;
|
||||
child.style.cssText += `height: ${child.height}px;`;
|
||||
}
|
||||
text += `[${keeps.length}]`;
|
||||
keeps.push(child.outerHTML);
|
||||
} else {
|
||||
text += child.textContent;
|
||||
}
|
||||
});
|
||||
|
||||
if (keeps.length > 0) {
|
||||
q = text;
|
||||
}
|
||||
}
|
||||
|
||||
// 太长或太短
|
||||
if (
|
||||
!q ||
|
||||
q.length < (this._setting.minLength ?? TRANS_MIN_LENGTH) ||
|
||||
q.length > (this._setting.maxLength ?? TRANS_MAX_LENGTH)
|
||||
) {
|
||||
if (this._invalidLength(q.replace(/\[(\d+)\]/g, "").trim())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log("---> ", q);
|
||||
// 专业术语
|
||||
if (this._terms.length > 0) {
|
||||
for (const term of this._terms) {
|
||||
const re = new RegExp(term[0], "g");
|
||||
q = q.replace(re, (t) => {
|
||||
const text = `[${keeps.length}]`;
|
||||
keeps.push(term[1] || t);
|
||||
return text;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
traEl = document.createElement(APP_LCNAME);
|
||||
traEl.style.visibility = "visible";
|
||||
@@ -395,7 +493,9 @@ export class Translator {
|
||||
"-webkit-line-clamp: unset; max-height: none; height: auto;";
|
||||
}
|
||||
|
||||
// console.log({ q, keeps });
|
||||
|
||||
const root = createRoot(traEl);
|
||||
root.render(<Content q={q} translator={this} />);
|
||||
root.render(<Content q={q} keeps={keeps} translator={this} />);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@ function brFixer(node, tag = "p") {
|
||||
}
|
||||
node.setAttribute(fixedSign, "true");
|
||||
|
||||
var gapTags = ["BR", "WBR"];
|
||||
var newlineTags = [
|
||||
const gapTags = ["BR", "WBR"];
|
||||
const newlineTags = [
|
||||
"DIV",
|
||||
"UL",
|
||||
"OL",
|
||||
@@ -85,9 +85,10 @@ function brFixer(node, tag = "p") {
|
||||
"HR",
|
||||
"PRE",
|
||||
"TABLE",
|
||||
"BLOCKQUOTE",
|
||||
];
|
||||
|
||||
var html = "";
|
||||
let html = "";
|
||||
node.childNodes.forEach(function (child, index) {
|
||||
if (index === 0) {
|
||||
html += `<${tag} class="kiss-p">`;
|
||||
@@ -99,8 +100,8 @@ function brFixer(node, tag = "p") {
|
||||
html += `</${tag}>${child.outerHTML}<${tag} class="kiss-p">`;
|
||||
} else if (child.outerHTML) {
|
||||
html += child.outerHTML;
|
||||
} else if (child.nodeValue) {
|
||||
html += child.nodeValue;
|
||||
} else if (child.textContent) {
|
||||
html += child.textContent;
|
||||
}
|
||||
|
||||
if (index === node.childNodes.length - 1) {
|
||||
@@ -160,7 +161,7 @@ const fixerMap = {
|
||||
* @param {*} rootSelector
|
||||
*/
|
||||
function run(selector, fixer, rootSelector) {
|
||||
var mutaObserver = new MutationObserver(function (mutations) {
|
||||
const mutaObserver = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
mutation.addedNodes.forEach(function (addNode) {
|
||||
if (addNode && addNode.querySelectorAll) {
|
||||
@@ -172,7 +173,7 @@ function run(selector, fixer, rootSelector) {
|
||||
});
|
||||
});
|
||||
|
||||
var rootNodes = [document];
|
||||
let rootNodes = [document];
|
||||
if (rootSelector) {
|
||||
rootNodes = document.querySelectorAll(rootSelector);
|
||||
}
|
||||
@@ -218,28 +219,38 @@ export const loadOrFetchWebfix = async (url) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 匹配站点
|
||||
* 执行fixer
|
||||
* @param {*} param0
|
||||
*/
|
||||
export async function runWebfix({ injectWebfix }) {
|
||||
export async function runFixer({ selector, fixer, rootSelector }) {
|
||||
try {
|
||||
if (!injectWebfix) {
|
||||
return;
|
||||
}
|
||||
run(selector, fixerMap[fixer], rootSelector);
|
||||
} catch (err) {
|
||||
console.error(`[kiss-webfix run]: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
const href = document.location.href;
|
||||
/**
|
||||
* 匹配fixer配置
|
||||
*/
|
||||
export async function matchFixer(href, { injectWebfix }) {
|
||||
if (!injectWebfix) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const userSites = await getWebfixRulesWithDefault();
|
||||
const subSites = await loadOrFetchWebfix(process.env.REACT_APP_WEBFIXURL);
|
||||
const sites = [...userSites, ...subSites];
|
||||
for (var i = 0; i < sites.length; i++) {
|
||||
var site = sites[i];
|
||||
if (isMatch(href, site.pattern)) {
|
||||
if (fixerMap[site.fixer]) {
|
||||
run(site.selector, fixerMap[site.fixer], site.rootSelector);
|
||||
}
|
||||
break;
|
||||
for (let i = 0; i < sites.length; i++) {
|
||||
const site = sites[i];
|
||||
if (isMatch(href, site.pattern) && fixerMap[site.fixer]) {
|
||||
return site;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[kiss-webfix]: ${err.message}`);
|
||||
console.error(`[kiss-webfix match]: ${err.message}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,11 +95,11 @@ export default function Action({ translator, fab }) {
|
||||
// 注册菜单
|
||||
try {
|
||||
const menuCommandIds = [];
|
||||
const { contextMenus = true } = translator.setting;
|
||||
contextMenus &&
|
||||
const { contextMenuType } = translator.setting;
|
||||
contextMenuType !== 0 &&
|
||||
menuCommandIds.push(
|
||||
GM.registerMenuCommand(
|
||||
"Toggle Translate (Alt+q)",
|
||||
"Toggle Translate",
|
||||
(event) => {
|
||||
translator.toggle();
|
||||
sendIframeMsg(MSG_TRANS_TOGGLE);
|
||||
@@ -108,7 +108,7 @@ export default function Action({ translator, fab }) {
|
||||
"Q"
|
||||
),
|
||||
GM.registerMenuCommand(
|
||||
"Toggle Style (Alt+c)",
|
||||
"Toggle Style",
|
||||
(event) => {
|
||||
translator.toggleStyle();
|
||||
sendIframeMsg(MSG_TRANS_TOGGLE_STYLE);
|
||||
@@ -117,14 +117,14 @@ export default function Action({ translator, fab }) {
|
||||
"C"
|
||||
),
|
||||
GM.registerMenuCommand(
|
||||
"Open Menu (Alt+k)",
|
||||
"Open Menu",
|
||||
(event) => {
|
||||
setShowPopup((pre) => !pre);
|
||||
},
|
||||
"K"
|
||||
),
|
||||
GM.registerMenuCommand(
|
||||
"Open Setting (Alt+o)",
|
||||
"Open Setting",
|
||||
(event) => {
|
||||
window.open(process.env.REACT_APP_OPTIONSPAGE, "_blank");
|
||||
},
|
||||
|
||||
@@ -112,7 +112,7 @@ function StyledSpan({ textStyle, textDiyStyle, bgColor, children }) {
|
||||
}
|
||||
}
|
||||
|
||||
export default function Content({ q, translator }) {
|
||||
export default function Content({ q, keeps, translator }) {
|
||||
const [rule, setRule] = useState(translator.rule);
|
||||
const { text, sameLang, loading } = useTranslate(q, rule, translator.setting);
|
||||
const { textStyle, bgColor = "", textDiyStyle = "" } = rule;
|
||||
@@ -145,18 +145,28 @@ export default function Content({ q, translator }) {
|
||||
);
|
||||
}
|
||||
|
||||
if (text && !sameLang) {
|
||||
return (
|
||||
<>
|
||||
{q.length >= newlineLength ? <br /> : " "}
|
||||
<StyledSpan
|
||||
textStyle={textStyle}
|
||||
textDiyStyle={textDiyStyle}
|
||||
bgColor={bgColor}
|
||||
>
|
||||
{text}
|
||||
</StyledSpan>
|
||||
</>
|
||||
);
|
||||
if (!text || sameLang) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{q.length >= newlineLength ? <br /> : " "}
|
||||
<StyledSpan
|
||||
textStyle={textStyle}
|
||||
textDiyStyle={textDiyStyle}
|
||||
bgColor={bgColor}
|
||||
>
|
||||
{keeps.length > 0 ? (
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: text.replace(/\[(\d+)\]/g, (_, p) => keeps[parseInt(p)]),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
text
|
||||
)}
|
||||
</StyledSpan>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import CircularProgress from "@mui/material/CircularProgress";
|
||||
import {
|
||||
OPT_TRANS_ALL,
|
||||
OPT_TRANS_MICROSOFT,
|
||||
OPT_TRANS_DEEPL,
|
||||
OPT_TRANS_DEEPLFREE,
|
||||
OPT_TRANS_BAIDU,
|
||||
OPT_TRANS_TENCENT,
|
||||
OPT_TRANS_OPENAI,
|
||||
OPT_TRANS_GEMINI,
|
||||
OPT_TRANS_CLOUDFLAREAI,
|
||||
OPT_TRANS_CUSTOMIZE,
|
||||
URL_KISS_PROXY,
|
||||
} from "../../config";
|
||||
@@ -96,6 +98,13 @@ function ApiFields({ translator }) {
|
||||
OPT_TRANS_TENCENT,
|
||||
];
|
||||
|
||||
const mulkeysTranslators = [
|
||||
OPT_TRANS_DEEPL,
|
||||
OPT_TRANS_OPENAI,
|
||||
OPT_TRANS_GEMINI,
|
||||
OPT_TRANS_CLOUDFLAREAI,
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack spacing={3}>
|
||||
{!buildinTranslators.includes(translator) && (
|
||||
@@ -113,6 +122,10 @@ function ApiFields({ translator }) {
|
||||
name="key"
|
||||
value={key}
|
||||
onChange={handleChange}
|
||||
multiline={mulkeysTranslators.includes(translator)}
|
||||
helperText={
|
||||
mulkeysTranslators.includes(translator) ? i18n("mulkeys_help") : ""
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -35,7 +35,7 @@ function DictField({ word }) {
|
||||
fromLang: "en",
|
||||
toLang: "zh-CN",
|
||||
});
|
||||
setDictResult(dictRes[2].dict_result);
|
||||
dictRes[2].type === 1 && setDictResult(JSON.parse(dictRes[2].result));
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
|
||||
@@ -65,6 +65,8 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
const {
|
||||
pattern,
|
||||
selector,
|
||||
keepSelector = "",
|
||||
terms = "",
|
||||
translator,
|
||||
fromLang,
|
||||
toLang,
|
||||
@@ -179,6 +181,26 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
onFocus={handleFocus}
|
||||
multiline
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("keep_selector")}
|
||||
helperText={i18n("keep_selector_helper")}
|
||||
name="keepSelector"
|
||||
value={keepSelector}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("terms")}
|
||||
helperText={i18n("terms_helper")}
|
||||
name="terms"
|
||||
value={terms}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
|
||||
@@ -67,8 +67,8 @@ export default function Settings() {
|
||||
case "touchTranslate":
|
||||
value = limitNumber(value, 0, 4);
|
||||
break;
|
||||
case "contextMenus":
|
||||
isExt && sendBgMsg(MSG_CONTEXT_MENUS, { contextMenus: value });
|
||||
case "contextMenuType":
|
||||
isExt && sendBgMsg(MSG_CONTEXT_MENUS, { contextMenuType: value });
|
||||
break;
|
||||
default:
|
||||
}
|
||||
@@ -96,7 +96,8 @@ export default function Settings() {
|
||||
newlineLength = TRANS_NEWLINE_LENGTH,
|
||||
mouseKey = OPT_MOUSEKEY_DISABLE,
|
||||
detectRemote = false,
|
||||
contextMenus = true,
|
||||
contextMenuType = 1,
|
||||
transTitle = false,
|
||||
touchTranslate = 2,
|
||||
blacklist = DEFAULT_BLACKLIST.join(",\n"),
|
||||
disableLangs = [],
|
||||
@@ -168,11 +169,11 @@ export default function Settings() {
|
||||
/>
|
||||
|
||||
<FormControl size="small">
|
||||
<InputLabel>{i18n("mouseover_translation")}</InputLabel>
|
||||
<InputLabel>{i18n("translate_timing")}</InputLabel>
|
||||
<Select
|
||||
name="mouseKey"
|
||||
value={mouseKey}
|
||||
label={i18n("mouseover_translation")}
|
||||
label={i18n("translate_timing")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{OPT_MOUSEKEY_ALL.map((item) => (
|
||||
@@ -183,6 +184,19 @@ export default function Settings() {
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl size="small">
|
||||
<InputLabel>{i18n("translate_page_title")}</InputLabel>
|
||||
<Select
|
||||
name="transTitle"
|
||||
value={transTitle}
|
||||
label={i18n("translate_page_title")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
<MenuItem value={false}>{i18n("disable")}</MenuItem>
|
||||
<MenuItem value={true}>{i18n("enable")}</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl size="small">
|
||||
<InputLabel>{i18n("touch_translate_shortcut")}</InputLabel>
|
||||
<Select
|
||||
@@ -215,15 +229,16 @@ export default function Settings() {
|
||||
</FormControl>
|
||||
|
||||
<FormControl size="small">
|
||||
<InputLabel>{i18n("add_context_menus")}</InputLabel>
|
||||
<InputLabel>{i18n("context_menus")}</InputLabel>
|
||||
<Select
|
||||
name="contextMenus"
|
||||
value={contextMenus}
|
||||
label={i18n("add_context_menus")}
|
||||
name="contextMenuType"
|
||||
value={contextMenuType}
|
||||
label={i18n("context_menus")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
<MenuItem value={false}>{i18n("disable")}</MenuItem>
|
||||
<MenuItem value={true}>{i18n("enable")}</MenuItem>
|
||||
<MenuItem value={0}>{i18n("hide_context_menus")}</MenuItem>
|
||||
<MenuItem value={1}>{i18n("simple_context_menus")}</MenuItem>
|
||||
<MenuItem value={2}>{i18n("secondary_context_menus")}</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import Switch from "@mui/material/Switch";
|
||||
import { useCallback } from "react";
|
||||
import { limitNumber } from "../../libs/utils";
|
||||
import { useTranbox } from "../../hooks/Tranbox";
|
||||
import { isExt } from "../../libs/client";
|
||||
|
||||
export default function Tranbox() {
|
||||
const i18n = useI18n();
|
||||
@@ -159,11 +160,13 @@ export default function Tranbox() {
|
||||
<MenuItem value={true}>{i18n("hide")}</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<ShortcutInput
|
||||
value={tranboxShortcut}
|
||||
onChange={handleShortcutInput}
|
||||
label={i18n("trigger_tranbox_shortcut")}
|
||||
/>
|
||||
{!isExt && (
|
||||
<ShortcutInput
|
||||
value={tranboxShortcut}
|
||||
onChange={handleShortcutInput}
|
||||
label={i18n("trigger_tranbox_shortcut")}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -18,11 +18,11 @@ import {
|
||||
MSG_TRANS_PUTRULE,
|
||||
MSG_OPEN_OPTIONS,
|
||||
MSG_SAVE_RULE,
|
||||
MSG_COMMAND_SHORTCUTS,
|
||||
OPT_TRANS_ALL,
|
||||
OPT_LANGS_FROM,
|
||||
OPT_LANGS_TO,
|
||||
OPT_STYLE_ALL,
|
||||
OPT_STYLE_USE_COLOR,
|
||||
} from "../../config";
|
||||
import { sendIframeMsg } from "../../libs/iframe";
|
||||
import { saveRule } from "../../libs/rules";
|
||||
@@ -31,6 +31,7 @@ import { tryClearCaches } from "../../libs";
|
||||
export default function Popup({ setShowPopup, translator: tran }) {
|
||||
const i18n = useI18n();
|
||||
const [rule, setRule] = useState(tran?.rule);
|
||||
const [commands, setCommands] = useState({});
|
||||
|
||||
const handleOpenSetting = () => {
|
||||
if (!tran) {
|
||||
@@ -85,7 +86,7 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
const tab = await getTabInfo();
|
||||
href = tab.url;
|
||||
}
|
||||
const newRule = { ...rule, pattern: href };
|
||||
const newRule = { ...rule, pattern: href.split("/")[2] };
|
||||
if (isExt && tran) {
|
||||
sendBgMsg(MSG_SAVE_RULE, newRule);
|
||||
} else {
|
||||
@@ -112,6 +113,32 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
})();
|
||||
}, [tran]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const commands = {};
|
||||
if (isExt) {
|
||||
const res = await sendBgMsg(MSG_COMMAND_SHORTCUTS);
|
||||
if (!res.error) {
|
||||
res.data.forEach(({ name, shortcut }) => {
|
||||
commands[name] = shortcut;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const shortcuts = tran.setting.shortcuts;
|
||||
if (shortcuts) {
|
||||
Object.entries(shortcuts).forEach(([key, val]) => {
|
||||
commands[key] = val.join("+");
|
||||
});
|
||||
}
|
||||
}
|
||||
setCommands(commands);
|
||||
} catch (err) {
|
||||
console.log("[query cmds]", err);
|
||||
}
|
||||
})();
|
||||
}, [tran]);
|
||||
|
||||
if (!rule) {
|
||||
return (
|
||||
<Box minWidth={300}>
|
||||
@@ -130,7 +157,7 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
);
|
||||
}
|
||||
|
||||
const { transOpen, translator, fromLang, toLang, textStyle, bgColor } = rule;
|
||||
const { transOpen, translator, fromLang, toLang, textStyle } = rule;
|
||||
|
||||
return (
|
||||
<Box minWidth={300}>
|
||||
@@ -154,13 +181,12 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
onChange={handleTransToggle}
|
||||
/>
|
||||
}
|
||||
label={i18n("translate_alt")}
|
||||
label={
|
||||
commands["toggleTranslate"]
|
||||
? `${i18n("translate_alt")}(${commands["toggleTranslate"]})`
|
||||
: i18n("translate_alt")
|
||||
}
|
||||
/>
|
||||
{!isExt && (
|
||||
<Button variant="text" onClick={handleClearCache}>
|
||||
{i18n("clear_cache")}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<TextField
|
||||
@@ -217,7 +243,11 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
size="small"
|
||||
value={textStyle}
|
||||
name="textStyle"
|
||||
label={i18n("text_style_alt")}
|
||||
label={
|
||||
commands["toggleStyle"]
|
||||
? `${i18n("text_style_alt")}(${commands["toggleStyle"]})`
|
||||
: i18n("text_style_alt")
|
||||
}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{OPT_STYLE_ALL.map((item) => (
|
||||
@@ -227,7 +257,7 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
))}
|
||||
</TextField>
|
||||
|
||||
{OPT_STYLE_USE_COLOR.includes(textStyle) && (
|
||||
{/* {OPT_STYLE_USE_COLOR.includes(textStyle) && (
|
||||
<TextField
|
||||
size="small"
|
||||
name="bgColor"
|
||||
@@ -235,7 +265,7 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
label={i18n("bg_color")}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
@@ -246,6 +276,11 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
<Button variant="text" onClick={handleSaveRule}>
|
||||
{i18n("save_rule")}
|
||||
</Button>
|
||||
{!isExt && (
|
||||
<Button variant="text" onClick={handleClearCache}>
|
||||
{i18n("clear_cache")}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="text" onClick={handleOpenSetting}>
|
||||
{i18n("setting")}
|
||||
</Button>
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Chip from "@mui/material/Chip";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import FavBtn from "./FavBtn";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
const exchangeMap = {
|
||||
word_third: "第三人称单数",
|
||||
word_ing: "现在分词",
|
||||
word_done: "过去式",
|
||||
word_past: "过去分词",
|
||||
word_pl: "复数",
|
||||
word_proto: "词源",
|
||||
const phonicMap = {
|
||||
en_phonic: "英",
|
||||
us_phonic: "美",
|
||||
};
|
||||
|
||||
export default function DictCont({ dictResult }) {
|
||||
@@ -26,40 +21,28 @@ export default function DictCont({ dictResult }) {
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Typography variant="subtitle1" style={{ fontWeight: "bold" }}>
|
||||
{dictResult.simple_means?.word_name}
|
||||
{dictResult.src}
|
||||
</Typography>
|
||||
<FavBtn word={dictResult.simple_means?.word_name} />
|
||||
<FavBtn word={dictResult.src} />
|
||||
</Stack>
|
||||
|
||||
{dictResult.simple_means?.symbols?.map(({ ph_en, ph_am, parts }, idx) => (
|
||||
<Typography key={idx} component="div">
|
||||
{(ph_en || ph_am) && (
|
||||
<Typography>{`英 /${ph_en || ""}/ 美 /${ph_am || ""}/`}</Typography>
|
||||
)}
|
||||
<ul style={{ margin: "0.5em 0" }}>
|
||||
{parts.map(({ part, means }, idx) => (
|
||||
<li key={idx}>
|
||||
{part ? `[${part}] ${means.join("; ")}` : means.join("; ")}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Typography component="div">
|
||||
<Typography>
|
||||
{dictResult.voice
|
||||
.map(Object.entries)
|
||||
.map((item) => item[0])
|
||||
.map(([key, val]) => `${phonicMap[key] || key} ${val}`)
|
||||
.join(" ")}
|
||||
</Typography>
|
||||
))}
|
||||
|
||||
<Typography>
|
||||
{Object.entries(dictResult.simple_means?.exchange || {})
|
||||
.map(([key, val]) => `${exchangeMap[key] || key}: ${val.join(", ")}`)
|
||||
.join("; ")}
|
||||
</Typography>
|
||||
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
|
||||
{Object.values(dictResult.simple_means?.tags || {})
|
||||
.flat()
|
||||
.filter((item) => item)
|
||||
.map((item) => (
|
||||
<Chip label={item} size="small" />
|
||||
<ul style={{ margin: "0.5em 0" }}>
|
||||
{dictResult.content[0].mean.map(({ pre, cont }, idx) => (
|
||||
<li key={idx}>
|
||||
{pre && `[${pre}] `}
|
||||
{Object.keys(cont).join("; ")}
|
||||
</li>
|
||||
))}
|
||||
</Stack>
|
||||
</ul>
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
65
src/views/Selection/DictContV2.js
Normal file
65
src/views/Selection/DictContV2.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Chip from "@mui/material/Chip";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import FavBtn from "./FavBtn";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
const exchangeMap = {
|
||||
word_third: "第三人称单数",
|
||||
word_ing: "现在分词",
|
||||
word_done: "过去式",
|
||||
word_past: "过去分词",
|
||||
word_pl: "复数",
|
||||
word_proto: "词源",
|
||||
};
|
||||
|
||||
export default function DictCont({ dictResult }) {
|
||||
if (!dictResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Typography variant="subtitle1" style={{ fontWeight: "bold" }}>
|
||||
{dictResult.simple_means?.word_name}
|
||||
</Typography>
|
||||
<FavBtn word={dictResult.simple_means?.word_name} />
|
||||
</Stack>
|
||||
|
||||
{dictResult.simple_means?.symbols?.map(({ ph_en, ph_am, parts }, idx) => (
|
||||
<Typography key={idx} component="div">
|
||||
{(ph_en || ph_am) && (
|
||||
<Typography>{`英 /${ph_en || ""}/ 美 /${ph_am || ""}/`}</Typography>
|
||||
)}
|
||||
<ul style={{ margin: "0.5em 0" }}>
|
||||
{parts.map(({ part, means }, idx) => (
|
||||
<li key={idx}>
|
||||
{part ? `[${part}] ${means.join("; ")}` : means.join("; ")}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Typography>
|
||||
))}
|
||||
|
||||
<Typography>
|
||||
{Object.entries(dictResult.simple_means?.exchange || {})
|
||||
.map(([key, val]) => `${exchangeMap[key] || key}: ${val.join(", ")}`)
|
||||
.join("; ")}
|
||||
</Typography>
|
||||
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
|
||||
{Object.values(dictResult.simple_means?.tags || {})
|
||||
.flat()
|
||||
.filter((item) => item)
|
||||
.map((item) => (
|
||||
<Chip label={item} size="small" />
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Box from "@mui/material/Box";
|
||||
import { isMobile } from "../../libs/mobile";
|
||||
|
||||
function Pointer({
|
||||
direction,
|
||||
@@ -16,21 +17,23 @@ function Pointer({
|
||||
const [origin, setOrigin] = useState(null);
|
||||
|
||||
function handlePointerDown(e) {
|
||||
e.target.setPointerCapture(e.pointerId);
|
||||
!isMobile && e.target.setPointerCapture(e.pointerId);
|
||||
const { clientX, clientY } = isMobile ? e.targetTouches[0] : e;
|
||||
setOrigin({
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: size.w,
|
||||
h: size.h,
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
clientX,
|
||||
clientY,
|
||||
});
|
||||
}
|
||||
|
||||
function handlePointerMove(e) {
|
||||
const { clientX, clientY } = isMobile ? e.targetTouches[0] : e;
|
||||
if (origin) {
|
||||
const dx = e.clientX - origin.clientX;
|
||||
const dy = e.clientY - origin.clientY;
|
||||
const dx = clientX - origin.clientX;
|
||||
const dy = clientY - origin.clientY;
|
||||
let x = position.x;
|
||||
let y = position.y;
|
||||
let w = size.w;
|
||||
@@ -101,16 +104,24 @@ function Pointer({
|
||||
}
|
||||
|
||||
function handlePointerUp(e) {
|
||||
e.stopPropagation();
|
||||
setOrigin(null);
|
||||
}
|
||||
|
||||
const touchProps = isMobile
|
||||
? {
|
||||
onTouchStart: handlePointerDown,
|
||||
onTouchMove: handlePointerMove,
|
||||
onTouchEnd: handlePointerUp,
|
||||
}
|
||||
: {
|
||||
onPointerDown: handlePointerDown,
|
||||
onPointerMove: handlePointerMove,
|
||||
onPointerUp: handlePointerUp,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
>
|
||||
<div {...props} {...touchProps}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -162,6 +173,7 @@ export default function DraggableResizable({
|
||||
return (
|
||||
<Box
|
||||
style={{
|
||||
touchAction: "none",
|
||||
position: "fixed",
|
||||
left: position.x,
|
||||
top: position.y,
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import { isMobile } from "../../libs/mobile";
|
||||
|
||||
export default function TranBtn({ onClick, position, tranboxSetting }) {
|
||||
const left = position.x + tranboxSetting.btnOffsetX;
|
||||
const top = position.y + tranboxSetting.btnOffsetY;
|
||||
|
||||
const touchProps = isMobile
|
||||
? {
|
||||
onTouchEnd: onClick,
|
||||
}
|
||||
: {
|
||||
onMouseUp: onClick,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
position: "absolute",
|
||||
left: position.x + tranboxSetting.btnOffsetX,
|
||||
top: position.y + tranboxSetting.btnOffsetY,
|
||||
left,
|
||||
top,
|
||||
zIndex: 2147483647,
|
||||
}}
|
||||
onClick={onClick}
|
||||
onMouseUp={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
{...touchProps}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -59,7 +59,8 @@ export default function TranCont({
|
||||
// 词典
|
||||
if (isValidWord(text) && toLang.startsWith("zh")) {
|
||||
if (fromLang === "en" && translator === OPT_TRANS_BAIDU) {
|
||||
setDictResult(tranRes[2].dict_result);
|
||||
tranRes[2].type === 1 &&
|
||||
setDictResult(JSON.parse(tranRes[2].result));
|
||||
} else {
|
||||
const dictRes = await apiTranslate({
|
||||
text,
|
||||
@@ -67,7 +68,8 @@ export default function TranCont({
|
||||
fromLang: "en",
|
||||
toLang: "zh-CN",
|
||||
});
|
||||
setDictResult(dictRes[2].dict_result);
|
||||
dictRes[2].type === 1 &&
|
||||
setDictResult(JSON.parse(dictRes[2].result));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -2,20 +2,31 @@ import { useState, useEffect, useCallback } from "react";
|
||||
import TranBtn from "./TranBtn";
|
||||
import TranBox from "./TranBox";
|
||||
import { shortcutRegister } from "../../libs/shortcut";
|
||||
import { sleep } from "../../libs/utils";
|
||||
import { isGm } from "../../libs/client";
|
||||
import { sleep, limitNumber } from "../../libs/utils";
|
||||
import { isGm, isExt } from "../../libs/client";
|
||||
import { MSG_OPEN_TRANBOX, DEFAULT_TRANBOX_SHORTCUT } from "../../config";
|
||||
import { isMobile } from "../../libs/mobile";
|
||||
|
||||
export default function Slection({
|
||||
contextMenuType,
|
||||
tranboxSetting,
|
||||
transApis,
|
||||
}) {
|
||||
const boxWidth = limitNumber(window.innerWidth, 300, 600);
|
||||
const boxHeight = limitNumber(window.innerHeight, 200, 400);
|
||||
|
||||
export default function Slection({ contextMenus, tranboxSetting, transApis }) {
|
||||
const [showBox, setShowBox] = useState(false);
|
||||
const [showBtn, setShowBtn] = useState(false);
|
||||
const [selectedText, setSelText] = useState("");
|
||||
const [text, setText] = useState("");
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
const [boxSize, setBoxSize] = useState({ w: 600, h: 400 });
|
||||
const [boxSize, setBoxSize] = useState({
|
||||
w: boxWidth,
|
||||
h: boxHeight,
|
||||
});
|
||||
const [boxPosition, setBoxPosition] = useState({
|
||||
x: (window.innerWidth - 600) / 2,
|
||||
y: (window.innerHeight - 400) / 2,
|
||||
x: (window.innerWidth - boxWidth) / 2,
|
||||
y: (window.innerHeight - boxHeight) / 2,
|
||||
});
|
||||
|
||||
const handleClick = (e) => {
|
||||
@@ -42,6 +53,7 @@ export default function Slection({ contextMenus, tranboxSetting, transApis }) {
|
||||
|
||||
useEffect(() => {
|
||||
async function handleMouseup(e) {
|
||||
e.stopPropagation();
|
||||
await sleep(10);
|
||||
|
||||
const selectedText = window.getSelection()?.toString()?.trim() || "";
|
||||
@@ -51,18 +63,28 @@ export default function Slection({ contextMenus, tranboxSetting, transApis }) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { pageX, pageY } = isMobile ? e.changedTouches[0] : e;
|
||||
!tranboxSetting.hideTranBtn && setShowBtn(true);
|
||||
// setPosition({ x: e.clientX, y: e.clientY });
|
||||
setPosition({ x: e.pageX, y: e.pageY });
|
||||
setPosition({ x: pageX, y: pageY });
|
||||
}
|
||||
|
||||
// todo: mobile support
|
||||
window.addEventListener("mouseup", handleMouseup);
|
||||
// window.addEventListener(isMobile ? "touchend" : "mouseup", handleMouseup);
|
||||
return () => {
|
||||
window.removeEventListener("mouseup", handleMouseup);
|
||||
window.removeEventListener(
|
||||
isMobile ? "touchend" : "mouseup",
|
||||
handleMouseup
|
||||
);
|
||||
};
|
||||
}, [tranboxSetting.hideTranBtn]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isExt) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clearShortcut = shortcutRegister(
|
||||
tranboxSetting.tranboxShortcut || DEFAULT_TRANBOX_SHORTCUT,
|
||||
handleTranbox
|
||||
@@ -88,10 +110,10 @@ export default function Slection({ contextMenus, tranboxSetting, transApis }) {
|
||||
// 注册菜单
|
||||
try {
|
||||
const menuCommandIds = [];
|
||||
contextMenus &&
|
||||
contextMenuType !== 0 &&
|
||||
menuCommandIds.push(
|
||||
GM.registerMenuCommand(
|
||||
"Translate Selected Text (Alt+S)",
|
||||
"Translate Selected Text",
|
||||
(event) => {
|
||||
handleTranbox();
|
||||
},
|
||||
@@ -107,7 +129,7 @@ export default function Slection({ contextMenus, tranboxSetting, transApis }) {
|
||||
} catch (err) {
|
||||
console.log("[registerMenuCommand]", err);
|
||||
}
|
||||
}, [handleTranbox, contextMenus]);
|
||||
}, [handleTranbox, contextMenuType]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user