LearnNewsExamplesServices
Frontmatter
id19662
titleGrid: content-sized cells measure their wrapped text in place
stateClosed
labels
enhancementaigrid
assigneesneo-opus-ada
createdAtOct 12, 2026, 4:08 PM
updatedAtOct 12, 2026, 10:45 PM
githubUrlhttps://github.com/neomjs/neo/issues/19662
authorneo-opus-ada
commentsCount2
parentIssue19658
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 19661 Grid: an opt-in row-geometry model drives offsets, extents and navigation
blocking[x] 19666 Grid: a 10k wrapped-text example proves content-sized rows end to end, [x] 19663 Grid: a hidden pre-render measures rows ahead of mounting
closedAtOct 12, 2026, 9:45 PM
milestonev13.3

Grid: content-sized cells measure their wrapped text in place

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

Context

This is leaf L2 of epic #19658, blocked by #19661 (the geometry model). Mounted rows get their real heights by measuring the displayed text in place.

The Problem

Content-sized mode needs each mounted row's natural height: the maximum across its declared wrapping text columns, in every body. The constrained outer row and cell are not a natural-content oracle, because cells inherit the row's height and rows are contain: strict.

The Architectural Reality

  • The cell: .neo-grid-cell is display: flex, align-items: center, height: inherit, padding: 2px 10px 2px, with 1px bottom and right borders, border-box (Body.scss).
  • The flex trap (epic review, Stage 4): inside a flex cell, a block child is a flex item, and its automatic minimum width is its longest word. overflow-wrap: break-word then never breaks a long word, which overflows instead. D#19645 probe 2 showed that a flex cell missed 43 of 900 cases and a block cell missed 0.
  • The read: DomAccess.getLayoutRect({id: [...]}) measures transform-immune layout boxes in one batch with one forced layout.

The Fix

  • Declared columns: a column flag (name settled at intake, e.g. wrapText: true) declares a height-driving text column.
  • The text container: in content-sized mode a declared cell renders its text in a block container with white-space: normal, overflow-wrap: break-word and height: auto. Either the cell becomes display: block in this mode, or the container gets min-width: 0; flex: 1 1 auto, so long words break.
  • The measurement: after a mount or update, the App Worker requests the layout rects of the mounted text containers in one batch. It adds the cell's vertical padding and border, takes the maximum per row across columns and bodies, and feeds #19661's height setter.
  • The generation fence: each request carries a view/record/layout generation (content, width and font context), and a response whose generation no longer matches is dropped. Recycled slots never write a stale record.

Contract Ledger

Target surface Source of authority Proposed behavior Fallback / edge case Docs Evidence
Column flag declaring a height-driving text column #19658 Only declared columns drive height; others take the row height Without declared columns, rows keep the estimate JSDoc Unit + e2e
Content-sized cell CSS Epic review Stage 4 Long words break inside the column width — SCSS comment Spec with a word longer than the column
Measurement generation fence D#19645 (Sophie 18878897) Stale responses are dropped — JSDoc Recycled-slot spec

Acceptance Criteria

  • A declared column's wrapped text gives the row its natural height; the row height is the maximum across declared columns in all three bodies.
  • A word longer than the column width breaks inside the cell instead of overflowing; this is a spec.
  • A measurement that returns after the slot was recycled to another record, or after a width or content change, does not update geometry; this is a spec.
  • Mounted rows need one batched layout read per update, not one per row.
  • With the mode off, nothing in this leaf runs.

Out of Scope

Rows and columns not yet mounted, measure-ahead, and font-arrival invalidation (L3); scroll correction (L4).

Avoided Traps

  • Measuring the constrained row or cell box, which only reports its imposed height.
  • A per-row main-thread ResizeObserver that writes heights: the App Worker owns geometry, and main only reports observations.
  • Calling a stateful renderer a second time just to size it.

Related

#19658 · #19661 (blocked by) · D#19645 · #19515 / #19643 (announce files before branching)

unowned-rationale: open for any peer; the epic steward (Ada) picks it up otherwise.

Origin Session ID: daac81df-5205-405e-a3af-aec56e5cca45

tobiu closed this issue on Oct 12, 2026, 9:45 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