Docs

Vertical Tabs Dock Toggle — Delivery Notes

The vertical Tabs navigator (ADR-0012) defaults to the right dock, but it’s sometimes wanted on the left. This adds a single tab-bar control to the Tabs pane that flips it right ⇄ left, keeping the width you were looking at. This note is the delivery write-up — what shipped and the two dock frictions it exposed.

Status

Implemented and verified live. In tranquil-automations (lib/tab-controls.js, lib/pane-controls.js).

PieceState
tabs-toggle-dock control in the Tabs pane’s tab-bar cluster
Direction-aware icon/title (shows the side it will move to)
Dock width preserved across the flip
pane-controls supports function-valued icon/title

The behavior wanted

“Add a control to the Tabs pane that toggles its dock between left and right. Default stays right, but sometimes I want it on the left.” And, on first cut: “when flipping, retain the width — going left→right it’s quite narrow on the right.”

What shipped

Two owned files, no core edits:

  • The control (tab-controls.js) — a second item added to the existing Tabs-view registration (alongside “Close All Tabs”). toggleTabsDock() finds the live view, reads its current dock via pane.getContainer().getLocation(), and pane.moveItemToPane(view, targetDock.getActivePane(), …) into the opposite side dock, then show()s and activates it. The icon/title are functions of the current location: a layout-sidebar-left glyph + “Move Tabs to left dock” while on the right, flipping to layout-sidebar-right while on the left.
  • Function-valued controls (pane-controls.js) — renderCluster now evaluates icon/title when they’re functions (backward-compatible with the existing string defs), so the toggle reflects live state. The move re-renders both the source and target panes (item leaving / arriving), so the button updates to the correct direction immediately after each flip.

The gotchas

  1. Width collapsed to the preferred size on move. Removing the last item from a dock resets that dock’s size to null (dock.js handleDidRemovePaneItem). The target dock, seeing a null size on handleDidAddPaneItem, falls back to getInitialSize() — i.e. the item’s getPreferredWidth() (220px) — so a wider left dock arrived narrow on the right. Fix: read the source dock’s state.size before the move and reapply it to the target dock after (targetDock.setState({ size })). Dock exposes no public size getter/setter, so this touches state/setState directly — internal but stable, and not a core-file edit.
  2. PaneContainer ≠ Dock. pane.getContainer() returns the PaneContainer (which has getLocation()), not the Dock that owns state.size. The size read/write goes through atom.workspace.getLeftDock() / getRightDock(), keyed off the location.

Deferred

  • The control only renders in the center/left/right panes (pane-controls doesn’t inject into the bottom dock), so a Tabs view dragged to the bottom won’t show the toggle. Out of scope — the toggle is a left/right affordance, and the default stays the right dock.