Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c57a0a11fa | ||
|
|
fa244b2097 | ||
|
|
79612f8a1b | ||
|
|
2bf79dbc51 | ||
|
|
c2658d5dd0 | ||
|
|
13684884c7 | ||
|
|
f216a9254e | ||
|
|
dbdbcbba2d | ||
|
|
2ee4609192 | ||
|
|
0d93cf78f7 | ||
|
|
3398ca0dd7 | ||
|
|
c1778fbcbb | ||
|
|
1ef9974c05 | ||
|
|
399c6b6fed | ||
|
|
62a60eee44 | ||
|
|
54339af885 | ||
|
|
06cfd33e60 | ||
|
|
08c9d78d2a | ||
|
|
e7a5e5dce1 | ||
|
|
3a59a127d1 | ||
|
|
26f213cad2 | ||
|
|
7b6148302d | ||
|
|
38c781b8f3 | ||
|
|
64d827fdcd | ||
|
|
74ad812f37 | ||
|
|
364c829119 | ||
|
|
1ac2c5b61e | ||
|
|
0766199353 | ||
|
|
878bccf151 | ||
|
|
acbd258296 | ||
|
|
54a14e6e5a |
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.8
|
||||
REACT_APP_VERSION=1.7.0
|
||||
|
||||
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
|
||||
|
||||
|
||||
66
README.en.md
66
README.en.md
@@ -16,7 +16,34 @@ If you also like a little more simplicity, welcome to pick it up.
|
||||
|
||||
## Features
|
||||
|
||||
- Keep it simple, smart
|
||||
- [x] Keep it simple, smart
|
||||
- [x] Open source
|
||||
- [x] Adapt to common browsers
|
||||
- [x] Chrome/Edge/Firefox/Kiwi
|
||||
- [ ] Safari
|
||||
- [x] Supports multiple translation services
|
||||
- [x] Google/Microsoft/DeepL/OpenAI
|
||||
- [x] Custom translation interface
|
||||
- [x] Data synchronization function
|
||||
- [x] Custom rules + rule subscription
|
||||
- [x] Custom style
|
||||
- [x] Custom shortcut keys
|
||||
- `Alt+Q` Toggle Translation
|
||||
- `Alt+C` Toggle Styles
|
||||
- `Alt+K` Open Popup
|
||||
- `Alt+O` Open Options
|
||||
|
||||
## Download
|
||||
|
||||
- [x] Browser extension
|
||||
- [x] Chrome [Installation address](https://chrome.google.com/webstore/detail/kiss-translator/bdiifdefkgmcblbcghdlonllpjhhjgof?hl=zh-CN)
|
||||
- [x] Edge [Installation address](https://microsoftedge.microsoft.com/addons/detail/%E7%AE%80%E7%BA%A6%E7%BF%BB%E8%AF%91/jemckldkclkinpjighnoilpbldbdmmlh?hl=zh-CN)
|
||||
- [x] Firefox [Installation address](https://addons.mozilla.org/zh-CN/firefox/addon/kiss-translator/)
|
||||
- [ ] Safari
|
||||
- [x] GreaseMonkey Script
|
||||
- [x] Chrome/Edge/Firefox ([Tampermonkey](https://www.tampermonkey.net/)/[Violentmonkey](https://violentmonkey.github.io/)) [Installation link 1](https://fishjar.github.io/kiss-translator/kiss-translator.user.js)、 [Installation link 2](https://kiss-translator.rayjar.com/kiss-translator.user.js)
|
||||
- Greasy Fork [Installation address](https://greasyfork.org/zh-CN/scripts/472840-kiss-translator)
|
||||
- [x] iOS Safari ([Userscripts Safari](https://github.com/quoid/userscripts)) [Installation link 1](https://fishjar.github.io/kiss-translator/kiss-translator-ios-safari.user.js)、 [Installation link 2](https://kiss-translator.rayjar.com/kiss-translator.user-ios-safari.js)
|
||||
|
||||
## Associated ProjectS
|
||||
|
||||
@@ -38,42 +65,7 @@ If you also like a little more simplicity, welcome to pick it up.
|
||||
- Supports query of English words, sentences and Chinese characters.
|
||||
- Supports history records and word collections.
|
||||
|
||||
## Description
|
||||
|
||||
### Support shortcut keys
|
||||
|
||||
- `Alt+Q` Toggle Translation
|
||||
- `Alt+C` Toggle Styles
|
||||
- `Alt+K` Open Menu
|
||||
|
||||
## Schedule
|
||||
|
||||
- [x] Provide trial installation package
|
||||
- [x] Adapt browser
|
||||
- [x] Chrome
|
||||
- [x] Edge
|
||||
- [x] Firefox
|
||||
- [ ] Safari
|
||||
- [x] Kiwi
|
||||
- [x] Support translation services
|
||||
- [x] Google
|
||||
- [x] Microsoft
|
||||
- [x] DeepL
|
||||
- [x] OpenAI
|
||||
- [x] Upload to app Store
|
||||
- [x] Chrome [Install Link](https://chrome.google.com/webstore/detail/kiss-translator/bdiifdefkgmcblbcghdlonllpjhhjgof)
|
||||
- [x] Edge [Install Link](https://microsoftedge.microsoft.com/addons/detail/kiss-translator/jemckldkclkinpjighnoilpbldbdmmlh)
|
||||
- [x] Firefox [Install Link](https://addons.mozilla.org/en-US/firefox/addon/kiss-translator/)
|
||||
- [ ] Safari
|
||||
- [x] Greasy Fork [Install Link](https://greasyfork.org/en/scripts/472840-kiss-translator)
|
||||
- [x] Open source
|
||||
- [x] Data Synchronization Function
|
||||
- [x] Greasemonkey Script ([Setting Page 1](https://fishjar.github.io/kiss-translator/options.html)、[Setting Page 2](https://kiss-translator.rayjar.com/options))
|
||||
- [x] [Tampermonkey](https://www.tampermonkey.net/) (Chrome/Edge/Firefox) [Install Link 1](https://fishjar.github.io/kiss-translator/kiss-translator.user.js)、[Install Link 2](https://kiss-translator.rayjar.com/kiss-translator.user.js)
|
||||
- [x] [Violentmonkey](https://violentmonkey.github.io/) (Chrome/Edge/Firefox) [Install Link 1](https://fishjar.github.io/kiss-translator/kiss-translator.user.js)、[Install Link 2](https://kiss-translator.rayjar.com/kiss-translator.user.js)
|
||||
- [x] [Userscripts Safari](https://github.com/quoid/userscripts) (iOS Safari) [Install Link 1](https://fishjar.github.io/kiss-translator/kiss-translator-ios-safari.user.js)、[Install Link 2](https://kiss-translator.rayjar.com/kiss-translator.user-ios-safari.js)
|
||||
|
||||
## Guide
|
||||
## Development Guidelines
|
||||
|
||||
```sh
|
||||
git clone https://github.com/fishjar/kiss-translator.git
|
||||
|
||||
66
README.md
66
README.md
@@ -16,7 +16,34 @@
|
||||
|
||||
## 特点
|
||||
|
||||
- 保持简约
|
||||
- [x] 保持简约
|
||||
- [x] 开放源代码
|
||||
- [x] 适配常见浏览器
|
||||
- [x] Chrome/Edge/Firefox/Kiwi
|
||||
- [ ] Safari
|
||||
- [x] 支持多种翻译服务
|
||||
- [x] Google/Microsoft/DeepL/OpenAI
|
||||
- [x] 自定义翻译接口
|
||||
- [x] 数据同步功能
|
||||
- [x] 自定义规则 + 规则订阅
|
||||
- [x] 自定义样式
|
||||
- [x] 自定义快捷键
|
||||
- `Alt+Q` 开启翻译
|
||||
- `Alt+C` 切换样式
|
||||
- `Alt+K` 打开弹窗
|
||||
- `Alt+O` 打开设置
|
||||
|
||||
## 下载
|
||||
|
||||
- [x] 浏览器扩展
|
||||
- [x] Chrome [安装地址](https://chrome.google.com/webstore/detail/kiss-translator/bdiifdefkgmcblbcghdlonllpjhhjgof?hl=zh-CN)
|
||||
- [x] Edge [安装地址](https://microsoftedge.microsoft.com/addons/detail/%E7%AE%80%E7%BA%A6%E7%BF%BB%E8%AF%91/jemckldkclkinpjighnoilpbldbdmmlh?hl=zh-CN)
|
||||
- [x] Firefox [安装地址](https://addons.mozilla.org/zh-CN/firefox/addon/kiss-translator/)
|
||||
- [ ] Safari
|
||||
- [x] 油猴脚本
|
||||
- [x] Chrome/Edge/Firefox ([Tampermonkey](https://www.tampermonkey.net/)/[Violentmonkey](https://violentmonkey.github.io/)) [安装链接 1](https://fishjar.github.io/kiss-translator/kiss-translator.user.js)、 [安装链接 2](https://kiss-translator.rayjar.com/kiss-translator.user.js)
|
||||
- Greasy Fork [安装地址](https://greasyfork.org/zh-CN/scripts/472840-kiss-translator)
|
||||
- [x] iOS Safari ([Userscripts Safari](https://github.com/quoid/userscripts)) [安装链接 1](https://fishjar.github.io/kiss-translator/kiss-translator-ios-safari.user.js)、 [安装链接 2](https://kiss-translator.rayjar.com/kiss-translator.user-ios-safari.js)
|
||||
|
||||
## 关联项目
|
||||
|
||||
@@ -38,42 +65,7 @@
|
||||
- 支持英文单词、句子、汉字的查询。
|
||||
- 支持历史记录、单词收藏。
|
||||
|
||||
## 简要说明
|
||||
|
||||
### 支持快捷键
|
||||
|
||||
- `Alt+Q` 开启翻译
|
||||
- `Alt+C` 切换样式
|
||||
- `Alt+K` 打开菜单
|
||||
|
||||
## 进度
|
||||
|
||||
- [x] 提供试用安装包
|
||||
- [x] 适配浏览器
|
||||
- [x] Chrome
|
||||
- [x] Edge
|
||||
- [x] Firefox
|
||||
- [ ] Safari
|
||||
- [x] Kiwi
|
||||
- [x] 支持翻译服务
|
||||
- [x] Google
|
||||
- [x] Microsoft
|
||||
- [x] DeepL
|
||||
- [x] OpenAI
|
||||
- [x] 上架应用市场
|
||||
- [x] Chrome [安装地址](https://chrome.google.com/webstore/detail/kiss-translator/bdiifdefkgmcblbcghdlonllpjhhjgof?hl=zh-CN)
|
||||
- [x] Edge [安装地址](https://microsoftedge.microsoft.com/addons/detail/%E7%AE%80%E7%BA%A6%E7%BF%BB%E8%AF%91/jemckldkclkinpjighnoilpbldbdmmlh?hl=zh-CN)
|
||||
- [x] Firefox [安装地址](https://addons.mozilla.org/zh-CN/firefox/addon/kiss-translator/)
|
||||
- [ ] Safari
|
||||
- [x] Greasy Fork [安装地址](https://greasyfork.org/zh-CN/scripts/472840-kiss-translator)
|
||||
- [x] 开放源代码
|
||||
- [x] 数据同步功能
|
||||
- [x] 油猴脚本 ([设置页面 1](https://fishjar.github.io/kiss-translator/options.html)、[设置页面 2](https://kiss-translator.rayjar.com/options))
|
||||
- [x] [Tampermonkey](https://www.tampermonkey.net/) (Chrome/Edge/Firefox) [安装链接 1](https://fishjar.github.io/kiss-translator/kiss-translator.user.js)、[安装链接 2](https://kiss-translator.rayjar.com/kiss-translator.user.js)
|
||||
- [x] [Violentmonkey](https://violentmonkey.github.io/) (Chrome/Edge/Firefox) [安装链接 1](https://fishjar.github.io/kiss-translator/kiss-translator.user.js)、[安装链接 2](https://kiss-translator.rayjar.com/kiss-translator.user.js)
|
||||
- [x] [Userscripts Safari](https://github.com/quoid/userscripts) (iOS Safari) [安装链接 1](https://fishjar.github.io/kiss-translator/kiss-translator-ios-safari.user.js)、[安装链接 2](https://kiss-translator.rayjar.com/kiss-translator.user-ios-safari.js)
|
||||
|
||||
## 指引
|
||||
## 开发指引
|
||||
|
||||
```sh
|
||||
git clone https://github.com/fishjar/kiss-translator.git
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kiss-translator",
|
||||
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
|
||||
"version": "1.6.8",
|
||||
"version": "1.7.0",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -10,5 +10,8 @@
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "Toggle Style"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "Open Options"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
"message": "一个简约的双语网页翻译扩展 & 油猴脚本"
|
||||
},
|
||||
"toggle_translate": {
|
||||
"message": "切换翻译"
|
||||
"message": "开启翻译"
|
||||
},
|
||||
"toggle_style": {
|
||||
"message": "切换样式"
|
||||
},
|
||||
"open_options": {
|
||||
"message": "打开设置"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "1.6.8",
|
||||
"version": "1.7.0",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
@@ -33,6 +33,12 @@
|
||||
"default": "Alt+C"
|
||||
},
|
||||
"description": "__MSG_toggle_style__"
|
||||
},
|
||||
"openOptions": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+O"
|
||||
},
|
||||
"description": "__MSG_open_options__"
|
||||
}
|
||||
},
|
||||
"permissions": ["<all_urls>", "storage"],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_app_name__",
|
||||
"description": "__MSG_app_description__",
|
||||
"version": "1.6.8",
|
||||
"version": "1.7.0",
|
||||
"default_locale": "en",
|
||||
"author": "Gabe<yugang2002@gmail.com>",
|
||||
"homepage_url": "https://github.com/fishjar/kiss-translator",
|
||||
@@ -34,6 +34,12 @@
|
||||
"default": "Alt+C"
|
||||
},
|
||||
"description": "__MSG_toggle_style__"
|
||||
},
|
||||
"openOptions": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+O"
|
||||
},
|
||||
"description": "__MSG_open_options__"
|
||||
}
|
||||
},
|
||||
"permissions": ["storage"],
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
MSG_TRANS_TOGGLE_STYLE,
|
||||
CMD_TOGGLE_TRANSLATE,
|
||||
CMD_TOGGLE_STYLE,
|
||||
CMD_OPEN_OPTIONS,
|
||||
} from "./config";
|
||||
import { getSettingWithDefault, tryInitDefaultData } from "./libs/storage";
|
||||
import { trySyncSettingAndRules } from "./libs/sync";
|
||||
@@ -85,6 +86,9 @@ browser.commands.onCommand.addListener((command) => {
|
||||
case CMD_TOGGLE_STYLE:
|
||||
sendTabMsg(MSG_TRANS_TOGGLE_STYLE);
|
||||
break;
|
||||
case CMD_OPEN_OPTIONS:
|
||||
browser.runtime.openOptionsPage();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
@@ -43,6 +43,7 @@ const customApiLangs = `["en", "English - English"],
|
||||
`;
|
||||
|
||||
const customApiHelpZH = `/// 自定义翻译源接口说明
|
||||
|
||||
// 请求(Request)数据将按下面规范发送
|
||||
{
|
||||
url: {{YOUR_URL}},
|
||||
@@ -70,6 +71,7 @@ ${customApiLangs}
|
||||
`;
|
||||
|
||||
const customApiHelpEN = `/// Custom translation source interface description
|
||||
|
||||
// Request data will be sent according to the following specifications
|
||||
{
|
||||
url: {{YOUR_URL}},
|
||||
@@ -271,15 +273,15 @@ export const I18N = {
|
||||
},
|
||||
personal_rules: {
|
||||
zh: `个人规则`,
|
||||
en: `Personal Rules`,
|
||||
en: `Rules`,
|
||||
},
|
||||
subscribe_rules: {
|
||||
zh: `订阅规则`,
|
||||
en: `Subscribe Rules`,
|
||||
en: `Subscribe`,
|
||||
},
|
||||
overwrite_subscribe_rules: {
|
||||
zh: `覆写订阅规则`,
|
||||
en: `Overwrite Subscribe Rules`,
|
||||
en: `Overwrite`,
|
||||
},
|
||||
subscribe_url: {
|
||||
zh: `订阅地址`,
|
||||
@@ -342,8 +344,8 @@ export const I18N = {
|
||||
en: `Follow the syntax of "CSS"`,
|
||||
},
|
||||
setting: {
|
||||
zh: `设置`,
|
||||
en: `Setting`,
|
||||
zh: `设置 (Alt+O)`,
|
||||
en: `Setting (Alt+O)`,
|
||||
},
|
||||
pattern: {
|
||||
zh: `匹配网址`,
|
||||
@@ -521,4 +523,28 @@ export const I18N = {
|
||||
zh: `"打开弹窗"快捷键`,
|
||||
en: `"Open Popup" Shortcut`,
|
||||
},
|
||||
open_setting_shortcut: {
|
||||
zh: `"打开设置"快捷键`,
|
||||
en: `"Open Setting" Shortcut`,
|
||||
},
|
||||
hide_fab_button: {
|
||||
zh: `隐藏悬浮按钮`,
|
||||
en: `Hide Fab Button`,
|
||||
},
|
||||
show: {
|
||||
zh: `显示`,
|
||||
en: `Show`,
|
||||
},
|
||||
hide: {
|
||||
zh: `隐藏`,
|
||||
en: `Hide`,
|
||||
},
|
||||
save_rule: {
|
||||
zh: `保存规则`,
|
||||
en: `Save Rule`,
|
||||
},
|
||||
global_rule: {
|
||||
zh: `全局规则`,
|
||||
en: `Global Rule`,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ export const STOKEY_WEBFIXCACHE_PREFIX = `${APP_NAME}_webfixcache_`;
|
||||
|
||||
export const CMD_TOGGLE_TRANSLATE = "toggleTranslate";
|
||||
export const CMD_TOGGLE_STYLE = "toggleStyle";
|
||||
export const CMD_OPEN_OPTIONS = "openOptions";
|
||||
|
||||
export const CLIENT_WEB = "web";
|
||||
export const CLIENT_CHROME = "chrome";
|
||||
@@ -243,10 +244,12 @@ export const DEFAULT_TRANS_APIS = {
|
||||
export const OPT_SHORTCUT_TRANSLATE = "toggleTranslate";
|
||||
export const OPT_SHORTCUT_STYLE = "toggleStyle";
|
||||
export const OPT_SHORTCUT_POPUP = "togglePopup";
|
||||
export const OPT_SHORTCUT_SETTING = "openSetting";
|
||||
export const DEFAULT_SHORTCUTS = {
|
||||
[OPT_SHORTCUT_TRANSLATE]: ["Alt", "q"],
|
||||
[OPT_SHORTCUT_STYLE]: ["Alt", "c"],
|
||||
[OPT_SHORTCUT_POPUP]: ["Alt", "k"],
|
||||
[OPT_SHORTCUT_SETTING]: ["Alt", "o"],
|
||||
};
|
||||
|
||||
export const TRANS_MIN_LENGTH = 5; // 最短翻译长度
|
||||
@@ -269,6 +272,7 @@ export const DEFAULT_SETTING = {
|
||||
transApis: DEFAULT_TRANS_APIS, // 翻译接口
|
||||
mouseKey: OPT_MOUSEKEY_DISABLE, // 鼠标悬停翻译
|
||||
shortcuts: DEFAULT_SHORTCUTS, // 快捷键
|
||||
hideFab: false, // 是否隐藏按钮
|
||||
};
|
||||
|
||||
export const DEFAULT_RULES = [GLOBLA_RULE];
|
||||
@@ -281,4 +285,5 @@ export const DEFAULT_SYNC = {
|
||||
rulesUpdateAt: 0,
|
||||
rulesSyncAt: 0,
|
||||
subRulesSyncAt: 0, // 订阅规则同步时间
|
||||
dataCaches: {}, // 缓存同步时间
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { STOKEY_RULES, DEFAULT_RULES } from "../config";
|
||||
import { useStorage } from "./Storage";
|
||||
import { trySyncRules } from "../libs/sync";
|
||||
import { useSync } from "./Sync";
|
||||
import { checkRules } from "../libs/rules";
|
||||
import { useCallback } from "react";
|
||||
|
||||
@@ -11,19 +10,13 @@ import { useCallback } from "react";
|
||||
*/
|
||||
export function useRules() {
|
||||
const { data: list, save } = useStorage(STOKEY_RULES, DEFAULT_RULES);
|
||||
const {
|
||||
sync: { rulesUpdateAt },
|
||||
updateSync,
|
||||
} = useSync();
|
||||
|
||||
const updateRules = useCallback(
|
||||
async (rules) => {
|
||||
const updateAt = rulesUpdateAt ? Date.now() : 0;
|
||||
await save(rules);
|
||||
await updateSync({ rulesUpdateAt: updateAt });
|
||||
trySyncRules();
|
||||
trySyncRules(false, true);
|
||||
},
|
||||
[rulesUpdateAt, save, updateSync]
|
||||
[save]
|
||||
);
|
||||
|
||||
const add = useCallback(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { STOKEY_SETTING, DEFAULT_SETTING } from "../config";
|
||||
import { useStorage } from "./Storage";
|
||||
import { useSync } from "./Sync";
|
||||
import { trySyncSetting } from "../libs/sync";
|
||||
import { createContext, useCallback, useContext, useMemo } from "react";
|
||||
import { debounce } from "../libs/utils";
|
||||
@@ -16,27 +15,21 @@ export function SettingProvider({ children }) {
|
||||
STOKEY_SETTING,
|
||||
DEFAULT_SETTING
|
||||
);
|
||||
const {
|
||||
sync: { settingUpdateAt },
|
||||
updateSync,
|
||||
} = useSync();
|
||||
|
||||
const syncSetting = useMemo(
|
||||
() =>
|
||||
debounce(() => {
|
||||
trySyncSetting();
|
||||
trySyncSetting(false, true);
|
||||
}, [2000]),
|
||||
[]
|
||||
);
|
||||
|
||||
const updateSetting = useCallback(
|
||||
async (obj) => {
|
||||
const updateAt = settingUpdateAt ? Date.now() : 0;
|
||||
await update(obj);
|
||||
await updateSync({ settingUpdateAt: updateAt });
|
||||
syncSetting();
|
||||
},
|
||||
[settingUpdateAt, update, updateSync, syncSetting]
|
||||
[update, syncSetting]
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useSetting } from "./Setting";
|
||||
export function useShortcut(action) {
|
||||
const { setting, updateSetting } = useSetting();
|
||||
const shortcuts = setting?.shortcuts || DEFAULT_SHORTCUTS;
|
||||
const shortcut = shortcuts[action] || [];
|
||||
|
||||
const setShortcut = useCallback(
|
||||
async (val) => {
|
||||
@@ -14,5 +15,5 @@ export function useShortcut(action) {
|
||||
[action, shortcuts, updateSetting]
|
||||
);
|
||||
|
||||
return { shortcut: shortcuts[action] || [], setShortcut };
|
||||
return { shortcut, setShortcut };
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export function useStorage(key, defaultVal = null) {
|
||||
if (val) {
|
||||
setData(val);
|
||||
} else if (defaultVal) {
|
||||
setData(defaultVal);
|
||||
await storage.setObj(key, defaultVal);
|
||||
}
|
||||
}, [key, defaultVal]);
|
||||
|
||||
@@ -48,7 +48,7 @@ export function useSubRules() {
|
||||
const addSub = useCallback(
|
||||
async (url) => {
|
||||
const subrulesList = [...list];
|
||||
subrulesList.push({ url, selected: false, syncAt: Date.now() });
|
||||
subrulesList.push({ url, selected: false });
|
||||
await updateSetting({ subrulesList });
|
||||
},
|
||||
[list, updateSetting]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useCallback } from "react";
|
||||
import { STOKEY_SYNC, DEFAULT_SYNC } from "../config";
|
||||
import { useStorage } from "./Storage";
|
||||
|
||||
@@ -6,6 +7,40 @@ import { useStorage } from "./Storage";
|
||||
* @returns
|
||||
*/
|
||||
export function useSync() {
|
||||
const { data, update } = useStorage(STOKEY_SYNC, DEFAULT_SYNC);
|
||||
return { sync: data, updateSync: update };
|
||||
const { data, update, reload } = useStorage(STOKEY_SYNC, DEFAULT_SYNC);
|
||||
return { sync: data, updateSync: update, reloadSync: reload };
|
||||
}
|
||||
|
||||
/**
|
||||
* caches sync hook
|
||||
* @param {*} url
|
||||
* @returns
|
||||
*/
|
||||
export function useSyncCaches() {
|
||||
const { sync, updateSync, reloadSync } = useSync();
|
||||
|
||||
const updateDataCache = useCallback(
|
||||
async (url) => {
|
||||
const dataCaches = sync.dataCaches || {};
|
||||
dataCaches[url] = Date.now();
|
||||
await updateSync({ dataCaches });
|
||||
},
|
||||
[sync, updateSync]
|
||||
);
|
||||
|
||||
const deleteDataCache = useCallback(
|
||||
async (url) => {
|
||||
const dataCaches = sync.dataCaches || {};
|
||||
delete dataCaches[url];
|
||||
await updateSync({ dataCaches });
|
||||
},
|
||||
[sync, updateSync]
|
||||
);
|
||||
|
||||
return {
|
||||
dataCaches: sync.dataCaches || {},
|
||||
updateDataCache,
|
||||
deleteDataCache,
|
||||
reloadSync,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,3 +19,12 @@ export const sendTabMsg = async (action, args) => {
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
return browser.tabs.sendMessage(tabs[0].id, { action, args });
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前tab信息
|
||||
* @returns
|
||||
*/
|
||||
export const getTabInfo = async () => {
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
return tabs[0];
|
||||
};
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
DEFAULT_OW_RULE,
|
||||
} from "../config";
|
||||
import { loadOrFetchSubRules } from "./subRules";
|
||||
import { getRulesWithDefault, setRules } from "./storage";
|
||||
import { trySyncRules } from "./sync";
|
||||
|
||||
/**
|
||||
* 根据href匹配规则
|
||||
@@ -47,9 +49,8 @@ export const matchRule = async (
|
||||
mixRule[key] = val;
|
||||
});
|
||||
|
||||
const subRules = (await loadOrFetchSubRules(selectedSub.url)).map(
|
||||
(item) => ({ ...item, ...mixRule })
|
||||
);
|
||||
let subRules = await loadOrFetchSubRules(selectedSub.url);
|
||||
subRules = subRules.map((item) => ({ ...item, ...mixRule }));
|
||||
rules.splice(-1, 0, ...subRules);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -134,3 +135,19 @@ export const checkRules = (rules) => {
|
||||
|
||||
return rules;
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存或更新rule
|
||||
* @param {*} newRule
|
||||
*/
|
||||
export const saveRule = async (newRule) => {
|
||||
const rules = await getRulesWithDefault();
|
||||
const rule = rules.find((item) => isMatch(newRule.pattern, item.pattern));
|
||||
if (rule && rule.pattern !== GLOBAL_KEY) {
|
||||
Object.assign(rule, { ...newRule, pattern: rule.pattern });
|
||||
} else {
|
||||
rules.unshift(newRule);
|
||||
}
|
||||
await setRules(rules);
|
||||
trySyncRules(false, true);
|
||||
};
|
||||
|
||||
@@ -39,6 +39,10 @@ export const shortcutListener = (fn, target = document, timeout = 3000) => {
|
||||
target.addEventListener("keydown", handleKeydown);
|
||||
target.addEventListener("keyup", handleKeyup);
|
||||
return () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
target.removeEventListener("keydown", handleKeydown);
|
||||
target.removeEventListener("keyup", handleKeyup);
|
||||
};
|
||||
@@ -51,9 +55,12 @@ export const shortcutListener = (fn, target = document, timeout = 3000) => {
|
||||
* @param {*} target
|
||||
* @returns
|
||||
*/
|
||||
export const shortcutRegister = (targetKeys, fn, target = document) => {
|
||||
export const shortcutRegister = (targetKeys = [], fn, target = document) => {
|
||||
return shortcutListener((curkeys) => {
|
||||
if (isSameSet(new Set(targetKeys), new Set(curkeys))) {
|
||||
if (
|
||||
targetKeys.length > 0 &&
|
||||
isSameSet(new Set(targetKeys), new Set(curkeys))
|
||||
) {
|
||||
fn();
|
||||
}
|
||||
}, target);
|
||||
|
||||
@@ -10,6 +10,16 @@ import { apiFetch } from "../apis";
|
||||
import { checkRules } from "./rules";
|
||||
import { isAllchar } from "./utils";
|
||||
|
||||
/**
|
||||
* 更新缓存同步时间
|
||||
* @param {*} url
|
||||
*/
|
||||
const updateSyncDataCache = async (url) => {
|
||||
const { dataCaches = {} } = await getSyncWithDefault();
|
||||
dataCaches[url] = Date.now();
|
||||
await updateSync({ dataCaches });
|
||||
};
|
||||
|
||||
/**
|
||||
* 同步订阅规则
|
||||
* @param {*} url
|
||||
@@ -35,6 +45,7 @@ export const syncAllSubRules = async (subrulesList, isBg = false) => {
|
||||
for (let subrules of subrulesList) {
|
||||
try {
|
||||
await syncSubRules(subrules.url, isBg);
|
||||
await updateSyncDataCache(subrules.url);
|
||||
} catch (err) {
|
||||
console.log(`[sync subrule error]: ${subrules.url}`, err);
|
||||
}
|
||||
@@ -70,9 +81,10 @@ export const trySyncAllSubRules = async ({ subrulesList }, isBg = false) => {
|
||||
* @returns
|
||||
*/
|
||||
export const loadOrFetchSubRules = async (url) => {
|
||||
const rules = await getSubRules(url);
|
||||
if (rules?.length) {
|
||||
return rules;
|
||||
let rules = await getSubRules(url);
|
||||
if (!rules || rules.length === 0) {
|
||||
rules = await syncSubRules(url);
|
||||
await updateSyncDataCache(url);
|
||||
}
|
||||
return syncSubRules(url);
|
||||
return rules || [];
|
||||
};
|
||||
|
||||
@@ -19,12 +19,21 @@ import { sha256 } from "./utils";
|
||||
* 同步设置
|
||||
* @returns
|
||||
*/
|
||||
const syncSetting = async (isBg = false) => {
|
||||
const { syncUrl, syncKey, settingUpdateAt } = await getSyncWithDefault();
|
||||
const syncSetting = async (isBg = false, isForce = false) => {
|
||||
let {
|
||||
syncUrl,
|
||||
syncKey,
|
||||
settingUpdateAt = 0,
|
||||
settingSyncAt = 0,
|
||||
} = await getSyncWithDefault();
|
||||
if (!syncUrl || !syncKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isForce) {
|
||||
settingUpdateAt = Date.now();
|
||||
}
|
||||
|
||||
const setting = await getSettingWithDefault();
|
||||
const res = await apiSyncData(
|
||||
syncUrl,
|
||||
@@ -32,26 +41,25 @@ const syncSetting = async (isBg = false) => {
|
||||
{
|
||||
key: KV_SETTING_KEY,
|
||||
value: setting,
|
||||
updateAt: settingUpdateAt,
|
||||
updateAt: settingSyncAt === 0 ? 0 : settingUpdateAt,
|
||||
},
|
||||
isBg
|
||||
);
|
||||
|
||||
if (res && res.updateAt > settingUpdateAt) {
|
||||
await updateSync({
|
||||
settingUpdateAt: res.updateAt,
|
||||
settingSyncAt: res.updateAt,
|
||||
});
|
||||
if (res.updateAt > settingUpdateAt) {
|
||||
await setSetting(res.value);
|
||||
return res.value;
|
||||
} else {
|
||||
await updateSync({ settingSyncAt: res.updateAt });
|
||||
}
|
||||
await updateSync({
|
||||
settingUpdateAt: res.updateAt,
|
||||
settingSyncAt: Date.now(),
|
||||
});
|
||||
|
||||
return res.value;
|
||||
};
|
||||
|
||||
export const trySyncSetting = async (isBg = false) => {
|
||||
export const trySyncSetting = async (isBg = false, isForce = false) => {
|
||||
try {
|
||||
return await syncSetting(isBg);
|
||||
return await syncSetting(isBg, isForce);
|
||||
} catch (err) {
|
||||
console.log("[sync setting]", err);
|
||||
}
|
||||
@@ -61,12 +69,21 @@ export const trySyncSetting = async (isBg = false) => {
|
||||
* 同步规则
|
||||
* @returns
|
||||
*/
|
||||
const syncRules = async (isBg = false) => {
|
||||
const { syncUrl, syncKey, rulesUpdateAt } = await getSyncWithDefault();
|
||||
const syncRules = async (isBg = false, isForce = false) => {
|
||||
let {
|
||||
syncUrl,
|
||||
syncKey,
|
||||
rulesUpdateAt = 0,
|
||||
rulesSyncAt = 0,
|
||||
} = await getSyncWithDefault();
|
||||
if (!syncUrl || !syncKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isForce) {
|
||||
rulesUpdateAt = Date.now();
|
||||
}
|
||||
|
||||
const rules = await getRulesWithDefault();
|
||||
const res = await apiSyncData(
|
||||
syncUrl,
|
||||
@@ -74,26 +91,25 @@ const syncRules = async (isBg = false) => {
|
||||
{
|
||||
key: KV_RULES_KEY,
|
||||
value: rules,
|
||||
updateAt: rulesUpdateAt,
|
||||
updateAt: rulesSyncAt === 0 ? 0 : rulesUpdateAt,
|
||||
},
|
||||
isBg
|
||||
);
|
||||
|
||||
if (res && res.updateAt > rulesUpdateAt) {
|
||||
await updateSync({
|
||||
rulesUpdateAt: res.updateAt,
|
||||
rulesSyncAt: res.updateAt,
|
||||
});
|
||||
if (res.updateAt > rulesUpdateAt) {
|
||||
await setRules(res.value);
|
||||
return res.value;
|
||||
} else {
|
||||
await updateSync({ rulesSyncAt: res.updateAt });
|
||||
}
|
||||
await updateSync({
|
||||
rulesUpdateAt: res.updateAt,
|
||||
rulesSyncAt: Date.now(),
|
||||
});
|
||||
|
||||
return res.value;
|
||||
};
|
||||
|
||||
export const trySyncRules = async (isBg = false) => {
|
||||
export const trySyncRules = async (isBg = false, isForce = false) => {
|
||||
try {
|
||||
return await syncRules(isBg);
|
||||
return await syncRules(isBg, isForce);
|
||||
} catch (err) {
|
||||
console.log("[sync user rules]", err);
|
||||
}
|
||||
|
||||
@@ -5,27 +5,34 @@ import { apiFetch } from "../apis";
|
||||
/**
|
||||
* 修复程序类型
|
||||
*/
|
||||
const WEBFIX_BR = "br";
|
||||
const FIXER_BR = "br";
|
||||
const FIXER_FONTSIZE = "fontSize";
|
||||
|
||||
/**
|
||||
* 需要修复的站点列表
|
||||
* - pattern 匹配网址
|
||||
* - selector 需要修复的选择器
|
||||
* - rootSlector 需要监听的选择器,可留空
|
||||
* - rootSelector 需要监听的选择器,可留空
|
||||
* - fixer 修复函数,可针对不同网址,选用不同修复函数
|
||||
*/
|
||||
const DEFAULT_SITES = [
|
||||
{
|
||||
pattern: "www.phoronix.com",
|
||||
selector: ".content",
|
||||
rootSlector: "",
|
||||
fixer: WEBFIX_BR,
|
||||
rootSelector: "",
|
||||
fixer: FIXER_BR,
|
||||
},
|
||||
{
|
||||
pattern: "t.me/s/*",
|
||||
pattern: "t.me/s/",
|
||||
selector: ".tgme_widget_message_text",
|
||||
rootSlector: ".tgme_channel_history",
|
||||
fixer: WEBFIX_BR,
|
||||
rootSelector: ".tgme_channel_history",
|
||||
fixer: FIXER_BR,
|
||||
},
|
||||
{
|
||||
pattern: "baidu.com",
|
||||
selector: "html",
|
||||
rootSelector: "",
|
||||
fixer: FIXER_FONTSIZE,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -87,20 +94,29 @@ function brFixer(node) {
|
||||
node.innerHTML = html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复字体大小问题,如 baidu.com
|
||||
* @param {*} node
|
||||
*/
|
||||
function fontSizeFixer(node) {
|
||||
node.style.cssText += "font-size:1em;";
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复程序映射
|
||||
*/
|
||||
const fixerMap = {
|
||||
[WEBFIX_BR]: brFixer,
|
||||
[FIXER_BR]: brFixer,
|
||||
[FIXER_FONTSIZE]: fontSizeFixer,
|
||||
};
|
||||
|
||||
/**
|
||||
* 查找、监听节点,并执行修复函数
|
||||
* @param {*} selector
|
||||
* @param {*} fixer
|
||||
* @param {*} rootSlector
|
||||
* @param {*} rootSelector
|
||||
*/
|
||||
function run(selector, fixer, rootSlector) {
|
||||
function run(selector, fixer, rootSelector) {
|
||||
var mutaObserver = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
mutation.addedNodes.forEach(function (addNode) {
|
||||
@@ -110,8 +126,8 @@ function run(selector, fixer, rootSlector) {
|
||||
});
|
||||
|
||||
var rootNodes = [document];
|
||||
if (rootSlector) {
|
||||
rootNodes = document.querySelectorAll(rootSlector);
|
||||
if (rootSelector) {
|
||||
rootNodes = document.querySelectorAll(rootSelector);
|
||||
}
|
||||
|
||||
rootNodes.forEach(function (rootNode) {
|
||||
@@ -165,7 +181,7 @@ export async function webfix(href, { injectWebfix }) {
|
||||
var site = sites[i];
|
||||
if (isMatch(href, site.pattern)) {
|
||||
if (fixerMap[site.fixer]) {
|
||||
run(site.selector, fixerMap[site.fixer], site.rootSlector);
|
||||
run(site.selector, fixerMap[site.fixer], site.rootSelector);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
OPT_SHORTCUT_TRANSLATE,
|
||||
OPT_SHORTCUT_STYLE,
|
||||
OPT_SHORTCUT_POPUP,
|
||||
OPT_SHORTCUT_SETTING,
|
||||
} from "../../config";
|
||||
import { shortcutRegister } from "../../libs/shortcut";
|
||||
|
||||
@@ -64,6 +65,9 @@ export default function Action({ translator, fab }) {
|
||||
shortcutRegister(shortcuts[OPT_SHORTCUT_POPUP], () => {
|
||||
setShowPopup((pre) => !pre);
|
||||
}),
|
||||
shortcutRegister(shortcuts[OPT_SHORTCUT_SETTING], () => {
|
||||
window.open(process.env.REACT_APP_OPTIONSPAGE, "_blank");
|
||||
}),
|
||||
];
|
||||
|
||||
return () => {
|
||||
@@ -80,7 +84,7 @@ export default function Action({ translator, fab }) {
|
||||
try {
|
||||
menuCommandIds.push(
|
||||
GM.registerMenuCommand(
|
||||
"Toggle Translate",
|
||||
"Toggle Translate (Alt+q)",
|
||||
(event) => {
|
||||
translator.toggle();
|
||||
setShowPopup(false);
|
||||
@@ -88,7 +92,7 @@ export default function Action({ translator, fab }) {
|
||||
"Q"
|
||||
),
|
||||
GM.registerMenuCommand(
|
||||
"Toggle Style",
|
||||
"Toggle Style (Alt+c)",
|
||||
(event) => {
|
||||
translator.toggleStyle();
|
||||
setShowPopup(false);
|
||||
@@ -96,11 +100,18 @@ export default function Action({ translator, fab }) {
|
||||
"C"
|
||||
),
|
||||
GM.registerMenuCommand(
|
||||
"Open Menu",
|
||||
"Open Menu (Alt+k)",
|
||||
(event) => {
|
||||
setShowPopup((pre) => !pre);
|
||||
},
|
||||
"K"
|
||||
),
|
||||
GM.registerMenuCommand(
|
||||
"Open Setting (Alt+o)",
|
||||
(event) => {
|
||||
window.open(process.env.REACT_APP_OPTIONSPAGE, "_blank");
|
||||
},
|
||||
"O"
|
||||
)
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -183,7 +194,7 @@ export default function Action({ translator, fab }) {
|
||||
key="fab"
|
||||
snapEdge
|
||||
{...fabProps}
|
||||
show={!showPopup}
|
||||
show={translator.setting.hideFab ? false : !showPopup}
|
||||
onStart={handleStart}
|
||||
onMove={handleMove}
|
||||
handler={
|
||||
|
||||
@@ -48,6 +48,7 @@ import { delSubRules, getSyncWithDefault } from "../../libs/storage";
|
||||
import OwSubRule from "./OwSubRule";
|
||||
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
||||
import HelpButton from "./HelpButton";
|
||||
import { useSyncCaches } from "../../hooks/Sync";
|
||||
|
||||
function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
const initFormValues = rule || {
|
||||
@@ -362,6 +363,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
|
||||
}
|
||||
|
||||
function RuleAccordion({ rule, rules }) {
|
||||
const i18n = useI18n();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const handleChange = (e) => {
|
||||
@@ -376,7 +378,9 @@ function RuleAccordion({ rule, rules }) {
|
||||
opacity: rules ? 1 : 0.5,
|
||||
}}
|
||||
>
|
||||
{rule.pattern}
|
||||
{rule.pattern === GLOBAL_KEY
|
||||
? `[${i18n("global_rule")}] ${rule.pattern}`
|
||||
: rule.pattern}
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
@@ -621,8 +625,9 @@ function SubRulesItem({
|
||||
syncAt,
|
||||
selectedUrl,
|
||||
delSub,
|
||||
updateSub,
|
||||
setSelectedRules,
|
||||
updateDataCache,
|
||||
deleteDataCache,
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -630,6 +635,7 @@ function SubRulesItem({
|
||||
try {
|
||||
await delSub(url);
|
||||
await delSubRules(url);
|
||||
await deleteDataCache(url);
|
||||
} catch (err) {
|
||||
console.log("[del subrules]", err);
|
||||
}
|
||||
@@ -642,7 +648,7 @@ function SubRulesItem({
|
||||
if (rules.length > 0 && url === selectedUrl) {
|
||||
setSelectedRules(rules);
|
||||
}
|
||||
await updateSub(url, { syncAt: Date.now() });
|
||||
await updateDataCache(url);
|
||||
} catch (err) {
|
||||
console.log("[sync sub rules]", err);
|
||||
} finally {
|
||||
@@ -655,7 +661,7 @@ function SubRulesItem({
|
||||
<FormControlLabel value={url} control={<Radio />} label={url} />
|
||||
|
||||
{syncAt && (
|
||||
<span style={{ marginLeft: "0.5em", opacity: 0.6 }}>
|
||||
<span style={{ marginLeft: "0.5em", opacity: 0.5 }}>
|
||||
[{new Date(syncAt).toLocaleString()}]
|
||||
</span>
|
||||
)}
|
||||
@@ -677,7 +683,7 @@ function SubRulesItem({
|
||||
);
|
||||
}
|
||||
|
||||
function SubRulesEdit({ subList, addSub }) {
|
||||
function SubRulesEdit({ subList, addSub, updateDataCache }) {
|
||||
const i18n = useI18n();
|
||||
const [inputText, setInputText] = useState("");
|
||||
const [inputError, setInputError] = useState("");
|
||||
@@ -712,6 +718,7 @@ function SubRulesEdit({ subList, addSub }) {
|
||||
throw new Error("empty rules");
|
||||
}
|
||||
await addSub(url);
|
||||
await updateDataCache(url);
|
||||
setShowInput(false);
|
||||
setInputText("");
|
||||
} catch (err) {
|
||||
@@ -784,7 +791,6 @@ function SubRules({ subRules }) {
|
||||
const {
|
||||
subList,
|
||||
selectSub,
|
||||
updateSub,
|
||||
addSub,
|
||||
delSub,
|
||||
selectedUrl,
|
||||
@@ -792,27 +798,38 @@ function SubRules({ subRules }) {
|
||||
setSelectedRules,
|
||||
loading,
|
||||
} = subRules;
|
||||
const { dataCaches, updateDataCache, deleteDataCache, reloadSync } =
|
||||
useSyncCaches();
|
||||
|
||||
const handleSelect = (e) => {
|
||||
const url = e.target.value;
|
||||
selectSub(url);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reloadSync();
|
||||
}, [selectedRules, reloadSync]);
|
||||
|
||||
return (
|
||||
<Stack spacing={3}>
|
||||
<SubRulesEdit subList={subList} addSub={addSub} />
|
||||
<SubRulesEdit
|
||||
subList={subList}
|
||||
addSub={addSub}
|
||||
updateDataCache={updateDataCache}
|
||||
/>
|
||||
|
||||
<RadioGroup value={selectedUrl} onChange={handleSelect}>
|
||||
{subList.map((item, index) => (
|
||||
<SubRulesItem
|
||||
key={item.url}
|
||||
url={item.url}
|
||||
syncAt={item.syncAt}
|
||||
syncAt={dataCaches[item.url]}
|
||||
index={index}
|
||||
selectedUrl={selectedUrl}
|
||||
delSub={delSub}
|
||||
updateSub={updateSub}
|
||||
setSelectedRules={setSelectedRules}
|
||||
updateDataCache={updateDataCache}
|
||||
deleteDataCache={deleteDataCache}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
OPT_SHORTCUT_TRANSLATE,
|
||||
OPT_SHORTCUT_STYLE,
|
||||
OPT_SHORTCUT_POPUP,
|
||||
OPT_SHORTCUT_SETTING,
|
||||
} from "../../config";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { useShortcut } from "../../hooks/Shortcut";
|
||||
@@ -33,29 +34,26 @@ function ShortcutItem({ action, label }) {
|
||||
const { shortcut, setShortcut } = useShortcut(action);
|
||||
const [disabled, setDisabled] = useState(true);
|
||||
const inputRef = useRef(null);
|
||||
const [formval, setFormval] = useState(shortcut);
|
||||
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setFormval(shortcut);
|
||||
return;
|
||||
}
|
||||
|
||||
inputRef.current.focus();
|
||||
setFormval([]);
|
||||
setShortcut([]);
|
||||
|
||||
const clearShortcut = shortcutListener((curkeys, allkeys) => {
|
||||
setFormval(allkeys);
|
||||
setShortcut(allkeys);
|
||||
if (curkeys.length === 0) {
|
||||
setDisabled(true);
|
||||
setShortcut(allkeys);
|
||||
}
|
||||
}, inputRef.current);
|
||||
|
||||
return () => {
|
||||
clearShortcut();
|
||||
};
|
||||
}, [disabled, setShortcut, shortcut]);
|
||||
}, [disabled, setShortcut]);
|
||||
|
||||
return (
|
||||
<Stack direction="row">
|
||||
@@ -63,7 +61,7 @@ function ShortcutItem({ action, label }) {
|
||||
size="small"
|
||||
label={label}
|
||||
name={label}
|
||||
value={formval.join(" + ")}
|
||||
value={shortcut.join(" + ")}
|
||||
fullWidth
|
||||
inputRef={inputRef}
|
||||
disabled={disabled}
|
||||
@@ -131,6 +129,7 @@ export default function Settings() {
|
||||
clearCache,
|
||||
newlineLength = TRANS_NEWLINE_LENGTH,
|
||||
mouseKey = OPT_MOUSEKEY_DISABLE,
|
||||
hideFab = false,
|
||||
} = setting;
|
||||
|
||||
return (
|
||||
@@ -232,26 +231,46 @@ export default function Settings() {
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
) : (
|
||||
<Grid container rowSpacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={12} md={4} lg={4}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_TRANSLATE}
|
||||
label={i18n("toggle_translate_shortcut")}
|
||||
/>
|
||||
<>
|
||||
<FormControl size="small">
|
||||
<InputLabel>{i18n("hide_fab_button")}</InputLabel>
|
||||
<Select
|
||||
name="hideFab"
|
||||
value={hideFab}
|
||||
label={i18n("hide_fab_button")}
|
||||
onChange={handleChange}
|
||||
>
|
||||
<MenuItem value={false}>{i18n("show")}</MenuItem>
|
||||
<MenuItem value={true}>{i18n("hide")}</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Grid container rowSpacing={2} columns={12}>
|
||||
<Grid item xs={12} sm={12} md={3} lg={3}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_TRANSLATE}
|
||||
label={i18n("toggle_translate_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={3} lg={3}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_STYLE}
|
||||
label={i18n("toggle_style_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={3} lg={3}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_POPUP}
|
||||
label={i18n("toggle_popup_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={3} lg={3}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_SETTING}
|
||||
label={i18n("open_setting_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={4} lg={4}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_STYLE}
|
||||
label={i18n("toggle_style_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={4} lg={4}>
|
||||
<ShortcutItem
|
||||
action={OPT_SHORTCUT_POPUP}
|
||||
label={i18n("toggle_popup_shortcut")}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useI18n } from "../../hooks/I18n";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Accordion from "@mui/material/Accordion";
|
||||
@@ -21,14 +21,14 @@ import HelpButton from "./HelpButton";
|
||||
import { URL_KISS_RULES_NEW_ISSUE } from "../../config";
|
||||
|
||||
function ApiFields({ site }) {
|
||||
const { selector, rootSlector, fixer } = site;
|
||||
const { selector, rootSelector, fixer } = site;
|
||||
return (
|
||||
<Stack spacing={3}>
|
||||
<TextField
|
||||
size="small"
|
||||
label={"rootSlector"}
|
||||
name="rootSlector"
|
||||
value={rootSlector || "document"}
|
||||
label={"rootSelector"}
|
||||
name="rootSelector"
|
||||
value={rootSelector || "document"}
|
||||
disabled
|
||||
/>
|
||||
<TextField
|
||||
@@ -75,11 +75,17 @@ export default function Webfix() {
|
||||
const alert = useAlert();
|
||||
const { setting, updateSetting } = useSetting();
|
||||
|
||||
const loadSites = useCallback(async () => {
|
||||
const sites = await loadOrFetchWebfix(process.env.REACT_APP_WEBFIXURL);
|
||||
setSites(sites);
|
||||
}, []);
|
||||
|
||||
const handleSyncTest = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
setLoading(true);
|
||||
await syncWebfix(process.env.REACT_APP_WEBFIXURL);
|
||||
await loadSites();
|
||||
alert.success(i18n("sync_success"));
|
||||
} catch (err) {
|
||||
console.log("[sync webfix]", err);
|
||||
@@ -93,15 +99,14 @@ export default function Webfix() {
|
||||
(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const sites = await loadOrFetchWebfix(process.env.REACT_APP_WEBFIXURL);
|
||||
setSites(sites);
|
||||
await loadSites();
|
||||
} catch (err) {
|
||||
console.log("[load webfix]", err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
}, [loadSites]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function Options() {
|
||||
// 检查版本是否一致
|
||||
if (version !== process.env.REACT_APP_VERSION) {
|
||||
setError(
|
||||
`The version of the script(v${version}) and this page(v${process.env.REACT_APP_VERSION}) are inconsistent.`
|
||||
`The version is inconsistent, please check whether the script(v${version}) is the latest version(v${process.env.REACT_APP_VERSION}). (版本不一致,请检查脚本(v${version})是否为最新版(v${process.env.REACT_APP_VERSION}))`
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export default function Options() {
|
||||
}
|
||||
|
||||
if (++i > 8) {
|
||||
setError("Time out.");
|
||||
setError("Time out. (连接超时)");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -78,26 +78,26 @@ export default function Options() {
|
||||
</Divider>
|
||||
<h2>
|
||||
Please confirm whether to install or enable KISS Translator
|
||||
GreaseMonkey script?
|
||||
GreaseMonkey script? (请检查是否安装或启用简约翻译油猴脚本)
|
||||
</h2>
|
||||
<Stack spacing={2}>
|
||||
<Link href={process.env.REACT_APP_USERSCRIPT_DOWNLOADURL}>
|
||||
Install Userscript 1
|
||||
Install Userscript for Tampermonkey/Violentmonkey 1 (油猴脚本 安装地址 1)
|
||||
</Link>
|
||||
<Link href={process.env.REACT_APP_USERSCRIPT_DOWNLOADURL2}>
|
||||
Install Userscript 2
|
||||
Install Userscript for Tampermonkey/Violentmonkey 2 (油猴脚本 安装地址 2)
|
||||
</Link>
|
||||
<Link href={process.env.REACT_APP_USERSCRIPT_IOS_DOWNLOADURL}>
|
||||
Install Userscript Safari 1
|
||||
Install Userscript for iOS Safari 1 (油猴脚本 iOS Safari专用 安装地址 1)
|
||||
</Link>
|
||||
<Link href={process.env.REACT_APP_USERSCRIPT_IOS_DOWNLOADURL2}>
|
||||
Install Userscript Safari 2
|
||||
Install Userscript for iOS Safari 2 (油猴脚本 iOS Safari专用 安装地址 2)
|
||||
</Link>
|
||||
<Link href={process.env.REACT_APP_OPTIONSPAGE}>
|
||||
Open Options Page 1
|
||||
Open Options Page 1 (打开设置页面 1)
|
||||
</Link>
|
||||
<Link href={process.env.REACT_APP_OPTIONSPAGE2}>
|
||||
Open Options Page 2
|
||||
Open Options Page 2 (打开设置页面 2)
|
||||
</Link>
|
||||
</Stack>
|
||||
</center>
|
||||
|
||||
@@ -5,7 +5,7 @@ import MenuItem from "@mui/material/MenuItem";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import Button from "@mui/material/Button";
|
||||
import { sendTabMsg } from "../../libs/msg";
|
||||
import { sendTabMsg, getTabInfo } from "../../libs/msg";
|
||||
import { browser } from "../../libs/browser";
|
||||
import { isExt } from "../../libs/client";
|
||||
import { useI18n } from "../../hooks/I18n";
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
CACHE_NAME,
|
||||
} from "../../config";
|
||||
import { sendIframeMsg } from "../../libs/iframe";
|
||||
import { saveRule } from "../../libs/rules";
|
||||
|
||||
export default function Popup({ setShowPopup, translator: tran }) {
|
||||
const i18n = useI18n();
|
||||
@@ -77,6 +78,20 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveRule = async () => {
|
||||
try {
|
||||
let host = window.location.host;
|
||||
if (isExt) {
|
||||
const tab = await getTabInfo();
|
||||
const url = new URL(tab.url);
|
||||
host = url.host;
|
||||
}
|
||||
saveRule({ ...rule, pattern: host });
|
||||
} catch (err) {
|
||||
console.log("[save rule]", err);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isExt) {
|
||||
return;
|
||||
@@ -218,9 +233,19 @@ export default function Popup({ setShowPopup, translator: tran }) {
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button variant="text" onClick={handleOpenSetting}>
|
||||
{i18n("setting")}
|
||||
</Button>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
spacing={2}
|
||||
>
|
||||
<Button variant="text" onClick={handleSaveRule}>
|
||||
{i18n("save_rule")}
|
||||
</Button>
|
||||
<Button variant="text" onClick={handleOpenSetting}>
|
||||
{i18n("setting")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user