Primitives / State
Virtual: a windowed list
Virtual owns a virtualized list's scroll position, measured heights, and
layout: Model { scrollTop, heights, scrolling, generation, estimatedHeight,
overscan, gap, paddingStart, paddingEnd }, Messages Scrolled, Measured,
Prune, Settled, args for the layout plus optional initialScrollTop and
initialHeights restores (sanitized like live measurements) and a settleMs
silence (default 150). Every scroll marks scrolling until the silence settles;
suspend loaders and parallax on it.
Two Mounts feed it: Viewport reports the container's own scrolls and
MeasureRow({ key }) a row's height. Four functions read it:
windowFor(model, keys, viewportHeight)answers which rows to render and the spacer height;isAtEnd(model, keys, viewportHeight, threshold)is the infinite-scroll check (an empty list counts as ended), anddistanceToEndthe pixels left, for a prefetch threshold;offsetForcomputes a scroll target the application actuates itself.
They share one table of row offsets, built once per keys array and heights
record and then searched, so keep the keys array between renders (derive it
where the list changes, not in the view) and a scroll costs O(log n) rather
than a pass over every row. Both are compared by identity: never change a keys
array in place. Prune drops heights for departed keys, since the bundle never
sees key order. Poisoned positions and heights are ignored, never stored.
For a window-scrolled list, map the scroll entry into Scrolled; for
follow-bottom, hold the end while isAtEnd and scroll on extend; to anchor a
prepend, re-Scrolled by the totals' delta. Render each row keyed, with
aria-rowcount and posinset from the window, so per-row placements keep
identity.
stickyHeader(sections, start) answers which section header sticks; CSS
position: sticky does the sticking. masonry(keys, heights, options) packs
fixed-width columns shortest-first into { placements, totalHeight }: layout
only, every placed item renders, so it fits hundreds of images rather than
hundreds of thousands. The sums never name an axis: pass column widths as
heights and a horizontal offset as the scroll position to window a carousel
the same way. Windowed grids and per-index estimates stay out by design.
range
range(start, end, step?) counts half-open numbers; the page list above is
range(1, (pageCount(model) ?? 0) + 1). A zero or non-finite step throws,
naming it.
Failure
Clamping absorbs an out-of-range page or total; a negative total and a non-finite size are ignored or rejected at the boundary. A bad history capacity or range step throws where it is written.