fix(desktop): forward app hotkeys from browser panes via main-process chord index
Fixes #6186
Bug
With a browser pane focused, every app shortcut dies: the pane is a guest webContents (out-of-process <webview>), so a keydown while the guest has focus never reaches the host document where react-hotkeys-hook listens. The only interception path is main's before-input-event, and that handler hardcoded exactly two chords (⌘W / ⌘R matched by produced character via input.key). ⌘B (TOGGLE_WORKSPACE_SIDEBAR), ⌘L (TOGGLE_SIDEBAR) and every other renderer-registered chord fell through to the page.
Fix
Forward the app's full chord index to main and match there by physical position:
shared/browser-pane-chords.ts(new, pure — no Electron/DOM/zustand imports):normalizeToken/canonicalizeChord/eventToChordmoved from the renderer (re-exported fromresolveHotkeyFromEvent.tsso existing imports keep working), plusinputToChord(ElectronInput→ canonical chord viainput.code),matchAppChord(index lookup with a guest-owned exclusion list),tokenToCode/chordToEventInit(canonical chord → synthetic DOMKeyboardEventfor re-dispatch).browser-manager.ts(main):setAppChords()receives the renderer's canonical chord set;setupBeforeInputmatches on physical key position (input.code) instead ofinput.key, drops theshift/altandmeta||controlguards (the chord table is the gate now), keeps dedicatedclose-pane/reload-paneevents for the legacy TabView path (which has no globalCLOSE_PANEhotkey), and emitsapp-hotkey:<paneId>with the matched chord for everything else.- Router (
browser.ts):setAppChordsmutation +onAppHotkeysubscription. resolveHotkeyFromEvent.ts: pushesregisteredAppChordskeys to main on every rebuild (overrides / layout / adaptive-toggle changes), so the main-side index never drifts.usePersistentWebview(v2): subscribes toonAppHotkeyand re-dispatches a syntheticKeyboardEvent, letting the normal focus-scopeduseHotkeyregistrations resolve it exactly as they do when the host document has focus.- Exclusions: ⌘F / ⌘P / ⌘G stay with the guest (page find / print / find-next are more useful than the pane-scoped app bindings sharing those chords).
- Docs:
KEYBOARD_SYSTEM.md"Known gaps" row.
This also fixes the Dvorak half of the report: the old handler matched whichever physical key prints "w" (input.key), while the registry binds ⌘W to physical KeyW — two different keys on non-QWERTY layouts. Matching on input.code anchors both sides to physical position.
Validation
bun testonshared/browser-pane-chords.test.ts: 16/16 pass (⌘B match, Dvorak ⌘W anchoring, shift/alt chords, guest exclusion, modifier-only nulls,tokenToCode/chordToEventInitround-trips)tsc --noEmiton apps/desktop: 0 errors- Biome: clean
- Manual repro from the issue (⌘B / ⌘L in a focused browser pane) now dispatches the chord; ⌘W / ⌘R unchanged; ⌘, / ⌘/ still work via menu accelerators.
Summary by cubic
Forward app hotkeys from focused browser panes by mirroring the renderer’s chord index to main and re-dispatching matches on the host document. Adds reliable retry/backoff for chord sync, restores dead shortcuts (⌘B/⌘L), and anchors to physical keys across layouts (e.g., Dvorak ⌘W).
- Bug Fixes
- Main: match chords in
before-input-eventby physicalcode(IME-safe), emitapp-hotkey:<paneId>, and keep dedicated ⌘W/⌘R close/reload events. - Renderer: push the canonical chord index to main on every rebuild with bounded retry backoff and stale-retry cancellation; sends the newest snapshot after in-flight mutations settle.
- Shared: exclude find/print/find-next and guest editing shortcuts; editing exclusions use the produced key (
input.key) and ignore shift-gated chords (e.g., ⌘⇧C), so undo on non‑QWERTY stays with the page while COPY_PATH still forwards. - Browser panes: subscribe to
onAppHotkeyand dispatch a synthetic keydown ondocument; keeponClosePaneand catchclose()errors.
- Main: match chords in
Summary by CodeRabbit
- New Features
- Browser-pane keyboard shortcuts now support synchronized application hotkeys.
- Matching shortcuts are forwarded consistently across panes and keyboard layouts.
- Application hotkey updates are synchronized automatically, including retry handling.
- Bug Fixes
- Improved handling of modifier combinations, aliases, and ignored key events.
- Preserved browser-native Find, Print, editing, close, and reload shortcuts.
- Documentation
- Documented browser-pane hotkey forwarding behavior.