fix(desktop): forward app hotkeys from browser panes via main-process chord index

Closed
AN
3mo ago
+847-86

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 / eventToChord moved from the renderer (re-exported from resolveHotkeyFromEvent.ts so existing imports keep working), plus inputToChord (Electron Input → canonical chord via input.code), matchAppChord (index lookup with a guest-owned exclusion list), tokenToCode / chordToEventInit (canonical chord → synthetic DOM KeyboardEvent for re-dispatch).
  • browser-manager.ts (main): setAppChords() receives the renderer's canonical chord set; setupBeforeInput matches on physical key position (input.code) instead of input.key, drops the shift/alt and meta||control guards (the chord table is the gate now), keeps dedicated close-pane / reload-pane events for the legacy TabView path (which has no global CLOSE_PANE hotkey), and emits app-hotkey:<paneId> with the matched chord for everything else.
  • Router (browser.ts): setAppChords mutation + onAppHotkey subscription.
  • resolveHotkeyFromEvent.ts: pushes registeredAppChords keys to main on every rebuild (overrides / layout / adaptive-toggle changes), so the main-side index never drifts.
  • usePersistentWebview (v2): subscribes to onAppHotkey and re-dispatches a synthetic KeyboardEvent, letting the normal focus-scoped useHotkey registrations 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 test on shared/browser-pane-chords.test.ts: 16/16 pass (⌘B match, Dvorak ⌘W anchoring, shift/alt chords, guest exclusion, modifier-only nulls, tokenToCode/chordToEventInit round-trips)
  • tsc --noEmit on 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-event by physical code (IME-safe), emit app-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 onAppHotkey and dispatch a synthetic keydown on document; keep onClosePane and catch close() errors.

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.