Context
The headless, unit-testable half of #14771 (tab-overflow affordance, ADR 0029 §2.4, docking epic #13158). Per #14771's AC-1 the hidden-set is a pure computation spec; this leaf lands + proves that algorithm unit-first, de-risking the tricky part (the active-always-visible invariant + edge cases) before the browser-gated visual dropdown consumes it. Spec posted on #14771 (lead design note).
The Fix
A pure DockLayoutAdapter.computeTabOverflow({items, extent, activeItemId, controlWidth}) → {visible: String[], hidden: String[]} — deterministic over its inputs, no DOM read, no render. The adapter measures header widths at render time (the browser half) and passes them in; this function is pure over those measurements.
items — ordered [{id, headerWidth}] in projection order.
extent — available header-strip width.
activeItemId — invariant: never in hidden (cannot overflow the tab you are viewing).
controlWidth — the overflow control's own width, reserved from extent only when overflow occurs.
Acceptance Criteria
Out of Scope
The visual affordance (the dropdown control, header-width measurement, appear/disappear on render, interaction coverage on examples/dashboard/dock) — that is the browser-gated half, stays under #14771. No dock-specific tab-container fork (inherited constraint).
Related
Parent #13158 · headless core of #14771 · authority ADR 0029 §2.4. Decision Record impact: aligned-with ADR 0029.
Dup-sweep: rg computeTabOverflow|tabOverflow src/ test/ → none; open-issue sweep → only #14771 (the parent).
Context
The headless, unit-testable half of #14771 (tab-overflow affordance, ADR 0029 §2.4, docking epic #13158). Per #14771's AC-1 the hidden-set is a pure computation spec; this leaf lands + proves that algorithm unit-first, de-risking the tricky part (the active-always-visible invariant + edge cases) before the browser-gated visual dropdown consumes it. Spec posted on #14771 (lead design note).
The Fix
A pure
DockLayoutAdapter.computeTabOverflow({items, extent, activeItemId, controlWidth}) → {visible: String[], hidden: String[]}— deterministic over its inputs, no DOM read, no render. The adapter measures header widths at render time (the browser half) and passes them in; this function is pure over those measurements.items— ordered[{id, headerWidth}]in projection order.extent— available header-strip width.activeItemId— invariant: never inhidden(cannot overflow the tab you are viewing).controlWidth— the overflow control's own width, reserved fromextentonly when overflow occurs.Acceptance Criteria
computeTabOverflowreturns the correct{visible, hidden}for the 5 pinned cases: everything-fits (no control, no reserved width); overflow (reservecontrolWidth, pack in order); active-always-visible (force-keep the active item, push the last-fitting non-active to hidden); single item wider thanextent(visible, degenerate);hiddenpreservesitemsorder.activeItemIdpath per #14771 AC-2).Out of Scope
The visual affordance (the dropdown control, header-width measurement, appear/disappear on render, interaction coverage on
examples/dashboard/dock) — that is the browser-gated half, stays under #14771. No dock-specific tab-container fork (inherited constraint).Related
Parent #13158 · headless core of #14771 · authority ADR 0029 §2.4. Decision Record impact: aligned-with ADR 0029. Dup-sweep:
rg computeTabOverflow|tabOverflow src/ test/→ none; open-issue sweep → only #14771 (the parent).