feat: support youdao dict
This commit is contained in:
@@ -45,7 +45,7 @@
|
|||||||
"description": "__MSG_open_options__"
|
"description": "__MSG_open_options__"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permissions": ["storage", "contextMenus", "scripting", "declarativeNetRequest"],
|
"permissions": ["storage", "contextMenus", "scripting", "declarativeNetRequest", "declarativeNetRequestWithHostAccess"],
|
||||||
"host_permissions": ["<all_urls>"],
|
"host_permissions": ["<all_urls>"],
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "images/logo16.png",
|
"16": "images/logo16.png",
|
||||||
|
|||||||
@@ -159,6 +159,77 @@ export const apiBaiduSuggest = async (text) => {
|
|||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有道翻译建议
|
||||||
|
* @param {*} text
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const apiYoudaoSuggest = async (text) => {
|
||||||
|
const params = {
|
||||||
|
num: 5,
|
||||||
|
ver: 3.0,
|
||||||
|
doctype: "json",
|
||||||
|
cache: false,
|
||||||
|
le: "en",
|
||||||
|
q: text,
|
||||||
|
};
|
||||||
|
const input = `https://dict.youdao.com/suggest?${queryString.stringify(params)}`;
|
||||||
|
const init = {
|
||||||
|
headers: {
|
||||||
|
accept: "application/json, text/plain, */*",
|
||||||
|
"accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,ja;q=0.6",
|
||||||
|
"content-type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
method: "GET",
|
||||||
|
};
|
||||||
|
const res = await fetchData(input, init, { useCache: true });
|
||||||
|
|
||||||
|
if (res?.result?.code === 200) {
|
||||||
|
await putHttpCachePolyfill(input, init, res);
|
||||||
|
return res.data.entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有道词典
|
||||||
|
* @param {*} text
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const apiYoudaoDict = async (text) => {
|
||||||
|
const params = {
|
||||||
|
doctype: "json",
|
||||||
|
jsonversion: 4,
|
||||||
|
};
|
||||||
|
const input = `https://dict.youdao.com/jsonapi_s?${queryString.stringify(params)}`;
|
||||||
|
const body = queryString.stringify({
|
||||||
|
q: "search",
|
||||||
|
le: "en",
|
||||||
|
t: 3,
|
||||||
|
client: "web",
|
||||||
|
// sign: "",
|
||||||
|
keyfrom: "webdict",
|
||||||
|
});
|
||||||
|
const init = {
|
||||||
|
headers: {
|
||||||
|
accept: "application/json, text/plain, */*",
|
||||||
|
"accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,ja;q=0.6",
|
||||||
|
"content-type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body,
|
||||||
|
};
|
||||||
|
const res = await fetchData(input, init, { useCache: true });
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
await putHttpCachePolyfill(input, init, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 百度语音
|
* 百度语音
|
||||||
* @param {*} text
|
* @param {*} text
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
MSG_INJECT_CSS,
|
MSG_INJECT_CSS,
|
||||||
MSG_UPDATE_CSP,
|
MSG_UPDATE_CSP,
|
||||||
DEFAULT_CSPLIST,
|
DEFAULT_CSPLIST,
|
||||||
|
DEFAULT_ORILIST,
|
||||||
CMD_TOGGLE_TRANSLATE,
|
CMD_TOGGLE_TRANSLATE,
|
||||||
CMD_TOGGLE_STYLE,
|
CMD_TOGGLE_STYLE,
|
||||||
CMD_OPEN_OPTIONS,
|
CMD_OPEN_OPTIONS,
|
||||||
@@ -33,7 +34,9 @@ import { kissLog } from "./libs/log";
|
|||||||
|
|
||||||
globalThis.ContextType = "BACKGROUND";
|
globalThis.ContextType = "BACKGROUND";
|
||||||
|
|
||||||
const REMOVE_HEADERS = [
|
const CSP_RULE_START_ID = 1;
|
||||||
|
const ORI_RULE_START_ID = 10000;
|
||||||
|
const CSP_REMOVE_HEADERS = [
|
||||||
`content-security-policy`,
|
`content-security-policy`,
|
||||||
`content-security-policy-report-only`,
|
`content-security-policy-report-only`,
|
||||||
`x-webkit-csp`,
|
`x-webkit-csp`,
|
||||||
@@ -94,17 +97,34 @@ async function addContextMenus(contextMenuType = 1) {
|
|||||||
* 更新CSP策略
|
* 更新CSP策略
|
||||||
* @param {*} csplist
|
* @param {*} csplist
|
||||||
*/
|
*/
|
||||||
async function updateCspRules(csplist = DEFAULT_CSPLIST.join(",\n")) {
|
async function updateCspRules({ csplist, orilist }) {
|
||||||
try {
|
try {
|
||||||
const newRules = csplist
|
const oldRules = await browser.declarativeNetRequest.getDynamicRules();
|
||||||
.split(/\n|,/)
|
|
||||||
.map((url) => url.trim())
|
const rulesToAdd = [];
|
||||||
.filter(Boolean)
|
const idsToRemove = [];
|
||||||
.map((url, idx) => ({
|
|
||||||
id: idx + 1,
|
if (csplist !== undefined) {
|
||||||
|
let processedCspList = csplist;
|
||||||
|
if (typeof processedCspList === "string") {
|
||||||
|
processedCspList = processedCspList
|
||||||
|
.split(/\n|,/)
|
||||||
|
.map((url) => url.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldCspRuleIds = oldRules
|
||||||
|
.filter(
|
||||||
|
(rule) => rule.id >= CSP_RULE_START_ID && rule.id < ORI_RULE_START_ID
|
||||||
|
)
|
||||||
|
.map((rule) => rule.id);
|
||||||
|
idsToRemove.push(...oldCspRuleIds);
|
||||||
|
|
||||||
|
const newCspRules = processedCspList.map((url, index) => ({
|
||||||
|
id: CSP_RULE_START_ID + index,
|
||||||
action: {
|
action: {
|
||||||
type: "modifyHeaders",
|
type: "modifyHeaders",
|
||||||
responseHeaders: REMOVE_HEADERS.map((header) => ({
|
responseHeaders: CSP_REMOVE_HEADERS.map((header) => ({
|
||||||
operation: "remove",
|
operation: "remove",
|
||||||
header,
|
header,
|
||||||
})),
|
})),
|
||||||
@@ -114,12 +134,43 @@ async function updateCspRules(csplist = DEFAULT_CSPLIST.join(",\n")) {
|
|||||||
resourceTypes: ["main_frame", "sub_frame"],
|
resourceTypes: ["main_frame", "sub_frame"],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
const oldRules = await browser.declarativeNetRequest.getDynamicRules();
|
rulesToAdd.push(...newCspRules);
|
||||||
const oldRuleIds = oldRules.map((rule) => rule.id);
|
}
|
||||||
await browser.declarativeNetRequest.updateDynamicRules({
|
|
||||||
removeRuleIds: oldRuleIds,
|
if (orilist !== undefined) {
|
||||||
addRules: newRules,
|
let processedOriList = orilist;
|
||||||
});
|
if (typeof processedOriList === "string") {
|
||||||
|
processedOriList = processedOriList
|
||||||
|
.split(/\n|,/)
|
||||||
|
.map((url) => url.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldOriRuleIds = oldRules
|
||||||
|
.filter((rule) => rule.id >= ORI_RULE_START_ID)
|
||||||
|
.map((rule) => rule.id);
|
||||||
|
idsToRemove.push(...oldOriRuleIds);
|
||||||
|
|
||||||
|
const newOriRules = processedOriList.map((url, index) => ({
|
||||||
|
id: ORI_RULE_START_ID + index,
|
||||||
|
action: {
|
||||||
|
type: "modifyHeaders",
|
||||||
|
requestHeaders: [{ header: "Origin", operation: "remove" }],
|
||||||
|
},
|
||||||
|
condition: {
|
||||||
|
urlFilter: url,
|
||||||
|
resourceTypes: ["xmlhttprequest"],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
rulesToAdd.push(...newOriRules);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idsToRemove.length > 0 || rulesToAdd.length > 0) {
|
||||||
|
await browser.declarativeNetRequest.updateDynamicRules({
|
||||||
|
removeRuleIds: idsToRemove,
|
||||||
|
addRules: rulesToAdd,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
kissLog("update csp rules", err);
|
kissLog("update csp rules", err);
|
||||||
}
|
}
|
||||||
@@ -149,7 +200,7 @@ browser.runtime.onInstalled.addListener(() => {
|
|||||||
addContextMenus();
|
addContextMenus();
|
||||||
|
|
||||||
// 禁用CSP
|
// 禁用CSP
|
||||||
updateCspRules();
|
updateCspRules({ csplist: DEFAULT_CSPLIST, orilist: DEFAULT_ORILIST });
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +210,7 @@ browser.runtime.onStartup.addListener(async () => {
|
|||||||
// 同步数据
|
// 同步数据
|
||||||
await trySyncSettingAndRules();
|
await trySyncSettingAndRules();
|
||||||
|
|
||||||
const { clearCache, contextMenuType, subrulesList, csplist } =
|
const { clearCache, contextMenuType, subrulesList, csplist, orilist } =
|
||||||
await getSettingWithDefault();
|
await getSettingWithDefault();
|
||||||
|
|
||||||
// 清除缓存
|
// 清除缓存
|
||||||
@@ -177,7 +228,7 @@ browser.runtime.onStartup.addListener(async () => {
|
|||||||
addContextMenus(contextMenuType);
|
addContextMenus(contextMenuType);
|
||||||
|
|
||||||
// 禁用CSP
|
// 禁用CSP
|
||||||
updateCspRules(csplist);
|
updateCspRules({ csplist, orilist });
|
||||||
|
|
||||||
// 同步订阅规则
|
// 同步订阅规则
|
||||||
trySyncAllSubRules({ subrulesList });
|
trySyncAllSubRules({ subrulesList });
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ export const INPUT_PLACE_KEY = "{{key}}"; // 占位符
|
|||||||
export const INPUT_PLACE_MODEL = "{{model}}"; // 占位符
|
export const INPUT_PLACE_MODEL = "{{model}}"; // 占位符
|
||||||
|
|
||||||
export const OPT_DICT_BAIDU = "Baidu";
|
export const OPT_DICT_BAIDU = "Baidu";
|
||||||
|
export const OPT_DICT_YOUDAO = "Youdao";
|
||||||
|
export const OPT_DICT_ALL = [OPT_DICT_BAIDU, OPT_DICT_YOUDAO];
|
||||||
|
export const OPT_DICT_MAP = new Set(OPT_DICT_ALL);
|
||||||
|
|
||||||
|
export const OPT_SUG_BAIDU = "Baidu";
|
||||||
|
export const OPT_SUG_YOUDAO = "Youdao";
|
||||||
|
export const OPT_SUG_ALL = [OPT_SUG_BAIDU, OPT_SUG_YOUDAO];
|
||||||
|
export const OPT_SUG_MAP = new Set(OPT_SUG_ALL);
|
||||||
|
|
||||||
export const OPT_TRANS_GOOGLE = "Google";
|
export const OPT_TRANS_GOOGLE = "Google";
|
||||||
export const OPT_TRANS_GOOGLE_2 = "Google2";
|
export const OPT_TRANS_GOOGLE_2 = "Google2";
|
||||||
@@ -63,9 +71,6 @@ export const OPT_LANGDETECTOR_ALL = [
|
|||||||
OPT_TRANS_TENCENT,
|
OPT_TRANS_TENCENT,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const OPT_DICT_ALL = [OPT_TRANS_BAIDU];
|
|
||||||
export const OPT_DICT_MAP = new Set(OPT_DICT_ALL);
|
|
||||||
|
|
||||||
// 翻译引擎特殊集合
|
// 翻译引擎特殊集合
|
||||||
export const API_SPE_TYPES = {
|
export const API_SPE_TYPES = {
|
||||||
// 内置翻译
|
// 内置翻译
|
||||||
|
|||||||
@@ -1148,6 +1148,11 @@ export const I18N = {
|
|||||||
en: `Translate Blacklist`,
|
en: `Translate Blacklist`,
|
||||||
zh_TW: `停用翻譯名單`,
|
zh_TW: `停用翻譯名單`,
|
||||||
},
|
},
|
||||||
|
disabled_orilist: {
|
||||||
|
zh: `禁用Origin名单`,
|
||||||
|
en: `Disabled Origin List`,
|
||||||
|
zh_TW: `停用 Origin 名單`,
|
||||||
|
},
|
||||||
disabled_csplist: {
|
disabled_csplist: {
|
||||||
zh: `禁用CSP名单`,
|
zh: `禁用CSP名单`,
|
||||||
en: `Disabled CSP List`,
|
en: `Disabled CSP List`,
|
||||||
@@ -1323,6 +1328,11 @@ export const I18N = {
|
|||||||
en: `English Dictionary`,
|
en: `English Dictionary`,
|
||||||
zh_TW: `英文字典`,
|
zh_TW: `英文字典`,
|
||||||
},
|
},
|
||||||
|
english_suggest: {
|
||||||
|
zh: `英文建议`,
|
||||||
|
en: `English Suggest`,
|
||||||
|
zh_TW: `英文建議`,
|
||||||
|
},
|
||||||
api_name: {
|
api_name: {
|
||||||
zh: `接口名称`,
|
zh: `接口名称`,
|
||||||
en: `API Name`,
|
en: `API Name`,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
OPT_DICT_BAIDU,
|
OPT_DICT_BAIDU,
|
||||||
|
OPT_SUG_BAIDU,
|
||||||
DEFAULT_HTTP_TIMEOUT,
|
DEFAULT_HTTP_TIMEOUT,
|
||||||
OPT_TRANS_MICROSOFT,
|
OPT_TRANS_MICROSOFT,
|
||||||
DEFAULT_API_LIST,
|
DEFAULT_API_LIST,
|
||||||
@@ -28,6 +29,7 @@ export const DEFAULT_BLACKLIST = [
|
|||||||
"login.dingtalk.com",
|
"login.dingtalk.com",
|
||||||
]; // 禁用翻译名单
|
]; // 禁用翻译名单
|
||||||
export const DEFAULT_CSPLIST = ["https://github.com"]; // 禁用CSP名单
|
export const DEFAULT_CSPLIST = ["https://github.com"]; // 禁用CSP名单
|
||||||
|
export const DEFAULT_ORILIST = ["https://dict.youdao.com"]; // 移除Origin名单
|
||||||
|
|
||||||
// 同步设置
|
// 同步设置
|
||||||
export const OPT_SYNCTYPE_WORKER = "KISS-Worker";
|
export const OPT_SYNCTYPE_WORKER = "KISS-Worker";
|
||||||
@@ -90,6 +92,7 @@ export const DEFAULT_TRANBOX_SETTING = {
|
|||||||
triggerMode: OPT_TRANBOX_TRIGGER_CLICK, // 触发翻译方式
|
triggerMode: OPT_TRANBOX_TRIGGER_CLICK, // 触发翻译方式
|
||||||
// extStyles: "", // 附加样式
|
// extStyles: "", // 附加样式
|
||||||
enDict: OPT_DICT_BAIDU, // 英文词典
|
enDict: OPT_DICT_BAIDU, // 英文词典
|
||||||
|
enSug: OPT_SUG_BAIDU, // 英文建议
|
||||||
};
|
};
|
||||||
|
|
||||||
// 订阅列表
|
// 订阅列表
|
||||||
@@ -143,6 +146,7 @@ export const DEFAULT_SETTING = {
|
|||||||
touchTranslate: 2, // 触屏翻译
|
touchTranslate: 2, // 触屏翻译
|
||||||
blacklist: DEFAULT_BLACKLIST.join(",\n"), // 禁用翻译名单
|
blacklist: DEFAULT_BLACKLIST.join(",\n"), // 禁用翻译名单
|
||||||
csplist: DEFAULT_CSPLIST.join(",\n"), // 禁用CSP名单
|
csplist: DEFAULT_CSPLIST.join(",\n"), // 禁用CSP名单
|
||||||
|
orilist: DEFAULT_ORILIST.join(",\n"), // 禁用CSP名单
|
||||||
// disableLangs: [], // 不翻译的语言(移至rule,作废)
|
// disableLangs: [], // 不翻译的语言(移至rule,作废)
|
||||||
skipLangs: [], // 不翻译的语言(从rule移回)
|
skipLangs: [], // 不翻译的语言(从rule移回)
|
||||||
transInterval: 100, // 翻译等待时间
|
transInterval: 100, // 翻译等待时间
|
||||||
|
|||||||
@@ -1,40 +1,152 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
|
|
||||||
/**
|
export const useAsync = () => {
|
||||||
* fetch data hook
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const useFetch = (url) => {
|
|
||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
const execute = useCallback(async (fn, ...args) => {
|
||||||
if (!url) {
|
if (!fn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
setLoading(true);
|
||||||
setLoading(true);
|
setError(null);
|
||||||
try {
|
|
||||||
const res = await fetch(url);
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`[${res.status}] ${res.statusText}`);
|
|
||||||
}
|
|
||||||
let data;
|
|
||||||
if (res.headers.get("Content-Type")?.includes("json")) {
|
|
||||||
data = await res.json();
|
|
||||||
} else {
|
|
||||||
data = await res.text();
|
|
||||||
}
|
|
||||||
setData(data);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, [url]);
|
|
||||||
|
|
||||||
return [data, loading, error];
|
try {
|
||||||
|
const res = await fn(...args);
|
||||||
|
setData(res);
|
||||||
|
setLoading(false);
|
||||||
|
return res;
|
||||||
|
} catch (err) {
|
||||||
|
setError(err?.message || "An unknown error occurred");
|
||||||
|
setLoading(false);
|
||||||
|
// throw err;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const reset = useCallback(() => {
|
||||||
|
setData(null);
|
||||||
|
setLoading(false);
|
||||||
|
setError(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { data, loading, error, execute, reset };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAsyncNow = (fn, arg) => {
|
||||||
|
const { execute, ...asyncState } = useAsync();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (fn) {
|
||||||
|
execute(fn, arg);
|
||||||
|
}
|
||||||
|
}, [execute, fn, arg]);
|
||||||
|
|
||||||
|
return { ...asyncState };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetch = () => {
|
||||||
|
const { execute, ...asyncState } = useAsync();
|
||||||
|
|
||||||
|
const requester = useCallback(async (url, options) => {
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorInfo = await response.text();
|
||||||
|
throw new Error(
|
||||||
|
`Request failed: ${response.status} ${response.statusText} - ${errorInfo}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (response.status === 204) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.headers.get("Content-Type")?.includes("json")) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.text();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const get = useCallback(
|
||||||
|
async (url, options = {}) => {
|
||||||
|
try {
|
||||||
|
const result = await execute(requester, url, {
|
||||||
|
...options,
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[execute, requester]
|
||||||
|
);
|
||||||
|
|
||||||
|
const post = useCallback(
|
||||||
|
async (url, body, options = {}) => {
|
||||||
|
try {
|
||||||
|
const result = await execute(requester, url, {
|
||||||
|
...options,
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json", ...options.headers },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[execute, requester]
|
||||||
|
);
|
||||||
|
|
||||||
|
const put = useCallback(
|
||||||
|
async (url, body, options = {}) => {
|
||||||
|
try {
|
||||||
|
const result = await execute(requester, url, {
|
||||||
|
...options,
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json", ...options.headers },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[execute, requester]
|
||||||
|
);
|
||||||
|
|
||||||
|
const del = useCallback(
|
||||||
|
async (url, options = {}) => {
|
||||||
|
try {
|
||||||
|
const result = await execute(requester, url, {
|
||||||
|
...options,
|
||||||
|
method: "DELETE",
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[execute, requester]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...asyncState,
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
del,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGet = (url) => {
|
||||||
|
const { get, ...fetchState } = useFetch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (url) get(url);
|
||||||
|
}, [url, get]);
|
||||||
|
|
||||||
|
return { ...fetchState };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useSetting } from "./Setting";
|
import { useSetting } from "./Setting";
|
||||||
import { I18N, URL_RAW_PREFIX } from "../config";
|
import { I18N, URL_RAW_PREFIX } from "../config";
|
||||||
import { useFetch } from "./Fetch";
|
import { useGet } from "./Fetch";
|
||||||
|
|
||||||
export const getI18n = (uiLang, key, defaultText = "") => {
|
export const getI18n = (uiLang, key, defaultText = "") => {
|
||||||
return I18N?.[key]?.[uiLang] ?? defaultText;
|
return I18N?.[key]?.[uiLang] ?? defaultText;
|
||||||
@@ -25,5 +25,5 @@ export const useI18nMd = (key) => {
|
|||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const fileName = i18n(key);
|
const fileName = i18n(key);
|
||||||
const url = fileName ? `${URL_RAW_PREFIX}/${fileName}` : "";
|
const url = fileName ? `${URL_RAW_PREFIX}/${fileName}` : "";
|
||||||
return useFetch(url);
|
return useGet(url);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ import Paper from "@mui/material/Paper";
|
|||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Link from "@mui/material/Link";
|
import Link from "@mui/material/Link";
|
||||||
import { useFetch } from "./hooks/Fetch";
|
import { useGet } from "./hooks/Fetch";
|
||||||
import { I18N, URL_RAW_PREFIX } from "./config";
|
import { I18N, URL_RAW_PREFIX } from "./config";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [lang, setLang] = useState("zh");
|
const [lang, setLang] = useState("zh");
|
||||||
const [data, loading, error] = useFetch(
|
const { data, loading, error } = useGet(
|
||||||
`${URL_RAW_PREFIX}/${I18N?.["about_md"]?.[lang]}`
|
`${URL_RAW_PREFIX}/${I18N?.["about_md"]?.[lang]}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper sx={{ padding: 2, margin: 2 }}>
|
<Paper sx={{ padding: 2, margin: 2 }}>
|
||||||
<Stack spacing={2} direction="row" justifyContent="flex-end">
|
<Stack spacing={2} direction="row" justifyContent="flex-end">
|
||||||
@@ -47,7 +48,7 @@ function App() {
|
|||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</center>
|
</center>
|
||||||
) : (
|
) : (
|
||||||
<ReactMarkdown children={error ? error.message : data} />
|
<ReactMarkdown children={error || data} />
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useI18n, useI18nMd } from "../../hooks/I18n";
|
|||||||
|
|
||||||
export default function About() {
|
export default function About() {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const [data, loading, error] = useI18nMd("about_md");
|
const { data, loading, error } = useI18nMd("about_md");
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ import { kissLog } from "../../libs/log";
|
|||||||
import { apiTranslate } from "../../apis";
|
import { apiTranslate } from "../../apis";
|
||||||
import { OPT_TRANS_BAIDU, PHONIC_MAP } from "../../config";
|
import { OPT_TRANS_BAIDU, PHONIC_MAP } from "../../config";
|
||||||
import { useConfirm } from "../../hooks/Confirm";
|
import { useConfirm } from "../../hooks/Confirm";
|
||||||
|
import { useSetting } from "../../hooks/Setting";
|
||||||
|
|
||||||
function FavAccordion({ word, index }) {
|
function FavAccordion({ word, index }) {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
const { setting } = useSetting();
|
||||||
|
const { enDict, enSug } = setting?.tranboxSetting || {};
|
||||||
|
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
setExpanded((pre) => !pre);
|
setExpanded((pre) => !pre);
|
||||||
@@ -40,8 +43,8 @@ function FavAccordion({ word, index }) {
|
|||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<DictCont text={word} />
|
<DictCont text={word} enDict={enDict} />
|
||||||
<SugCont text={word} />
|
<SugCont text={word} enSug={enSug} />
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default function Playgound() {
|
|||||||
const { setting } = useSetting();
|
const { setting } = useSetting();
|
||||||
const { transApis, langDetector, tranboxSetting } =
|
const { transApis, langDetector, tranboxSetting } =
|
||||||
setting || DEFAULT_SETTING;
|
setting || DEFAULT_SETTING;
|
||||||
const { apiSlugs, fromLang, toLang, toLang2, enDict } =
|
const { apiSlugs, fromLang, toLang, toLang2, enDict, enSug } =
|
||||||
tranboxSetting || DEFAULT_TRANBOX_SETTING;
|
tranboxSetting || DEFAULT_TRANBOX_SETTING;
|
||||||
return (
|
return (
|
||||||
<TranForm
|
<TranForm
|
||||||
@@ -22,6 +22,7 @@ export default function Playgound() {
|
|||||||
simpleStyle={false}
|
simpleStyle={false}
|
||||||
langDetector={langDetector}
|
langDetector={langDetector}
|
||||||
enDict={enDict}
|
enDict={enDict}
|
||||||
|
enSug={enSug}
|
||||||
isPlaygound={true}
|
isPlaygound={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
OPT_SHORTCUT_SETTING,
|
OPT_SHORTCUT_SETTING,
|
||||||
DEFAULT_BLACKLIST,
|
DEFAULT_BLACKLIST,
|
||||||
DEFAULT_CSPLIST,
|
DEFAULT_CSPLIST,
|
||||||
|
DEFAULT_ORILIST,
|
||||||
MSG_CONTEXT_MENUS,
|
MSG_CONTEXT_MENUS,
|
||||||
MSG_UPDATE_CSP,
|
MSG_UPDATE_CSP,
|
||||||
DEFAULT_HTTP_TIMEOUT,
|
DEFAULT_HTTP_TIMEOUT,
|
||||||
@@ -79,7 +80,10 @@ export default function Settings() {
|
|||||||
isExt && sendBgMsg(MSG_CONTEXT_MENUS, value);
|
isExt && sendBgMsg(MSG_CONTEXT_MENUS, value);
|
||||||
break;
|
break;
|
||||||
case "csplist":
|
case "csplist":
|
||||||
isExt && sendBgMsg(MSG_UPDATE_CSP, value);
|
isExt && sendBgMsg(MSG_UPDATE_CSP, { csplist: value });
|
||||||
|
break;
|
||||||
|
case "orilist":
|
||||||
|
isExt && sendBgMsg(MSG_UPDATE_CSP, { orilist: value });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -116,6 +120,7 @@ export default function Settings() {
|
|||||||
touchTranslate = 2,
|
touchTranslate = 2,
|
||||||
blacklist = DEFAULT_BLACKLIST.join(",\n"),
|
blacklist = DEFAULT_BLACKLIST.join(",\n"),
|
||||||
csplist = DEFAULT_CSPLIST.join(",\n"),
|
csplist = DEFAULT_CSPLIST.join(",\n"),
|
||||||
|
orilist = DEFAULT_ORILIST.join(",\n"),
|
||||||
transInterval = 100,
|
transInterval = 100,
|
||||||
langDetector = "-",
|
langDetector = "-",
|
||||||
preInit = true,
|
preInit = true,
|
||||||
@@ -399,6 +404,15 @@ export default function Settings() {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
multiline
|
multiline
|
||||||
/>
|
/>
|
||||||
|
<TextField
|
||||||
|
size="small"
|
||||||
|
label={i18n("disabled_orilist")}
|
||||||
|
helperText={i18n("pattern_helper")}
|
||||||
|
name="orilist"
|
||||||
|
value={orilist}
|
||||||
|
onChange={handleChange}
|
||||||
|
multiline
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import {
|
|||||||
OPT_TRANBOX_TRIGGER_ALL,
|
OPT_TRANBOX_TRIGGER_ALL,
|
||||||
OPT_DICT_BAIDU,
|
OPT_DICT_BAIDU,
|
||||||
OPT_DICT_ALL,
|
OPT_DICT_ALL,
|
||||||
|
OPT_SUG_ALL,
|
||||||
|
OPT_SUG_BAIDU,
|
||||||
} from "../../config";
|
} from "../../config";
|
||||||
import ShortcutInput from "./ShortcutInput";
|
import ShortcutInput from "./ShortcutInput";
|
||||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||||
@@ -68,6 +70,7 @@ export default function Tranbox() {
|
|||||||
triggerMode = OPT_TRANBOX_TRIGGER_CLICK,
|
triggerMode = OPT_TRANBOX_TRIGGER_CLICK,
|
||||||
// extStyles = "",
|
// extStyles = "",
|
||||||
enDict = OPT_DICT_BAIDU,
|
enDict = OPT_DICT_BAIDU,
|
||||||
|
enSug = OPT_SUG_BAIDU,
|
||||||
} = tranboxSetting;
|
} = tranboxSetting;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -89,7 +92,7 @@ export default function Tranbox() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Grid container spacing={2} columns={12}>
|
<Grid container spacing={2} columns={12}>
|
||||||
<Grid item xs={12} sm={12} md={12} lg={6}>
|
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
fullWidth
|
fullWidth
|
||||||
@@ -181,6 +184,24 @@ export default function Tranbox() {
|
|||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
select
|
||||||
|
size="small"
|
||||||
|
name="enSug"
|
||||||
|
value={enSug}
|
||||||
|
label={i18n("english_suggest")}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<MenuItem value={"-"}>{i18n("disable")}</MenuItem>
|
||||||
|
{OPT_SUG_ALL.map((item) => (
|
||||||
|
<MenuItem value={item} key={item}>
|
||||||
|
{item}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
</Grid>
|
||||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
|
|||||||
@@ -1,81 +1,40 @@
|
|||||||
import { useState, useEffect, useMemo } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import FavBtn from "./FavBtn";
|
import FavBtn from "./FavBtn";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import AudioBtn from "./AudioBtn";
|
import AudioBtn from "./AudioBtn";
|
||||||
import CircularProgress from "@mui/material/CircularProgress";
|
import CircularProgress from "@mui/material/CircularProgress";
|
||||||
|
import Divider from "@mui/material/Divider";
|
||||||
import Alert from "@mui/material/Alert";
|
import Alert from "@mui/material/Alert";
|
||||||
import { OPT_TRANS_BAIDU, PHONIC_MAP } from "../../config";
|
import { OPT_DICT_BAIDU, OPT_DICT_YOUDAO, PHONIC_MAP } from "../../config";
|
||||||
import { apiTranslate } from "../../apis";
|
|
||||||
import { isValidWord } from "../../libs/utils";
|
|
||||||
import CopyBtn from "./CopyBtn";
|
import CopyBtn from "./CopyBtn";
|
||||||
|
import { useAsyncNow } from "../../hooks/Fetch";
|
||||||
|
import { apiYoudaoDict } from "../../apis";
|
||||||
|
|
||||||
function DictBaidu({ text, setCopyText }) {
|
function DictBaidu({ text, setCopyText }) {
|
||||||
const [loading, setLoading] = useState(true);
|
// useEffect(() => {
|
||||||
const [error, setError] = useState("");
|
// if (!data) {
|
||||||
const [dictResult, setDictResult] = useState(null);
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
useEffect(() => {
|
// const copyText = [
|
||||||
(async () => {
|
// data.src,
|
||||||
try {
|
// data.voice
|
||||||
setLoading(true);
|
// ?.map(Object.entries)
|
||||||
setError("");
|
// .map((item) => item[0])
|
||||||
setDictResult(null);
|
// .map(([key, val]) => `${PHONIC_MAP[key]?.[0] || key} ${val}`)
|
||||||
|
// .join(" "),
|
||||||
|
// data.content[0].mean
|
||||||
|
// .map(({ pre, cont }) => {
|
||||||
|
// return `${pre ? `[${pre}] ` : ""}${Object.keys(cont).join("; ")}`;
|
||||||
|
// })
|
||||||
|
// .join("\n"),
|
||||||
|
// ].join("\n");
|
||||||
|
|
||||||
// if (!isValidWord(text)) {
|
// setCopyText(copyText);
|
||||||
// return;
|
// }, [data, setCopyText]);
|
||||||
// }
|
|
||||||
|
|
||||||
// // todo: 修复
|
return <Typography>baidu dict not supported yet</Typography>;
|
||||||
// const dictRes = await apiTranslate({
|
|
||||||
// text,
|
|
||||||
// apiSlug: OPT_TRANS_BAIDU,
|
|
||||||
// fromLang: "en",
|
|
||||||
// toLang: "zh-CN",
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (dictRes[2]?.type === 1) {
|
|
||||||
// setDictResult(JSON.parse(dictRes[2].result));
|
|
||||||
// }
|
|
||||||
} catch (err) {
|
|
||||||
setError(err.message);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, [text]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!dictResult) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const copyText = [
|
|
||||||
dictResult.src,
|
|
||||||
dictResult.voice
|
|
||||||
?.map(Object.entries)
|
|
||||||
.map((item) => item[0])
|
|
||||||
.map(([key, val]) => `${PHONIC_MAP[key]?.[0] || key} ${val}`)
|
|
||||||
.join(" "),
|
|
||||||
dictResult.content[0].mean
|
|
||||||
.map(({ pre, cont }) => {
|
|
||||||
return `${pre ? `[${pre}] ` : ""}${Object.keys(cont).join("; ")}`;
|
|
||||||
})
|
|
||||||
.join("\n"),
|
|
||||||
].join("\n");
|
|
||||||
|
|
||||||
setCopyText(copyText);
|
|
||||||
}, [dictResult, setCopyText]);
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <CircularProgress size={16} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return <Alert severity="error">{error}</Alert>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Typography>baidu: {text}</Typography>;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
/* {dictResult && (
|
/* {dictResult && (
|
||||||
@@ -109,11 +68,56 @@ function DictBaidu({ text, setCopyText }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DictYoudao({ text, setCopyText }) {
|
||||||
|
const { loading, error, data } = useAsyncNow(apiYoudaoDict, text);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyText = [
|
||||||
|
text,
|
||||||
|
data?.ec?.word?.trs
|
||||||
|
?.map(({ pos, tran }) => `${pos ? `[${pos}] ` : ""}${tran}`)
|
||||||
|
.join("\n"),
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
setCopyText(copyText);
|
||||||
|
}, [data, setCopyText]);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <CircularProgress size={16} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Alert severity="error">{error}</Alert>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Typography component="div">
|
||||||
|
<Typography component="ul">
|
||||||
|
{data?.ec?.word?.trs?.map(({ pos, tran }, idx) => (
|
||||||
|
<Typography component="li" key={idx}>
|
||||||
|
{pos && `[${pos}] `}
|
||||||
|
{tran}
|
||||||
|
</Typography>
|
||||||
|
))}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function DictCont({ text, enDict }) {
|
export default function DictCont({ text, enDict }) {
|
||||||
const [copyText, setCopyText] = useState(text);
|
const [copyText, setCopyText] = useState(text);
|
||||||
|
|
||||||
const dictMap = {
|
const dictMap = {
|
||||||
[OPT_TRANS_BAIDU]: <DictBaidu text={text} setCopyText={setCopyText} />,
|
[OPT_DICT_BAIDU]: <DictBaidu text={text} setCopyText={setCopyText} />,
|
||||||
|
[OPT_DICT_YOUDAO]: <DictYoudao text={text} setCopyText={setCopyText} />,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -130,6 +134,8 @@ export default function DictCont({ text, enDict }) {
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
{dictMap[enDict] || <Typography>Dict not support</Typography>}
|
{dictMap[enDict] || <Typography>Dict not support</Typography>}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,28 +1,30 @@
|
|||||||
import { useState, useEffect } from "react";
|
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import { apiBaiduSuggest } from "../../apis";
|
import CircularProgress from "@mui/material/CircularProgress";
|
||||||
|
import Divider from "@mui/material/Divider";
|
||||||
|
import Alert from "@mui/material/Alert";
|
||||||
|
import { apiBaiduSuggest, apiYoudaoSuggest } from "../../apis";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
|
import { OPT_SUG_BAIDU, OPT_SUG_YOUDAO } from "../../config";
|
||||||
|
import { useAsyncNow } from "../../hooks/Fetch";
|
||||||
|
|
||||||
export default function SugCont({ text }) {
|
function SugBaidu({ text }) {
|
||||||
const [sugs, setSugs] = useState([]);
|
const { loading, error, data } = useAsyncNow(apiBaiduSuggest, text);
|
||||||
|
|
||||||
useEffect(() => {
|
if (loading) {
|
||||||
(async () => {
|
return <CircularProgress size={16} />;
|
||||||
try {
|
}
|
||||||
setSugs(await apiBaiduSuggest(text));
|
|
||||||
} catch (err) {
|
|
||||||
// skip
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, [text]);
|
|
||||||
|
|
||||||
if (sugs.length === 0) {
|
if (error) {
|
||||||
return;
|
return <Alert severity="error">{error}</Alert>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={1}>
|
<>
|
||||||
{sugs.map(({ k, v }) => (
|
{data.map(({ k, v }) => (
|
||||||
<Typography component="div" key={k}>
|
<Typography component="div" key={k}>
|
||||||
<Typography>{k}</Typography>
|
<Typography>{k}</Typography>
|
||||||
<Typography component="ul" style={{ margin: "0" }}>
|
<Typography component="ul" style={{ margin: "0" }}>
|
||||||
@@ -30,6 +32,49 @@ export default function SugCont({ text }) {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Typography>
|
</Typography>
|
||||||
))}
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SugYoudao({ text }) {
|
||||||
|
const { loading, error, data } = useAsyncNow(apiYoudaoSuggest, text);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <CircularProgress size={16} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Alert severity="error">{error}</Alert>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{data.map(({ entry, explain }) => (
|
||||||
|
<Typography component="div" key={entry}>
|
||||||
|
<Typography>{entry}</Typography>
|
||||||
|
<Typography component="ul" style={{ margin: "0" }}>
|
||||||
|
<Typography component="li">{explain}</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SugCont({ text, enSug }) {
|
||||||
|
const sugMap = {
|
||||||
|
[OPT_SUG_BAIDU]: <SugBaidu text={text} />,
|
||||||
|
[OPT_SUG_YOUDAO]: <SugYoudao text={text} />,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<Divider />
|
||||||
|
{sugMap[enSug] || <Typography>Sug not support</Typography>}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export default function TranBox({
|
|||||||
text,
|
text,
|
||||||
setText,
|
setText,
|
||||||
setShowBox,
|
setShowBox,
|
||||||
tranboxSetting: { apiSlugs, fromLang, toLang, toLang2 },
|
tranboxSetting: { enDict, enSug, apiSlugs, fromLang, toLang, toLang2 },
|
||||||
transApis,
|
transApis,
|
||||||
boxSize,
|
boxSize,
|
||||||
setBoxSize,
|
setBoxSize,
|
||||||
@@ -128,7 +128,6 @@ export default function TranBox({
|
|||||||
setFollowSelection,
|
setFollowSelection,
|
||||||
extStyles = "",
|
extStyles = "",
|
||||||
langDetector,
|
langDetector,
|
||||||
enDict,
|
|
||||||
}) {
|
}) {
|
||||||
const [mouseHover, setMouseHover] = useState(false);
|
const [mouseHover, setMouseHover] = useState(false);
|
||||||
// todo: 这里的 SettingProvider 不应和 background 的共用
|
// todo: 这里的 SettingProvider 不应和 background 的共用
|
||||||
@@ -168,6 +167,7 @@ export default function TranBox({
|
|||||||
simpleStyle={simpleStyle}
|
simpleStyle={simpleStyle}
|
||||||
langDetector={langDetector}
|
langDetector={langDetector}
|
||||||
enDict={enDict}
|
enDict={enDict}
|
||||||
|
enSug={enSug}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</DraggableResizable>
|
</DraggableResizable>
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import {
|
|||||||
OPT_LANGS_TO,
|
OPT_LANGS_TO,
|
||||||
OPT_LANGDETECTOR_ALL,
|
OPT_LANGDETECTOR_ALL,
|
||||||
OPT_DICT_ALL,
|
OPT_DICT_ALL,
|
||||||
|
OPT_SUG_ALL,
|
||||||
OPT_LANGS_MAP,
|
OPT_LANGS_MAP,
|
||||||
OPT_DICT_MAP,
|
OPT_DICT_MAP,
|
||||||
|
OPT_SUG_MAP,
|
||||||
} from "../../config";
|
} from "../../config";
|
||||||
import { useState, useMemo, useEffect } from "react";
|
import { useState, useMemo, useEffect } from "react";
|
||||||
import TranCont from "./TranCont";
|
import TranCont from "./TranCont";
|
||||||
@@ -30,11 +32,12 @@ export default function TranForm({
|
|||||||
apiSlugs: initApiSlugs,
|
apiSlugs: initApiSlugs,
|
||||||
fromLang: initFromLang,
|
fromLang: initFromLang,
|
||||||
toLang: initToLang,
|
toLang: initToLang,
|
||||||
toLang2,
|
toLang2: initToLang2,
|
||||||
transApis,
|
transApis,
|
||||||
simpleStyle = false,
|
simpleStyle = false,
|
||||||
langDetector: initLangDetector = "-",
|
langDetector: initLangDetector = "-",
|
||||||
enDict: initEnDict = "-",
|
enDict: initEnDict = "-",
|
||||||
|
enSug: initEnSug = "-",
|
||||||
isPlaygound = false,
|
isPlaygound = false,
|
||||||
}) {
|
}) {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
@@ -44,11 +47,19 @@ export default function TranForm({
|
|||||||
const [apiSlugs, setApiSlugs] = useState(initApiSlugs);
|
const [apiSlugs, setApiSlugs] = useState(initApiSlugs);
|
||||||
const [fromLang, setFromLang] = useState(initFromLang);
|
const [fromLang, setFromLang] = useState(initFromLang);
|
||||||
const [toLang, setToLang] = useState(initToLang);
|
const [toLang, setToLang] = useState(initToLang);
|
||||||
|
const [toLang2, setToLang2] = useState(initToLang2);
|
||||||
const [langDetector, setLangDetector] = useState(initLangDetector);
|
const [langDetector, setLangDetector] = useState(initLangDetector);
|
||||||
const [enDict, setEnDict] = useState(initEnDict);
|
const [enDict, setEnDict] = useState(initEnDict);
|
||||||
|
const [enSug, setEnSug] = useState(initEnSug);
|
||||||
const [deLang, setDeLang] = useState("");
|
const [deLang, setDeLang] = useState("");
|
||||||
const [deLoading, setDeLoading] = useState(false);
|
const [deLoading, setDeLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editMode) {
|
||||||
|
setEditText(text);
|
||||||
|
}
|
||||||
|
}, [text, editMode]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!text.trim()) {
|
if (!text.trim()) {
|
||||||
setDeLang("");
|
setDeLang("");
|
||||||
@@ -96,6 +107,7 @@ export default function TranForm({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const isWord = useMemo(() => isValidWord(text), [text]);
|
const isWord = useMemo(() => isValidWord(text), [text]);
|
||||||
|
const xs = useMemo(() => (isPlaygound ? 3 : 4), [isPlaygound]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={simpleStyle ? 1 : 2}>
|
<Stack spacing={simpleStyle ? 1 : 2}>
|
||||||
@@ -103,18 +115,18 @@ export default function TranForm({
|
|||||||
<>
|
<>
|
||||||
<Box>
|
<Box>
|
||||||
<Grid container spacing={2} columns={12}>
|
<Grid container spacing={2} columns={12}>
|
||||||
<Grid item xs={4} sm={4} md={4} lg={4}>
|
<Grid item xs={xs}>
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
SelectProps={{
|
SelectProps={{
|
||||||
multiple: true,
|
multiple: true,
|
||||||
MenuProps: { disablePortal: true },
|
MenuProps: { disablePortal: !isPlaygound },
|
||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
size="small"
|
||||||
value={apiSlugs}
|
value={apiSlugs}
|
||||||
name="apiSlugs"
|
name="apiSlugs"
|
||||||
label={i18n("translate_service")}
|
label={i18n("translate_service_multiple")}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setApiSlugs(e.target.value);
|
setApiSlugs(e.target.value);
|
||||||
}}
|
}}
|
||||||
@@ -126,10 +138,10 @@ export default function TranForm({
|
|||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={4} sm={4} md={4} lg={4}>
|
<Grid item xs={xs}>
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
SelectProps={{ MenuProps: { disablePortal: true } }}
|
SelectProps={{ MenuProps: { disablePortal: !isPlaygound } }}
|
||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
size="small"
|
||||||
name="fromLang"
|
name="fromLang"
|
||||||
@@ -146,10 +158,10 @@ export default function TranForm({
|
|||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={4} sm={4} md={4} lg={4}>
|
<Grid item xs={xs}>
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
SelectProps={{ MenuProps: { disablePortal: true } }}
|
SelectProps={{ MenuProps: { disablePortal: !isPlaygound } }}
|
||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
size="small"
|
||||||
name="toLang"
|
name="toLang"
|
||||||
@@ -166,73 +178,120 @@ export default function TranForm({
|
|||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
{isPlaygound && (
|
||||||
|
<>
|
||||||
|
<Grid item xs={xs}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
SelectProps={{
|
||||||
|
MenuProps: { disablePortal: !isPlaygound },
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
size="small"
|
||||||
|
name="toLang2"
|
||||||
|
value={toLang2}
|
||||||
|
label={i18n("to_lang2")}
|
||||||
|
onChange={(e) => {
|
||||||
|
setToLang2(e.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{OPT_LANGS_TO.map(([lang, name]) => (
|
||||||
|
<MenuItem key={lang} value={lang}>
|
||||||
|
{name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={xs}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
SelectProps={{
|
||||||
|
MenuProps: { disablePortal: !isPlaygound },
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
size="small"
|
||||||
|
name="enDict"
|
||||||
|
value={enDict}
|
||||||
|
label={i18n("english_dict")}
|
||||||
|
onChange={(e) => {
|
||||||
|
setEnDict(e.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value={"-"}>{i18n("disable")}</MenuItem>
|
||||||
|
{OPT_DICT_ALL.map((item) => (
|
||||||
|
<MenuItem value={item} key={item}>
|
||||||
|
{item}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={xs}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
SelectProps={{
|
||||||
|
MenuProps: { disablePortal: !isPlaygound },
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
size="small"
|
||||||
|
name="enSug"
|
||||||
|
value={enSug}
|
||||||
|
label={i18n("english_suggest")}
|
||||||
|
onChange={(e) => {
|
||||||
|
setEnSug(e.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value={"-"}>{i18n("disable")}</MenuItem>
|
||||||
|
{OPT_SUG_ALL.map((item) => (
|
||||||
|
<MenuItem value={item} key={item}>
|
||||||
|
{item}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={xs}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
SelectProps={{
|
||||||
|
MenuProps: { disablePortal: !isPlaygound },
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
size="small"
|
||||||
|
name="langDetector"
|
||||||
|
value={langDetector}
|
||||||
|
label={i18n("detected_lang")}
|
||||||
|
onChange={(e) => {
|
||||||
|
setLangDetector(e.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value={"-"}>{i18n("disable")}</MenuItem>
|
||||||
|
{OPT_LANGDETECTOR_ALL.map((item) => (
|
||||||
|
<MenuItem value={item} key={item}>
|
||||||
|
{item}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={xs}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
size="small"
|
||||||
|
name="deLang"
|
||||||
|
value={deLang && OPT_LANGS_MAP.get(deLang)}
|
||||||
|
label={i18n("detected_result")}
|
||||||
|
disabled
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: deLoading ? (
|
||||||
|
<CircularProgress size={16} />
|
||||||
|
) : null,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{isPlaygound && (
|
|
||||||
<Box>
|
|
||||||
<Grid container spacing={2} columns={12}>
|
|
||||||
<Grid item xs={4} sm={4} md={4} lg={4}>
|
|
||||||
<TextField
|
|
||||||
select
|
|
||||||
SelectProps={{ MenuProps: { disablePortal: true } }}
|
|
||||||
fullWidth
|
|
||||||
size="small"
|
|
||||||
name="enDict"
|
|
||||||
value={enDict}
|
|
||||||
label={i18n("english_dict")}
|
|
||||||
onChange={(e) => {
|
|
||||||
setEnDict(e.target.value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem value={"-"}>{i18n("disable")}</MenuItem>
|
|
||||||
{OPT_DICT_ALL.map((item) => (
|
|
||||||
<MenuItem value={item} key={item}>
|
|
||||||
{item}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</TextField>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4} sm={4} md={4} lg={4}>
|
|
||||||
<TextField
|
|
||||||
select
|
|
||||||
SelectProps={{ MenuProps: { disablePortal: true } }}
|
|
||||||
fullWidth
|
|
||||||
size="small"
|
|
||||||
name="langDetector"
|
|
||||||
value={langDetector}
|
|
||||||
label={i18n("detected_lang")}
|
|
||||||
onChange={(e) => {
|
|
||||||
setLangDetector(e.target.value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem value={"-"}>{i18n("disable")}</MenuItem>
|
|
||||||
{OPT_LANGDETECTOR_ALL.map((item) => (
|
|
||||||
<MenuItem value={item} key={item}>
|
|
||||||
{item}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</TextField>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4} sm={4} md={4} lg={4}>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
size="small"
|
|
||||||
name="deLang"
|
|
||||||
value={deLang && OPT_LANGS_MAP.get(deLang)}
|
|
||||||
label={i18n("detected_result")}
|
|
||||||
disabled
|
|
||||||
InputProps={{
|
|
||||||
startAdornment: deLoading ? (
|
|
||||||
<CircularProgress size={16} />
|
|
||||||
) : null,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<TextField
|
<TextField
|
||||||
size="small"
|
size="small"
|
||||||
@@ -267,6 +326,8 @@ export default function TranForm({
|
|||||||
size="small"
|
size="small"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
setEditMode(false);
|
||||||
|
setText(editText.trim());
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DoneIcon fontSize="inherit" />
|
<DoneIcon fontSize="inherit" />
|
||||||
@@ -295,10 +356,11 @@ export default function TranForm({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{isWord && OPT_DICT_MAP.has(enDict) && (
|
{isWord && OPT_DICT_MAP.has(enDict) && (
|
||||||
<>
|
<DictCont text={text} enDict={enDict} />
|
||||||
<DictCont text={text} enDict={enDict} />
|
)}
|
||||||
<SugCont text={text} />
|
|
||||||
</>
|
{isWord && OPT_SUG_MAP.has(enSug) && (
|
||||||
|
<SugCont text={text} enSug={enSug} />
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
OPT_TRANBOX_TRIGGER_CLICK,
|
OPT_TRANBOX_TRIGGER_CLICK,
|
||||||
OPT_TRANBOX_TRIGGER_HOVER,
|
OPT_TRANBOX_TRIGGER_HOVER,
|
||||||
OPT_TRANBOX_TRIGGER_SELECT,
|
OPT_TRANBOX_TRIGGER_SELECT,
|
||||||
OPT_DICT_BAIDU,
|
|
||||||
} from "../../config";
|
} from "../../config";
|
||||||
import { isMobile } from "../../libs/mobile";
|
import { isMobile } from "../../libs/mobile";
|
||||||
import { kissLog } from "../../libs/log";
|
import { kissLog } from "../../libs/log";
|
||||||
@@ -35,7 +34,6 @@ export default function Slection({
|
|||||||
btnOffsetY,
|
btnOffsetY,
|
||||||
boxOffsetX = 0,
|
boxOffsetX = 0,
|
||||||
boxOffsetY = 10,
|
boxOffsetY = 10,
|
||||||
enDict = OPT_DICT_BAIDU,
|
|
||||||
} = tranboxSetting;
|
} = tranboxSetting;
|
||||||
|
|
||||||
const boxWidth =
|
const boxWidth =
|
||||||
@@ -238,7 +236,6 @@ export default function Slection({
|
|||||||
setFollowSelection={setFollowSelection}
|
setFollowSelection={setFollowSelection}
|
||||||
// extStyles={extStyles}
|
// extStyles={extStyles}
|
||||||
langDetector={langDetector}
|
langDetector={langDetector}
|
||||||
enDict={enDict}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user