Docs

ADR-0008: Tree-view ↔ active-tab sync and bookmark-tab lifecycle

Status: Superseded by ADR-0012 · Date: 2026-07-11 · Superseded: 2026-07-21

Superseded. The sync + bookmark-tab lifecycle described below was removed in full. In practice it added significant cross-package complexity (tree-view patches, a .url reverse-map, close-and-trash, drag-tab-to-save) and made the file tree behave in ways that aren’t idiomatic for a browser-first editor. It is replaced by a simpler, more browser-native option — rendering a pane’s tabs vertically in the right dock — recorded in ADR-0012. The ctrl-tab binding reverts to plain positional cycling (ADR-0006); the browser model keeps its filePath plumbing because the core .url open/save system relies on it. The record below is retained as history.

Update (2026-07-24) — drag-tab-to-save restored, standalone. The one piece users missed was the drag-a-browser-tab-onto-a-tree-view-folder → save a .url gesture (tranquil-drag-drop). It has been brought back on its own, decoupled from the removed sync/bookmark-tab lifecycle, with two deliberate changes from the version described below: (1) no numeric NNN- prefix — the file is named <title>.url, with uniquePath’s counter suffix resolving collisions; (2) the base name comes from the tab’s live page <title> (via a liveTabTitle reader that mirrors tranquil-browser’s tabDisplayTitle and the vertical Tabs pane’s labelFor), falling back to the URL hostname. Everything else in this ADR — the autoReveal sync, the selectActiveFile patch, the .url reverse-map, ⌥⌘W close-and-trash — stays removed.

Context

Tranquil is browser-first (ADR-0002). A common workflow is folders of .url bookmark files (often numeric-prefixed, 001-…, 002-…) opened as browser tabs. Users expect the tree-view to act as a navigation sidebar that stays in lockstep with the active tab (switch tab → that file highlights), and expect pruning a bookmark to be a single action rather than “close the tab, then hunt the file down in the tree and delete it.”

Building this surfaced three gaps, all rooted in the same place — Tranquil leans on pathless tabs and docks far more than upstream Pulsar assumes:

  1. Upstream TreeView#selectActiveFile() calls deselect() whenever the active pane item has no file path. Because browser tabs are pathless, every switch to one wiped the tree highlight — and a single tree click appeared to need two, because the pending open left a browser tab active and the resulting deselect() erased the click’s own selection.
  2. A browser tab knows its http URL but lost its source .url path on reload (serialize() dropped filePath), so restored tabs couldn’t be mapped back to their bookmark.
  3. Tab-oriented actions invoked while a dock (the tree-view) is focused targeted the dock’s pane, not the center — so ⌘-based tab commands and Ctrl+Tab did nothing.

tree-view is third-party Pulsar core and is never modified — all behavior lives in owned packages (tranquil-config, tranquil-automations, tranquil-browser) plus the fork’s keymaps. This work also refines the Ctrl+Tab binding from ADR-0006 (see Decision) and builds on the browser-tab model from ADR-0005.

Decision

Deliver tree ↔ tab sync by enabling upstream tree-view.autoReveal and monkey-patching the live tree-view instance from an owned package so pathless tabs keep (not clear) the selection; give browser tabs a durable .url identity (persist + backfill) so they map to their bookmark; add ⌥⌘W to close a browser tab and move its .url to the Trash; and target the center pane for tab actions invoked while a dock is focused. Concretely:

  • Sync toggle — default tree-view.autoReveal on / focusOnReveal off (tranquil-config); a Tree View: Sync Selection With Active Tab command + View-menu toggle (tranquil-automations).
  • Keep-selection patch — wrap the tree-view instance’s selectActiveFile so a pathless / not-yet-rendered active item leaves the current selection intact.
  • Durable .url identity — persist filePath in the browser model’s serialize()/copy() (tranquil-browser); backfill legacy tabs by reverse-mapping a tab’s URL to its .url file (matching each file’s URL= line, cached index) inside the same patch.
  • ⌥⌘W close + trash — a shared helper reachable from both an atom-workspace command/keymap and the browser view’s keyHandler (a focused webview swallows the keys), using shell.trashItem (recoverable), no confirmation, with a toast.
  • Drag-tab-to-save — dropping a browser tab onto a tree-view folder auto-saves it as a numbered NNN-…​.url (no prompt), reusing opt+cmd+click’s saveNumberedLink numbering keyed to the drop-target folder (tranquil-drag-drop). This makes bookmark creation and the numeric ordering scheme consistent across the link-click and tab-drop gestures.
  • Center targeting under dock focus — the ⌥⌘W helper falls back to getCenter().getActivePaneItem(); Ctrl+Tab/Ctrl+Shift+Tab rebind to new tranquil-automations:cycle-{next,previous}-center-tab commands (acting on getCenter().getActivePane()), refining ADR-0006 (whose pane:show-next-item target only cycled when the center had focus).

Options considered

  • Sync mechanism — reuse autoReveal + patch selectActiveFile (chosen): the reveal/highlight logic already ships upstream; we only stop it from deselecting on pathless tabs. Con: monkey-patches a third-party instance method (fragile to upstream changes). Alternatives rejected: a fully custom active-item observer (reinvents upstream reveal/expand) and editing tree-view (violates the never-modify-core rule).
  • .url identity — persist filePath + URL-reverse-map backfill (chosen): fixes reloaded tabs at the root and repairs legacy tabs live; both selectActiveFile and revealActiveFile then work via the normal getPath() path. Rejected: scanning .url files on every tab switch (I/O per switch); changing getURI() to return the .url path (breaks reopen/dedup tracking).
  • Delete semantics — Trash, no confirm (chosen): shell.trashItem is recoverable and matches tree-view / tranquil-drag-drop; a deliberate shortcut shouldn’t nag. Rejected: permanent unlink (unrecoverable); confirm-every-time (defeats a one-keystroke action).
  • Dock-focus tab actions — center-targeting commands + center active item (chosen): one small command pair, no third-party edits, works from any focus. Rejected: operating on the tree’s selected entry instead (surprising when it differs from the active tab); forcing focus into the center on every cycle (heavier, and the sync already moves the highlight).

Consequences

  • Easier: the tree mirrors the active tab (including .url bookmark tabs); bookmark pruning is one keystroke; browser tabs survive reload with their bookmark identity; Ctrl+Tab and ⌥⌘W work no matter what’s focused.
  • Risk — instance monkey-patching. selectActiveFile is wrapped on the live tree-view instance. A Pulsar tree-view upgrade that changes that method’s shape is the re-evaluation trigger; the patch is self-contained in tranquil-automations and reverts on deactivate.
  • Minor: the .url reverse-map is cached (5s TTL), so a freshly-created bookmark can take a moment to resolve; deleting a .url leaves a gap in the numeric prefix sequence (accepted).
  • Refines ADR-0006 without superseding it: the plain positional, no-MRU cycling decision stands; only the bound command changed (to a center-targeting one) so cycling survives dock focus.

Unfinished / deferred work

  • Tab-strip order ↔ tree order. A non-prefixed file sorts to the bottom of the (alphanumeric) tree but opens mid-strip; making either follow the other was deferred — tree-view has no per-folder sort hook, “open order” isn’t persisted, and auto-reordering fights manual drag-ordering. Tree stays alphanumeric; tabs stay in open order.
  • Ctrl+Tab focus-follow. Cycling from a focused tree changes the center’s active tab but leaves focus on the tree (the highlight follows via sync); pulling focus into the center is a possible future tweak.
  • Other cycling keys (cmd-{/cmd-}, cmd-alt-←/→, ctrl-pageup/ctrl-pagedown) still use core pane:show-* and remain center-focus-only; only Ctrl+Tab was retargeted.
  • No live menu checkmark for the sync toggle — Atom package menus can’t reflect config state, so the toggle reports via a toast instead.

Validation

Verified live in a running window via DevTools instrumentation: the tree selection follows the active tab (single click highlights on the first click; switching to a .url browser tab backfills its path and selects the matching bookmark; selection persists across pathless-tab switches); ⌥⌘W closes the active browser tab and trashes its .url (recoverable) from webview, tab-strip, and tree focus; and Ctrl+Tab cycles the center tabs from a focused tree-view. Implementation spans tranquil-config, tranquil-automations, tranquil-browser, and the tranquil-client keymaps. Full delivery write-up: Tree View Sync — Delivery Notes.