docs blog library
github

Fullscreen ownership

Read Conversation presentation for source and persistence contracts. The mounted fullscreen view owns navigation; its painter and the native scrollbox cannot independently change the reading position.

flowchart LR
  Document[Semantic document] --> Bridge[Atomic bridge snapshot]
  Bridge --> Layout[Document-owned layout index]
  Layout --> Viewport[Viewport controller]
  Input[Committed keyboard / wheel / drag input] --> Viewport
  Viewport --> Painter[Visible-row painter]
  Painter --> Runtime[Terminal runtime]
  Runtime --> Terminal[Physical terminal]
  Terminal --> Oracle[Independent VT observer]

Reading position

App owns focus and committed input callbacks. Viewport controller stores live-follow intent or an anchor for each visited document. Scrolling up detaches; navigating to the bottom, pressing End, or submitting a message follows live output. Incoming text alone cannot change an anchored reader’s intent.

An anchor identifies a source block, a part, and a UTF-16 position in displayed text after Markdown interpretation. Wrapping supplies grapheme-safe boundaries. Prose and reasoning therefore follow the same content through width changes. Complex fence, table, and receipt rows use stable row-part fallbacks; these do not promise arbitrary character-level anchoring through structural rewrites. Removing an anchored block tries surviving neighboring blocks, then clamps the old position. Occlusion by an overlay pauses observation without changing follow intent. The unseen-content baseline follows a semantic tail, so reflow or expansion above it does not count as newly arrived text.

Child documents have separate reading positions. Their layout is disposed when the mounted document changes, while only small navigation records are retained. Drag timers and selection are canceled on document change, occlusion, and unmount. Transcript surface retains native selection/layout support but disables competing native keyboard, wheel, and automatic scrolling.

Layout budgets

Transcript layout is an explicit instance, not a process-global row cache. Call update with an immutable block list and an epoch containing width, glyphs, theme revision, and a captured palette. window realizes intersecting chunks; flatten deliberately realizes everything and belongs in tests or benchmarks. Dispose the instance when its document retires.

The default painted-row LRU retains at most 2,048 rows across 32 chunks. Streaming wrap/highlight reuse is limited to 2,048 rows and 524,288 UTF-16 characters. Compact height and source-offset metadata still grows with history. An update visits the block list and relayouts dirty chunks. A single oversized block or packed receipt run still wraps transiently in full; it is excluded from the painted cache. These bounds limit retained derived state, not source size or the maximum temporary allocation for an individual block.

Source-position metadata cache

Per-frame row metadata (the chunk partAt/starts/ends maps and the cross-chunk source-position index) is built once per chunk and carried on the chunk itself; an update reuses every chunk object the wrapping phase proved unchanged. The cross-chunk index — row prefix offsets plus the per-block and per-part maps — is copy-on-written per update: a fresh index copies the reusable chunk-identity prefix (memcpy of the prefix array and a map copy that keeps only entries fully contained in, or spanning, that prefix) and records only the changed tail. An index handed to a caller is therefore never mutated: held snapshots keep their exact row lengths, source lookups, and block bounds across appends, rewrites, promotion, and geometry or palette epoch changes (transcript-layout-metadata.test.ts pins this). Limitations: the copy still walks the whole previous block/part map once per frame, so a very large settled history pays a linear copy cost before the tail rebuild; the first frame after any epoch change is a full cold build; and the copy assumes settled chunks are immutable, so a caller that mutates a block or chunk it passed in previously defeats the cache (treat block lists as immutable).

Palette and glyph context belong to a layout epoch. Realizing an older index after a theme change must use its captured palette. Do not temporarily replace global theme state while laying out rows.

Verification

Run controller and viewport integration tests for history hold during streaming, resize, folds, child visits, selection, and End. Run layout tests for bounded caches, disposal, and epoch coherence. The terminal oracle checks emitted bytes independently. Resource qualification uses bench/ui-pipeline.bench.ts and bench/conversation-memory.bench.ts; compare matched workloads and report peak RSS separately from retained heap.

machine-readable: /docs/maintainers/fullscreen-ownership.md · /llms.txt · /llms-full.txt