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
.urlreverse-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. Thectrl-tabbinding reverts to plain positional cycling (ADR-0006); the browser model keeps itsfilePathplumbing because the core.urlopen/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
.urlgesture (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 numericNNN-prefix — the file is named<title>.url, withuniquePath’s counter suffix resolving collisions; (2) the base name comes from the tab’s live page<title>(via aliveTabTitlereader that mirrorstranquil-browser’stabDisplayTitleand the vertical Tabs pane’slabelFor), falling back to the URL hostname. Everything else in this ADR — theautoRevealsync, theselectActiveFilepatch, the.urlreverse-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:
- Upstream
TreeView#selectActiveFile()callsdeselect()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 resultingdeselect()erased the click’s own selection. - A browser tab knows its http URL but lost its source
.urlpath on reload (serialize()droppedfilePath), so restored tabs couldn’t be mapped back to their bookmark. - 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+Tabdid 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.autoRevealon /focusOnRevealoff (tranquil-config); aTree View: Sync Selection With Active Tabcommand + View-menu toggle (tranquil-automations). - Keep-selection patch — wrap the tree-view instance’s
selectActiveFileso a pathless / not-yet-rendered active item leaves the current selection intact. - Durable
.urlidentity — persistfilePathin the browser model’sserialize()/copy()(tranquil-browser); backfill legacy tabs by reverse-mapping a tab’s URL to its.urlfile (matching each file’sURL=line, cached index) inside the same patch. - ⌥⌘W close + trash — a shared helper reachable from both an
atom-workspacecommand/keymap and the browser view’skeyHandler(a focused webview swallows the keys), usingshell.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’ssaveNumberedLinknumbering 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+Tabrebind to newtranquil-automations:cycle-{next,previous}-center-tabcommands (acting ongetCenter().getActivePane()), refining ADR-0006 (whosepane:show-next-itemtarget only cycled when the center had focus).
Options considered
- Sync mechanism — reuse
autoReveal+ patchselectActiveFile(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). .urlidentity — persistfilePath+ URL-reverse-map backfill (chosen): fixes reloaded tabs at the root and repairs legacy tabs live; bothselectActiveFileandrevealActiveFilethen work via the normalgetPath()path. Rejected: scanning.urlfiles on every tab switch (I/O per switch); changinggetURI()to return the.urlpath (breaks reopen/dedup tracking).- Delete semantics — Trash, no confirm (chosen):
shell.trashItemis recoverable and matches tree-view / tranquil-drag-drop; a deliberate shortcut shouldn’t nag. Rejected: permanentunlink(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
.urlbookmark tabs); bookmark pruning is one keystroke; browser tabs survive reload with their bookmark identity;Ctrl+Taband ⌥⌘W work no matter what’s focused. - Risk — instance monkey-patching.
selectActiveFileis 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 intranquil-automationsand reverts on deactivate. - Minor: the
.urlreverse-map is cached (5s TTL), so a freshly-created bookmark can take a moment to resolve; deleting a.urlleaves 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+Tabfocus-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 corepane:show-*and remain center-focus-only; onlyCtrl+Tabwas 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.