Docs

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:

PhaseWhatWhere
0win/linux keymap parity (ctrl-l/ctrl-r/ctrl-shift-r)keymaps/tranquil-browser.json
1one 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
2guest keyHandler collapsed onto keyToCommandId() → dispatch the same commands; only F12/F5/F10 stay view-locallib/tranquil-browser-view.js, lib/browser-commands.js
3one 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
4one cmd-k split owner; host interceptor defers to core for non-browser itemsnew lib/split-chord.js, lib/tranquil-browser.js
5view focus() contract (focuses the guest via Atom’s pane-element delegation); removed the onDidChangeActivePaneItem side-channellib/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.