diff --git a/package.json b/package.json index 89e5d98..998e20b 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "@emotion/styled": "^11.10.8", "@mui/icons-material": "^5.11.11", "@mui/material": "^5.11.12", - "@violentmonkey/shortcut": "^1.3.0", "query-string": "^8.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/config/i18n.js b/src/config/i18n.js index 3ca9903..f008479 100644 --- a/src/config/i18n.js +++ b/src/config/i18n.js @@ -489,4 +489,20 @@ export const I18N = { zh: `恢复默认`, en: `Restore Default`, }, + shortcuts_setting: { + zh: `快捷键设置`, + en: `Shortcuts Setting`, + }, + toggle_translate_shortcut: { + zh: `开启翻译快捷键`, + en: `Toggle Translate Shortcut`, + }, + toggle_style_shortcut: { + zh: `切换样式快捷键`, + en: `Toggle Style Shortcut`, + }, + toggle_popup_shortcut: { + zh: `打开弹窗快捷键`, + en: `Open Popup Shortcut`, + }, }; diff --git a/src/config/index.js b/src/config/index.js index ac53fd3..c00fcc8 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -238,6 +238,16 @@ 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 DEFAULT_SHORTCUTS = { + [OPT_SHORTCUT_TRANSLATE]: ["Alt", "q"], + [OPT_SHORTCUT_STYLE]: ["Alt", "c"], + [OPT_SHORTCUT_POPUP]: ["Alt", "k"], +}; + export const TRANS_MIN_LENGTH = 5; // 最短翻译长度 export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度 export const TRANS_NEWLINE_LENGTH = 40; // 换行字符数 @@ -256,6 +266,7 @@ export const DEFAULT_SETTING = { owSubrule: DEFAULT_OW_RULE, // 覆写订阅规则 transApis: DEFAULT_TRANS_APIS, // 翻译接口 mouseKey: OPT_MOUSEKEY_DISABLE, // 鼠标悬停翻译 + shortcuts: DEFAULT_SHORTCUTS, // 快捷键 }; export const DEFAULT_RULES = [GLOBLA_RULE]; diff --git a/src/hooks/Shortcut.js b/src/hooks/Shortcut.js new file mode 100644 index 0000000..ad8bc61 --- /dev/null +++ b/src/hooks/Shortcut.js @@ -0,0 +1,22 @@ +import { useCallback } from "react"; +import { DEFAULT_SHORTCUTS } from "../config"; +import { useSetting } from "./Setting"; + +export function useShortcut(action) { + const { setting, updateSetting } = useSetting(); + const shortcuts = setting?.shortcuts || DEFAULT_SHORTCUTS; + + const setShortcut = useCallback( + async (val) => { + await updateSetting({ + shortcuts: { + ...shortcuts, + [action]: val, + }, + }); + }, + [action, shortcuts, updateSetting] + ); + + return { shortcut: shortcuts[action] || [], setShortcut }; +} diff --git a/src/libs/utils.js b/src/libs/utils.js index 6fad207..9c1fca6 100644 --- a/src/libs/utils.js +++ b/src/libs/utils.js @@ -139,3 +139,14 @@ export const sha256 = async (text, salt) => { * @returns */ export const genEventName = () => btoa(Math.random()).slice(3, 11); + +/** + * 判断两个 Set 是否相同 + * @param {*} a + * @param {*} b + * @returns + */ +export const isSameSet = (a, b) => { + const s = new Set([...a, ...b]); + return s.size === a.size && s.size === b.size; +}; diff --git a/src/views/Action/index.js b/src/views/Action/index.js index 98006bb..063a47f 100644 --- a/src/views/Action/index.js +++ b/src/views/Action/index.js @@ -6,10 +6,10 @@ import Draggable from "./Draggable"; import { useEffect, useState, useMemo, useCallback } from "react"; import { SettingProvider } from "../../hooks/Setting"; import Popup from "../Popup"; -import { debounce } from "../../libs/utils"; -import * as shortcut from "@violentmonkey/shortcut"; +import { debounce, isSameSet } from "../../libs/utils"; import { isGm } from "../../libs/client"; import Header from "../Popup/Header"; +import { DEFAULT_SHORTCUTS, OPT_SHORTCUT_TRANSLATE } from "../../config"; export default function Action({ translator, fab }) { const fabWidth = 40; @@ -43,22 +43,55 @@ export default function Action({ translator, fab }) { setMoved(true); }, []); + // useEffect(() => { + // // 注册快捷键 + // const handleKeydown = (e) => { + // if (!e.altKey) { + // return; + // } + // if (e.code === "KeyQ") { + // translator.toggle(); + // setShowPopup(false); + // } else if (e.code === "KeyC") { + // translator.toggleStyle(); + // setShowPopup(false); + // } else if (e.code === "KeyK") { + // setShowPopup((pre) => !pre); + // } + // }; + // window.addEventListener("keydown", handleKeydown); + // return () => { + // window.removeEventListener("keydown", handleKeydown); + // }; + // }, [translator]); + useEffect(() => { // 注册快捷键 - shortcut.register("a-q", () => { - translator.toggle(); - setShowPopup(false); - }); - shortcut.register("a-c", () => { - translator.toggleStyle(); - setShowPopup(false); - }); - shortcut.register("a-k", () => { - setShowPopup((pre) => !pre); - }); + const shortcuts = translator.setting.shortcuts || DEFAULT_SHORTCUTS; + const keys = new Set(); + const handleKeydown = (e) => { + console.log("keydown", e); + e.code && keys.add(e.key); + + console.log("keys", keys); + const isSame = isSameSet( + keys, + new Set(shortcuts[OPT_SHORTCUT_TRANSLATE]) + ); + console.log("isSame", keys, isSame); + }; + + const handleKeyup = (e) => { + console.log("keyup", e); + keys.delete(e.key); + }; + + window.addEventListener("keydown", handleKeydown); + window.addEventListener("keyup", handleKeyup); return () => { - shortcut.disable(); + window.removeEventListener("keydown", handleKeydown); + window.removeEventListener("keyup", handleKeyup); }; }, [translator]); diff --git a/src/views/Options/Setting.js b/src/views/Options/Setting.js index 02e9578..24df99f 100644 --- a/src/views/Options/Setting.js +++ b/src/views/Options/Setting.js @@ -12,13 +12,89 @@ import { limitNumber } from "../../libs/utils"; import { useI18n } from "../../hooks/I18n"; import { useAlert } from "../../hooks/Alert"; import { isExt } from "../../libs/client"; +import IconButton from "@mui/material/IconButton"; +import EditIcon from "@mui/icons-material/Edit"; +import Grid from "@mui/material/Grid"; import { UI_LANGS, TRANS_NEWLINE_LENGTH, CACHE_NAME, OPT_MOUSEKEY_ALL, OPT_MOUSEKEY_DISABLE, + OPT_SHORTCUT_TRANSLATE, + OPT_SHORTCUT_STYLE, + OPT_SHORTCUT_POPUP, } from "../../config"; +import { useEffect, useState, useRef } from "react"; +import { useShortcut } from "../../hooks/Shortcut"; + +function ShortcutItem({ action, label }) { + const { shortcut, setShortcut } = useShortcut(action); + const [disabled, setDisabled] = useState(true); + const [focused, setFocus] = useState(false); + const [formval, setFormval] = useState(shortcut); + const inputRef = useRef(null); + + useEffect(() => { + if (!disabled) { + inputRef.current.focus(); + setFormval([]); + } + }, [disabled]); + + useEffect(() => { + if (!focused) { + return; + } + + const keys = new Set(); + const handleKeydown = (e) => { + // console.log("keydown", e); + e.code && keys.add(e.key); + setFormval([...keys]); + }; + const handleKeyup = (e) => { + // console.log("keyup", e); + keys.delete(e.key); + }; + + window.addEventListener("keydown", handleKeydown); + window.addEventListener("keyup", handleKeyup); + return () => { + window.removeEventListener("keydown", handleKeydown); + window.removeEventListener("keyup", handleKeyup); + }; + }, [focused]); + + return ( + + { + setFocus(true); + }} + onBlur={(e) => { + setFocus(false); + setDisabled(true); + setShortcut(formval); + }} + /> + { + setDisabled(false); + }} + > + {} + + + ); +} export default function Settings() { const i18n = useI18n(); @@ -151,7 +227,7 @@ export default function Settings() { - {isExt && ( + {isExt ? ( {i18n("if_clear_cache")}