From b3c07f89f4898dcbeb4ce6c9f5646d21fe6815a2 Mon Sep 17 00:00:00 2001 From: amosblomqvist Date: Fri, 25 Sep 2026 02:39:11 +0300 Subject: [PATCH] =?UTF-8?q?Fix=20=E2=8C=98B:=20dispatch/listen=20event-nam?= =?UTF-8?q?e=20mismatch;=20thinking=20pill=20=E2=86=92=20"Thinking=20(?= =?UTF-8?q?=E2=87=A7=E2=87=A5)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause (interlude, owner 2026-09-25): keyboard-shortcuts dispatches registry actions on the lh:kbd- convention, but the sidebar's listener sat on the pre-convention name "lh:toggle-sidebar" — a name nothing ever dispatches, so ⌘B silently did nothing while its tooltip and cheat-sheet row kept claiming the binding. A sweep of every lh: listener confirmed the sidebar was the lone stray (chat-renamed and graph-revision are deliberate non-kbd events). Fix, structural: kbdEventForAction(action) exported from src/core/keyboard.ts — the ONE spelling — and both sides compose through it (dispatcher's default branch, sidebar's toggle listener); keyboard-shortcuts re-exports the helper so the contract pin can assert no second spelling exists. The mismatch is now impossible by construction. Thinking pill: label trimmed to "Thinking" with its real shortcut via the registry-composition path — "Thinking (⇧⇥)" (cycle-thinking, rebind-live like every Hint suffix). Pins (tests/keyboard-toggle-sidebar.test.ts): the full chain — mod+b → actionForBinding → toggle-sidebar → kbdEventForAction → lh:kbd-toggle-sidebar; formatBinding("shift+tab") → ⇧⇥; the dispatcher-imports-the-same-helper contract. Live-verified on :3001: ⌘B visually toggles on home, /chat/[id], and /chat/new (sidebar 0↔288px, hamburger slot 0↔36); with the rebind lh:keymap {"toggle-sidebar":"mod+j"}: ⌘J toggles and ⌘B is inert (rebind-live); thinking pill renders "Thinking (⇧⇥)"; keymap restored, sidebar left open. --- components/keyboard-shortcuts.tsx | 8 ++++- components/model-picker.tsx | 7 ++-- components/sidebar.tsx | 14 +++++--- src/core/keyboard.ts | 12 +++++++ tests/keyboard-toggle-sidebar.test.ts | 51 +++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 tests/keyboard-toggle-sidebar.test.ts diff --git a/components/keyboard-shortcuts.tsx b/components/keyboard-shortcuts.tsx index 2bcffb2..19e6977 100644 --- a/components/keyboard-shortcuts.tsx +++ b/components/keyboard-shortcuts.tsx @@ -31,11 +31,17 @@ import { eventBinding, formatBinding, isTypingSurface, + kbdEventForAction, KEYBOARD_ACTIONS, } from "@/src/core"; import { isDictationActive } from "@/components/dictation"; import { useSettingsDialog } from "@/components/settings/settings-dialog.tsx"; +// Re-exported for the contract pin (tests/keyboard-toggle-sidebar.test.ts): +// the dispatcher composes event names through the ONE helper — a second +// spelling here is how the ⌘B bug happened. +export { kbdEventForAction }; + export function KeyboardShortcuts() { const router = useRouter(); const [cheatOpen, setCheatOpen] = useState(false); @@ -109,7 +115,7 @@ export function KeyboardShortcuts() { handlersRef.current.setCheatOpen(true); break; default: - window.dispatchEvent(new CustomEvent(`lh:kbd-${action}`)); + window.dispatchEvent(new CustomEvent(kbdEventForAction(action))); } }; window.addEventListener("keydown", onKeyDown); diff --git a/components/model-picker.tsx b/components/model-picker.tsx index 15d4536..59536d1 100644 --- a/components/model-picker.tsx +++ b/components/model-picker.tsx @@ -277,8 +277,11 @@ export function ModelControl() {