From a662096acd9cc90da494b59c9a0878db42db5cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BA=BE=E6=B5=9A?= Date: Wed, 28 May 2025 23:11:21 +0800 Subject: [PATCH 1/3] feat: add Ctrl+D and Win+Tab keyboard functionalities with translations --- .../src/components/menu/keyboard/ctrl-d.tsx | 43 +++++++++++++++++++ .../src/components/menu/keyboard/index.tsx | 4 ++ .../src/components/menu/keyboard/win-tab.tsx | 43 +++++++++++++++++++ browser/src/i18n/locales/be.ts | 8 ++-- browser/src/i18n/locales/de.ts | 8 ++-- browser/src/i18n/locales/en.ts | 4 +- browser/src/i18n/locales/nl.ts | 8 ++-- browser/src/i18n/locales/ru.ts | 4 +- browser/src/i18n/locales/zh.ts | 4 +- 9 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 browser/src/components/menu/keyboard/ctrl-d.tsx create mode 100644 browser/src/components/menu/keyboard/win-tab.tsx diff --git a/browser/src/components/menu/keyboard/ctrl-d.tsx b/browser/src/components/menu/keyboard/ctrl-d.tsx new file mode 100644 index 0000000..6cbda05 --- /dev/null +++ b/browser/src/components/menu/keyboard/ctrl-d.tsx @@ -0,0 +1,43 @@ +import { useState } from 'react'; +import { SendHorizonal } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import { device } from '@/libs/device'; +import { Modifiers } from '@/libs/device/keyboard.ts'; +import { KeyboardCodes } from '@/libs/keyboard'; + +export const CtrlD = () => { + const { t } = useTranslation(); + const [isLoading, setIsLoading] = useState(false); + + async function ctrlD(): Promise { + if (isLoading) return; + setIsLoading(true); + + try { + const modifiers = new Modifiers(); + modifiers.leftCtrl = true; + await send(modifiers, KeyboardCodes.get('KeyD')!); + } catch (e) { + console.log(e); + } finally { + setIsLoading(false); + } + } + + async function send(modifiers: Modifiers, code: number) { + const keys = [0x00, 0x00, code, 0x00, 0x00, 0x00]; + await device.sendKeyboardData(modifiers, keys); + await device.sendKeyboardData(new Modifiers(), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + } + + return ( +
+ + {t('keyboard.ctrlD')} +
+ ); +} diff --git a/browser/src/components/menu/keyboard/index.tsx b/browser/src/components/menu/keyboard/index.tsx index 68a652b..223d1b8 100644 --- a/browser/src/components/menu/keyboard/index.tsx +++ b/browser/src/components/menu/keyboard/index.tsx @@ -5,6 +5,8 @@ import { KeyboardIcon } from 'lucide-react'; import { Paste } from './paste.tsx'; import { VirtualKeyboard } from './virtual-keyboard.tsx'; import { CtrlAltDel } from './ctrl-alt-del'; +import { CtrlD } from './ctrl-d.tsx'; +import { WinTab } from './win-tab.tsx'; export const Keyboard = () => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -13,6 +15,8 @@ export const Keyboard = () => { <> + + ); diff --git a/browser/src/components/menu/keyboard/win-tab.tsx b/browser/src/components/menu/keyboard/win-tab.tsx new file mode 100644 index 0000000..d190fef --- /dev/null +++ b/browser/src/components/menu/keyboard/win-tab.tsx @@ -0,0 +1,43 @@ +import { useState } from 'react'; +import { SendHorizonal } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import { device } from '@/libs/device'; +import { Modifiers } from '@/libs/device/keyboard.ts'; +import { KeyboardCodes } from '@/libs/keyboard'; + +export const WinTab = () => { + const { t } = useTranslation(); + const [isLoading, setIsLoading] = useState(false); + + async function winTab(): Promise { + if (isLoading) return; + setIsLoading(true); + + try { + const modifiers = new Modifiers(); + modifiers.leftWindows = true; + await send(modifiers, KeyboardCodes.get('Tab')!); + } catch (e) { + console.log(e); + } finally { + setIsLoading(false); + } + } + + async function send(modifiers: Modifiers, code: number) { + const keys = [0x00, 0x00, code, 0x00, 0x00, 0x00]; + await device.sendKeyboardData(modifiers, keys); + await device.sendKeyboardData(new Modifiers(), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + } + + return ( +
+ + {t('keyboard.winTab')} +
+ ); +} diff --git a/browser/src/i18n/locales/be.ts b/browser/src/i18n/locales/be.ts index 094db16..e842148 100644 --- a/browser/src/i18n/locales/be.ts +++ b/browser/src/i18n/locales/be.ts @@ -37,7 +37,9 @@ const be = { keyboard: { paste: 'Plakken', virtualKeyboard: 'Virtueel klavier', - ctrlAltDel: 'Ctrl + Alt + Delete' + ctrlAltDel: 'Ctrl + Alt + Delete', + ctrlD: 'Ctrl + D', + winTab: 'Win + Tab', }, mouse: { cursor: { @@ -62,5 +64,5 @@ const be = { } } }; - - export default be; \ No newline at end of file + + export default be; diff --git a/browser/src/i18n/locales/de.ts b/browser/src/i18n/locales/de.ts index 79dbb70..a245d7f 100644 --- a/browser/src/i18n/locales/de.ts +++ b/browser/src/i18n/locales/de.ts @@ -37,7 +37,9 @@ const de = { keyboard: { paste: 'Einfügen', virtualKeyboard: 'Virtuelle Tastatur', - ctrlAltDel: 'Strg + Alt + Entfernen' + ctrlAltDel: 'Strg + Alt + Entfernen', + ctrlD: 'Strg + D', + winTab: 'Win + Tab', }, mouse: { cursor: { @@ -62,5 +64,5 @@ const de = { } } }; - - export default de; \ No newline at end of file + + export default de; diff --git a/browser/src/i18n/locales/en.ts b/browser/src/i18n/locales/en.ts index 59ea63b..c50610c 100644 --- a/browser/src/i18n/locales/en.ts +++ b/browser/src/i18n/locales/en.ts @@ -37,7 +37,9 @@ const en = { keyboard: { paste: 'Paste', virtualKeyboard: 'Keyboard', - ctrlAltDel: 'Ctrl + Alt + Delete' + ctrlAltDel: 'Ctrl + Alt + Delete', + ctrlD: 'Ctrl + D', + winTab: 'Win + Tab', }, mouse: { cursor: { diff --git a/browser/src/i18n/locales/nl.ts b/browser/src/i18n/locales/nl.ts index abab0ea..0896f9b 100644 --- a/browser/src/i18n/locales/nl.ts +++ b/browser/src/i18n/locales/nl.ts @@ -37,7 +37,9 @@ const nl = { keyboard: { paste: 'Plakken', virtualKeyboard: 'Virtueel toetsenbord', - ctrlAltDel: 'Ctrl + Alt + Delete' + ctrlAltDel: 'Ctrl + Alt + Delete', + ctrlD: 'Ctrl + D', + winTab: 'Win + Tab', }, mouse: { cursor: { @@ -62,5 +64,5 @@ const nl = { } } }; - - export default nl; \ No newline at end of file + + export default nl; diff --git a/browser/src/i18n/locales/ru.ts b/browser/src/i18n/locales/ru.ts index c1ee4c8..befcf50 100644 --- a/browser/src/i18n/locales/ru.ts +++ b/browser/src/i18n/locales/ru.ts @@ -37,7 +37,9 @@ const ru = { keyboard: { paste: 'Вставить текст', virtualKeyboard: 'Виртуальная клавиатура', - ctrlAltDel: 'Ctrl + Alt + Delete' + ctrlAltDel: 'Ctrl + Alt + Delete', + ctrlD: 'Ctrl + D', + winTab: 'Win + Tab', }, mouse: { cursor: { diff --git a/browser/src/i18n/locales/zh.ts b/browser/src/i18n/locales/zh.ts index e7f1f85..1cfc9d3 100644 --- a/browser/src/i18n/locales/zh.ts +++ b/browser/src/i18n/locales/zh.ts @@ -35,7 +35,9 @@ const zh = { keyboard: { paste: '粘贴', virtualKeyboard: '虚拟键盘', - ctrlAltDel: 'Ctrl + Alt + Delete' + ctrlAltDel: 'Ctrl + Alt + Delete', + ctrlD: 'Ctrl + D', + winTab: 'Win + Tab', }, mouse: { cursor: { From 90eeec3093138453bbd247d837d5ff59baa909c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BA=BE=E6=B5=9A?= Date: Wed, 28 May 2025 23:36:06 +0800 Subject: [PATCH 2/3] feat: add keyboard shortcuts menu and update translations --- .../src/components/menu/keyboard/index.tsx | 8 ++--- .../menu/keyboard/shortcuts-menu.tsx | 36 +++++++++++++++++++ browser/src/i18n/locales/be.ts | 1 + browser/src/i18n/locales/de.ts | 1 + browser/src/i18n/locales/en.ts | 1 + browser/src/i18n/locales/nl.ts | 1 + browser/src/i18n/locales/ru.ts | 1 + browser/src/i18n/locales/zh.ts | 1 + 8 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 browser/src/components/menu/keyboard/shortcuts-menu.tsx diff --git a/browser/src/components/menu/keyboard/index.tsx b/browser/src/components/menu/keyboard/index.tsx index 223d1b8..ff59175 100644 --- a/browser/src/components/menu/keyboard/index.tsx +++ b/browser/src/components/menu/keyboard/index.tsx @@ -4,9 +4,7 @@ import { KeyboardIcon } from 'lucide-react'; import { Paste } from './paste.tsx'; import { VirtualKeyboard } from './virtual-keyboard.tsx'; -import { CtrlAltDel } from './ctrl-alt-del'; -import { CtrlD } from './ctrl-d.tsx'; -import { WinTab } from './win-tab.tsx'; +import { KeyboardShortcutsMenu } from './shortcuts-menu.tsx'; export const Keyboard = () => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -14,10 +12,8 @@ export const Keyboard = () => { const content = ( <> - - - + ); diff --git a/browser/src/components/menu/keyboard/shortcuts-menu.tsx b/browser/src/components/menu/keyboard/shortcuts-menu.tsx new file mode 100644 index 0000000..cc88ca2 --- /dev/null +++ b/browser/src/components/menu/keyboard/shortcuts-menu.tsx @@ -0,0 +1,36 @@ +import { useState } from 'react'; +import { SendHorizonal } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { Popover } from 'antd'; + +import { CtrlAltDel } from './ctrl-alt-del'; +import { CtrlD } from './ctrl-d.tsx'; +import { WinTab } from './win-tab.tsx'; + +export const KeyboardShortcutsMenu = () => { + const { t } = useTranslation(); + const [open, setOpen] = useState(false); + + return ( + + + + + + } + trigger="click" + placement="rightTop" + align={{ offset: [14, 0] }} + open={open} + onOpenChange={setOpen} + arrow={false} + > +
+ + {t('keyboard.shortcuts')} +
+
+ ); +}; diff --git a/browser/src/i18n/locales/be.ts b/browser/src/i18n/locales/be.ts index e842148..3afee03 100644 --- a/browser/src/i18n/locales/be.ts +++ b/browser/src/i18n/locales/be.ts @@ -37,6 +37,7 @@ const be = { keyboard: { paste: 'Plakken', virtualKeyboard: 'Virtueel klavier', + shortcuts: 'Sneltoetsen', ctrlAltDel: 'Ctrl + Alt + Delete', ctrlD: 'Ctrl + D', winTab: 'Win + Tab', diff --git a/browser/src/i18n/locales/de.ts b/browser/src/i18n/locales/de.ts index a245d7f..03dce33 100644 --- a/browser/src/i18n/locales/de.ts +++ b/browser/src/i18n/locales/de.ts @@ -37,6 +37,7 @@ const de = { keyboard: { paste: 'Einfügen', virtualKeyboard: 'Virtuelle Tastatur', + shortcuts: 'Tastenkürzel', ctrlAltDel: 'Strg + Alt + Entfernen', ctrlD: 'Strg + D', winTab: 'Win + Tab', diff --git a/browser/src/i18n/locales/en.ts b/browser/src/i18n/locales/en.ts index c50610c..a971bd9 100644 --- a/browser/src/i18n/locales/en.ts +++ b/browser/src/i18n/locales/en.ts @@ -37,6 +37,7 @@ const en = { keyboard: { paste: 'Paste', virtualKeyboard: 'Keyboard', + shortcuts: 'Shortcuts', ctrlAltDel: 'Ctrl + Alt + Delete', ctrlD: 'Ctrl + D', winTab: 'Win + Tab', diff --git a/browser/src/i18n/locales/nl.ts b/browser/src/i18n/locales/nl.ts index 0896f9b..21934e6 100644 --- a/browser/src/i18n/locales/nl.ts +++ b/browser/src/i18n/locales/nl.ts @@ -37,6 +37,7 @@ const nl = { keyboard: { paste: 'Plakken', virtualKeyboard: 'Virtueel toetsenbord', + shortcuts: 'Sneltoetsen', ctrlAltDel: 'Ctrl + Alt + Delete', ctrlD: 'Ctrl + D', winTab: 'Win + Tab', diff --git a/browser/src/i18n/locales/ru.ts b/browser/src/i18n/locales/ru.ts index befcf50..9c70f15 100644 --- a/browser/src/i18n/locales/ru.ts +++ b/browser/src/i18n/locales/ru.ts @@ -37,6 +37,7 @@ const ru = { keyboard: { paste: 'Вставить текст', virtualKeyboard: 'Виртуальная клавиатура', + shortcuts: 'Сочетания клавиш', ctrlAltDel: 'Ctrl + Alt + Delete', ctrlD: 'Ctrl + D', winTab: 'Win + Tab', diff --git a/browser/src/i18n/locales/zh.ts b/browser/src/i18n/locales/zh.ts index 1cfc9d3..773a87e 100644 --- a/browser/src/i18n/locales/zh.ts +++ b/browser/src/i18n/locales/zh.ts @@ -35,6 +35,7 @@ const zh = { keyboard: { paste: '粘贴', virtualKeyboard: '虚拟键盘', + shortcuts: '快捷键', ctrlAltDel: 'Ctrl + Alt + Delete', ctrlD: 'Ctrl + D', winTab: 'Win + Tab', From be5a233e56a070d05debee0262bf87f65190e2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BA=BE=E6=B5=9A?= Date: Thu, 29 May 2025 16:28:14 +0800 Subject: [PATCH 3/3] feat: refactor keyboard shortcuts menu and consolidate shortcut components --- .../src/components/menu/keyboard/ctrl-d.tsx | 43 ------------------- .../{ctrl-alt-del.tsx => shortcut.tsx} | 28 ++++++------ .../menu/keyboard/shortcuts-menu.tsx | 31 ++++++++++--- .../src/components/menu/keyboard/win-tab.tsx | 43 ------------------- 4 files changed, 40 insertions(+), 105 deletions(-) delete mode 100644 browser/src/components/menu/keyboard/ctrl-d.tsx rename browser/src/components/menu/keyboard/{ctrl-alt-del.tsx => shortcut.tsx} (60%) delete mode 100644 browser/src/components/menu/keyboard/win-tab.tsx diff --git a/browser/src/components/menu/keyboard/ctrl-d.tsx b/browser/src/components/menu/keyboard/ctrl-d.tsx deleted file mode 100644 index 6cbda05..0000000 --- a/browser/src/components/menu/keyboard/ctrl-d.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useState } from 'react'; -import { SendHorizonal } from 'lucide-react'; -import { useTranslation } from 'react-i18next'; - -import { device } from '@/libs/device'; -import { Modifiers } from '@/libs/device/keyboard.ts'; -import { KeyboardCodes } from '@/libs/keyboard'; - -export const CtrlD = () => { - const { t } = useTranslation(); - const [isLoading, setIsLoading] = useState(false); - - async function ctrlD(): Promise { - if (isLoading) return; - setIsLoading(true); - - try { - const modifiers = new Modifiers(); - modifiers.leftCtrl = true; - await send(modifiers, KeyboardCodes.get('KeyD')!); - } catch (e) { - console.log(e); - } finally { - setIsLoading(false); - } - } - - async function send(modifiers: Modifiers, code: number) { - const keys = [0x00, 0x00, code, 0x00, 0x00, 0x00]; - await device.sendKeyboardData(modifiers, keys); - await device.sendKeyboardData(new Modifiers(), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - } - - return ( -
- - {t('keyboard.ctrlD')} -
- ); -} diff --git a/browser/src/components/menu/keyboard/ctrl-alt-del.tsx b/browser/src/components/menu/keyboard/shortcut.tsx similarity index 60% rename from browser/src/components/menu/keyboard/ctrl-alt-del.tsx rename to browser/src/components/menu/keyboard/shortcut.tsx index 4f1e12e..95a0f6f 100644 --- a/browser/src/components/menu/keyboard/ctrl-alt-del.tsx +++ b/browser/src/components/menu/keyboard/shortcut.tsx @@ -1,24 +1,27 @@ import { useState } from 'react'; import { SendHorizonal } from 'lucide-react'; -import { useTranslation } from 'react-i18next'; import { device } from '@/libs/device'; import { Modifiers } from '@/libs/device/keyboard.ts'; import { KeyboardCodes } from '@/libs/keyboard'; -export const CtrlAltDel = () => { - const { t } = useTranslation(); +interface ShortcutProps { + label: string; + modifiers?: Partial; + keyCode: string; +} + +export const Shortcut = ({ label, modifiers = {}, keyCode }: ShortcutProps) => { const [isLoading, setIsLoading] = useState(false); - async function ctrlAltDel(): Promise { + async function handleClick(): Promise { if (isLoading) return; setIsLoading(true); try { - const modifiers = new Modifiers(); - modifiers.leftCtrl = true; - modifiers.leftAlt = true; - await send(modifiers, KeyboardCodes.get('Delete')!); + const mods = new Modifiers(); + Object.assign(mods, modifiers); + await send(mods, KeyboardCodes.get(keyCode)!); } catch (e) { console.log(e); } finally { @@ -26,20 +29,19 @@ export const CtrlAltDel = () => { } } - async function send(modifiers: Modifiers, code: number) { + async function send(mods: Modifiers, code: number) { const keys = [0x00, 0x00, code, 0x00, 0x00, 0x00]; - await device.sendKeyboardData(modifiers, keys); - + await device.sendKeyboardData(mods, keys); await device.sendKeyboardData(new Modifiers(), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); } return (
- {t('keyboard.ctrlAltDel')} + {label}
); }; diff --git a/browser/src/components/menu/keyboard/shortcuts-menu.tsx b/browser/src/components/menu/keyboard/shortcuts-menu.tsx index cc88ca2..6f960cf 100644 --- a/browser/src/components/menu/keyboard/shortcuts-menu.tsx +++ b/browser/src/components/menu/keyboard/shortcuts-menu.tsx @@ -3,9 +3,7 @@ import { SendHorizonal } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { Popover } from 'antd'; -import { CtrlAltDel } from './ctrl-alt-del'; -import { CtrlD } from './ctrl-d.tsx'; -import { WinTab } from './win-tab.tsx'; +import { Shortcut } from './shortcut.tsx'; export const KeyboardShortcutsMenu = () => { const { t } = useTranslation(); @@ -15,9 +13,30 @@ export const KeyboardShortcutsMenu = () => { - - - + {[ + { + label: t('keyboard.ctrlAltDel'), + modifiers: { leftCtrl: true, leftAlt: true }, + keyCode: 'Delete', + }, + { + label: t('keyboard.ctrlD'), + modifiers: { leftCtrl: true }, + keyCode: 'KeyD', + }, + { + label: t('keyboard.winTab'), + modifiers: { leftWindows: true }, + keyCode: 'Tab', + }, + ].map((shortcut) => ( + + ))} } trigger="click" diff --git a/browser/src/components/menu/keyboard/win-tab.tsx b/browser/src/components/menu/keyboard/win-tab.tsx deleted file mode 100644 index d190fef..0000000 --- a/browser/src/components/menu/keyboard/win-tab.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useState } from 'react'; -import { SendHorizonal } from 'lucide-react'; -import { useTranslation } from 'react-i18next'; - -import { device } from '@/libs/device'; -import { Modifiers } from '@/libs/device/keyboard.ts'; -import { KeyboardCodes } from '@/libs/keyboard'; - -export const WinTab = () => { - const { t } = useTranslation(); - const [isLoading, setIsLoading] = useState(false); - - async function winTab(): Promise { - if (isLoading) return; - setIsLoading(true); - - try { - const modifiers = new Modifiers(); - modifiers.leftWindows = true; - await send(modifiers, KeyboardCodes.get('Tab')!); - } catch (e) { - console.log(e); - } finally { - setIsLoading(false); - } - } - - async function send(modifiers: Modifiers, code: number) { - const keys = [0x00, 0x00, code, 0x00, 0x00, 0x00]; - await device.sendKeyboardData(modifiers, keys); - await device.sendKeyboardData(new Modifiers(), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - } - - return ( -
- - {t('keyboard.winTab')} -
- ); -}