feat: move settings to rule

This commit is contained in:
Gabe Yuan
2024-03-16 23:37:27 +08:00
parent 9e9c56a3b4
commit 14b5ba9c4c
23 changed files with 337 additions and 829 deletions

View File

@@ -1,5 +1,4 @@
import { isMatch } from "./utils";
import { DEFAULT_BLACKLIST } from "../config";
/**
* 检查是否在黑名单中
@@ -7,7 +6,5 @@ import { DEFAULT_BLACKLIST } from "../config";
* @param {*} param1
* @returns
*/
export const isInBlacklist = (
href,
{ blacklist = DEFAULT_BLACKLIST.join(",\n") }
) => blacklist.split(/\n|,/).some((url) => isMatch(href, url.trim()));
export const isInBlacklist = (href, { blacklist }) =>
blacklist.split(/\n|,/).some((url) => isMatch(href, url.trim()));

View File

@@ -7,7 +7,6 @@ import {
import { genEventName, removeEndchar, matchInputStr, sleep } from "./utils";
import { stepShortcutRegister } from "./shortcut";
import { apiTranslate } from "../apis";
import { tryDetectLang } from ".";
import { loadingSvg } from "./svg";
function isInputNode(node) {
@@ -83,7 +82,7 @@ function removeLoading(node, loadingId) {
/**
* 输入框翻译
*/
export default function inputTranslate ({
export default function inputTranslate({
inputRule: {
transOpen,
triggerShortcut,
@@ -95,7 +94,6 @@ export default function inputTranslate ({
transSign,
} = DEFAULT_INPUT_RULE,
transApis,
detectRemote,
}) {
if (!transOpen) {
return;
@@ -156,11 +154,6 @@ export default function inputTranslate ({
try {
addLoading(node, loadingId);
const deLang = await tryDetectLang(text, detectRemote);
if (deLang && toLang.includes(deLang)) {
return;
}
const [trText, isSame] = await apiTranslate({
translator,
text,

View File

@@ -6,13 +6,13 @@ import {
OPT_STYLE_ALL,
OPT_LANGS_FROM,
OPT_LANGS_TO,
OPT_TIMING_ALL,
GLOBLA_RULE,
DEFAULT_SUBRULES_LIST,
DEFAULT_OW_RULE,
} from "../config";
import { loadOrFetchSubRules } from "./subRules";
import { getRulesWithDefault, setRules } from "./storage";
import { trySyncRules } from "./sync";
import { FIXER_ALL } from "./webfix";
/**
* 根据href匹配规则
@@ -22,11 +22,7 @@ import { trySyncRules } from "./sync";
*/
export const matchRule = async (
href,
{
injectRules = true,
subrulesList = DEFAULT_SUBRULES_LIST,
owSubrule = DEFAULT_OW_RULE,
}
{ injectRules, subrulesList, owSubrule }
) => {
const rules = await getRulesWithDefault();
if (injectRules) {
@@ -60,18 +56,49 @@ export const matchRule = async (
const rule = rules.find((r) =>
r.pattern.split(",").some((p) => isMatch(href, p.trim()))
);
const globalRule = rules.find((r) => r.pattern === GLOBAL_KEY) || GLOBLA_RULE;
const globalRule = {
...GLOBLA_RULE,
...(rules.find((r) => r.pattern === GLOBAL_KEY) || {}),
};
if (!rule) {
return globalRule;
}
rule.selector = rule.selector?.trim() || globalRule.selector;
rule.keepSelector = rule.keepSelector?.trim() || globalRule.keepSelector;
rule.terms = rule.terms?.trim() || globalRule.terms;
rule.selectStyle = rule.selectStyle?.trim() || globalRule.selectStyle;
rule.parentStyle = rule.parentStyle?.trim() || globalRule.parentStyle;
rule.injectJs = rule.injectJs?.trim() || globalRule.injectJs;
rule.injectCss = rule.injectCss?.trim() || globalRule.injectCss;
[
"selector",
"keepSelector",
"terms",
"selectStyle",
"parentStyle",
"injectJs",
"injectCss",
"fixerSelector",
].forEach((key) => {
if (!rule[key]?.trim()) {
rule[key] = globalRule[key];
}
});
[
"translator",
"fromLang",
"toLang",
"transOpen",
"transOnly",
"transTiming",
"transTag",
"transTitle",
"detectRemote",
"fixerFunc",
].forEach((key) => {
if (rule[key] === undefined || rule[key] === GLOBAL_KEY) {
rule[key] = globalRule[key];
}
});
if (!rule.skipLangs || rule.skipLangs.length === 0) {
rule.skipLangs = globalRule.skipLangs;
}
if (rule.textStyle === GLOBAL_KEY) {
rule.textStyle = globalRule.textStyle;
rule.bgColor = globalRule.bgColor;
@@ -80,11 +107,6 @@ export const matchRule = async (
rule.bgColor = rule.bgColor?.trim() || globalRule.bgColor;
rule.textDiyStyle = rule.textDiyStyle?.trim() || globalRule.textDiyStyle;
}
["translator", "fromLang", "toLang", "transOpen"].forEach((key) => {
if (rule[key] === GLOBAL_KEY) {
rule[key] = globalRule[key];
}
});
return rule;
};
@@ -131,6 +153,14 @@ export const checkRules = (rules) => {
transOpen,
bgColor,
textDiyStyle,
transOnly,
transTiming,
transTag,
transTitle,
detectRemote,
skipLangs,
fixerSelector,
fixerFunc,
}) => ({
pattern: pattern.trim(),
selector: type(selector) === "string" ? selector : "",
@@ -147,6 +177,14 @@ export const checkRules = (rules) => {
toLang: matchValue([GLOBAL_KEY, ...toLangs], toLang),
textStyle: matchValue([GLOBAL_KEY, ...OPT_STYLE_ALL], textStyle),
transOpen: matchValue([GLOBAL_KEY, "true", "false"], transOpen),
transOnly: matchValue([GLOBAL_KEY, "true", "false"], transOnly),
transTiming: matchValue([GLOBAL_KEY, ...OPT_TIMING_ALL], transTiming),
transTag: matchValue([GLOBAL_KEY, "font", "span"], transTag),
transTitle: matchValue([GLOBAL_KEY, "true", "false"], transTitle),
detectRemote: matchValue([GLOBAL_KEY, "true", "false"], detectRemote),
skipLangs: type(skipLangs) === "array" ? skipLangs : [],
fixerSelector: type(fixerSelector) === "string" ? fixerSelector : "",
fixerFunc: matchValue([GLOBAL_KEY, ...FIXER_ALL], fixerFunc),
})
);

View File

@@ -1,14 +1,12 @@
import {
STOKEY_SETTING,
STOKEY_RULES,
STOKEY_WFRULES,
STOKEY_WORDS,
STOKEY_FAB,
STOKEY_SYNC,
STOKEY_MSAUTH,
STOKEY_BDAUTH,
STOKEY_RULESCACHE_PREFIX,
STOKEY_WEBFIXCACHE_PREFIX,
DEFAULT_SETTING,
DEFAULT_RULES,
DEFAULT_SYNC,
@@ -86,8 +84,10 @@ export const storage = {
* 设置信息
*/
export const getSetting = () => getObj(STOKEY_SETTING);
export const getSettingWithDefault = async () =>
(await getSetting()) || DEFAULT_SETTING;
export const getSettingWithDefault = async () => ({
...DEFAULT_SETTING,
...((await getSetting()) || {}),
});
export const setSetting = (val) => setObj(STOKEY_SETTING, val);
export const updateSetting = (obj) => putObj(STOKEY_SETTING, obj);
@@ -99,14 +99,6 @@ export const getRulesWithDefault = async () =>
(await getRules()) || DEFAULT_RULES;
export const setRules = (val) => setObj(STOKEY_RULES, val);
/**
* 修复规则列表
*/
export const getWebfixRules = () => getObj(STOKEY_WFRULES);
export const getWebfixRulesWithDefault = async () =>
(await getWebfixRules()) || [];
export const setWebfixRules = (val) => setObj(STOKEY_WFRULES, val);
/**
* 词汇列表
*/
@@ -123,14 +115,6 @@ export const delSubRules = (url) => del(STOKEY_RULESCACHE_PREFIX + url);
export const setSubRules = (url, val) =>
setObj(STOKEY_RULESCACHE_PREFIX + url, val);
/**
* 修复站点
*/
export const getWebfix = (url) => getObj(STOKEY_WEBFIXCACHE_PREFIX + url);
export const getWebfixWithDefault = async () => (await getWebfix()) || [];
export const setWebfix = (url, val) =>
setObj(STOKEY_WEBFIXCACHE_PREFIX + url, val);
/**
* fab位置
*/

View File

@@ -8,7 +8,6 @@ import {
import { apiFetch } from "../apis";
import { checkRules } from "./rules";
import { isAllchar } from "./utils";
import { syncWebfix } from "./webfix";
/**
* 更新缓存同步时间
@@ -66,9 +65,6 @@ export const trySyncAllSubRules = async ({ subrulesList }) => {
// 同步订阅规则
await syncAllSubRules(subrulesList);
await updateSync({ subRulesSyncAt: now });
// 同步修复规则
await syncWebfix(process.env.REACT_APP_WEBFIXURL);
}
} catch (err) {
console.log("[try sync all subrules]", err);

View File

@@ -2,7 +2,6 @@ import {
APP_LCNAME,
KV_SETTING_KEY,
KV_RULES_KEY,
KV_WFRULES_KEY,
KV_WORDS_KEY,
KV_RULES_SHARE_KEY,
KV_SALT_SHARE,
@@ -14,10 +13,8 @@ import {
getSettingWithDefault,
getRulesWithDefault,
getWordsWithDefault,
getWebfixRulesWithDefault,
setSetting,
setRules,
setWebfixRules,
setWords,
} from "./storage";
import { apiSyncData } from "../apis";
@@ -141,25 +138,6 @@ export const trySyncRules = async () => {
}
};
/**
* 同步修复规则
* @returns
*/
const syncWebfixRules = async () => {
const res = await syncData(KV_WFRULES_KEY, getWebfixRulesWithDefault);
if (res?.isNew) {
await setWebfixRules(res.value);
}
};
export const trySyncWebfixRules = async () => {
try {
await syncWebfixRules();
} catch (err) {
console.log("[sync user webfix rules]", err);
}
};
/**
* 同步词汇
* @returns
@@ -207,13 +185,11 @@ export const syncShareRules = async ({ rules, syncUrl, syncKey }) => {
export const syncSettingAndRules = async () => {
await syncSetting();
await syncRules();
await syncWebfixRules();
await syncWords();
};
export const trySyncSettingAndRules = async () => {
await trySyncSetting();
await trySyncRules();
await trySyncWebfixRules();
await trySyncWords();
};

View File

@@ -9,9 +9,9 @@ import {
OPT_STYLE_DASHLINE,
OPT_STYLE_FUZZY,
SHADOW_KEY,
OPT_MOUSEKEY_DISABLE,
OPT_MOUSEKEY_PAGEOPEN,
OPT_MOUSEKEY_MOUSEOVER,
OPT_TIMING_PAGESCROLL,
OPT_TIMING_PAGEOPEN,
OPT_TIMING_MOUSEOVER,
DEFAULT_TRANS_APIS,
} from "../config";
import Content from "../views/Content";
@@ -29,7 +29,6 @@ import { injectInlineJs, injectInternalCss } from "./injector";
export class Translator {
_rule = {};
_setting = {};
_fixerSetting = null;
_rootNodes = new Set();
_tranNodes = new Map();
_skipNodeNames = [
@@ -104,14 +103,13 @@ export class Translator {
};
};
constructor(rule, setting, fixerSetting) {
constructor(rule, setting) {
const { fetchInterval, fetchLimit } = setting;
updateFetchPool(fetchInterval, fetchLimit);
this._overrideAttachShadow();
this._setting = setting;
this._rule = rule;
this._fixerSetting = fixerSetting;
this._keepSelector = (rule.keepSelector || "")
.split(SHADOW_KEY)
@@ -267,14 +265,15 @@ export class Translator {
};
_register = () => {
const { fromLang, toLang, injectJs, injectCss } = this._rule;
const { fromLang, toLang, injectJs, injectCss, fixerSelector, fixerFunc } =
this._rule;
if (fromLang === toLang) {
return;
}
// webfix
if (this._fixerSetting) {
runFixer(this._fixerSetting);
if (fixerSelector && fixerFunc !== "-") {
runFixer(fixerSelector, fixerFunc);
}
// 注入用户JS/CSS
@@ -299,14 +298,14 @@ export class Translator {
});
if (
!this._setting.mouseKey ||
this._setting.mouseKey === OPT_MOUSEKEY_DISABLE
!this._rule.transTiming ||
this._rule.transTiming === OPT_TIMING_PAGESCROLL
) {
// 监听节点显示
this._tranNodes.forEach((_, node) => {
this._interseObserver.observe(node);
});
} else if (this._setting.mouseKey === OPT_MOUSEKEY_PAGEOPEN) {
} else if (this._rule.transTiming === OPT_TIMING_PAGEOPEN) {
// 全文直接翻译
this._tranNodes.forEach((_, node) => {
this._render(node);
@@ -321,7 +320,7 @@ export class Translator {
}
// 翻译页面标题
if (this._setting.transTitle && !this._docTitle) {
if (this._rule.transTitle === "true" && !this._docTitle) {
const title = document.title;
this._docTitle = title;
this.translateText(title).then((trText) => {
@@ -336,8 +335,8 @@ export class Translator {
return;
}
const key = this._setting.mouseKey.slice(3);
if (this._setting.mouseKey === OPT_MOUSEKEY_MOUSEOVER || e[key]) {
const key = this._rule.transTiming.slice(3);
if (this._rule.transTiming === OPT_TIMING_MOUSEOVER || e[key]) {
e.target.removeEventListener("mouseenter", this._handleMouseover);
e.target.removeEventListener("mouseleave", this._handleMouseout);
this._render(e.target);
@@ -357,7 +356,7 @@ export class Translator {
_handleKeydown = (e) => {
// console.log("keydown", e);
const key = this._setting.mouseKey.slice(3);
const key = this._rule.transTiming.slice(3);
if (e[key] && this._mouseoverNode) {
this._mouseoverNode.removeEventListener(
"mouseenter",
@@ -392,12 +391,12 @@ export class Translator {
this._tranNodes.forEach((innerHTML, node) => {
if (
!this._setting.mouseKey ||
this._setting.mouseKey === OPT_MOUSEKEY_DISABLE
!this._rule.transTiming ||
this._rule.transTiming === OPT_TIMING_PAGESCROLL
) {
// 解除节点显示监听
this._interseObserver.unobserve(node);
} else if (this._setting.mouseKey !== OPT_MOUSEKEY_PAGEOPEN) {
} else if (this._rule.transTiming !== OPT_TIMING_PAGEOPEN) {
// 移除鼠标悬停监听
// node.style.pointerEvents = "none";
node.removeEventListener("mouseenter", this._handleMouseover);
@@ -405,7 +404,7 @@ export class Translator {
}
// 移除/恢复元素
if (innerHTML && this._setting.transOnly) {
if (innerHTML && this._rule.transOnly === "true") {
node.innerHTML = innerHTML;
} else {
node.querySelector(APP_LCNAME)?.remove();
@@ -446,7 +445,7 @@ export class Translator {
// 已翻译
if (traEl) {
if (this._setting.transOnly) {
if (this._rule.transOnly === "true") {
return;
}
@@ -465,7 +464,7 @@ export class Translator {
}
let q = el.innerText.trim();
if (this._setting.transOnly) {
if (this._rule.transOnly === "true") {
this._tranNodes.set(el, el.innerHTML);
} else {
this._tranNodes.set(el, q);
@@ -522,7 +521,7 @@ export class Translator {
traEl = document.createElement(APP_LCNAME);
traEl.style.visibility = "visible";
// if (this._setting.transOnly) {
// if (this._rule.transOnly === "true") {
// el.innerHTML = "";
// }
const { selectStyle, parentStyle } = this._rule;
@@ -532,8 +531,6 @@ export class Translator {
el.parentElement.style.cssText += parentStyle;
}
// console.log({ q, keeps });
const root = createRoot(traEl);
root.render(<Content q={q} keeps={keeps} translator={this} $el={el} />);
};

View File

@@ -1,55 +1,18 @@
import { isMatch } from "./utils";
import { getWebfix, setWebfix, getWebfixRulesWithDefault } from "./storage";
import { apiFetch } from "../apis";
/**
* 修复程序类型
*/
const FIXER_NONE = "-";
const FIXER_BR = "br";
const FIXER_BN = "bn";
const FIXER_BR_DIV = "brToDiv";
const FIXER_BN_DIV = "bnToDiv";
const FIXER_FONTSIZE = "fontSize";
export const FIXER_ALL = [
FIXER_NONE,
FIXER_BR,
FIXER_BN,
FIXER_BR_DIV,
FIXER_BN_DIV,
// FIXER_FONTSIZE,
];
/**
* 需要修复的站点列表
* - pattern 匹配网址
* - selector 需要修复的选择器
* - rootSelector 需要监听的选择器,可留空
* - fixer 修复函数,可针对不同网址,选用不同修复函数
*/
const DEFAULT_SITES = [
{
pattern: "www.phoronix.com",
selector: ".content",
rootSelector: "",
fixer: FIXER_BR,
},
{
pattern: "t.me/s/",
selector: ".tgme_widget_message_text",
rootSelector: ".tgme_channel_history",
fixer: FIXER_BR,
},
{
pattern: "baidu.com",
selector: "html",
rootSelector: "",
fixer: FIXER_FONTSIZE,
},
{
pattern: "chat.openai.com",
selector: "div[data-testid^=conversation-turn] .items-start > div",
rootSelector: "",
fixer: FIXER_BN,
},
];
/**
@@ -135,14 +98,6 @@ function bnDivFixer(node) {
return bnFixer(node, "div");
}
/**
* 修复字体大小问题,如 baidu.com
* @param {*} node
*/
function fontSizeFixer(node) {
node.style.cssText += "font-size:1em;";
}
/**
* 修复程序映射
*/
@@ -151,7 +106,6 @@ const fixerMap = {
[FIXER_BN]: bnFixer,
[FIXER_BR_DIV]: brDivFixer,
[FIXER_BN_DIV]: bnDivFixer,
[FIXER_FONTSIZE]: fontSizeFixer,
};
/**
@@ -189,68 +143,16 @@ function run(selector, fixer, rootSelector) {
});
}
/**
* 同步远程数据
* @param {*} url
* @returns
*/
export const syncWebfix = async (url) => {
const sites = await apiFetch(url);
await setWebfix(url, sites);
return sites;
};
/**
* 从缓存或远程加载修复站点
* @param {*} url
* @returns
*/
export const loadOrFetchWebfix = async (url) => {
try {
let sites = await getWebfix(url);
if (sites?.length) {
return sites;
}
return syncWebfix(url);
} catch (err) {
console.log("[load webfix]", err.message);
return DEFAULT_SITES;
}
};
/**
* 执行fixer
* @param {*} param0
*/
export async function runFixer({ selector, fixer, rootSelector }) {
export async function runFixer(selector, fixer = "-", rootSelector) {
try {
run(selector, fixerMap[fixer], rootSelector);
if (Object.keys(fixerMap).includes(fixer)) {
run(selector, fixerMap[fixer], rootSelector);
}
} catch (err) {
console.error(`[kiss-webfix run]: ${err.message}`);
}
}
/**
* 匹配fixer配置
*/
export async function matchFixer(href, { injectWebfix }) {
if (!injectWebfix) {
return null;
}
try {
const userSites = await getWebfixRulesWithDefault();
const subSites = await loadOrFetchWebfix(process.env.REACT_APP_WEBFIXURL);
const sites = [...userSites, ...subSites];
for (let i = 0; i < sites.length; i++) {
const site = sites[i];
if (isMatch(href, site.pattern) && fixerMap[site.fixer]) {
return site;
}
}
} catch (err) {
console.error(`[kiss-webfix match]: ${err.message}`);
}
return null;
}