Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8caa34bbe | ||
|
|
c2fd1fe9e0 | ||
|
|
2773a76af8 | ||
|
|
1dc7026e8f | ||
|
|
b36ede7393 | ||
|
|
b18721a4e5 | ||
|
|
01676bc682 |
2
.env
2
.env
@@ -2,7 +2,7 @@ GENERATE_SOURCEMAP=false
|
|||||||
|
|
||||||
REACT_APP_NAME=KISS Translator
|
REACT_APP_NAME=KISS Translator
|
||||||
REACT_APP_NAME_CN=简约翻译
|
REACT_APP_NAME_CN=简约翻译
|
||||||
REACT_APP_VERSION=1.4.5
|
REACT_APP_VERSION=1.4.6
|
||||||
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
||||||
REACT_APP_OPTIONSPAGE=https://kiss-translator.rayjar.com/options
|
REACT_APP_OPTIONSPAGE=https://kiss-translator.rayjar.com/options
|
||||||
REACT_APP_OPTIONSPAGE2=https://fishjar.github.io/kiss-translator/options.html
|
REACT_APP_OPTIONSPAGE2=https://fishjar.github.io/kiss-translator/options.html
|
||||||
|
|||||||
@@ -83,13 +83,9 @@ const userscriptWebpack = (config, env) => {
|
|||||||
// @icon ${process.env.REACT_APP_LOGOURL}
|
// @icon ${process.env.REACT_APP_LOGOURL}
|
||||||
// @downloadURL ${process.env.REACT_APP_USERSCRIPT_DOWNLOADURL}
|
// @downloadURL ${process.env.REACT_APP_USERSCRIPT_DOWNLOADURL}
|
||||||
// @updateURL ${process.env.REACT_APP_USERSCRIPT_DOWNLOADURL}
|
// @updateURL ${process.env.REACT_APP_USERSCRIPT_DOWNLOADURL}
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM.xmlHttpRequest
|
||||||
// @grant GM.xmlhttpRequest
|
|
||||||
// @grant GM_setValue
|
|
||||||
// @grant GM.setValue
|
// @grant GM.setValue
|
||||||
// @grant GM_getValue
|
|
||||||
// @grant GM.getValue
|
// @grant GM.getValue
|
||||||
// @grant GM_deleteValue
|
|
||||||
// @grant GM.deleteValue
|
// @grant GM.deleteValue
|
||||||
// @grant unsafeWindow
|
// @grant unsafeWindow
|
||||||
// @connect translate.googleapis.com
|
// @connect translate.googleapis.com
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "kiss-translator",
|
"name": "kiss-translator",
|
||||||
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
||||||
"version": "1.4.5",
|
"version": "1.4.6",
|
||||||
"author": "Gabe<yugang2002@gmail.com>",
|
"author": "Gabe<yugang2002@gmail.com>",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -25,7 +25,8 @@
|
|||||||
"build:firefox": "rm -rf build/firefox && cp -r build/chrome build/firefox && cat ./build/firefox/manifest.firefox.json > ./build/firefox/manifest.json",
|
"build:firefox": "rm -rf build/firefox && cp -r build/chrome build/firefox && cat ./build/firefox/manifest.firefox.json > ./build/firefox/manifest.json",
|
||||||
"build:web": "rm -rf build/web && BUILD_PATH=./build/web REACT_APP_CLIENT=userscript react-app-rewired build",
|
"build:web": "rm -rf build/web && BUILD_PATH=./build/web REACT_APP_CLIENT=userscript react-app-rewired build",
|
||||||
"build:userscript": "rm -rf build/userscript && mkdir build/userscript && cp build/web/kiss-translator.user.js build/userscript/kiss-translator.user.js",
|
"build:userscript": "rm -rf build/userscript && mkdir build/userscript && cp build/web/kiss-translator.user.js build/userscript/kiss-translator.user.js",
|
||||||
"build": "yarn build:chrome && yarn build:edge && yarn build:firefox && yarn build:web && yarn build:userscript",
|
"build:rules": "babel-node src/rules.js",
|
||||||
|
"build": "yarn build:chrome && yarn build:edge && yarn build:firefox && yarn build:web && yarn build:userscript && yarn build:rules",
|
||||||
"deploy:web": "wrangler pages deploy ./build/web --project-name kiss-translator",
|
"deploy:web": "wrangler pages deploy ./build/web --project-name kiss-translator",
|
||||||
"test": "react-app-rewired test",
|
"test": "react-app-rewired test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject"
|
||||||
@@ -53,6 +54,10 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.22.10",
|
||||||
|
"@babel/node": "^7.22.10",
|
||||||
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
|
"@babel/preset-env": "^7.22.10",
|
||||||
"react-app-rewired": "^2.2.1",
|
"react-app-rewired": "^2.2.1",
|
||||||
"wrangler": "^3.4.0"
|
"wrangler": "^3.4.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "__MSG_app_name__",
|
"name": "__MSG_app_name__",
|
||||||
"description": "__MSG_app_description__",
|
"description": "__MSG_app_description__",
|
||||||
"version": "1.4.5",
|
"version": "1.4.6",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"author": "Gabe<yugang2002@gmail.com>",
|
"author": "Gabe<yugang2002@gmail.com>",
|
||||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "__MSG_app_name__",
|
"name": "__MSG_app_name__",
|
||||||
"description": "__MSG_app_description__",
|
"description": "__MSG_app_description__",
|
||||||
"version": "1.4.5",
|
"version": "1.4.6",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"author": "Gabe<yugang2002@gmail.com>",
|
"author": "Gabe<yugang2002@gmail.com>",
|
||||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||||
|
|||||||
@@ -153,8 +153,8 @@ export const I18N = {
|
|||||||
en: `URL pattern`,
|
en: `URL pattern`,
|
||||||
},
|
},
|
||||||
pattern_helper: {
|
pattern_helper: {
|
||||||
zh: `多个URL支持英文逗号“,”分隔`,
|
zh: `1、支持星号(*)通配符。2、多个URL支持英文逗号“,”分隔。`,
|
||||||
en: `Multiple URLs can be separated by English commas ","`,
|
en: `1. The asterisk (*) wildcard is supported. 2. Multiple URLs can be separated by English commas ",".`,
|
||||||
},
|
},
|
||||||
selector_helper: {
|
selector_helper: {
|
||||||
zh: `1、遵循CSS选择器规则。2、留空表示采用全局设置。`,
|
zh: `1、遵循CSS选择器规则。2、留空表示采用全局设置。`,
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { DEFAULT_SELECTOR, RULES } from "./rules";
|
import {
|
||||||
|
DEFAULT_SELECTOR,
|
||||||
|
GLOBAL_KEY,
|
||||||
|
DEFAULT_RULE,
|
||||||
|
BUILTIN_RULES,
|
||||||
|
} from "./rules";
|
||||||
export { I18N, UI_LANGS } from "./i18n";
|
export { I18N, UI_LANGS } from "./i18n";
|
||||||
|
export { GLOBAL_KEY, DEFAULT_RULE, BUILTIN_RULES };
|
||||||
|
|
||||||
const APP_NAME = process.env.REACT_APP_NAME.trim().split(/\s+/).join("-");
|
const APP_NAME = process.env.REACT_APP_NAME.trim().split(/\s+/).join("-");
|
||||||
|
|
||||||
@@ -11,8 +17,6 @@ export const STOKEY_RULES = `${APP_NAME}_rules`;
|
|||||||
export const STOKEY_SYNC = `${APP_NAME}_sync`;
|
export const STOKEY_SYNC = `${APP_NAME}_sync`;
|
||||||
export const STOKEY_FAB = `${APP_NAME}_fab`;
|
export const STOKEY_FAB = `${APP_NAME}_fab`;
|
||||||
|
|
||||||
export const GLOBAL_KEY = "*";
|
|
||||||
|
|
||||||
export const CLIENT_WEB = "web";
|
export const CLIENT_WEB = "web";
|
||||||
export const CLIENT_CHROME = "chrome";
|
export const CLIENT_CHROME = "chrome";
|
||||||
export const CLIENT_EDGE = "edge";
|
export const CLIENT_EDGE = "edge";
|
||||||
@@ -143,18 +147,6 @@ export const GLOBLA_RULE = {
|
|||||||
bgColor: "",
|
bgColor: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
// 默认规则
|
|
||||||
export const DEFAULT_RULE = {
|
|
||||||
pattern: "",
|
|
||||||
selector: "",
|
|
||||||
translator: GLOBAL_KEY,
|
|
||||||
fromLang: GLOBAL_KEY,
|
|
||||||
toLang: GLOBAL_KEY,
|
|
||||||
textStyle: GLOBAL_KEY,
|
|
||||||
transOpen: GLOBAL_KEY,
|
|
||||||
bgColor: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DEFAULT_SETTING = {
|
export const DEFAULT_SETTING = {
|
||||||
darkMode: false, // 深色模式
|
darkMode: false, // 深色模式
|
||||||
uiLang: "en", // 界面语言
|
uiLang: "en", // 界面语言
|
||||||
@@ -169,20 +161,7 @@ export const DEFAULT_SETTING = {
|
|||||||
openaiPrompt: `You will be provided with a sentence in ${PROMPT_PLACE_FROM}, and your task is to translate it into ${PROMPT_PLACE_TO}.`,
|
openaiPrompt: `You will be provided with a sentence in ${PROMPT_PLACE_FROM}, and your task is to translate it into ${PROMPT_PLACE_TO}.`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_RULES = [
|
export const DEFAULT_RULES = [GLOBLA_RULE];
|
||||||
{
|
|
||||||
...DEFAULT_RULE,
|
|
||||||
...RULES[0],
|
|
||||||
transOpen: "true",
|
|
||||||
},
|
|
||||||
GLOBLA_RULE,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const BUILTIN_RULES = RULES.map((item) => ({
|
|
||||||
...DEFAULT_RULE,
|
|
||||||
...item,
|
|
||||||
transOpen: "true",
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const TRANS_MIN_LENGTH = 5; // 最短翻译长度
|
export const TRANS_MIN_LENGTH = 5; // 最短翻译长度
|
||||||
export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度
|
export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度
|
||||||
|
|||||||
@@ -2,7 +2,20 @@ const els = `li, p, h1, h2, h3, h4, h5, h6, dd`;
|
|||||||
|
|
||||||
export const DEFAULT_SELECTOR = `:is(${els})`;
|
export const DEFAULT_SELECTOR = `:is(${els})`;
|
||||||
|
|
||||||
export const RULES = [
|
export const GLOBAL_KEY = "*";
|
||||||
|
|
||||||
|
export const DEFAULT_RULE = {
|
||||||
|
pattern: "",
|
||||||
|
selector: "",
|
||||||
|
translator: GLOBAL_KEY,
|
||||||
|
fromLang: GLOBAL_KEY,
|
||||||
|
toLang: GLOBAL_KEY,
|
||||||
|
textStyle: GLOBAL_KEY,
|
||||||
|
transOpen: GLOBAL_KEY,
|
||||||
|
bgColor: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const RULES = [
|
||||||
{
|
{
|
||||||
pattern: `www.google.com/search`,
|
pattern: `www.google.com/search`,
|
||||||
selector: `h3, .IsZvec, .VwiC3b`,
|
selector: `h3, .IsZvec, .VwiC3b`,
|
||||||
@@ -132,3 +145,9 @@ export const RULES = [
|
|||||||
selector: `h1, #video-title, #content-text, #title, yt-attributed-string>span>span`,
|
selector: `h1, #video-title, #content-text, #title, yt-attributed-string>span>span`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const BUILTIN_RULES = RULES.map((item) => ({
|
||||||
|
...DEFAULT_RULE,
|
||||||
|
...item,
|
||||||
|
transOpen: "true",
|
||||||
|
}));
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { browser, isExt, isGm, isWeb } from "../libs/browser";
|
|||||||
import {
|
import {
|
||||||
STOKEY_SETTING,
|
STOKEY_SETTING,
|
||||||
STOKEY_RULES,
|
STOKEY_RULES,
|
||||||
STOKEY_MSAUTH,
|
|
||||||
STOKEY_SYNC,
|
STOKEY_SYNC,
|
||||||
DEFAULT_SETTING,
|
DEFAULT_SETTING,
|
||||||
DEFAULT_RULES,
|
DEFAULT_RULES,
|
||||||
@@ -15,12 +14,13 @@ import storage from "../libs/storage";
|
|||||||
* 默认配置
|
* 默认配置
|
||||||
*/
|
*/
|
||||||
export const defaultStorage = {
|
export const defaultStorage = {
|
||||||
[STOKEY_MSAUTH]: null,
|
|
||||||
[STOKEY_SETTING]: DEFAULT_SETTING,
|
[STOKEY_SETTING]: DEFAULT_SETTING,
|
||||||
[STOKEY_RULES]: DEFAULT_RULES,
|
[STOKEY_RULES]: DEFAULT_RULES,
|
||||||
[STOKEY_SYNC]: DEFAULT_SYNC,
|
[STOKEY_SYNC]: DEFAULT_SYNC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const activeKeys = Object.keys(defaultStorage);
|
||||||
|
|
||||||
const StoragesContext = createContext(null);
|
const StoragesContext = createContext(null);
|
||||||
|
|
||||||
export function StoragesProvider({ children }) {
|
export function StoragesProvider({ children }) {
|
||||||
@@ -38,7 +38,10 @@ export function StoragesProvider({ children }) {
|
|||||||
}
|
}
|
||||||
const newStorages = {};
|
const newStorages = {};
|
||||||
Object.entries(changes)
|
Object.entries(changes)
|
||||||
.filter(([_, { oldValue, newValue }]) => oldValue !== newValue)
|
.filter(
|
||||||
|
([key, { oldValue, newValue }]) =>
|
||||||
|
activeKeys.includes(key) && oldValue !== newValue
|
||||||
|
)
|
||||||
.forEach(([key, { newValue }]) => {
|
.forEach(([key, { newValue }]) => {
|
||||||
newStorages[key] = JSON.parse(newValue);
|
newStorages[key] = JSON.parse(newValue);
|
||||||
});
|
});
|
||||||
@@ -51,8 +54,7 @@ export function StoragesProvider({ children }) {
|
|||||||
// 首次从storage同步配置到内存
|
// 首次从storage同步配置到内存
|
||||||
(async () => {
|
(async () => {
|
||||||
const curStorages = {};
|
const curStorages = {};
|
||||||
const keys = Object.keys(defaultStorage);
|
for (const key of activeKeys) {
|
||||||
for (const key of keys) {
|
|
||||||
const val = await storage.get(key);
|
const val = await storage.get(key);
|
||||||
if (val) {
|
if (val) {
|
||||||
curStorages[key] = JSON.parse(val);
|
curStorages[key] = JSON.parse(val);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
BUILTIN_RULES,
|
BUILTIN_RULES,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import { browser } from "./browser";
|
import { browser } from "./browser";
|
||||||
|
import { isMatch } from "./utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取节点列表并转为数组
|
* 获取节点列表并转为数组
|
||||||
@@ -48,7 +49,6 @@ export const setFab = async (obj) => await storage.setObj(STOKEY_FAB, obj);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据href匹配规则
|
* 根据href匹配规则
|
||||||
* TODO: 支持通配符(*)匹配
|
|
||||||
* @param {*} rules
|
* @param {*} rules
|
||||||
* @param {string} href
|
* @param {string} href
|
||||||
* @returns
|
* @returns
|
||||||
@@ -59,7 +59,7 @@ export const matchRule = (rules, href, { injectRules }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const rule = rules.find((rule) =>
|
const rule = rules.find((rule) =>
|
||||||
rule.pattern.split(",").some((p) => href.includes(p.trim()))
|
rule.pattern.split(",").some((p) => isMatch(href, p.trim()))
|
||||||
);
|
);
|
||||||
const globalRule =
|
const globalRule =
|
||||||
rules.find((rule) =>
|
rules.find((rule) =>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
EVENT_KISS,
|
EVENT_KISS,
|
||||||
MSG_TRANS_CURRULE,
|
MSG_TRANS_CURRULE,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import { StoragesProvider } from "../hooks/Storage";
|
|
||||||
import { queryEls } from ".";
|
import { queryEls } from ".";
|
||||||
import Content from "../views/Content";
|
import Content from "../views/Content";
|
||||||
import { fetchUpdate, fetchClear } from "./fetch";
|
import { fetchUpdate, fetchClear } from "./fetch";
|
||||||
@@ -144,10 +143,6 @@ export class Translator {
|
|||||||
"-webkit-line-clamp: unset; max-height: none; height: auto;";
|
"-webkit-line-clamp: unset; max-height: none; height: auto;";
|
||||||
|
|
||||||
const root = createRoot(span);
|
const root = createRoot(span);
|
||||||
root.render(
|
root.render(<Content q={q} translator={this} />);
|
||||||
<StoragesProvider>
|
|
||||||
<Content q={q} translator={this} />
|
|
||||||
</StoragesProvider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,3 +51,40 @@ export const debounce = (func, delay = 200) => {
|
|||||||
}, delay);
|
}, delay);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串通配符(*)匹配
|
||||||
|
* @param {*} s
|
||||||
|
* @param {*} p
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const isMatch = (s, p) => {
|
||||||
|
if (s.length === 0 || p.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = `*${p}*`;
|
||||||
|
|
||||||
|
let [sIndex, pIndex] = [0, 0];
|
||||||
|
let [sRecord, pRecord] = [-1, -1];
|
||||||
|
while (sIndex < s.length && pRecord < p.length) {
|
||||||
|
if (p[pIndex] === "*") {
|
||||||
|
pIndex++;
|
||||||
|
[sRecord, pRecord] = [sIndex, pIndex];
|
||||||
|
} else if (s[sIndex] === p[pIndex]) {
|
||||||
|
sIndex++;
|
||||||
|
pIndex++;
|
||||||
|
} else if (sRecord + 1 < s.length) {
|
||||||
|
sRecord++;
|
||||||
|
[sIndex, pIndex] = [sRecord, pRecord];
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.length === pIndex) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.slice(pIndex).replaceAll("*", "") === "";
|
||||||
|
};
|
||||||
|
|||||||
17
src/rules.js
Normal file
17
src/rules.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { BUILTIN_RULES } from "./config/rules";
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
try {
|
||||||
|
const data = JSON.stringify(BUILTIN_RULES, null, " ");
|
||||||
|
const file = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"../build/web/kiss-translator-rules.json"
|
||||||
|
);
|
||||||
|
fs.writeFileSync(file, data);
|
||||||
|
console.info(`Built-in rules generated: ${file}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -76,7 +76,7 @@ export default function Action({ translator, fab }) {
|
|||||||
windowSize,
|
windowSize,
|
||||||
width: fabWidth,
|
width: fabWidth,
|
||||||
height: fabWidth,
|
height: fabWidth,
|
||||||
left: fab.x ?? windowSize.w - fabWidth,
|
left: fab.x ?? 0,
|
||||||
top: fab.y ?? windowSize.h / 2,
|
top: fab.y ?? windowSize.h / 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user