From f3dd89420918be162f8308bc94427291927c10ae Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 4 Aug 2026 20:39:45 +0300 Subject: [PATCH] feat(ui): numbered context-panel surface switching with configurable prefix - Add switch_context_surface shortcut (default Cmd/Ctrl + 1..9, 0 for the 10th surface) that opens/closes/switches context panel rail surfaces by their visible order, configurable and persisted in Settings -> Shortcuts. - Show order-number badges on rail icons while the modifier is held >500ms; dismiss on release, blur, or a number press until the next press-and-hold. - Remove the legacy mod+2/3/4 (diff/terminal/git) and switch_tab_1..9 bindings so numbered surface switching goes only through the new mechanism. - Replace the help-dialog 'Switch Project' row with the surface-switch row and update the shortcuts footer/header icons to the command icon. --- .../components/layout/ContextPanelRail.tsx | 126 +++++++++-- .../openchamber/KeyboardShortcutsSettings.tsx | 45 +++- .../session/sidebar/SidebarFooter.tsx | 2 +- packages/ui/src/components/ui/HelpDialog.tsx | 12 +- packages/ui/src/hooks/useKeyboardShortcuts.ts | 79 ++++++- .../ui/src/lib/i18n/messages/de.settings.ts | 2 + packages/ui/src/lib/i18n/messages/de.ts | 2 +- .../ui/src/lib/i18n/messages/en.settings.ts | 2 + packages/ui/src/lib/i18n/messages/en.ts | 2 +- .../ui/src/lib/i18n/messages/es.settings.ts | 2 + packages/ui/src/lib/i18n/messages/es.ts | 2 +- .../ui/src/lib/i18n/messages/fr.settings.ts | 2 + packages/ui/src/lib/i18n/messages/fr.ts | 2 +- .../ui/src/lib/i18n/messages/ja.settings.ts | 2 + packages/ui/src/lib/i18n/messages/ja.ts | 2 +- .../ui/src/lib/i18n/messages/ko.settings.ts | 2 + packages/ui/src/lib/i18n/messages/ko.ts | 2 +- .../ui/src/lib/i18n/messages/pl.settings.ts | 2 + packages/ui/src/lib/i18n/messages/pl.ts | 2 +- .../src/lib/i18n/messages/pt-BR.settings.ts | 2 + packages/ui/src/lib/i18n/messages/pt-BR.ts | 2 +- .../ui/src/lib/i18n/messages/uk.settings.ts | 2 + packages/ui/src/lib/i18n/messages/uk.ts | 2 +- .../src/lib/i18n/messages/zh-CN.settings.ts | 2 + packages/ui/src/lib/i18n/messages/zh-CN.ts | 2 +- .../src/lib/i18n/messages/zh-TW.settings.ts | 2 + packages/ui/src/lib/i18n/messages/zh-TW.ts | 2 +- packages/ui/src/lib/shortcuts.test.ts | 80 +++++++ packages/ui/src/lib/shortcuts.ts | 213 ++++++++++++------ packages/ui/src/lib/surfaces/DOCUMENTATION.md | 7 + packages/ui/src/lib/surfaces/registry.test.ts | 53 +++++ packages/ui/src/lib/surfaces/registry.ts | 37 +++ 32 files changed, 574 insertions(+), 124 deletions(-) create mode 100644 packages/ui/src/lib/shortcuts.test.ts create mode 100644 packages/ui/src/lib/surfaces/registry.test.ts diff --git a/packages/ui/src/components/layout/ContextPanelRail.tsx b/packages/ui/src/components/layout/ContextPanelRail.tsx index 5d522e5c..43692cbb 100644 --- a/packages/ui/src/components/layout/ContextPanelRail.tsx +++ b/packages/ui/src/components/layout/ContextPanelRail.tsx @@ -24,18 +24,23 @@ import { useDeviceInfo } from '@/lib/device'; import { isVSCodeRuntime } from '@/lib/desktop'; import { useI18n } from '@/lib/i18n'; import { + getVisibleContextRailSurfaces, sortContextSurfaces, type ContextSurfaceDescriptor, } from '@/lib/surfaces/registry'; +import { + getEffectiveShortcutPrefix, + isShortcutPrefixHeld, +} from '@/lib/shortcuts'; import { cn } from '@/lib/utils'; import { useFeatureFlagsStore } from '@/stores/useFeatureFlagsStore'; import { useGitStatus } from '@/stores/useGitStore'; import { normalizeContextPanelDirectoryKey, useUIStore } from '@/stores/useUIStore'; const RAIL_TOOLTIP_DELAY_MS = 150; -// Tablet width and up: below this the walkthrough cannot show a stop and its -// code side by side, which is the whole point of the surface. -const WALKTHROUGH_MIN_WIDTH = 768; +// Hold the surface-switch modifier for this long before revealing the order +// number badges on the rail icons. +const RAIL_NUMBER_HOLD_DELAY_MS = 500; const EMPTY_TABS: never[] = []; type RailItemProps = { @@ -44,6 +49,8 @@ type RailItemProps = { showActivityDot: boolean; label: string; description: string; + orderNumber?: number | null; + showOrderNumber?: boolean; onSelect: (surface: ContextSurfaceDescriptor) => void; }; @@ -53,6 +60,8 @@ const ContextPanelRailItem: React.FC = ({ showActivityDot, label, description, + orderNumber, + showOrderNumber, onSelect, }) => { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ @@ -86,12 +95,20 @@ const ContextPanelRailItem: React.FC = ({ ) : ( )} - {showActivityDot ? ( + {showActivityDot && !showOrderNumber ? (