Context
This is leaf L1 of epic #19658 (content-sized rows), graduated from D#19645. It replaces fixed-height arithmetic with one per-view geometry model, opt-in, before any measurement exists. Heights enter through an internal setter that tests drive directly, so geometry correctness can be proved in isolation. Measurement leaves L2 and L3 feed the same setter later.
The Problem
Every row-position consumer multiplies or divides by one rowHeight (dev bf91293bde):
Body: pool rows (L413), start index (L475), scrollToIndex (L1408–1411), extent (L1634).
Row: height and transform (L399–400).
View.syncBodies: start index (L380).
VerticalScrollbar: extent (L152).
Variable heights break all of them at once. The two extents must also agree: Body and the proxy scrollbar write their extents independently today.
The Architectural Reality
The App Worker owns geometry, and the three bodies (start, center, end) of one view share each row's height. GridRowScrollPinning reads scroll-origin deltas, not row heights (L117 and L159), and is unaffected here; L4 handles corrections.
The Fix
- Opt-in mode on
grid.Container, propagated to its bodies. Fixed height stays the default, and its arithmetic is untouched.
- The geometry model, per view:
- a record-id-keyed height map, with the configured
rowHeight as the estimate for unknown rows (the measured mean replaces it in L3);
- a
Float64Array of prefix offsets, rebuilt once per batch of height changes;
- binary-search offset → row lookup.
- Consumers in this mode take it:
Row offset and height, View.syncBodies, pixel-based pool coverage (viewport plus overscan, capped by row count, with the smallest known height as the floor), both extents, and Body.scrollToIndex.
- One internal setter (record id → px) applies heights and rebuilds offsets. There is no public height callback: consumer sizing policies stay deferred per D#19645.
- The viewport height that pixel coverage and keyboard alignment measure against is the view's own layout height (
Container.passSizeToBody). (Amended during implementation, see below.)
Contract Ledger
| Target surface |
Source of authority |
Proposed behavior |
Fallback / edge case |
Docs |
Evidence |
Opt-in config on grid.Container (name settled at intake, e.g. contentSizedRows) |
#19658 / D#19645 |
Off by default; on, the bodies use the geometry model |
Off is byte-for-byte today's path |
JSDoc + Anchor & Echo |
Unit + fixed-height e2e control |
| Internal height setter (record id → px) |
This leaf |
Updates heights and rebuilds prefix offsets per batch |
Unknown rows use the estimate |
JSDoc |
Unit |
Body.scrollToIndex in this mode |
STEP_BACK point 2 |
Uses offsets, not index × rowHeight |
— |
JSDoc |
Unit + e2e |
availableHeight (Container.passSizeToBody) |
This leaf, e2e finding |
The view's layout height |
Both modes: fixed grids' keyboard alignment is corrected too |
Inline comment |
e2e in both modes, with a negative control |
Acceptance Criteria
Amendment (implementation finding)
The browser e2e for "the final row is fully visible" failed in both modes, so the cause is not row geometry. Container.passSizeToBody derived availableHeight as container − header − footer, which also counted the container borders and the horizontal scrollbar below the view: 770px against a 751px view in the fixture. Keyboard scrolling aligns a row's bottom with availableHeight, so the row ended 19px out of sight. That was reproduced on the fixed-height grid-focus grid (rows 12+ clipped by 19px). The derivation dates from #6184 (Jan 2025) and predates the in-flow horizontal scrollbar. Measuring the view itself fixes both modes, and this leaf's AC cannot pass without it, so it is in scope here rather than split out.
Out of Scope
Measuring content (L2, L3), scroll correction with pinning (L4), AnimateRows (L5), and the example plus e2e journey (L6).
Avoided Traps
- A Fenwick tree: the plain prefix array rebuilds 100k rows in about 0.07 ms (D#19645 probe 5).
- A public row-height callback: deferred.
- Changing the fixed-height arithmetic. The viewport measurement both modes share is the one exception, per the amendment.
Related
#19658 (epic) · D#19645 · #19515 (Sophie touches Row and selection; announce files before branching) · #19643 (Vega; header/Container)
unowned-rationale: L1 of my stewarded epic, open for any peer; I take it if no one claims it.
Origin Session ID: daac81df-5205-405e-a3af-aec56e5cca45
Retrieval Hint: "grid row geometry model prefix offsets both extents scrollToIndex content-sized opt-in"
Context
This is leaf L1 of epic #19658 (content-sized rows), graduated from D#19645. It replaces fixed-height arithmetic with one per-view geometry model, opt-in, before any measurement exists. Heights enter through an internal setter that tests drive directly, so geometry correctness can be proved in isolation. Measurement leaves L2 and L3 feed the same setter later.
The Problem
Every row-position consumer multiplies or divides by one
rowHeight(devbf91293bde):Body: pool rows (L413), start index (L475),scrollToIndex(L1408–1411), extent (L1634).Row: height and transform (L399–400).View.syncBodies: start index (L380).VerticalScrollbar: extent (L152).Variable heights break all of them at once. The two extents must also agree:
Bodyand the proxy scrollbar write their extents independently today.The Architectural Reality
The App Worker owns geometry, and the three bodies (start, center, end) of one view share each row's height.
GridRowScrollPinningreads scroll-origin deltas, not row heights (L117 and L159), and is unaffected here; L4 handles corrections.The Fix
grid.Container, propagated to its bodies. Fixed height stays the default, and its arithmetic is untouched.rowHeightas the estimate for unknown rows (the measured mean replaces it in L3);Float64Arrayof prefix offsets, rebuilt once per batch of height changes;Rowoffset and height,View.syncBodies, pixel-based pool coverage (viewport plus overscan, capped by row count, with the smallest known height as the floor), both extents, andBody.scrollToIndex.Container.passSizeToBody). (Amended during implementation, see below.)Contract Ledger
grid.Container(name settled at intake, e.g.contentSizedRows)Body.scrollToIndexin this modeindex × rowHeightavailableHeight(Container.passSizeToBody)Acceptance Criteria
BodyandVerticalScrollbar) and the start index all agree with the prefix offsets.scrollToIndexreaches the right row, including the final row, which is reachable and fully visible.Amendment (implementation finding)
The browser e2e for "the final row is fully visible" failed in both modes, so the cause is not row geometry.
Container.passSizeToBodyderivedavailableHeightas container − header − footer, which also counted the container borders and the horizontal scrollbar below the view: 770px against a 751px view in the fixture. Keyboard scrolling aligns a row's bottom withavailableHeight, so the row ended 19px out of sight. That was reproduced on the fixed-heightgrid-focusgrid (rows 12+ clipped by 19px). The derivation dates from #6184 (Jan 2025) and predates the in-flow horizontal scrollbar. Measuring the view itself fixes both modes, and this leaf's AC cannot pass without it, so it is in scope here rather than split out.Out of Scope
Measuring content (L2, L3), scroll correction with pinning (L4),
AnimateRows(L5), and the example plus e2e journey (L6).Avoided Traps
Related
#19658 (epic) · D#19645 · #19515 (Sophie touches
Rowand selection; announce files before branching) · #19643 (Vega; header/Container)unowned-rationale: L1 of my stewarded epic, open for any peer; I take it if no one claims it.
Origin Session ID: daac81df-5205-405e-a3af-aec56e5cca45 Retrieval Hint: "grid row geometry model prefix offsets both extents scrollToIndex content-sized opt-in"