Compare commits

...

7 Commits

Author SHA1 Message Date
Gabe Yuan
a8caa34bbe v1.4.6 2023-08-19 15:16:33 +08:00
Gabe Yuan
c2fd1fe9e0 fix storage bug 2023-08-19 13:48:03 +08:00
Gabe Yuan
2773a76af8 yarn install 2023-08-18 23:50:06 +08:00
Gabe Yuan
1dc7026e8f add rules generate script 2023-08-18 16:48:44 +08:00
Gabe Yuan
b36ede7393 fix userscript grant 2023-08-18 13:19:40 +08:00
Gabe Yuan
b18721a4e5 wildcard is supported 2023-08-18 13:16:17 +08:00
Gabe Yuan
01676bc682 fix fab at left default 2023-08-17 16:22:04 +08:00
16 changed files with 2547 additions and 2271 deletions

5
.babelrc Normal file
View File

@@ -0,0 +1,5 @@
{
"presets": [
"@babel/preset-env"
]
}

2
.env
View File

@@ -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

View File

@@ -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

View File

@@ -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"
} }

View File

@@ -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",

View File

@@ -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",

View File

@@ -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、留空表示采用全局设置。`,

View File

@@ -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; // 最长翻译长度

View File

@@ -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",
}));

View File

@@ -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);

View File

@@ -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) =>

View File

@@ -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>
);
}; };
} }

View File

@@ -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
View 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);
}
})();

View File

@@ -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,
}; };

4651
yarn.lock

File diff suppressed because it is too large Load Diff