From b06de60f6bfa4144112656850650b5811bdea657 Mon Sep 17 00:00:00 2001 From: Chris Scott <99081550+chriswritescode-dev@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:47:52 -0400 Subject: [PATCH 1/4] feat(ui): app-wide keyboard shortcuts with TUI keybind import --- frontend/src/App.tsx | 40 ++- frontend/src/api/types/settings.ts | 5 +- .../src/components/agent/AgentQuickSelect.tsx | 1 - .../message/PromptInput.command.test.tsx | 24 ++ .../src/components/message/PromptInput.tsx | 63 ++-- .../model/ModelQuickSelect.test.tsx | 87 ++++- .../src/components/model/ModelQuickSelect.tsx | 135 +++++++- .../navigation/ToolSidePanel.test.tsx | 23 +- .../components/navigation/ToolSidePanel.tsx | 17 +- .../settings/KeyboardShortcuts.test.tsx | 138 +++++++- .../components/settings/KeyboardShortcuts.tsx | 191 +++++++---- .../src/components/ui/bottom-sheet.test.tsx | 70 +++- frontend/src/components/ui/bottom-sheet.tsx | 13 + frontend/src/components/ui/dialog.test.tsx | 32 ++ frontend/src/components/ui/dialog.tsx | 21 +- .../src/components/ui/side-drawer.test.tsx | 44 ++- frontend/src/components/ui/side-drawer.tsx | 15 + .../KeyboardShortcutsContext.test.tsx | 302 ++++++++++++++++++ .../src/contexts/KeyboardShortcutsContext.tsx | 177 ++++++++++ .../hooks/useGlobalShortcutActions.test.tsx | 136 ++++++++ .../src/hooks/useGlobalShortcutActions.ts | 66 ++++ .../src/hooks/useKeyboardShortcuts.test.tsx | 34 -- frontend/src/hooks/useKeyboardShortcuts.ts | 193 ----------- frontend/src/hooks/useToolPanel.test.ts | 84 +++++ frontend/src/hooks/useToolPanel.ts | 31 +- frontend/src/lib/keyboardShortcuts.test.ts | 32 ++ frontend/src/lib/keyboardShortcuts.ts | 113 +++++++ frontend/src/lib/overlayFocus.test.ts | 110 +++++++ frontend/src/lib/overlayFocus.ts | 100 ++++++ frontend/src/lib/tuiKeybindImport.test.ts | 218 +++++++++++++ frontend/src/lib/tuiKeybindImport.ts | 285 +++++++++++++++++ frontend/src/pages/SessionDetail.tsx | 61 ++-- .../SessionDetail.assistant-loading.test.tsx | 7 +- .../__tests__/SessionDetail.commands.test.tsx | 4 +- .../SessionDetail.form-prompt.test.tsx | 4 +- .../__tests__/SessionDetail.polling.test.tsx | 7 +- .../SessionDetail.scroll-floating.test.tsx | 7 +- shared/src/schemas/settings.ts | 17 +- 38 files changed, 2467 insertions(+), 440 deletions(-) create mode 100644 frontend/src/contexts/KeyboardShortcutsContext.test.tsx create mode 100644 frontend/src/contexts/KeyboardShortcutsContext.tsx create mode 100644 frontend/src/hooks/useGlobalShortcutActions.test.tsx create mode 100644 frontend/src/hooks/useGlobalShortcutActions.ts delete mode 100644 frontend/src/hooks/useKeyboardShortcuts.test.tsx delete mode 100644 frontend/src/hooks/useKeyboardShortcuts.ts create mode 100644 frontend/src/hooks/useToolPanel.test.ts create mode 100644 frontend/src/lib/keyboardShortcuts.test.ts create mode 100644 frontend/src/lib/keyboardShortcuts.ts create mode 100644 frontend/src/lib/overlayFocus.test.ts create mode 100644 frontend/src/lib/overlayFocus.ts create mode 100644 frontend/src/lib/tuiKeybindImport.test.ts create mode 100644 frontend/src/lib/tuiKeybindImport.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fcbe2caa7..06d5782e9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -24,6 +24,7 @@ import { useMobileTabBar } from '@/hooks/useMobileTabBar' import { TTSProvider } from './contexts/TTSContext' import { ThemeProvider, useThemeMode } from './contexts/ThemeContext' import { AuthProvider } from './contexts/AuthContext' +import { KeyboardShortcutsProvider } from './contexts/KeyboardShortcutsContext' import { EventProvider, usePermissions, useEventContext } from '@/contexts/EventContext' import { SwipeNavigationProvider, useSwipeNavigation } from '@/contexts/SwipeNavigationContext' import { PermissionRequestDialog } from './components/session/PermissionRequestDialog' @@ -34,6 +35,7 @@ import { onNotificationClick } from '@/lib/serviceWorker' import { useAuth } from '@/hooks/useAuth' import { useOpenCodeFailureToast } from '@/hooks/useOpenCodeFailureToast' import { useOpenCodeServerActions } from '@/hooks/useOpenCodeServerActions' +import { useGlobalShortcutActions } from '@/hooks/useGlobalShortcutActions' import { RestartServerDialog } from '@/components/settings/RestartServerDialog' const queryClient = new QueryClient({ @@ -116,6 +118,11 @@ function PermissionDialogWrapper() { ) } +function GlobalShortcuts() { + useGlobalShortcutActions() + return null +} + function AppShell() { const navigate = useNavigate() const location = useLocation() @@ -188,21 +195,24 @@ function AppShell() { -
- -
- -
-
- - - - - - - - - + + +
+ +
+ +
+
+ + + + + + + + + +
diff --git a/frontend/src/api/types/settings.ts b/frontend/src/api/types/settings.ts index 9893a7041..302b5d372 100644 --- a/frontend/src/api/types/settings.ts +++ b/frontend/src/api/types/settings.ts @@ -4,6 +4,7 @@ import { DEFAULT_KEYBOARD_SHORTCUTS, DEFAULT_USER_PREFERENCES, DEFAULT_LEADER_KEY, + DEFAULT_DIRECT_SHORTCUTS, BLOCKED_SERVER_ENV_KEYS, selectPreferredOpenCodeConfigSourceName, type TTSConfig, @@ -27,8 +28,8 @@ import type { NotificationPreferences } from '@opencode-manager/shared/types' import { saveFile } from '@/lib/download' export type { TTSConfig, STTConfig, OpenCodeConfigFile, OpenCodeConfigSourceFile, OpenCodeConfigSourceName, UpdateOpenCodeConfigRequest, ModelConfig, ProviderConfig, SandboxPreferences, NotificationPreferences, SkillFileInfo, CreateSkillRequest, UpdateSkillRequest, SkillScope, InstallSkillFromGithubRequest, InstallSkillResponse } -export type { UserPreferences, UpdateSettingsRequest, CustomCommand, GitCredential, GitIdentity, GitIdentityProfile } from '@opencode-manager/shared' -export { DEFAULT_TTS_CONFIG, DEFAULT_STT_CONFIG, DEFAULT_KEYBOARD_SHORTCUTS, DEFAULT_USER_PREFERENCES, DEFAULT_LEADER_KEY, BLOCKED_SERVER_ENV_KEYS } +export type { UserPreferences, UpdateSettingsRequest, CustomCommand, GitCredential, GitIdentity, GitIdentityProfile, KeyboardShortcutAction } from '@opencode-manager/shared' +export { DEFAULT_TTS_CONFIG, DEFAULT_STT_CONFIG, DEFAULT_KEYBOARD_SHORTCUTS, DEFAULT_USER_PREFERENCES, DEFAULT_LEADER_KEY, DEFAULT_DIRECT_SHORTCUTS, BLOCKED_SERVER_ENV_KEYS } export { isOpenCodeConfigSourceName } from '@opencode-manager/shared' export function getOpenCodeConfigSources(config: OpenCodeConfigFile): OpenCodeConfigSourceFile[] { diff --git a/frontend/src/components/agent/AgentQuickSelect.tsx b/frontend/src/components/agent/AgentQuickSelect.tsx index ab6594263..fad74bdbd 100644 --- a/frontend/src/components/agent/AgentQuickSelect.tsx +++ b/frontend/src/components/agent/AgentQuickSelect.tsx @@ -54,7 +54,6 @@ export function AgentQuickSelect({ const buttonContent = (