fix: api hooks
This commit is contained in:
@@ -214,10 +214,9 @@ export const apiTranslate = async ({
|
||||
}
|
||||
|
||||
const { apiType, apiSlug, useBatchFetch } = apiSetting;
|
||||
const from =
|
||||
OPT_LANGS_SPECIAL[apiType].get(fromLang) ??
|
||||
OPT_LANGS_SPECIAL[apiType].get("auto");
|
||||
const to = OPT_LANGS_SPECIAL[apiType].get(toLang);
|
||||
const langMap = OPT_LANGS_SPECIAL[apiType];
|
||||
const from = langMap.get(fromLang) ?? langMap.get("auto");
|
||||
const to = langMap.get(toLang);
|
||||
if (!to) {
|
||||
kissLog(`target lang: ${toLang} not support`);
|
||||
return ["", false];
|
||||
@@ -249,6 +248,9 @@ export const apiTranslate = async ({
|
||||
const queue = getBatchQueue({
|
||||
from,
|
||||
to,
|
||||
fromLang,
|
||||
toLang,
|
||||
langMap,
|
||||
docInfo,
|
||||
apiSetting,
|
||||
usePool,
|
||||
@@ -257,22 +259,32 @@ export const apiTranslate = async ({
|
||||
const tranlation = await queue.addTask({ text });
|
||||
if (Array.isArray(tranlation)) {
|
||||
[trText, srLang = ""] = tranlation;
|
||||
} else if (typeof tranlation === "string") {
|
||||
trText = tranlation;
|
||||
}
|
||||
} else {
|
||||
const translations = await handleTranslate({
|
||||
texts: [text],
|
||||
from,
|
||||
to,
|
||||
fromLang,
|
||||
toLang,
|
||||
langMap,
|
||||
docInfo,
|
||||
apiSetting,
|
||||
usePool,
|
||||
});
|
||||
if (Array.isArray(translations?.[0])) {
|
||||
[trText, srLang = ""] = translations[0];
|
||||
if (Array.isArray(translations)) {
|
||||
if (Array.isArray(translations[0])) {
|
||||
[trText, srLang = ""] = translations[0];
|
||||
} else {
|
||||
[trText, srLang = ""] = translations;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isSame = srLang && (to.includes(srLang) || srLang.includes(to));
|
||||
// const isSame = srLang && (to.includes(srLang) || srLang.includes(to));
|
||||
const isSame = srLang && srLang.slice(0, 2) === to.slice(0, 2);
|
||||
|
||||
// 插入缓存
|
||||
if (useCache && trText) {
|
||||
|
||||
@@ -111,7 +111,7 @@ const parseAIRes = (raw) => {
|
||||
};
|
||||
|
||||
const genGoogle = ({ texts, from, to, url, key }) => {
|
||||
const params = {
|
||||
const params = queryString.stringify({
|
||||
client: "gtx",
|
||||
dt: "t",
|
||||
dj: 1,
|
||||
@@ -119,52 +119,42 @@ const genGoogle = ({ texts, from, to, url, key }) => {
|
||||
sl: from,
|
||||
tl: to,
|
||||
q: texts.join(" "),
|
||||
};
|
||||
const input = `${url}?${queryString.stringify(params)}`;
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
});
|
||||
url = `${url}?${params}`;
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
if (key) {
|
||||
init.headers.Authorization = `Bearer ${key}`;
|
||||
headers.Authorization = `Bearer ${key}`;
|
||||
}
|
||||
|
||||
return [input, init];
|
||||
return { url, headers, method: "GET" };
|
||||
};
|
||||
|
||||
const genGoogle2 = ({ texts, from, to, url, key }) => {
|
||||
const body = JSON.stringify([[texts, from, to], "wt_lib"]);
|
||||
const init = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json+protobuf",
|
||||
"X-Goog-API-Key": key,
|
||||
},
|
||||
body,
|
||||
const data = [[texts, from, to], "wt_lib"];
|
||||
const headers = {
|
||||
"Content-Type": "application/json+protobuf",
|
||||
"X-Goog-API-Key": key,
|
||||
};
|
||||
|
||||
return [url, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genMicrosoft = async ({ texts, from, to }) => {
|
||||
const [token] = await msAuth();
|
||||
const params = {
|
||||
const genMicrosoft = ({ texts, from, to, token }) => {
|
||||
const params = queryString.stringify({
|
||||
from,
|
||||
to,
|
||||
"api-version": "3.0",
|
||||
});
|
||||
const url = `https://api-edge.cognitive.microsofttranslator.com/translate?${params}`;
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
const input = `https://api-edge.cognitive.microsofttranslator.com/translate?${queryString.stringify(params)}`;
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(texts.map((text) => ({ Text: text }))),
|
||||
};
|
||||
const data = texts.map((text) => ({ Text: text }));
|
||||
|
||||
return [input, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genDeepl = ({ texts, from, to, url, key }) => {
|
||||
@@ -174,16 +164,12 @@ const genDeepl = ({ texts, from, to, url, key }) => {
|
||||
source_lang: from,
|
||||
// split_sentences: "0",
|
||||
};
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `DeepL-Auth-Key ${key}`,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `DeepL-Auth-Key ${key}`,
|
||||
};
|
||||
|
||||
return [url, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genDeeplX = ({ texts, from, to, url, key }) => {
|
||||
@@ -193,18 +179,14 @@ const genDeeplX = ({ texts, from, to, url, key }) => {
|
||||
source_lang: from,
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
if (key) {
|
||||
init.headers.Authorization = `Bearer ${key}`;
|
||||
headers.Authorization = `Bearer ${key}`;
|
||||
}
|
||||
|
||||
return [url, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genNiuTrans = ({ texts, from, to, url, key, dictNo, memoryNo }) => {
|
||||
@@ -217,15 +199,11 @@ const genNiuTrans = ({ texts, from, to, url, key, dictNo, memoryNo }) => {
|
||||
memoryNo,
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
|
||||
return [url, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genTencent = ({ texts, from, to }) => {
|
||||
@@ -246,19 +224,15 @@ const genTencent = ({ texts, from, to }) => {
|
||||
},
|
||||
};
|
||||
|
||||
const input = "https://transmart.qq.com/api/imt";
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"user-agent":
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
||||
referer: "https://transmart.qq.com/zh-CN/index",
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const url = "https://transmart.qq.com/api/imt";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"user-agent":
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
||||
referer: "https://transmart.qq.com/zh-CN/index",
|
||||
};
|
||||
|
||||
return [input, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genVolcengine = ({ texts, from, to }) => {
|
||||
@@ -268,22 +242,15 @@ const genVolcengine = ({ texts, from, to }) => {
|
||||
text: texts.join(" "),
|
||||
};
|
||||
|
||||
const input = "https://translate.volcengine.com/crx/translate/v1";
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const url = "https://translate.volcengine.com/crx/translate/v1";
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
|
||||
return [input, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genOpenAI = ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
url,
|
||||
key,
|
||||
systemPrompt,
|
||||
@@ -291,16 +258,8 @@ const genOpenAI = ({
|
||||
model,
|
||||
temperature,
|
||||
maxTokens,
|
||||
customHeader,
|
||||
customBody,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
}) => {
|
||||
systemPrompt = genSystemPrompt({ systemPrompt, from, to });
|
||||
userPrompt = genUserPrompt({ userPrompt, from, to, texts, docInfo });
|
||||
customHeader = parseJsonObj(customHeader);
|
||||
customBody = parseJsonObj(customBody);
|
||||
|
||||
const userMsg = {
|
||||
role: "user",
|
||||
content: userPrompt,
|
||||
@@ -317,27 +276,18 @@ const genOpenAI = ({
|
||||
],
|
||||
temperature,
|
||||
max_completion_tokens: maxTokens,
|
||||
...customBody,
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`, // OpenAI
|
||||
"api-key": key, // Azure OpenAI
|
||||
...customHeader,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`, // OpenAI
|
||||
// "api-key": key, // Azure OpenAI
|
||||
};
|
||||
|
||||
return [url, init, userMsg];
|
||||
return { url, data, headers, userMsg };
|
||||
};
|
||||
|
||||
const genGemini = ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
url,
|
||||
key,
|
||||
systemPrompt,
|
||||
@@ -345,18 +295,11 @@ const genGemini = ({
|
||||
model,
|
||||
temperature,
|
||||
maxTokens,
|
||||
customHeader,
|
||||
customBody,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
}) => {
|
||||
url = url
|
||||
.replaceAll(INPUT_PLACE_MODEL, model)
|
||||
.replaceAll(INPUT_PLACE_KEY, key);
|
||||
systemPrompt = genSystemPrompt({ systemPrompt, from, to });
|
||||
userPrompt = genUserPrompt({ userPrompt, from, to, texts, docInfo });
|
||||
customHeader = parseJsonObj(customHeader);
|
||||
customBody = parseJsonObj(customBody);
|
||||
|
||||
const userMsg = { role: "user", parts: [{ text: userPrompt }] };
|
||||
const data = {
|
||||
@@ -393,25 +336,15 @@ const genGemini = ({
|
||||
threshold: "BLOCK_NONE",
|
||||
},
|
||||
],
|
||||
...customBody,
|
||||
};
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
...customHeader,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
|
||||
return [url, init, userMsg];
|
||||
return { url, data, headers, userMsg };
|
||||
};
|
||||
|
||||
const genGemini2 = ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
url,
|
||||
key,
|
||||
systemPrompt,
|
||||
@@ -419,16 +352,8 @@ const genGemini2 = ({
|
||||
model,
|
||||
temperature,
|
||||
maxTokens,
|
||||
customHeader,
|
||||
customBody,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
}) => {
|
||||
systemPrompt = genSystemPrompt({ systemPrompt, from, to });
|
||||
userPrompt = genUserPrompt({ userPrompt, from, to, texts, docInfo });
|
||||
customHeader = parseJsonObj(customHeader);
|
||||
customBody = parseJsonObj(customBody);
|
||||
|
||||
const userMsg = {
|
||||
role: "user",
|
||||
content: userPrompt,
|
||||
@@ -445,26 +370,17 @@ const genGemini2 = ({
|
||||
],
|
||||
temperature,
|
||||
max_tokens: maxTokens,
|
||||
...customBody,
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
...customHeader,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
};
|
||||
|
||||
return [url, init, userMsg];
|
||||
return { url, data, headers, userMsg };
|
||||
};
|
||||
|
||||
const genClaude = ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
url,
|
||||
key,
|
||||
systemPrompt,
|
||||
@@ -472,16 +388,8 @@ const genClaude = ({
|
||||
model,
|
||||
temperature,
|
||||
maxTokens,
|
||||
customHeader,
|
||||
customBody,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
}) => {
|
||||
systemPrompt = genSystemPrompt({ systemPrompt, from, to });
|
||||
userPrompt = genUserPrompt({ userPrompt, from, to, texts, docInfo });
|
||||
customHeader = parseJsonObj(customHeader);
|
||||
customBody = parseJsonObj(customBody);
|
||||
|
||||
const userMsg = {
|
||||
role: "user",
|
||||
content: userPrompt,
|
||||
@@ -492,28 +400,19 @@ const genClaude = ({
|
||||
messages: [...hisMsgs, userMsg],
|
||||
temperature,
|
||||
max_tokens: maxTokens,
|
||||
...customBody,
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"anthropic-dangerous-direct-browser-access": "true",
|
||||
"x-api-key": key,
|
||||
...customHeader,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"anthropic-dangerous-direct-browser-access": "true",
|
||||
"x-api-key": key,
|
||||
};
|
||||
|
||||
return [url, init, userMsg];
|
||||
return { url, data, headers, userMsg };
|
||||
};
|
||||
|
||||
const genOpenRouter = ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
url,
|
||||
key,
|
||||
systemPrompt,
|
||||
@@ -521,16 +420,8 @@ const genOpenRouter = ({
|
||||
model,
|
||||
temperature,
|
||||
maxTokens,
|
||||
customHeader,
|
||||
customBody,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
}) => {
|
||||
systemPrompt = genSystemPrompt({ systemPrompt, from, to });
|
||||
userPrompt = genUserPrompt({ userPrompt, from, to, texts, docInfo });
|
||||
customHeader = parseJsonObj(customHeader);
|
||||
customBody = parseJsonObj(customBody);
|
||||
|
||||
const userMsg = {
|
||||
role: "user",
|
||||
content: userPrompt,
|
||||
@@ -547,26 +438,17 @@ const genOpenRouter = ({
|
||||
],
|
||||
temperature,
|
||||
max_tokens: maxTokens,
|
||||
...customBody,
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
...customHeader,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
};
|
||||
|
||||
return [url, init, userMsg];
|
||||
return { url, data, headers, userMsg };
|
||||
};
|
||||
|
||||
const genOllama = ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
think,
|
||||
url,
|
||||
key,
|
||||
@@ -575,16 +457,8 @@ const genOllama = ({
|
||||
model,
|
||||
temperature,
|
||||
maxTokens,
|
||||
customHeader,
|
||||
customBody,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
}) => {
|
||||
systemPrompt = genSystemPrompt({ systemPrompt, from, to });
|
||||
userPrompt = genUserPrompt({ userPrompt, from, to, texts, docInfo });
|
||||
customHeader = parseJsonObj(customHeader);
|
||||
customBody = parseJsonObj(customBody);
|
||||
|
||||
const userMsg = {
|
||||
role: "user",
|
||||
content: userPrompt,
|
||||
@@ -603,22 +477,16 @@ const genOllama = ({
|
||||
max_tokens: maxTokens,
|
||||
think,
|
||||
stream: false,
|
||||
...customBody,
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
...customHeader,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
if (key) {
|
||||
init.headers.Authorization = `Bearer ${key}`;
|
||||
headers.Authorization = `Bearer ${key}`;
|
||||
}
|
||||
|
||||
return [url, init, userMsg];
|
||||
return { url, data, headers, userMsg };
|
||||
};
|
||||
|
||||
const genCloudflareAI = ({ texts, from, to, url, key }) => {
|
||||
@@ -628,52 +496,65 @@ const genCloudflareAI = ({ texts, from, to, url, key }) => {
|
||||
target_lang: to,
|
||||
};
|
||||
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
};
|
||||
|
||||
return [url, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genCustom = ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
url,
|
||||
key,
|
||||
reqHook,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
}) => {
|
||||
if (reqHook?.trim()) {
|
||||
interpreter.run(`exports.reqHook = ${reqHook}`);
|
||||
return interpreter.exports.reqHook({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
url,
|
||||
key,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
});
|
||||
}
|
||||
|
||||
const genCustom = ({ texts, from, to, url, key }) => {
|
||||
const data = { texts, from, to };
|
||||
const init = {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
};
|
||||
|
||||
return [url, init];
|
||||
return { url, data, headers };
|
||||
};
|
||||
|
||||
const genReqFuncs = {
|
||||
[OPT_TRANS_GOOGLE]: genGoogle,
|
||||
[OPT_TRANS_GOOGLE_2]: genGoogle2,
|
||||
[OPT_TRANS_MICROSOFT]: genMicrosoft,
|
||||
[OPT_TRANS_DEEPL]: genDeepl,
|
||||
[OPT_TRANS_DEEPLFREE]: genDeeplFree,
|
||||
[OPT_TRANS_DEEPLX]: genDeeplX,
|
||||
[OPT_TRANS_NIUTRANS]: genNiuTrans,
|
||||
[OPT_TRANS_BAIDU]: genBaidu,
|
||||
[OPT_TRANS_TENCENT]: genTencent,
|
||||
[OPT_TRANS_VOLCENGINE]: genVolcengine,
|
||||
[OPT_TRANS_OPENAI]: genOpenAI,
|
||||
[OPT_TRANS_GEMINI]: genGemini,
|
||||
[OPT_TRANS_GEMINI_2]: genGemini2,
|
||||
[OPT_TRANS_CLAUDE]: genClaude,
|
||||
[OPT_TRANS_CLOUDFLAREAI]: genCloudflareAI,
|
||||
[OPT_TRANS_OLLAMA]: genOllama,
|
||||
[OPT_TRANS_OPENROUTER]: genOpenRouter,
|
||||
[OPT_TRANS_CUSTOMIZE]: genCustom,
|
||||
};
|
||||
|
||||
const genInit = ({
|
||||
url = "",
|
||||
data = null,
|
||||
headers = {},
|
||||
userMsg = null,
|
||||
method = "POST",
|
||||
}) => {
|
||||
if (!url) {
|
||||
throw new Error("genInit: url is empty");
|
||||
}
|
||||
|
||||
const init = {
|
||||
method,
|
||||
headers,
|
||||
};
|
||||
if (method !== "GET" && method !== "HEAD" && data) {
|
||||
Object.assign(init, { body: JSON.stringify(data) });
|
||||
}
|
||||
|
||||
return [url, init, userMsg];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -681,66 +562,70 @@ const genCustom = ({
|
||||
* @param {*}
|
||||
* @returns
|
||||
*/
|
||||
export const genTransReq = ({ apiType, apiSlug, ...args }) => {
|
||||
switch (apiType) {
|
||||
case OPT_TRANS_DEEPL:
|
||||
case OPT_TRANS_OPENAI:
|
||||
case OPT_TRANS_GEMINI:
|
||||
case OPT_TRANS_GEMINI_2:
|
||||
case OPT_TRANS_CLAUDE:
|
||||
case OPT_TRANS_CLOUDFLAREAI:
|
||||
case OPT_TRANS_OLLAMA:
|
||||
case OPT_TRANS_OPENROUTER:
|
||||
case OPT_TRANS_NIUTRANS:
|
||||
case OPT_TRANS_CUSTOMIZE:
|
||||
args.key = keyPick(apiSlug, args.key, keyMap);
|
||||
break;
|
||||
case OPT_TRANS_DEEPLX:
|
||||
args.url = keyPick(apiSlug, args.url, urlMap);
|
||||
break;
|
||||
default:
|
||||
export const genTransReq = async ({ reqHook, resHook, ...args }) => {
|
||||
const {
|
||||
apiType,
|
||||
apiSlug,
|
||||
key,
|
||||
systemPrompt,
|
||||
userPrompt,
|
||||
from,
|
||||
to,
|
||||
texts,
|
||||
docInfo,
|
||||
customHeader,
|
||||
customBody,
|
||||
} = args;
|
||||
|
||||
if (API_SPE_TYPES.mulkeys.has(apiType)) {
|
||||
args.key = keyPick(apiSlug, key, keyMap);
|
||||
}
|
||||
|
||||
switch (apiType) {
|
||||
case OPT_TRANS_GOOGLE:
|
||||
return genGoogle(args);
|
||||
case OPT_TRANS_GOOGLE_2:
|
||||
return genGoogle2(args);
|
||||
case OPT_TRANS_MICROSOFT:
|
||||
return genMicrosoft(args);
|
||||
case OPT_TRANS_DEEPL:
|
||||
return genDeepl(args);
|
||||
case OPT_TRANS_DEEPLFREE:
|
||||
return genDeeplFree(args);
|
||||
case OPT_TRANS_DEEPLX:
|
||||
return genDeeplX(args);
|
||||
case OPT_TRANS_NIUTRANS:
|
||||
return genNiuTrans(args);
|
||||
case OPT_TRANS_BAIDU:
|
||||
return genBaidu(args);
|
||||
case OPT_TRANS_TENCENT:
|
||||
return genTencent(args);
|
||||
case OPT_TRANS_VOLCENGINE:
|
||||
return genVolcengine(args);
|
||||
case OPT_TRANS_OPENAI:
|
||||
return genOpenAI(args);
|
||||
case OPT_TRANS_GEMINI:
|
||||
return genGemini(args);
|
||||
case OPT_TRANS_GEMINI_2:
|
||||
return genGemini2(args);
|
||||
case OPT_TRANS_CLAUDE:
|
||||
return genClaude(args);
|
||||
case OPT_TRANS_CLOUDFLAREAI:
|
||||
return genCloudflareAI(args);
|
||||
case OPT_TRANS_OLLAMA:
|
||||
return genOllama(args);
|
||||
case OPT_TRANS_OPENROUTER:
|
||||
return genOpenRouter(args);
|
||||
case OPT_TRANS_CUSTOMIZE:
|
||||
return genCustom(args);
|
||||
default:
|
||||
throw new Error(`[trans] ${apiType} not support`);
|
||||
if (apiType === OPT_TRANS_DEEPLX) {
|
||||
args.url = keyPick(apiSlug, args.url, urlMap);
|
||||
}
|
||||
|
||||
if (API_SPE_TYPES.ai.has(apiType)) {
|
||||
args.systemPrompt = genSystemPrompt({ systemPrompt, from, to });
|
||||
args.userPrompt = genUserPrompt({ userPrompt, from, to, texts, docInfo });
|
||||
}
|
||||
|
||||
const {
|
||||
url = "",
|
||||
data = null,
|
||||
headers = {},
|
||||
userMsg = null,
|
||||
method = "POST",
|
||||
} = genReqFuncs[apiType](args);
|
||||
|
||||
// 合并用户自定义headers和body
|
||||
if (customHeader?.trim()) {
|
||||
Object.assign(headers, parseJsonObj(customHeader));
|
||||
}
|
||||
if (customBody?.trim()) {
|
||||
Object.assign(data, parseJsonObj(customBody));
|
||||
}
|
||||
|
||||
// 执行 request hook
|
||||
if (reqHook?.trim()) {
|
||||
try {
|
||||
interpreter.run(`exports.reqHook = ${reqHook}`);
|
||||
const hookResult = await interpreter.exports.reqHook(args, {
|
||||
url,
|
||||
data,
|
||||
headers,
|
||||
userMsg,
|
||||
method,
|
||||
});
|
||||
if (hookResult && hookResult.url) {
|
||||
return genInit(hookResult);
|
||||
}
|
||||
} catch (err) {
|
||||
kissLog("run req hook", err);
|
||||
}
|
||||
}
|
||||
|
||||
return genInit({ url, data, headers, userMsg, method });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -749,10 +634,48 @@ export const genTransReq = ({ apiType, apiSlug, ...args }) => {
|
||||
* @param {*} param3
|
||||
* @returns
|
||||
*/
|
||||
export const parseTransRes = (
|
||||
export const parseTransRes = async (
|
||||
res,
|
||||
{ texts, from, to, resHook, thinkIgnore, history, userMsg, apiType }
|
||||
{
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
fromLang,
|
||||
toLang,
|
||||
langMap,
|
||||
resHook,
|
||||
thinkIgnore,
|
||||
history,
|
||||
userMsg,
|
||||
apiType,
|
||||
}
|
||||
) => {
|
||||
// 执行 response hook
|
||||
if (resHook?.trim()) {
|
||||
try {
|
||||
interpreter.run(`exports.resHook = ${resHook}`);
|
||||
const hookResult = await interpreter.exports.resHook({
|
||||
apiType,
|
||||
userMsg,
|
||||
res,
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
fromLang,
|
||||
toLang,
|
||||
langMap,
|
||||
});
|
||||
if (hookResult && Array.isArray(hookResult.translations)) {
|
||||
if (history && userMsg && hookResult.modelMsg) {
|
||||
history.add(userMsg, hookResult.modelMsg);
|
||||
}
|
||||
return hookResult.translations;
|
||||
}
|
||||
} catch (err) {
|
||||
kissLog("run res hook", err);
|
||||
}
|
||||
}
|
||||
|
||||
let modelMsg = "";
|
||||
|
||||
switch (apiType) {
|
||||
@@ -832,7 +755,9 @@ export const parseTransRes = (
|
||||
case OPT_TRANS_OLLAMA:
|
||||
modelMsg = res?.choices?.[0]?.message;
|
||||
|
||||
const deepModels = thinkIgnore.split(",").filter((model) => model.trim());
|
||||
const deepModels = thinkIgnore
|
||||
.split(",")
|
||||
.filter((model) => model?.trim());
|
||||
if (deepModels.some((model) => res?.model?.startsWith(model))) {
|
||||
modelMsg?.content.replace(/<think>[\s\S]*<\/think>/i, "");
|
||||
}
|
||||
@@ -845,23 +770,7 @@ export const parseTransRes = (
|
||||
}
|
||||
return parseAIRes(modelMsg?.content);
|
||||
case OPT_TRANS_CUSTOMIZE:
|
||||
if (resHook?.trim()) {
|
||||
interpreter.run(`exports.resHook = ${resHook}`);
|
||||
if (history) {
|
||||
const [translations, modelMsg] = interpreter.exports.resHook({
|
||||
res,
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
});
|
||||
userMsg && modelMsg && history.add(userMsg, modelMsg);
|
||||
return translations;
|
||||
} else {
|
||||
return interpreter.exports.resHook({ res, texts, from, to });
|
||||
}
|
||||
} else {
|
||||
return res?.map((item) => [item.text, item.src]);
|
||||
}
|
||||
return res?.map((item) => [item.text, item.src]);
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -877,6 +786,9 @@ export const handleTranslate = async ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
fromLang,
|
||||
toLang,
|
||||
langMap,
|
||||
docInfo,
|
||||
apiSetting,
|
||||
usePool,
|
||||
@@ -897,12 +809,21 @@ export const handleTranslate = async ({
|
||||
hisMsgs = history.getAll();
|
||||
}
|
||||
|
||||
let token = "";
|
||||
if (apiType === OPT_TRANS_MICROSOFT) {
|
||||
[token] = await msAuth();
|
||||
}
|
||||
|
||||
const [input, init, userMsg] = await genTransReq({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
fromLang,
|
||||
toLang,
|
||||
langMap,
|
||||
docInfo,
|
||||
hisMsgs,
|
||||
token,
|
||||
...apiSetting,
|
||||
});
|
||||
|
||||
@@ -921,6 +842,9 @@ export const handleTranslate = async ({
|
||||
texts,
|
||||
from,
|
||||
to,
|
||||
fromLang,
|
||||
toLang,
|
||||
langMap,
|
||||
history,
|
||||
userMsg,
|
||||
...apiSetting,
|
||||
|
||||
@@ -350,6 +350,18 @@ Output: {"translations":[{"id":1,"text":"一个<b>React</b>组件","sourceLangua
|
||||
|
||||
Fail-safe: On any error, return {"translations":[]}.`;
|
||||
|
||||
const defaultRequestHook = `async (args, { url, data, headers, userMsg, method } = {}) => {
|
||||
console.log("request hook args:", args);
|
||||
// return { url, data, headers, userMsg, method };
|
||||
}`;
|
||||
|
||||
const defaultResponseHook = `async ({ res, ...args }) => {
|
||||
console.log("reaponse hook args:", res, args);
|
||||
// const translations = [["你好", "zh"]];
|
||||
// const modelMsg = "";
|
||||
// return { translations, modelMsg };
|
||||
}`;
|
||||
|
||||
// 翻译接口默认参数
|
||||
const defaultApi = {
|
||||
apiSlug: "", // 唯一标识
|
||||
@@ -473,16 +485,8 @@ const defaultApiOpts = {
|
||||
[OPT_TRANS_CUSTOMIZE]: {
|
||||
...defaultApi,
|
||||
url: "https://translate.googleapis.com/translate_a/single?client=gtx&dj=1&dt=t&ie=UTF-8&q={{text}}&sl=en&tl=zh-CN",
|
||||
reqHook: `// Request Hook
|
||||
(text, from, to, url, key) => [url, {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
method: "GET",
|
||||
body: null,
|
||||
}]`,
|
||||
resHook: `// Response Hook
|
||||
(res, text, from, to) => [res.sentences.map((item) => item.trans).join(" "), to === res.src]`,
|
||||
reqHook: defaultRequestHook,
|
||||
resHook: defaultResponseHook,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -136,6 +136,52 @@ https://translate.googleapis.com/translate_a/single?client=gtx&dj=1&dt=t&ie=UTF-
|
||||
${customApiLangs}
|
||||
`;
|
||||
|
||||
const requestHookHelperZH = `1、第一个参数包含如下字段:'texts', 'from', 'to', 'url', 'key', 'model', 'systemPrompt', ...
|
||||
2、返回值必须是包含以下字段的对象: 'url', 'data', 'headers', 'userMsg', 'method'
|
||||
3、如返回空值,则hook函数不会产生任何效果。
|
||||
|
||||
// 示例
|
||||
async (args, { url, data, headers, userMsg, method } = {}) => {
|
||||
console.log("request hook args:", args);
|
||||
return { url, data, headers, userMsg, method };
|
||||
}`;
|
||||
|
||||
const requestHookHelperEN = `1. The first parameter contains the following fields: 'texts', 'from', 'to', 'url', 'key', 'model', 'systemPrompt', ...
|
||||
2. The return value must be an object containing the following fields: 'url', 'data', 'headers', 'userMsg', 'method'
|
||||
3. If a null value is returned, the hook function will have no effect.
|
||||
|
||||
// Example
|
||||
async (args, { url, data, headers, userMsg, method } = {}) => {
|
||||
console.log("request hook args:", args);
|
||||
return { url, data, headers, userMsg, method };
|
||||
}`;
|
||||
|
||||
const responsetHookHelperZH = `1、第一个参数包含如下字段:'res', ...
|
||||
2、返回值必须是包含以下字段的对象: 'translations', 'modelMsg'
|
||||
('translations' 应为一个二维数组,表示 [译文,源语言] 的列表)
|
||||
3、如返回空值,则hook函数不会产生任何效果。
|
||||
|
||||
// 示例
|
||||
async ({ res, ...args }) => {
|
||||
console.log("reaponse hook args:", res, args);
|
||||
const translations = [["你好", "zh"]];
|
||||
const modelMsg = "";
|
||||
return { translations, modelMsg };
|
||||
}`;
|
||||
|
||||
const responsetHookHelperEN = `1. The first parameter contains the following fields: 'res', ...
|
||||
2. The return value must be an object containing the following fields: 'translations', 'modelMsg'
|
||||
('translations' should be a two-dimensional array representing a list of [translation, source language]).
|
||||
3. If a null value is returned, the hook function will have no effect.
|
||||
|
||||
// Example
|
||||
async ({ res, ...args }) => {
|
||||
console.log("reaponse hook args:", res, args);
|
||||
const translations = [["你好", "zh"]];
|
||||
const modelMsg = "";
|
||||
return { translations, modelMsg };
|
||||
}`;
|
||||
|
||||
export const I18N = {
|
||||
app_name: {
|
||||
zh: `简约翻译`,
|
||||
@@ -152,6 +198,16 @@ export const I18N = {
|
||||
en: customApiHelpEN,
|
||||
zh_TW: customApiHelpZH,
|
||||
},
|
||||
request_hook_helper: {
|
||||
zh: requestHookHelperZH,
|
||||
en: requestHookHelperEN,
|
||||
zh_TW: requestHookHelperZH,
|
||||
},
|
||||
response_hook_helper: {
|
||||
zh: responsetHookHelperZH,
|
||||
en: responsetHookHelperEN,
|
||||
zh_TW: responsetHookHelperZH,
|
||||
},
|
||||
translate_alt: {
|
||||
zh: `翻译`,
|
||||
en: `Translate`,
|
||||
@@ -613,9 +669,9 @@ export const I18N = {
|
||||
zh_TW: `選擇器節點樣式`,
|
||||
},
|
||||
selector_style_helper: {
|
||||
zh: `开启翻译时注入,关闭翻译时不会移除。`,
|
||||
en: `It is injected when translation is turned on and will not be removed when translation is turned off.`,
|
||||
zh_TW: `在開啟翻譯時注入,關閉翻譯時不會移除。`,
|
||||
zh: `开启翻译时注入。`,
|
||||
en: `It is injected when translation is turned on.`,
|
||||
zh_TW: `在開啟翻譯時注入。`,
|
||||
},
|
||||
selector_parent_style: {
|
||||
zh: `选择器父节点样式`,
|
||||
@@ -1213,9 +1269,9 @@ export const I18N = {
|
||||
zh_TW: `翻譯開始 Hook`,
|
||||
},
|
||||
translate_start_hook_helper: {
|
||||
zh: `翻译前时运行,入参为: 翻译节点列表。`,
|
||||
en: `Run before translation, input parameters are: translation node list.`,
|
||||
zh_TW: `翻譯前時運行,入參為: 翻譯節點清單。`,
|
||||
zh: `翻译前时运行,入参为: ({hostNode, parentNode, nodes})`,
|
||||
en: `Run before translation, input parameters are: ({hostNode, parentNode, nodes})`,
|
||||
zh_TW: `翻譯前時運行,入參為: ({hostNode, parentNode, nodes})`,
|
||||
},
|
||||
translate_end_hook: {
|
||||
zh: `翻译完成钩子函数`,
|
||||
@@ -1223,9 +1279,9 @@ export const I18N = {
|
||||
zh_TW: `翻譯完成 Hook`,
|
||||
},
|
||||
translate_end_hook_helper: {
|
||||
zh: `翻译完成时运行,入参为: 翻译节点列表。`,
|
||||
en: `Run when translation is complete, input parameters are: translation node list.`,
|
||||
zh_TW: `翻譯完成時運行,入參為: 翻譯節點清單。`,
|
||||
zh: `翻译完成时运行,入参为: ({hostNode, parentNode, nodes, wrapperNode, innerNode})`,
|
||||
en: `Run when translation is complete, input parameters are: ({hostNode, parentNode, nodes, wrapperNode, innerNode})`,
|
||||
zh_TW: `翻譯完成時運行,入參為: ({hostNode, parentNode, nodes, wrapperNode, innerNode})`,
|
||||
},
|
||||
translate_remove_hook: {
|
||||
zh: `翻译移除钩子函数`,
|
||||
|
||||
@@ -74,7 +74,7 @@ export const DEFAULT_SELECTOR =
|
||||
"h1, h2, h3, h4, h5, h6, li, p, dd, blockquote, figcaption, label, legend";
|
||||
export const DEFAULT_IGNORE_SELECTOR =
|
||||
"button, code, footer, form, header, mark, nav, pre";
|
||||
export const DEFAULT_KEEP_SELECTOR = `code, img, svg, pre`;
|
||||
export const DEFAULT_KEEP_SELECTOR = `code, img, svg, pre, a:has(code)`;
|
||||
export const DEFAULT_RULE = {
|
||||
pattern: "", // 匹配网址
|
||||
selector: "", // 选择器
|
||||
|
||||
@@ -70,6 +70,9 @@ const BatchQueue = (
|
||||
try {
|
||||
const payloads = tasksToProcess.map((item) => item.payload);
|
||||
const responses = await sendBatchRequest(payloads);
|
||||
if (!Array.isArray(responses)) {
|
||||
throw new Error("responses format error");
|
||||
}
|
||||
|
||||
tasksToProcess.forEach((taskItem, index) => {
|
||||
const response = responses[index];
|
||||
|
||||
@@ -62,10 +62,11 @@ async function trySetObj(key, obj) {
|
||||
|
||||
async function getObj(key) {
|
||||
const val = await get(key);
|
||||
if (val === null || val === undefined) return null;
|
||||
try {
|
||||
return JSON.parse(val);
|
||||
} catch (err) {
|
||||
kissLog("parse json: ", key);
|
||||
kissLog("parse json in storage err: ", key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -255,6 +255,7 @@ export class Translator {
|
||||
#setting; // 设置选项
|
||||
#rule; // 规则
|
||||
#isInitialized = false; // 初始化状态
|
||||
#isJsInjected = false; // 注入用户JS
|
||||
#mouseHoverEnabled = false; // 鼠标悬停翻译
|
||||
#enabled = false; // 全局默认状态
|
||||
#runId = 0; // 用于中止过期的异步请求
|
||||
@@ -1248,6 +1249,11 @@ export class Translator {
|
||||
|
||||
// 注入JS/CSS
|
||||
#initInjector() {
|
||||
if (this.#isJsInjected) {
|
||||
return;
|
||||
}
|
||||
this.#isJsInjected = true;
|
||||
|
||||
try {
|
||||
const { injectJs, injectCss } = this.#rule;
|
||||
if (isExt) {
|
||||
@@ -1258,7 +1264,7 @@ export class Translator {
|
||||
injectCss && injectInternalCss(injectCss);
|
||||
}
|
||||
} catch (err) {
|
||||
kissLog("inject js");
|
||||
kissLog("inject js", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import { apiTranslate } from "../../apis";
|
||||
import Box from "@mui/material/Box";
|
||||
import { limitNumber, limitFloat } from "../../libs/utils";
|
||||
import ReusableAutocomplete from "./ReusableAutocomplete";
|
||||
import ShowMoreButton from "./ShowMoreButton";
|
||||
import {
|
||||
OPT_TRANS_DEEPLX,
|
||||
OPT_TRANS_OLLAMA,
|
||||
@@ -115,6 +116,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
const i18n = useI18n();
|
||||
const [formData, setFormData] = useState({});
|
||||
const [isModified, setIsModified] = useState(false);
|
||||
const [showMore, setShowMore] = useState(false);
|
||||
const confirm = useConfirm();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -282,7 +284,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
<>
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
{/* todo: 改成 ReusableAutocomplete 可选择和填写模型 */}
|
||||
<TextField
|
||||
size="small"
|
||||
@@ -293,7 +295,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<ReusableAutocomplete
|
||||
freeSolo
|
||||
size="small"
|
||||
@@ -305,7 +307,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -316,7 +318,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -327,7 +329,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}></Grid>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}></Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
@@ -393,27 +395,6 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box> */}
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("custom_header")}
|
||||
name="customHeader"
|
||||
value={customHeader}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={10}
|
||||
helperText={i18n("custom_header_help")}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("custom_body")}
|
||||
name="customBody"
|
||||
value={customBody}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={10}
|
||||
helperText={i18n("custom_body_help")}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -469,6 +450,14 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={10}
|
||||
FormHelperTextProps={{
|
||||
component: "div",
|
||||
}}
|
||||
helperText={
|
||||
<Box component="pre" sx={{ overflowX: "auto" }}>
|
||||
{i18n("request_hook_helper")}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
@@ -478,6 +467,14 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={10}
|
||||
FormHelperTextProps={{
|
||||
component: "div",
|
||||
}}
|
||||
helperText={
|
||||
<Box component="pre" sx={{ overflowX: "auto" }}>
|
||||
{i18n("response_hook_helper")}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@@ -485,7 +482,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
{API_SPE_TYPES.batch.has(api.apiType) && (
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
@@ -499,7 +496,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
<MenuItem value={true}>{i18n("enable")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -510,7 +507,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -521,7 +518,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -540,7 +537,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
<>
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
{" "}
|
||||
<TextField
|
||||
select
|
||||
@@ -555,7 +552,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
<MenuItem value={true}>{i18n("enable")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
{" "}
|
||||
<TextField
|
||||
size="small"
|
||||
@@ -574,7 +571,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -585,7 +582,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -596,7 +593,7 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -607,10 +604,74 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}></Grid>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}></Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{showMore && (
|
||||
<>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("custom_header")}
|
||||
name="customHeader"
|
||||
value={customHeader}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={10}
|
||||
helperText={i18n("custom_header_help")}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("custom_body")}
|
||||
name="customBody"
|
||||
value={customBody}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={10}
|
||||
helperText={i18n("custom_body_help")}
|
||||
/>
|
||||
|
||||
{apiType !== OPT_TRANS_CUSTOMIZE && (
|
||||
<>
|
||||
<TextField
|
||||
size="small"
|
||||
label={"Request Hook"}
|
||||
name="reqHook"
|
||||
value={reqHook}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={10}
|
||||
FormHelperTextProps={{
|
||||
component: "div",
|
||||
}}
|
||||
helperText={
|
||||
<Box component="pre" sx={{ overflowX: "auto" }}>
|
||||
{i18n("request_hook_helper")}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label={"Response Hook"}
|
||||
name="resHook"
|
||||
value={resHook}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={10}
|
||||
FormHelperTextProps={{
|
||||
component: "div",
|
||||
}}
|
||||
helperText={
|
||||
<Box component="pre" sx={{ overflowX: "auto" }}>
|
||||
{i18n("response_hook_helper")}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
@@ -652,9 +713,11 @@ function ApiFields({ apiSlug, isUserApi, deleteApi }) {
|
||||
}
|
||||
label={i18n("is_disabled")}
|
||||
/>
|
||||
|
||||
<ShowMoreButton showMore={showMore} onChange={setShowMore} />
|
||||
</Stack>
|
||||
|
||||
{apiType === OPT_TRANS_CUSTOMIZE && <pre>{i18n("custom_api_help")}</pre>}
|
||||
{/* {apiType === OPT_TRANS_CUSTOMIZE && <pre>{i18n("custom_api_help")}</pre>} */}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import Accordion from "@mui/material/Accordion";
|
||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||
import { useRules } from "../../hooks/Rules";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Grid from "@mui/material/Grid";
|
||||
@@ -62,6 +61,7 @@ import CancelIcon from "@mui/icons-material/Cancel";
|
||||
import SaveIcon from "@mui/icons-material/Save";
|
||||
import { kissLog } from "../../libs/log";
|
||||
import { useApiList } from "../../hooks/Api";
|
||||
import ShowMoreButton from "./ShowMoreButton";
|
||||
|
||||
function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
const initFormValues = useMemo(
|
||||
@@ -209,30 +209,6 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
const ShowMoreButton = showMore ? (
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
setShowMore(false);
|
||||
}}
|
||||
startIcon={<ExpandLessIcon />}
|
||||
>
|
||||
{i18n("less")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
setShowMore(true);
|
||||
}}
|
||||
startIcon={<ExpandMoreIcon />}
|
||||
>
|
||||
{i18n("more")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Stack spacing={2}>
|
||||
@@ -293,7 +269,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -309,7 +285,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
<MenuItem value={"false"}>{i18n("default_disabled")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -325,7 +301,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -341,7 +317,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -358,7 +334,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
</TextField>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -374,7 +350,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -390,7 +366,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -406,7 +382,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
<MenuItem value={"font"}>{`<font>`}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -423,7 +399,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
</TextField>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -442,7 +418,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -461,7 +437,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -480,7 +456,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
{/* <Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
{/* <Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -500,7 +476,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
</TextField>
|
||||
</Grid> */}
|
||||
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -521,7 +497,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
@@ -540,7 +516,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
@@ -714,7 +690,6 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
{i18n("delete")}
|
||||
</Button>
|
||||
)}
|
||||
{ShowMoreButton}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@@ -742,9 +717,9 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
>
|
||||
{i18n("restore_default")}
|
||||
</Button>
|
||||
{ShowMoreButton}
|
||||
</>
|
||||
)}
|
||||
<ShowMoreButton showMore={showMore} onChange={setShowMore} />
|
||||
</Stack>
|
||||
) : (
|
||||
// 添加
|
||||
@@ -765,7 +740,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
>
|
||||
{i18n("cancel")}
|
||||
</Button>
|
||||
{ShowMoreButton}
|
||||
<ShowMoreButton showMore={showMore} onChange={setShowMore} />
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
@@ -1078,7 +1053,7 @@ function SubRulesEdit({ subList, addSub, updateDataCache }) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (subList.find((item) => item.url === url)) {
|
||||
if (subList.some((item) => item.url === url)) {
|
||||
setInputError(i18n("error_duplicate_values"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export default function Settings() {
|
||||
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
@@ -166,7 +166,7 @@ export default function Settings() {
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
@@ -180,7 +180,7 @@ export default function Settings() {
|
||||
<MenuItem value={false}>{i18n("disable")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
@@ -196,7 +196,7 @@ export default function Settings() {
|
||||
<MenuItem value={true}>{i18n("hide")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
@@ -210,7 +210,7 @@ export default function Settings() {
|
||||
<MenuItem value={1}>{i18n("fab_click_translate")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
fullWidth
|
||||
size="small"
|
||||
@@ -221,7 +221,7 @@ export default function Settings() {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
fullWidth
|
||||
size="small"
|
||||
@@ -232,7 +232,7 @@ export default function Settings() {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
fullWidth
|
||||
size="small"
|
||||
@@ -243,7 +243,7 @@ export default function Settings() {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
fullWidth
|
||||
size="small"
|
||||
@@ -254,7 +254,7 @@ export default function Settings() {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
fullWidth
|
||||
size="small"
|
||||
@@ -265,7 +265,7 @@ export default function Settings() {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
@@ -282,7 +282,7 @@ export default function Settings() {
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
@@ -297,7 +297,7 @@ export default function Settings() {
|
||||
<MenuItem value={2}>{i18n("secondary_context_menus")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
@@ -353,25 +353,25 @@ export default function Settings() {
|
||||
<>
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_TRANSLATE}
|
||||
label={i18n("toggle_translate_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_STYLE}
|
||||
label={i18n("toggle_style_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_POPUP}
|
||||
label={i18n("toggle_popup_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={6} lg={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={3}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_SETTING}
|
||||
label={i18n("open_setting_shortcut")}
|
||||
|
||||
35
src/views/Options/ShowMoreButton.js
Normal file
35
src/views/Options/ShowMoreButton.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||
import Button from "@mui/material/Button";
|
||||
import { useI18n } from "../../hooks/I18n";
|
||||
|
||||
export default function ShowMoreButton({ onChange, showMore }) {
|
||||
const i18n = useI18n();
|
||||
const handleClick = () => {
|
||||
onChange((prev) => !prev);
|
||||
};
|
||||
|
||||
if (showMore) {
|
||||
return (
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={handleClick}
|
||||
startIcon={<ExpandLessIcon />}
|
||||
>
|
||||
{i18n("less")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={handleClick}
|
||||
startIcon={<ExpandMoreIcon />}
|
||||
>
|
||||
{i18n("more")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user