Context
Found in manual testing of apps/workstation on dev (2026-10-22): resorting tab headers in the pane that holds 12 tabs and an overflow ⋯. In @tobiu's screen recording, two drops (at 32.4 s and 47.5 s) are followed by a 200–250 ms transient. The pane's header spans the whole frame with most of the 12 tabs in one row, the pane body goes black, and the neighbouring 100k Matrix pane disappears. Then the layout snaps back. The flicker and reflow reach well outside the pane being dragged in.
Further frames from the same recording, all inside one tab-drag operation (@tobiu's screenshots follow in a comment):
- At about 36 s: the header spans the full width with six tabs and the overflow
⋯ in one row. Its band styling is gone and the pane body is black.
- Another moment: the header looks normal ("Workspace Topology Inspector | Priority Alert Observatory"), but the neighbouring 100k Matrix grid is black.
Measurements
Headless Chromium, 2200×1000, at 8bb3574d0f (its src/, apps/ and resources/scss/ are identical to dev at 84b711878a). A requestAnimationFrame sampler records the strip, its tab buttons, the pane and the 100k Matrix pane every frame.
| gesture |
frames after release with > 4 tab buttons laid out |
when |
tab right edge |
| in-strip swap (2nd tab 180 px left) |
3 |
+95 … +211 ms |
x 3032, in a strip of x 1237–1863 |
| a drag with a 45 px rise out of the strip and back (2nd or 3rd tab) |
3–5 in each of 4 runs |
+91 … +224 ms |
x 3032 |
| drag into the pane body (no tab-set change) |
0 |
— |
— |
In those frames every one of the 12 tab buttons is in flow (position: relative) inside a 626 px strip. In this layout the strip, pane and grid rects stay unchanged, so headless it is a flash inside the strip.
The out-of-pane effects in the recording are operator-observed, not reproduced headless: the strip running over the neighbour, the band styling lost, the body and the neighbouring grid going black. Reading the frames, they are the same measuring window in a layout where the restored tabs overflow past the pane, and the grid blackout is the grid re-rendering after that reflow. That reading is inferred.
The Problem
A reorder changes the tab set, so the overflow plugin recaptures natural widths. Overflow#project(true) step 1 puts every withheld header back into the live strip: setSilent({hidden: false}), delete vdom.removeDom, then owner.promiseUpdate(). It measures them with getDomRect, measures the strip extent in a second round trip, and only then applies the split again. Those are separate App↔main round trips, so the browser paints the restored state in between. The measuring window is visible for several frames on every reorder.
The plugin's own @summary says overflowing tabs "collapse behind one control whose menu reaches them". For the duration of the recapture they do not, and the user sees all of them.
The Architectural Reality
src/tab/plugin/Overflow.mjs: onTabSetChange() runs project(true) for insert/moveTo. project() step 1 restores the withheld headers (comment: "Restore them under this method's measuring latch, then reconcile their closest common parent once so getDomRect observes natural geometry"), and step 3 / applySplit withholds them again.
- The natural-width cache is keyed by button id (
naturalWidths[button.id]).
- Precedent for a measuring frame becoming visible: #19094 (a newborn split's overflow control flashed for one frame).
The Fix
Measure natural widths without painting them in the strip:
- No live re-insert. During the measuring window, restored headers take
position: absolute; visibility: hidden (out of flow, unpainted, still at max-content width), or are measured in an off-strip layer. The live strip never shows them and never reflows.
- Recapture only what is missing. A reorder changes order, not widths, and the cache is keyed by button id. Recapture only ids the cache lacks, such as newly inserted or re-created buttons. Check whether the dock's re-projection re-creates tab buttons on a reorder before relying on this.
Acceptance Criteria
Out of Scope
- Headers leaving the strip during the drag: #19988, a separate mechanism.
- #19982, the stale partition after maximize.
Related
- #19094 (closed): a measuring frame made visible, the overflow-control case.
- #19982, #19984, #19986: the same testing pass, the same plugin and pane.
Live latest-open sweep: checked latest 20 open issues at 2026-10-22T09:52:58Z; no equivalent found.
Keyword sweep: "tab reorder flicker overflow", "overflow recapture flash", "tab drag header top-left", "sort zone header flies"; closed adjacent only (#19094, #16424, #19533).
A2A claim sweep: no overlapping lane-claim.
MC sweep: "tab header resorting flickers on drop…", 6 results, no prior decision.
Own-assignment sweep: none overlapping.
Origin Session ID: 70e59a84-efd6-4e7a-9262-c5478bd314ff
Retrieval Hint: "tab reorder flashes all hidden tabs, Overflow recapture measuring window painted, removeDom restore"
Context
Found in manual testing of
apps/workstationondev(2026-10-22): resorting tab headers in the pane that holds 12 tabs and an overflow⋯. In @tobiu's screen recording, two drops (at 32.4 s and 47.5 s) are followed by a 200–250 ms transient. The pane's header spans the whole frame with most of the 12 tabs in one row, the pane body goes black, and the neighbouring 100k Matrix pane disappears. Then the layout snaps back. The flicker and reflow reach well outside the pane being dragged in.Further frames from the same recording, all inside one tab-drag operation (@tobiu's screenshots follow in a comment):
⋯in one row. Its band styling is gone and the pane body is black.Measurements
Headless Chromium, 2200×1000, at
8bb3574d0f(itssrc/,apps/andresources/scss/are identical todevat84b711878a). ArequestAnimationFramesampler records the strip, its tab buttons, the pane and the 100k Matrix pane every frame.In those frames every one of the 12 tab buttons is in flow (
position: relative) inside a 626 px strip. In this layout the strip, pane and grid rects stay unchanged, so headless it is a flash inside the strip.The out-of-pane effects in the recording are operator-observed, not reproduced headless: the strip running over the neighbour, the band styling lost, the body and the neighbouring grid going black. Reading the frames, they are the same measuring window in a layout where the restored tabs overflow past the pane, and the grid blackout is the grid re-rendering after that reflow. That reading is inferred.
The Problem
A reorder changes the tab set, so the overflow plugin recaptures natural widths.
Overflow#project(true)step 1 puts every withheld header back into the live strip:setSilent({hidden: false}),delete vdom.removeDom, thenowner.promiseUpdate(). It measures them withgetDomRect, measures the strip extent in a second round trip, and only then applies the split again. Those are separate App↔main round trips, so the browser paints the restored state in between. The measuring window is visible for several frames on every reorder.The plugin's own
@summarysays overflowing tabs "collapse behind one control whose menu reaches them". For the duration of the recapture they do not, and the user sees all of them.The Architectural Reality
src/tab/plugin/Overflow.mjs:onTabSetChange()runsproject(true)forinsert/moveTo.project()step 1 restores the withheld headers (comment: "Restore them under this method's measuring latch, then reconcile their closest common parent once so getDomRect observes natural geometry"), and step 3 /applySplitwithholds them again.naturalWidths[button.id]).The Fix
Measure natural widths without painting them in the strip:
position: absolute; visibility: hidden(out of flow, unpainted, still at max-content width), or are measured in an off-strip layer. The live strip never shows them and never reflows.Acceptance Criteria
test/playwright/e2e/workstation/(e.g.WorkstationTabReorderNoFlashNL.spec.mjs) drives an in-strip reorder with a real pointer in the 12-tab pane and samples every frame (requestAnimationFrame) from release to settle. It asserts the visible-tab count never exceeds the settled count, and that the strip, pane and 100k Matrix rects stay stable. It must be red ondev(3+ frames with 12 tabs in the strip) and green with the fix, and it joins the hosted Workstation tier (#19655).WorkstationTabOverflowCapNL,WorkstationFocusedTabOverflowNLand the overflow unit specs stay green.Out of Scope
Related
Live latest-open sweep: checked latest 20 open issues at 2026-10-22T09:52:58Z; no equivalent found. Keyword sweep: "tab reorder flicker overflow", "overflow recapture flash", "tab drag header top-left", "sort zone header flies"; closed adjacent only (#19094, #16424, #19533). A2A claim sweep: no overlapping lane-claim. MC sweep: "tab header resorting flickers on drop…", 6 results, no prior decision. Own-assignment sweep: none overlapping.
Origin Session ID: 70e59a84-efd6-4e7a-9262-c5478bd314ff Retrieval Hint: "tab reorder flashes all hidden tabs, Overflow recapture measuring window painted, removeDom restore"