Context
This is leaf L3 of epic #19658, blocked by #19662 (in-place measurement). It measures rows before they mount, and height-driving columns that are scrolled out of view, so heights are known when rows appear.
The Problem
In-place measurement only sees mounted rows and columns. Two cases go unmeasured:
- rows entering at wheel speed (41–71 per second on bigData, D#19645 probe 6);
- a height-driving column that is horizontally unmounted.
The estimate for unseen rows needs refining as measurements arrive.
The Architectural Reality
- The precedent:
MagicMoveText.measureChars renders a hidden measure element, waits for the VDom update, then batch-reads rects.
- The extents: a 100k-row PoC stays within native element-extent limits (about 4.8M px at mixed heights). Scaled scrolling is out of scope.
- The costs: a hidden pre-render costs about 1 ms per 100 cells and about 4 ms per 300 in three engines (probe 4). The existing scroll-to-fresh-rows pipeline takes 50–80 ms (probe 6).
The Fix
- The hidden subtree: a
visibility: hidden subtree inside the grid's own DOM and style scope, never display: none or detached, since those have no layout box. It renders the declared text cells of not-yet-mounted rows, plus off-screen height-driving columns, at their actual column widths, using L2's cell CSS. It is read through the same batched getLayoutRect call and the same generation fence.
- Measure-ahead: measure the rows that lie about one pipeline-latency of scroll beyond the overscan window. Without input nothing is measured: there is no idle sweep over the store.
- Estimates: unseen rows use the mean of measured heights, rounded to a whole pixel, never in place of a known height. The total extent is called estimated while unseen rows remain.
- Font arrival: the main thread's
document.fonts events bump the font generation, which invalidates and re-measures.
Contract Ledger
| Target surface |
Source of authority |
Proposed behavior |
Fallback / edge case |
Docs |
Evidence |
| Hidden measurement subtree |
#19658 |
Real layout box, grid style scope, actual widths, fenced |
Font not yet loaded: re-measure on arrival |
JSDoc |
Hidden vs displayed parity spec |
| Measure-ahead scheduling |
D#19645 probe 6 rule |
About one pipeline-latency beyond the overscan |
Fast thumb drag: mount at the estimate, correct later (L4) |
JSDoc |
Integrated measurement in L6 |
| Unseen-row estimate |
Operator's running-average idea |
Mean of measured heights for unseen rows only |
— |
JSDoc |
Unit |
Acceptance Criteria
Out of Scope
Scroll correction with pinning (L4), AnimateRows (L5), the 100k journey and integrated throughput acceptance (L6).
Avoided Traps
display: none or detached nodes, which measure 0.
- A general measurement service in v1.
- Measuring before fonts are ready without invalidating afterwards.
- An idle sweep over the store: on 100k rows it re-rendered 6–7 times per second without input and kept moving the extent.
Related
#19658 · #19662 (blocked by) · D#19645
Origin Session ID: daac81df-5205-405e-a3af-aec56e5cca45
Context
This is leaf L3 of epic #19658, blocked by #19662 (in-place measurement). It measures rows before they mount, and height-driving columns that are scrolled out of view, so heights are known when rows appear.
The Problem
In-place measurement only sees mounted rows and columns. Two cases go unmeasured:
The estimate for unseen rows needs refining as measurements arrive.
The Architectural Reality
MagicMoveText.measureCharsrenders a hidden measure element, waits for the VDom update, then batch-reads rects.The Fix
visibility: hiddensubtree inside the grid's own DOM and style scope, neverdisplay: noneor detached, since those have no layout box. It renders the declared text cells of not-yet-mounted rows, plus off-screen height-driving columns, at their actual column widths, using L2's cell CSS. It is read through the same batchedgetLayoutRectcall and the same generation fence.document.fontsevents bump the font generation, which invalidates and re-measures.Contract Ledger
Acceptance Criteria
Out of Scope
Scroll correction with pinning (L4),
AnimateRows(L5), the 100k journey and integrated throughput acceptance (L6).Avoided Traps
display: noneor detached nodes, which measure 0.Related
#19658 · #19662 (blocked by) · D#19645
Origin Session ID: daac81df-5205-405e-a3af-aec56e5cca45