LearnNewsExamplesServices
Frontmatter
id19661
titleGrid: an opt-in row-geometry model drives offsets, extents and navigation
stateClosed
labels
enhancementaigrid
assigneesneo-opus-ada
createdAtOct 12, 2026, 4:08 PM
updatedAtOct 12, 2026, 6:22 PM
githubUrlhttps://github.com/neomjs/neo/issues/19661
authorneo-opus-ada
commentsCount2
parentIssue19658
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 19665 Grid: AnimateRows uses the row geometry or refuses content-sized mode, [x] 19664 Grid: height corrections keep the visible row and the pinning origin stable, [x] 19662 Grid: content-sized cells measure their wrapped text in place
closedAtOct 12, 2026, 6:21 PM
milestonev13.3

Grid: an opt-in row-geometry model drives offsets, extents and navigation

Closed Backlog/active-chunk-8 enhancementaigrid
neo-opus-ada
neo-opus-ada commented on Oct 12, 2026, 4:08 PM

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

  • With the mode off, every existing grid spec passes unchanged; that is the fixed-height control.
  • With the mode on and synthetic mixed heights, row transforms, both extents (Body and VerticalScrollbar) and the start index all agree with the prefix offsets.
  • scrollToIndex reaches the right row, including the final row, which is reachable and fully visible.
  • Keyboard scrolling brings the target row fully into the view in both modes.
  • The three bodies of one view share each row's height, and two views at different widths keep independent models.
  • A sort or filter rebuilds the ordered offsets; the rebuild cost at 100k rows is measured and recorded in the PR.
  • New and changed methods carry JSDoc.

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"

tobiu referenced in commit ff0dc94 - "feat(grid): opt-in content-sized rows read one row geometry per view (#19661) (#19668) on Oct 12, 2026, 6:21 PM
tobiu closed this issue on Oct 12, 2026, 6:21 PM
tobiu referenced in commit 07b84f9 - "feat(grid): content-sized rows measure the wrapped text of wrapText columns (#19662) (#19672) on Oct 12, 2026, 9:45 PM