Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ab2910dc7 | ||
|
|
40d07f6764 | ||
|
|
5c8e216169 | ||
|
|
5ba061deda | ||
|
|
935c83185d | ||
|
|
6327391e65 | ||
|
|
3d656cf5b0 | ||
|
|
d570a0f1a2 | ||
|
|
503a71302c | ||
|
|
3e36ceb5b9 | ||
|
|
cde7a1d49f | ||
|
|
b14a38e4fb |
2
.env
2
.env
@@ -2,7 +2,7 @@ GENERATE_SOURCEMAP=false
|
||||
|
||||
REACT_APP_NAME=KISS Translator
|
||||
REACT_APP_NAME_CN=简约翻译
|
||||
REACT_APP_VERSION=1.6.0
|
||||
REACT_APP_VERSION=1.6.1
|
||||
|
||||
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kiss-translator",
|
||||
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
||||
"version": "1.6.0",
|
||||
"version": "1.6.1",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@@ -15,6 +15,7 @@
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-router-dom": "^6.10.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"styled-components": "^6.0.7",
|
||||
"webextension-polyfill": "^0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "1.6.0",
|
||||
"version": "1.6.1",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "1.6.0",
|
||||
"version": "1.6.1",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
|
||||
@@ -60,6 +60,10 @@ export const I18N = {
|
||||
zh: `最大翻译长度 (100-10000)`,
|
||||
en: `Max Translate Length (100-10000)`,
|
||||
},
|
||||
num_of_newline_characters: {
|
||||
zh: `换行字符数 (1-1000)`,
|
||||
en: `Number of Newline Characters (1-1000)`,
|
||||
},
|
||||
translate_service: {
|
||||
zh: `翻译服务`,
|
||||
en: `Translate Service`,
|
||||
@@ -84,6 +88,10 @@ export const I18N = {
|
||||
zh: `样式颜色`,
|
||||
en: `Style Color`,
|
||||
},
|
||||
remain_unchanged: {
|
||||
zh: `保留不变`,
|
||||
en: `Remain Unchanged`,
|
||||
},
|
||||
google_api: {
|
||||
zh: `谷歌翻译接口`,
|
||||
en: `Google Translate API`,
|
||||
@@ -132,6 +140,10 @@ export const I18N = {
|
||||
zh: `订阅规则`,
|
||||
en: `Subscribe Rules`,
|
||||
},
|
||||
overwrite_subscribe_rules: {
|
||||
zh: `覆写订阅规则`,
|
||||
en: `Overwrite Subscribe Rules`,
|
||||
},
|
||||
subscribe_url: {
|
||||
zh: `订阅地址`,
|
||||
en: `Subscribe URL`,
|
||||
@@ -152,6 +164,10 @@ export const I18N = {
|
||||
zh: `查看关于数据同步接口部署`,
|
||||
en: `View About Data Synchronization Interface Deployment`,
|
||||
},
|
||||
about_api_proxy: {
|
||||
zh: `查看自建一个翻译接口代理`,
|
||||
en: `Check out the self-built translation interface proxy`,
|
||||
},
|
||||
style_none: {
|
||||
zh: `无`,
|
||||
en: `None`,
|
||||
@@ -180,6 +196,14 @@ export const I18N = {
|
||||
zh: `高亮`,
|
||||
en: `Highlight`,
|
||||
},
|
||||
diy_style: {
|
||||
zh: `自定义样式`,
|
||||
en: `Custom Style`,
|
||||
},
|
||||
diy_style_helper: {
|
||||
zh: `遵循“styled-components”的语法`,
|
||||
en: `Follow the syntax of "styled-components"`,
|
||||
},
|
||||
setting: {
|
||||
zh: `设置`,
|
||||
en: `Setting`,
|
||||
@@ -193,8 +217,8 @@ export const I18N = {
|
||||
en: `1. The asterisk (*) wildcard is supported. 2. Multiple URLs can be separated by English commas ",".`,
|
||||
},
|
||||
selector_helper: {
|
||||
zh: `1、遵循CSS选择器规则。2、留空表示采用全局设置。3、多个CSS选择器之间用“;”隔开。4、“shadow root”选择器和内部选择器用“>>>”隔开。`,
|
||||
en: `1. Follow the CSS selector rules. 2. Leave blank to adopt the global setting. 3. Separate multiple CSS selectors with ";". 4. The "shadow root" selector and the internal selector are separated by ">>>".`,
|
||||
zh: `1、遵循CSS选择器语法。2、留空表示采用全局设置。3、多个CSS选择器之间用“;”隔开。4、“shadow root”选择器和内部选择器用“>>>”隔开。`,
|
||||
en: `1. Follow CSS selector syntax. 2. Leave blank to adopt the global setting. 3. Separate multiple CSS selectors with ";". 4. The "shadow root" selector and the internal selector are separated by ">>>".`,
|
||||
},
|
||||
translate_switch: {
|
||||
zh: `开启翻译`,
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import {
|
||||
DEFAULT_SELECTOR,
|
||||
GLOBAL_KEY,
|
||||
REMAIN_KEY,
|
||||
SHADOW_KEY,
|
||||
DEFAULT_RULE,
|
||||
DEFAULT_OW_RULE,
|
||||
BUILTIN_RULES,
|
||||
} from "./rules";
|
||||
import { APP_NAME, APP_LCNAME } from "./app";
|
||||
export { I18N, UI_LANGS } from "./i18n";
|
||||
export { GLOBAL_KEY, SHADOW_KEY, DEFAULT_RULE, BUILTIN_RULES, APP_LCNAME };
|
||||
export {
|
||||
GLOBAL_KEY,
|
||||
REMAIN_KEY,
|
||||
SHADOW_KEY,
|
||||
DEFAULT_RULE,
|
||||
DEFAULT_OW_RULE,
|
||||
BUILTIN_RULES,
|
||||
APP_LCNAME,
|
||||
};
|
||||
|
||||
export const STOKEY_MSAUTH = `${APP_NAME}_msauth`;
|
||||
export const STOKEY_SETTING = `${APP_NAME}_setting`;
|
||||
@@ -49,6 +59,7 @@ export const THEME_LIGHT = "light";
|
||||
export const THEME_DARK = "dark";
|
||||
|
||||
export const URL_KISS_WORKER = "https://github.com/fishjar/kiss-worker";
|
||||
export const URL_KISS_PROXY = "https://github.com/fishjar/kiss-proxy";
|
||||
export const URL_RAW_PREFIX =
|
||||
"https://raw.githubusercontent.com/fishjar/kiss-translator/master";
|
||||
export const URL_MICROSOFT_AUTH = "https://edge.microsoft.com/translate/auth";
|
||||
@@ -121,7 +132,8 @@ export const OPT_STYLE_DOTLINE = "dot_line"; // 点状线
|
||||
export const OPT_STYLE_DASHLINE = "dash_line"; // 虚线
|
||||
export const OPT_STYLE_WAVYLINE = "wavy_line"; // 波浪线
|
||||
export const OPT_STYLE_FUZZY = "fuzzy"; // 模糊
|
||||
export const OPT_STYLE_HIGHTLIGHT = "highlight"; // 高亮
|
||||
export const OPT_STYLE_HIGHLIGHT = "highlight"; // 高亮
|
||||
export const OPT_STYLE_DIY = "diy_style"; // 自定义样式
|
||||
export const OPT_STYLE_ALL = [
|
||||
OPT_STYLE_NONE,
|
||||
OPT_STYLE_LINE,
|
||||
@@ -129,7 +141,15 @@ export const OPT_STYLE_ALL = [
|
||||
OPT_STYLE_DASHLINE,
|
||||
OPT_STYLE_WAVYLINE,
|
||||
OPT_STYLE_FUZZY,
|
||||
OPT_STYLE_HIGHTLIGHT,
|
||||
OPT_STYLE_HIGHLIGHT,
|
||||
OPT_STYLE_DIY,
|
||||
];
|
||||
export const OPT_STYLE_USE_COLOR = [
|
||||
OPT_STYLE_LINE,
|
||||
OPT_STYLE_DOTLINE,
|
||||
OPT_STYLE_DASHLINE,
|
||||
OPT_STYLE_WAVYLINE,
|
||||
OPT_STYLE_HIGHLIGHT,
|
||||
];
|
||||
|
||||
export const DEFAULT_FETCH_LIMIT = 10; // 默认最大任务数量
|
||||
@@ -150,6 +170,7 @@ export const GLOBLA_RULE = {
|
||||
textStyle: OPT_STYLE_DASHLINE,
|
||||
transOpen: "false",
|
||||
bgColor: "",
|
||||
textDiyStyle: "",
|
||||
};
|
||||
|
||||
// 订阅列表
|
||||
@@ -166,6 +187,7 @@ export const DEFAULT_SUBRULES_LIST = [
|
||||
|
||||
export const TRANS_MIN_LENGTH = 5; // 最短翻译长度
|
||||
export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度
|
||||
export const TRANS_NEWLINE_LENGTH = 40; // 换行字符数
|
||||
|
||||
export const DEFAULT_SETTING = {
|
||||
darkMode: false, // 深色模式
|
||||
@@ -174,9 +196,11 @@ export const DEFAULT_SETTING = {
|
||||
fetchInterval: DEFAULT_FETCH_INTERVAL, // 任务间隔时间
|
||||
minLength: TRANS_MIN_LENGTH,
|
||||
maxLength: TRANS_MAX_LENGTH,
|
||||
newlineLength: TRANS_NEWLINE_LENGTH,
|
||||
clearCache: false, // 是否在浏览器下次启动时清除缓存
|
||||
injectRules: true, // 是否注入订阅规则
|
||||
subrulesList: DEFAULT_SUBRULES_LIST, // 订阅列表
|
||||
owSubrule: DEFAULT_OW_RULE, // 覆写订阅规则
|
||||
googleUrl: "https://translate.googleapis.com/translate_a/single", // 谷歌翻译接口
|
||||
openaiUrl: "https://api.openai.com/v1/chat/completions",
|
||||
openaiKey: "",
|
||||
|
||||
@@ -3,6 +3,7 @@ const els = `li, p, h1, h2, h3, h4, h5, h6, dd`;
|
||||
export const DEFAULT_SELECTOR = `:is(${els})`;
|
||||
|
||||
export const GLOBAL_KEY = "*";
|
||||
export const REMAIN_KEY = "-";
|
||||
|
||||
export const SHADOW_KEY = ">>>";
|
||||
|
||||
@@ -15,6 +16,30 @@ export const DEFAULT_RULE = {
|
||||
textStyle: GLOBAL_KEY,
|
||||
transOpen: GLOBAL_KEY,
|
||||
bgColor: "",
|
||||
textDiyStyle: "",
|
||||
};
|
||||
|
||||
const DEFAULT_DIY_STYLE = `color: #666;
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
LightGreen 20%,
|
||||
LightPink 20% 40%,
|
||||
LightSalmon 40% 60%,
|
||||
LightSeaGreen 60% 80%,
|
||||
LightSkyBlue 80%
|
||||
);
|
||||
&:hover {
|
||||
color: #333;
|
||||
};`;
|
||||
|
||||
export const DEFAULT_OW_RULE = {
|
||||
translator: REMAIN_KEY,
|
||||
fromLang: REMAIN_KEY,
|
||||
toLang: REMAIN_KEY,
|
||||
textStyle: REMAIN_KEY,
|
||||
transOpen: REMAIN_KEY,
|
||||
bgColor: "",
|
||||
textDiyStyle: DEFAULT_DIY_STYLE,
|
||||
};
|
||||
|
||||
const RULES = [
|
||||
|
||||
@@ -2,28 +2,38 @@ import { STOKEY_SETTING, DEFAULT_SETTING } from "../config";
|
||||
import { useStorage } from "./Storage";
|
||||
import { useSync } from "./Sync";
|
||||
import { trySyncSetting } from "../libs/sync";
|
||||
import { createContext, useCallback, useContext } from "react";
|
||||
import { createContext, useCallback, useContext, useMemo } from "react";
|
||||
import { debounce } from "../libs/utils";
|
||||
|
||||
const SettingContext = createContext({
|
||||
setting: null,
|
||||
updateSetting: async () => {},
|
||||
reloadSetting: async () => {},
|
||||
});
|
||||
|
||||
export function SettingProvider({ children }) {
|
||||
const { data, update } = useStorage(STOKEY_SETTING, DEFAULT_SETTING);
|
||||
const { data, update, reload } = useStorage(STOKEY_SETTING, DEFAULT_SETTING);
|
||||
const {
|
||||
sync: { settingUpdateAt },
|
||||
updateSync,
|
||||
} = useSync();
|
||||
|
||||
const syncSetting = useMemo(
|
||||
() =>
|
||||
debounce(() => {
|
||||
trySyncSetting();
|
||||
}, [2000]),
|
||||
[]
|
||||
);
|
||||
|
||||
const updateSetting = useCallback(
|
||||
async (obj) => {
|
||||
const updateAt = settingUpdateAt ? Date.now() : 0;
|
||||
await update(obj);
|
||||
await updateSync({ settingUpdateAt: updateAt });
|
||||
trySyncSetting();
|
||||
syncSetting();
|
||||
},
|
||||
[settingUpdateAt, update, updateSync]
|
||||
[settingUpdateAt, update, updateSync, syncSetting]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -31,6 +41,7 @@ export function SettingProvider({ children }) {
|
||||
value={{
|
||||
setting: data,
|
||||
updateSetting,
|
||||
reloadSetting: reload,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -25,16 +25,20 @@ export function useStorage(key, defaultVal = null) {
|
||||
await storage.del(key);
|
||||
}, [key]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const val = await storage.getObj(key);
|
||||
if (val) {
|
||||
setData(val);
|
||||
} else if (defaultVal) {
|
||||
await storage.setObj(key, defaultVal);
|
||||
}
|
||||
})();
|
||||
const reload = useCallback(async () => {
|
||||
const val = await storage.getObj(key);
|
||||
if (val) {
|
||||
setData(val);
|
||||
} else if (defaultVal) {
|
||||
await storage.setObj(key, defaultVal);
|
||||
}
|
||||
}, [key, defaultVal]);
|
||||
|
||||
return { data, save, update, remove };
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await reload();
|
||||
})();
|
||||
}, [reload]);
|
||||
|
||||
return { data, save, update, remove, reload };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEFAULT_SUBRULES_LIST } from "../config";
|
||||
import { DEFAULT_SUBRULES_LIST, DEFAULT_OW_RULE } from "../config";
|
||||
import { useSetting } from "./Setting";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { loadOrFetchSubRules } from "../libs/subRules";
|
||||
@@ -79,3 +79,21 @@ export function useSubRules() {
|
||||
loading,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 覆写订阅规则
|
||||
* @returns
|
||||
*/
|
||||
export function useOwSubRule() {
|
||||
const { setting, updateSetting } = useSetting();
|
||||
const { owSubrule = DEFAULT_OW_RULE } = setting;
|
||||
|
||||
const updateOwSubrule = useCallback(
|
||||
async (obj) => {
|
||||
await updateSetting({ owSubrule: { ...owSubrule, ...obj } });
|
||||
},
|
||||
[owSubrule, updateSetting]
|
||||
);
|
||||
|
||||
return { owSubrule, updateOwSubrule };
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { matchValue, type, isMatch } from "./utils";
|
||||
import {
|
||||
GLOBAL_KEY,
|
||||
REMAIN_KEY,
|
||||
OPT_TRANS_ALL,
|
||||
OPT_STYLE_ALL,
|
||||
OPT_LANGS_FROM,
|
||||
OPT_LANGS_TO,
|
||||
GLOBLA_RULE,
|
||||
DEFAULT_SUBRULES_LIST,
|
||||
DEFAULT_OW_RULE,
|
||||
} from "../config";
|
||||
import { loadOrFetchSubRules } from "./subRules";
|
||||
|
||||
@@ -19,14 +21,35 @@ import { loadOrFetchSubRules } from "./subRules";
|
||||
export const matchRule = async (
|
||||
rules,
|
||||
href,
|
||||
{ injectRules = true, subrulesList = DEFAULT_SUBRULES_LIST }
|
||||
{
|
||||
injectRules = true,
|
||||
subrulesList = DEFAULT_SUBRULES_LIST,
|
||||
owSubrule = DEFAULT_OW_RULE,
|
||||
}
|
||||
) => {
|
||||
rules = [...rules];
|
||||
if (injectRules) {
|
||||
try {
|
||||
const selectedSub = subrulesList.find((item) => item.selected);
|
||||
if (selectedSub?.url) {
|
||||
const subRules = await loadOrFetchSubRules(selectedSub.url);
|
||||
const mixRule = {};
|
||||
Object.entries(owSubrule)
|
||||
.filter(([key, val]) => {
|
||||
if (
|
||||
owSubrule.textStyle === REMAIN_KEY &&
|
||||
(key === "bgColor" || key === "textDiyStyle")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return val !== REMAIN_KEY;
|
||||
})
|
||||
.forEach(([key, val]) => {
|
||||
mixRule[key] = val;
|
||||
});
|
||||
|
||||
const subRules = (await loadOrFetchSubRules(selectedSub.url)).map(
|
||||
(item) => ({ ...item, ...mixRule })
|
||||
);
|
||||
rules.splice(-1, 0, ...subRules);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -39,8 +62,9 @@ export const matchRule = async (
|
||||
);
|
||||
|
||||
const globalRule =
|
||||
rules.find((r) => r.pattern.split(",").some((p) => p.trim() === "*")) ||
|
||||
GLOBLA_RULE;
|
||||
rules.find((r) =>
|
||||
r.pattern.split(",").some((p) => p.trim() === GLOBAL_KEY)
|
||||
) || GLOBLA_RULE;
|
||||
|
||||
if (!rule) {
|
||||
return globalRule;
|
||||
@@ -52,6 +76,8 @@ export const matchRule = async (
|
||||
GLOBLA_RULE.selector;
|
||||
|
||||
rule.bgColor = rule?.bgColor?.trim() || globalRule?.bgColor?.trim();
|
||||
rule.textDiyStyle =
|
||||
rule?.textDiyStyle?.trim() || globalRule?.textDiyStyle?.trim();
|
||||
|
||||
["translator", "fromLang", "toLang", "textStyle", "transOpen"].forEach(
|
||||
(key) => {
|
||||
@@ -99,10 +125,12 @@ export const checkRules = (rules) => {
|
||||
textStyle,
|
||||
transOpen,
|
||||
bgColor,
|
||||
textDiyStyle,
|
||||
}) => ({
|
||||
pattern: pattern.trim(),
|
||||
selector: type(selector) === "string" ? selector : "",
|
||||
bgColor: type(bgColor) === "string" ? bgColor : "",
|
||||
textDiyStyle: type(textDiyStyle) === "string" ? textDiyStyle : "",
|
||||
translator: matchValue([GLOBAL_KEY, ...OPT_TRANS_ALL], translator),
|
||||
fromLang: matchValue([GLOBAL_KEY, ...fromLangs], fromLang),
|
||||
toLang: matchValue([GLOBAL_KEY, ...toLangs], toLang),
|
||||
|
||||
@@ -43,6 +43,7 @@ const syncSetting = async (isBg = false) => {
|
||||
settingSyncAt: res.updateAt,
|
||||
});
|
||||
await setSetting(res.value);
|
||||
return res.value;
|
||||
} else {
|
||||
await updateSync({ settingSyncAt: res.updateAt });
|
||||
}
|
||||
@@ -50,7 +51,7 @@ const syncSetting = async (isBg = false) => {
|
||||
|
||||
export const trySyncSetting = async (isBg = false) => {
|
||||
try {
|
||||
await syncSetting(isBg);
|
||||
return await syncSetting(isBg);
|
||||
} catch (err) {
|
||||
console.log("[sync setting]", err);
|
||||
}
|
||||
@@ -84,6 +85,7 @@ const syncRules = async (isBg = false) => {
|
||||
rulesSyncAt: res.updateAt,
|
||||
});
|
||||
await setRules(res.value);
|
||||
return res.value;
|
||||
} else {
|
||||
await updateSync({ rulesSyncAt: res.updateAt });
|
||||
}
|
||||
@@ -91,7 +93,7 @@ const syncRules = async (isBg = false) => {
|
||||
|
||||
export const trySyncRules = async (isBg = false) => {
|
||||
try {
|
||||
await syncRules(isBg);
|
||||
return await syncRules(isBg);
|
||||
} catch (err) {
|
||||
console.log("[sync user rules]", err);
|
||||
}
|
||||
@@ -118,11 +120,9 @@ export const syncShareRules = async ({ rules, syncUrl, syncKey }) => {
|
||||
* @returns
|
||||
*/
|
||||
export const syncSettingAndRules = async (isBg = false) => {
|
||||
await syncSetting(isBg);
|
||||
await syncRules(isBg);
|
||||
return [await syncSetting(isBg), await syncRules(isBg)];
|
||||
};
|
||||
|
||||
export const trySyncSettingAndRules = async (isBg = false) => {
|
||||
await trySyncSetting(isBg);
|
||||
await trySyncRules(isBg);
|
||||
return [await trySyncSetting(isBg), await trySyncRules(isBg)];
|
||||
};
|
||||
|
||||
@@ -16,8 +16,8 @@ export default function Action({ translator, fab }) {
|
||||
const fabWidth = 40;
|
||||
const [showPopup, setShowPopup] = useState(false);
|
||||
const [windowSize, setWindowSize] = useState({
|
||||
w: document.documentElement.clientWidth,
|
||||
h: document.documentElement.clientHeight,
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
});
|
||||
const [moved, setMoved] = useState(false);
|
||||
|
||||
@@ -25,8 +25,8 @@ export default function Action({ translator, fab }) {
|
||||
() =>
|
||||
debounce(() => {
|
||||
setWindowSize({
|
||||
w: document.documentElement.clientWidth,
|
||||
h: document.documentElement.clientHeight,
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
});
|
||||
}),
|
||||
[]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState, useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import LoadingIcon from "./LoadingIcon";
|
||||
import {
|
||||
OPT_STYLE_LINE,
|
||||
@@ -6,26 +6,100 @@ import {
|
||||
OPT_STYLE_DASHLINE,
|
||||
OPT_STYLE_WAVYLINE,
|
||||
OPT_STYLE_FUZZY,
|
||||
OPT_STYLE_HIGHTLIGHT,
|
||||
OPT_STYLE_HIGHLIGHT,
|
||||
OPT_STYLE_DIY,
|
||||
DEFAULT_COLOR,
|
||||
EVENT_KISS,
|
||||
MSG_TRANS_CURRULE,
|
||||
TRANS_NEWLINE_LENGTH,
|
||||
} from "../../config";
|
||||
import { useTranslate } from "../../hooks/Translate";
|
||||
import styled from "styled-components";
|
||||
|
||||
const LineSpan = styled.span`
|
||||
opacity: 0.6;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: ${(props) => props.$lineStyle};
|
||||
text-decoration-color: ${(props) => props.$lineColor};
|
||||
text-decoration-thickness: 2px;
|
||||
text-underline-offset: 0.3em;
|
||||
-webkit-text-decoration-line: underline;
|
||||
-webkit-text-decoration-style: ${(props) => props.$lineStyle};
|
||||
-webkit-text-decoration-color: ${(props) => props.$lineColor};
|
||||
-webkit-text-decoration-thickness: 2px;
|
||||
-webkit-text-underline-offset: 0.3em;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
const FuzzySpan = styled.span`
|
||||
filter: blur(5px);
|
||||
transition: filter 0.2s ease-in-out;
|
||||
&hover: {
|
||||
filter: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const HighlightSpan = styled.span`
|
||||
color: #fff;
|
||||
background-color: ${(props) => props.$bgColor};
|
||||
&hover: {
|
||||
filter: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const DiySpan = styled.span`
|
||||
${(props) => props.$diyStyle}
|
||||
`;
|
||||
|
||||
function StyledSpan({ textStyle, textDiyStyle, bgColor, children }) {
|
||||
switch (textStyle) {
|
||||
case OPT_STYLE_LINE: // 下划线
|
||||
return (
|
||||
<LineSpan $lineStyle="solid" $lineColor={bgColor}>
|
||||
{children}
|
||||
</LineSpan>
|
||||
);
|
||||
case OPT_STYLE_DOTLINE: // 点状线
|
||||
return (
|
||||
<LineSpan $lineStyle="dotted" $lineColor={bgColor}>
|
||||
{children}
|
||||
</LineSpan>
|
||||
);
|
||||
case OPT_STYLE_DASHLINE: // 虚线
|
||||
return (
|
||||
<LineSpan $lineStyle="dashed" $lineColor={bgColor}>
|
||||
{children}
|
||||
</LineSpan>
|
||||
);
|
||||
case OPT_STYLE_WAVYLINE: // 波浪线
|
||||
return (
|
||||
<LineSpan $lineStyle="wavy" $lineColor={bgColor}>
|
||||
{children}
|
||||
</LineSpan>
|
||||
);
|
||||
case OPT_STYLE_FUZZY: // 模糊
|
||||
return <FuzzySpan>{children}</FuzzySpan>;
|
||||
case OPT_STYLE_HIGHLIGHT: // 高亮
|
||||
return (
|
||||
<HighlightSpan $bgColor={bgColor || DEFAULT_COLOR}>
|
||||
{children}
|
||||
</HighlightSpan>
|
||||
);
|
||||
case OPT_STYLE_DIY: // 自定义
|
||||
return <DiySpan $diyStyle={textDiyStyle}>{children}</DiySpan>;
|
||||
default:
|
||||
return <span>{children}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
export default function Content({ q, translator }) {
|
||||
const [rule, setRule] = useState(translator.rule);
|
||||
const [hover, setHover] = useState(false);
|
||||
const { text, sameLang, loading } = useTranslate(q, rule, translator.setting);
|
||||
const { textStyle, bgColor } = rule;
|
||||
const { textStyle, bgColor = "", textDiyStyle = "" } = rule;
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setHover(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setHover(false);
|
||||
};
|
||||
const { newlineLength = TRANS_NEWLINE_LENGTH } = translator.setting;
|
||||
|
||||
const handleKissEvent = (e) => {
|
||||
const { action, args } = e.detail;
|
||||
@@ -34,7 +108,6 @@ export default function Content({ q, translator }) {
|
||||
setRule(args);
|
||||
break;
|
||||
default:
|
||||
// console.log(`[popup] kissEvent action skip: ${action}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,49 +118,10 @@ export default function Content({ q, translator }) {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const style = useMemo(() => {
|
||||
const lineColor = bgColor || "";
|
||||
const underlineStyle = (st) => ({
|
||||
opacity: hover ? 1 : 0.6,
|
||||
textDecorationLine: "underline",
|
||||
textDecorationColor: lineColor,
|
||||
textDecorationStyle: st,
|
||||
textDecorationThickness: "2px",
|
||||
textUnderlineOffset: "0.3em",
|
||||
WebkittextDecorationLine: "underline",
|
||||
WebkittextDecorationColor: lineColor,
|
||||
WebkittextDecorationStyle: st,
|
||||
WebkittextDecorationThickness: "2px",
|
||||
WebkittextTextUnderlineOffset: "0.3em",
|
||||
});
|
||||
switch (textStyle) {
|
||||
case OPT_STYLE_LINE: // 下划线
|
||||
return underlineStyle("solid");
|
||||
case OPT_STYLE_DOTLINE: // 点状线
|
||||
return underlineStyle("dotted");
|
||||
case OPT_STYLE_DASHLINE: // 虚线
|
||||
return underlineStyle("dashed");
|
||||
case OPT_STYLE_WAVYLINE: // 波浪线
|
||||
return underlineStyle("wavy");
|
||||
case OPT_STYLE_FUZZY: // 模糊
|
||||
return {
|
||||
filter: hover ? "none" : "blur(5px)",
|
||||
transition: "filter 0.2s ease-in-out",
|
||||
};
|
||||
case OPT_STYLE_HIGHTLIGHT: // 高亮
|
||||
return {
|
||||
color: "#FFF",
|
||||
backgroundColor: bgColor || DEFAULT_COLOR,
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}, [textStyle, hover, bgColor]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<>
|
||||
{q.length > 40 ? <br /> : " "}
|
||||
{q.length > newlineLength ? <br /> : " "}
|
||||
<LoadingIcon />
|
||||
</>
|
||||
);
|
||||
@@ -96,14 +130,14 @@ export default function Content({ q, translator }) {
|
||||
if (text && !sameLang) {
|
||||
return (
|
||||
<>
|
||||
{q.length > 40 ? <br /> : " "}
|
||||
<span
|
||||
style={style}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
{q.length > newlineLength ? <br /> : " "}
|
||||
<StyledSpan
|
||||
textStyle={textStyle}
|
||||
textDiyStyle={textDiyStyle}
|
||||
bgColor={bgColor}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
</StyledSpan>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
175
src/views/Options/OwSubRule.js
Normal file
175
src/views/Options/OwSubRule.js
Normal file
@@ -0,0 +1,175 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import {
|
||||
GLOBAL_KEY,
|
||||
REMAIN_KEY,
|
||||
OPT_LANGS_FROM,
|
||||
OPT_LANGS_TO,
|
||||
OPT_TRANS_ALL,
|
||||
OPT_STYLE_ALL,
|
||||
OPT_STYLE_DIY,
|
||||
OPT_STYLE_USE_COLOR,
|
||||
} from "../../config";
|
||||
import { useI18n } from "../../hooks/I18n";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { useOwSubRule } from "../../hooks/SubRules";
|
||||
|
||||
export default function OwSubRule() {
|
||||
const i18n = useI18n();
|
||||
const { owSubrule, updateOwSubrule } = useOwSubRule();
|
||||
|
||||
const handleChange = (e) => {
|
||||
e.preventDefault();
|
||||
const { name, value } = e.target;
|
||||
updateOwSubrule({ [name]: value });
|
||||
};
|
||||
|
||||
const {
|
||||
translator,
|
||||
fromLang,
|
||||
toLang,
|
||||
textStyle,
|
||||
transOpen,
|
||||
bgColor,
|
||||
textDiyStyle,
|
||||
} = owSubrule;
|
||||
|
||||
const RemainItem = (
|
||||
<MenuItem key={REMAIN_KEY} value={REMAIN_KEY}>
|
||||
{i18n("remain_unchanged")}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
const GlobalItem = (
|
||||
<MenuItem key={GLOBAL_KEY} value={GLOBAL_KEY}>
|
||||
{GLOBAL_KEY}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack spacing={2}>
|
||||
<Box>
|
||||
<Grid container spacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={6} md={3} lg={2}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
fullWidth
|
||||
name="transOpen"
|
||||
value={transOpen}
|
||||
label={i18n("translate_switch")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{RemainItem}
|
||||
{GlobalItem}
|
||||
<MenuItem value={"true"}>{i18n("default_enabled")}</MenuItem>
|
||||
<MenuItem value={"false"}>{i18n("default_disabled")}</MenuItem>
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={3} lg={2}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
fullWidth
|
||||
name="translator"
|
||||
value={translator}
|
||||
label={i18n("translate_service")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{RemainItem}
|
||||
{GlobalItem}
|
||||
{OPT_TRANS_ALL.map((item) => (
|
||||
<MenuItem key={item} value={item}>
|
||||
{item}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={3} lg={2}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
fullWidth
|
||||
name="fromLang"
|
||||
value={fromLang}
|
||||
label={i18n("from_lang")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{RemainItem}
|
||||
{GlobalItem}
|
||||
{OPT_LANGS_FROM.map(([lang, name]) => (
|
||||
<MenuItem key={lang} value={lang}>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={3} lg={2}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
fullWidth
|
||||
name="toLang"
|
||||
value={toLang}
|
||||
label={i18n("to_lang")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{RemainItem}
|
||||
{GlobalItem}
|
||||
{OPT_LANGS_TO.map(([lang, name]) => (
|
||||
<MenuItem key={lang} value={lang}>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={3} lg={2}>
|
||||
<TextField
|
||||
select
|
||||
size="small"
|
||||
fullWidth
|
||||
name="textStyle"
|
||||
value={textStyle}
|
||||
label={i18n("text_style")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{RemainItem}
|
||||
{GlobalItem}
|
||||
{OPT_STYLE_ALL.map((item) => (
|
||||
<MenuItem key={item} value={item}>
|
||||
{i18n(item)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
{OPT_STYLE_USE_COLOR.includes(textStyle) && (
|
||||
<Grid item xs={12} sm={6} md={3} lg={2}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
name="bgColor"
|
||||
value={bgColor}
|
||||
label={i18n("bg_color")}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{textStyle === OPT_STYLE_DIY && (
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("diy_style")}
|
||||
helperText={i18n("diy_style_helper")}
|
||||
name="textDiyStyle"
|
||||
value={textDiyStyle}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
OPT_LANGS_TO,
|
||||
OPT_TRANS_ALL,
|
||||
OPT_STYLE_ALL,
|
||||
OPT_STYLE_DIY,
|
||||
OPT_STYLE_USE_COLOR,
|
||||
} from "../../config";
|
||||
import { useState, useRef, useEffect, useMemo } from "react";
|
||||
import { useI18n } from "../../hooks/I18n";
|
||||
@@ -42,9 +44,13 @@ import { useAlert } from "../../hooks/Alert";
|
||||
import { syncShareRules } from "../../libs/sync";
|
||||
import { debounce } from "../../libs/utils";
|
||||
import { delSubRules, getSyncWithDefault } from "../../libs/storage";
|
||||
import OwSubRule from "./OwSubRule";
|
||||
|
||||
function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
const initFormValues = rule || { ...DEFAULT_RULE, transOpen: "true" };
|
||||
const initFormValues = rule || {
|
||||
...DEFAULT_RULE,
|
||||
transOpen: "true",
|
||||
};
|
||||
const editMode = !!rule;
|
||||
|
||||
const i18n = useI18n();
|
||||
@@ -60,6 +66,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
textStyle,
|
||||
transOpen,
|
||||
bgColor,
|
||||
textDiyStyle,
|
||||
} = formValues;
|
||||
|
||||
const hasSamePattern = (str) => {
|
||||
@@ -134,7 +141,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
}
|
||||
};
|
||||
|
||||
const globalItem = rule?.pattern !== "*" && (
|
||||
const GlobalItem = rule?.pattern !== "*" && (
|
||||
<MenuItem key={GLOBAL_KEY} value={GLOBAL_KEY}>
|
||||
{GLOBAL_KEY}
|
||||
</MenuItem>
|
||||
@@ -181,7 +188,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{globalItem}
|
||||
{GlobalItem}
|
||||
<MenuItem value={"true"}>{i18n("default_enabled")}</MenuItem>
|
||||
<MenuItem value={"false"}>{i18n("default_disabled")}</MenuItem>
|
||||
</TextField>
|
||||
@@ -197,7 +204,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{globalItem}
|
||||
{GlobalItem}
|
||||
{OPT_TRANS_ALL.map((item) => (
|
||||
<MenuItem key={item} value={item}>
|
||||
{item}
|
||||
@@ -216,7 +223,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{globalItem}
|
||||
{GlobalItem}
|
||||
{OPT_LANGS_FROM.map(([lang, name]) => (
|
||||
<MenuItem key={lang} value={lang}>
|
||||
{name}
|
||||
@@ -235,7 +242,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{globalItem}
|
||||
{GlobalItem}
|
||||
{OPT_LANGS_TO.map(([lang, name]) => (
|
||||
<MenuItem key={lang} value={lang}>
|
||||
{name}
|
||||
@@ -254,7 +261,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{globalItem}
|
||||
{GlobalItem}
|
||||
{OPT_STYLE_ALL.map((item) => (
|
||||
<MenuItem key={item} value={item}>
|
||||
{i18n(item)}
|
||||
@@ -262,20 +269,35 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
))}
|
||||
</TextField>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={3} lg={2}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
name="bgColor"
|
||||
value={bgColor}
|
||||
label={i18n("bg_color")}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
{OPT_STYLE_USE_COLOR.includes(textStyle) && (
|
||||
<Grid item xs={12} sm={6} md={3} lg={2}>
|
||||
<TextField
|
||||
size="small"
|
||||
fullWidth
|
||||
name="bgColor"
|
||||
value={bgColor}
|
||||
label={i18n("bg_color")}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{textStyle === OPT_STYLE_DIY && (
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("diy_style")}
|
||||
helperText={i18n("diy_style_helper")}
|
||||
name="textDiyStyle"
|
||||
value={textDiyStyle}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
/>
|
||||
)}
|
||||
|
||||
{rules &&
|
||||
(editMode ? (
|
||||
// 编辑
|
||||
@@ -797,6 +819,7 @@ export default function Rules() {
|
||||
<Tabs value={activeTab} onChange={handleTabChange}>
|
||||
<Tab label={i18n("personal_rules")} />
|
||||
<Tab label={i18n("subscribe_rules")} />
|
||||
<Tab label={i18n("overwrite_subscribe_rules")} />
|
||||
</Tabs>
|
||||
</Box>
|
||||
<div hidden={activeTab !== 0}>
|
||||
@@ -805,6 +828,7 @@ export default function Rules() {
|
||||
<div hidden={activeTab !== 1}>
|
||||
{activeTab === 1 && <SubRules subRules={subRules} />}
|
||||
</div>
|
||||
<div hidden={activeTab !== 2}>{activeTab === 2 && <OwSubRule />}</div>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -5,10 +5,11 @@ import TextField from "@mui/material/TextField";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import FormControl from "@mui/material/FormControl";
|
||||
import Select from "@mui/material/Select";
|
||||
import Link from "@mui/material/Link";
|
||||
import { useSetting } from "../../hooks/Setting";
|
||||
import { limitNumber } from "../../libs/utils";
|
||||
import { useI18n } from "../../hooks/I18n";
|
||||
import { UI_LANGS } from "../../config";
|
||||
import { UI_LANGS, URL_KISS_PROXY, TRANS_NEWLINE_LENGTH } from "../../config";
|
||||
|
||||
export default function Settings() {
|
||||
const i18n = useI18n();
|
||||
@@ -30,6 +31,9 @@ export default function Settings() {
|
||||
case "maxLength":
|
||||
value = limitNumber(value, 100, 10000);
|
||||
break;
|
||||
case "newlineLength":
|
||||
value = limitNumber(value, 1, 1000);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
updateSetting({
|
||||
@@ -49,6 +53,7 @@ export default function Settings() {
|
||||
openaiModel,
|
||||
openaiPrompt,
|
||||
clearCache,
|
||||
newlineLength = TRANS_NEWLINE_LENGTH,
|
||||
} = setting;
|
||||
|
||||
return (
|
||||
@@ -106,6 +111,15 @@ export default function Settings() {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label={i18n("num_of_newline_characters")}
|
||||
type="number"
|
||||
name="newlineLength"
|
||||
value={newlineLength}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<FormControl size="small">
|
||||
<InputLabel>{i18n("clear_cache")}</InputLabel>
|
||||
<Select
|
||||
@@ -125,6 +139,9 @@ export default function Settings() {
|
||||
name="googleUrl"
|
||||
value={googleUrl}
|
||||
onChange={handleChange}
|
||||
helperText={
|
||||
<Link href={URL_KISS_PROXY}>{i18n("about_api_proxy")}</Link>
|
||||
}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
@@ -133,6 +150,9 @@ export default function Settings() {
|
||||
name="openaiUrl"
|
||||
value={openaiUrl}
|
||||
onChange={handleChange}
|
||||
helperText={
|
||||
<Link href={URL_KISS_PROXY}>{i18n("about_api_proxy")}</Link>
|
||||
}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
|
||||
@@ -12,12 +12,14 @@ import Button from "@mui/material/Button";
|
||||
import { useAlert } from "../../hooks/Alert";
|
||||
import SyncIcon from "@mui/icons-material/Sync";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import { useSetting } from "../../hooks/Setting";
|
||||
|
||||
export default function SyncSetting() {
|
||||
const i18n = useI18n();
|
||||
const { sync, updateSync } = useSync();
|
||||
const alert = useAlert();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { reloadSetting } = useSetting();
|
||||
|
||||
const handleChange = async (e) => {
|
||||
e.preventDefault();
|
||||
@@ -32,6 +34,7 @@ export default function SyncSetting() {
|
||||
try {
|
||||
setLoading(true);
|
||||
await syncSettingAndRules();
|
||||
await reloadSetting();
|
||||
alert.success(i18n("data_sync_success"));
|
||||
} catch (err) {
|
||||
console.log("[sync all]", err);
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
OPT_LANGS_FROM,
|
||||
OPT_LANGS_TO,
|
||||
OPT_STYLE_ALL,
|
||||
OPT_STYLE_USE_COLOR,
|
||||
} from "../../config";
|
||||
import { sendIframeMsg } from "../../libs/iframe";
|
||||
|
||||
@@ -172,13 +173,15 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
))}
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
name="bgColor"
|
||||
value={bgColor}
|
||||
label={i18n("bg_color")}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{OPT_STYLE_USE_COLOR.includes(textStyle) && (
|
||||
<TextField
|
||||
size="small"
|
||||
name="bgColor"
|
||||
value={bgColor}
|
||||
label={i18n("bg_color")}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button variant="text" onClick={handleOpenSetting}>
|
||||
{i18n("setting")}
|
||||
|
||||
Reference in New Issue
Block a user