From 6c8fd8fc5978507f4183577ba43b8ba15544416d Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Sun, 17 May 2026 14:35:50 +0300 Subject: [PATCH] refactor(app): reuse menu shortcuts in chrome --- src/app/shell/keyboard/registry.ts | 33 ++++++++++++++++++++++++------ src/app/shell/menu/shortcut.ts | 32 +++++++++++++++++++++++++++++ src/views/EditorView.vue | 3 ++- 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 src/app/shell/menu/shortcut.ts diff --git a/src/app/shell/keyboard/registry.ts b/src/app/shell/keyboard/registry.ts index 566056777..d9abc3d02 100644 --- a/src/app/shell/keyboard/registry.ts +++ b/src/app/shell/keyboard/registry.ts @@ -6,6 +6,7 @@ import { editorCommandMetadata } from '@open-pencil/vue' import type { EditorCommandId } from '@open-pencil/vue' import { TOOL_SHORTCUTS } from '@/app/editor/session' +import { appMenuTinykeysShortcut } from '@/app/shell/menu/shortcut' import { isEditing } from '@/app/shell/keyboard/focus' import { bindSpaceHandTool } from '@/app/shell/keyboard/space-tool' import type { @@ -83,14 +84,26 @@ export function registerKeyboardShortcuts(options: KeyboardShortcutOptions) { ), { id: 'export-selection-png', - keys: '$mod+Shift+KeyE', + keys: appMenuTinykeysShortcut('export-selection') ?? '$mod+Shift+KeyE', run: ({ actions }) => actions.exportSelectionPng() }, - { id: 'save-as', keys: '$mod+Shift+KeyS', run: ({ store }) => void store.saveFigFileAs() }, + { + id: 'save-as', + keys: appMenuTinykeysShortcut('save-as') ?? '$mod+Shift+KeyS', + run: ({ store }) => void store.saveFigFileAs() + }, ...commandShortcuts('selection.ungroup', 'edit.redo'), - { id: 'toggle-ui', keys: '$mod+Backslash', run: ({ actions }) => actions.toggleUI() }, + { + id: 'toggle-ui', + keys: appMenuTinykeysShortcut('toggle-ui') ?? '$mod+Backslash', + run: ({ actions }) => actions.toggleUI() + }, { id: 'toggle-ai', keys: '$mod+KeyJ', run: ({ actions }) => actions.toggleAI() }, - { id: 'close-tab', keys: '$mod+KeyW', run: ({ closeActiveTab }) => closeActiveTab() }, + { + id: 'close-tab', + keys: appMenuTinykeysShortcut('close') ?? '$mod+KeyW', + run: ({ closeActiveTab }) => closeActiveTab() + }, { id: 'new-tab', keys: ['$mod+KeyN', '$mod+KeyT'], run: ({ createTab }) => createTab() }, ...commandShortcuts( 'edit.undo', @@ -100,8 +113,16 @@ export function registerKeyboardShortcuts(options: KeyboardShortcutOptions) { 'selection.duplicate', 'selection.selectAll' ), - { id: 'save', keys: '$mod+KeyS', run: ({ store }) => void store.saveFigFile() }, - { id: 'open-file', keys: '$mod+KeyO', run: ({ openFileDialog }) => openFileDialog() }, + { + id: 'save', + keys: appMenuTinykeysShortcut('save') ?? '$mod+KeyS', + run: ({ store }) => void store.saveFigFile() + }, + { + id: 'open-file', + keys: appMenuTinykeysShortcut('open') ?? '$mod+KeyO', + run: ({ openFileDialog }) => openFileDialog() + }, ...commandShortcuts('selection.group'), { id: 'toggle-auto-layout', diff --git a/src/app/shell/menu/shortcut.ts b/src/app/shell/menu/shortcut.ts new file mode 100644 index 000000000..185872b06 --- /dev/null +++ b/src/app/shell/menu/shortcut.ts @@ -0,0 +1,32 @@ +import type { AppMenuActionItem, AppMenuEntry } from '@/app/shell/menu/schema' +import { APP_MENU_SCHEMA } from '@/app/shell/menu/schema' + +function isActionItem(entry: AppMenuEntry): entry is AppMenuActionItem { + return !('type' in entry && entry.type === 'separator') +} + +function findShortcutInEntries(entries: readonly AppMenuEntry[], id: string): string | undefined { + for (const entry of entries) { + if (!isActionItem(entry)) continue + if (entry.id === id) return entry.shortcut + const shortcut = entry.sub ? findShortcutInEntries(entry.sub, id) : undefined + if (shortcut) return shortcut + } + return undefined +} + +export function appMenuShortcut(id: string): string | undefined { + for (const group of APP_MENU_SCHEMA) { + const shortcut = findShortcutInEntries(group.items, id) + if (shortcut) return shortcut + } + return undefined +} + +export function appMenuTinykeysShortcut(id: string): string | string[] | undefined { + const shortcut = appMenuShortcut(id) + return shortcut + ?.replaceAll('MOD', '$mod') + .replaceAll('⇧', 'Shift') + .replaceAll('⌥', 'Alt') +} diff --git a/src/views/EditorView.vue b/src/views/EditorView.vue index cfacdeca8..310915b5c 100644 --- a/src/views/EditorView.vue +++ b/src/views/EditorView.vue @@ -13,6 +13,7 @@ import { useCollab, COLLAB_KEY } from '@/app/collab/use' import { connectAutomation } from '@/app/automation/bridge/server' import { spawnMCPIfNeeded } from '@/app/automation/mcp/spawn' import { isTauri } from '@/app/tauri/env' +import { appMenuShortcut } from '@/app/shell/menu/shortcut' import { createDemoShapes } from '@/app/demo/document' import { useEditorStore } from '@/app/editor/active-store' import { createTab, activeTab, getActiveStore, tabCount } from '@/app/tabs' @@ -194,7 +195,7 @@ onUnmounted(() => {