Browser Focus & Key Routing — Delivery Notes
Delivery summary for the browser focus/keyboard-routing consolidation. For the decision record and the why, read ADR-0018: Consolidated browser focus & keyboard routing. This note says what shipped, where, and in what order. It refines ADR-0006 (Ctrl+Tab is now center-scoped).
Status
Implemented; regression suites green; manual pass clean. Shipped across tranquil-browser (branch refactor/browser-focus-routing), the tranquil-config center-cycle commands and tranquil-client keymaps (landed on mainline first), and CDP suites in tranquil-test-suite.
Why
A browser tab embeds its page in an Electron <webview> guest; a focused guest swallows keys before
Pulsar’s keymap, so every shortcut was delivered two ways (keymap command and a guest keyHandler/webview-key-events path). Those copies drifted, and a run of “after X, key Y doesn’t
work” bugs (ctrl+tab dead after cmd-click; cmd+w/cmd+t/cmd+s dead after ctrl+tab) came from the
divergence, three separate cmd-k split implementations, and an unconditional guest-focus side-channel
racing Atom’s own focus delegation. The fix is one source of truth per concern.
What shipped
Landed first on mainline (the prior surgical fixes): tree-view keep-selection patch + ctrl+tab
center-cycle commands (tranquil-config), the ctrl-tab rebind (tranquil-client keymaps), and the
background-tab open / cmd+w intercept / cmd+s save-url (tranquil-browser).
Then the consolidation, in phases (one commit each) on refactor/browser-focus-routing:
| Phase | What | Where |
|---|---|---|
| 0 | win/linux keymap parity (ctrl-l/ctrl-r/ctrl-shift-r) | keymaps/tranquil-browser.json |
| 1 | one activeBrowser() accessor + declarative command table (uniform abort-fallthrough; new print command); focusUrlBar() extracted; saveCurrentTabUrl(browser?) | new lib/browser-commands.js, lib/tranquil-browser.js, lib/tranquil-browser-view.js |
| 2 | guest keyHandler collapsed onto keyToCommandId() → dispatch the same commands; only F12/F5/F10 stay view-local | lib/tranquil-browser-view.js, lib/browser-commands.js |
| 3 | one window-level webview-key-events listener (browserViewForWcId / handleGlobalGuestKey), replacing the per-tab ones (also fixes a listener leak) | lib/tranquil-browser.js, lib/utils.js |
| 4 | one cmd-k split owner; host interceptor defers to core for non-browser items | new lib/split-chord.js, lib/tranquil-browser.js |
| 5 | view focus() contract (focuses the guest via Atom’s pane-element delegation); removed the onDidChangeActivePaneItem side-channel | lib/tranquil-browser-view.js, lib/tranquil-browser.js |
Safety net (tests)
New CDP smoke suites in tranquil-test-suite (smoke/suites/), green before the refactor and after
every phase: focus-ctrl-tab-center, focus-treeview-keep-selection, focus-background-tab, plus a save-url command-path check added to url-bookmark-save. Run with deno task test.
CDP gotcha worth remembering: evals must return primitives — returning atom.workspace.open()’s
resolved value (the browser model) makes returnByValue fail with “Object reference chain is too
long”. Wrap opens as …open(url).then(() => true) and make command handlers return true.
What needs a human (not CDP-automatable)
The suites cover command behavior, host-level focus, tree-view selection, and .url output, but
cannot inject a keydown into a focused guest webview. Verified by a manual pass: the guest-focused
shortcut set (cmd-t/l/f/r/p/s, cmd-shift-a, alt-arrows, F12/F5/F10), the three cmd-k split contexts
(editor via core, focused guest, stuck-focus), and the Phase-5 focus flows (tab click, ctrl+tab across
dock/center, close-focus-next, vertical-tab-list click, window reload).
Follow-ups
A few minor quirks were noted during manual testing and deferred to a post-refactor cleanup pass; they are not regressions from this work.