Docs

ADR-0006: Plain browser-style tab cycling (Ctrl+Tab)

Status: Active · Date: 2026-07-07

References

  • ADR-0018: Consolidated browser focus & keyboard routingrefines this decision. Ctrl+Tab still cycles positionally with no MRU popup, but now targets the center pane regardless of which dock holds focus (tranquil:show-next/previous-item-in-center instead of the plain pane:show-*-item below), and the browser-view re-dispatch has moved into a single consolidated webview-key-events listener. The Decision/Consequences below describe the original 2026-07-07 rebind; read them together with ADR-0018 for the current behavior.

Context

Ctrl+Tab / Ctrl+Shift+Tab invoked Pulsar’s MRU (most-recently-used) tab switcher: holding Ctrl popped up a modal overlay listing open tabs and cycled through them by recency of use, committing the choice on Ctrl release. That is Atom/Pulsar’s editor-oriented default, but Tranquil is browser-first — its tabs are predominantly web pages, and users expect the universal browser convention: Ctrl+Tab → next tab, Ctrl+Shift+Tab → previous tab, cycling by tab position with wraparound and no popup. This follows the container-tabs / browser-tab work (ADR-0005) that is steadily making Tranquil’s tab strip behave like a browser’s.

The plain commands already exist and already work: pane:show-next-item / pane:show-previous-item (backing Pane.activateNextItem() / activatePreviousItem()) do positional cycling with wraparound and are already bound to other keys (cmd-}, ctrl-pagedown, etc.). The MRU popup (the tabs package’s MRUListView) is only ever triggered by the *-recently-used-item commands, so nothing extra needs to render or suppress it.

Decision

Rebind Ctrl+Tab / Ctrl+Shift+Tab to the plain pane:show-next-item / pane:show-previous-item commands in the three app keymaps, dropping the two ^ctrl key-up bindings that committed the MRU selection. The tabs package’s MRU switcher is left entirely untouched — it simply stops being reached. Because a focused browser <webview> swallows the keystroke (Pulsar’s keymap never sees it), the key is forwarded over IPC and dispatched to the same plain commands from the browser view’s webview-key-events handler, before its active-item guard, so cycling keeps working even when keyboard focus stays behind in the previously-focused webview after a switch.

Options considered

  • Option A: Repoint the app keymaps to the existing plain commands (chosen) — two-line change per keymap plus the browser-view re-dispatch. Reuses commands that already ship and are already proven (they power cmd-}). No third-party code touched. Con: the MRU switcher is no longer reachable via Ctrl+Tab (see Consequences).
  • Option B: Flip the tabs.enableMruTabSwitching config off — the tabs package’s updateTraversalKeybinds() already swaps Ctrl+Tab to pane:show-next-item when that setting is disabled. Rejected: its guard bails out when another source already binds Ctrl+Tab, and Tranquil’s own keymaps do bind it — so the swap is unreliable. It also would not cover the browser webview’s manual re-dispatch path, leaving focused browser tabs still showing the popup.
  • Option C: Modify or remove the MRU code in the tabs package — Rejected: violates the “never modify third-party packages” rule and is unnecessary, since simply not dispatching the MRU commands already suppresses the popup.

Consequences

  • Recency cycling via Ctrl+Tab is gone. Cycling is now strictly positional (left→right) with wraparound. Users who relied on “jump back to the last tab” via Ctrl+Tab lose that; the MRUListView code still lives in the tabs package and could be re-bound or exposed behind a setting if demand returns — that is the re-evaluation trigger.
  • Consistent across focus contexts. Because the browser-view re-dispatch uses the same commands, cycling behaves identically whether a browser page (focused <webview>) or a native editor tab has focus.
  • Cross-platform. All three keymaps (darwin, linux, win32) were updated; macOS is the primary target.
  • Other bindings unaffected. cmd-{/cmd-}, ctrl-pageup/ctrl-pagedown, cmd-alt-left/cmd-alt-right already mapped to the same plain commands and are unchanged.

Validation

The three keymap files parse cleanly (season CSON parser) and resolve ctrl-tabpane:show-next-item, ctrl-shift-tabpane:show-previous-item. The target commands are the same ones already driving cmd-} et al., so behavior is exercised code. Final interactive confirmation (Ctrl+Tab cycles with no popup, wraps at the ends, works from a focused browser page) is done by reloading a window. Implementation touched tranquil-client keymaps and tranquil-browser/lib/tranquil-browser-view.js.