/** * Grid carrier materialization: turning a prepared content grid into positioned * DOM carriers, with per-line reuse so streaming stays affordable. * * The deferred **projection walk** extraction (`DEC-0028` measurement, * `DEC-0019` pattern). The walk was deferred by `DEC-0020` and `DEC-0022` on the * grounds that `syncA11y` and `syncContentProjection` are one shared depth-first * walk and would have to move as a pair. Re-measured on `58c77ac` that premise * held for the two walk drivers and **not** for this member: the grid path is a * leaf of the walk, not part of it. * * ## Why this is separable when its callers are not * * `syncContentGridProjection` has exactly one call site, takes everything it * needs as parameters already, and calls nothing else on `Scene` except the * content-projection manager and the phase timer. Its own state is two memo * fields nobody outside it reads. It writes no state any other domain reads, so * there is no back-edge to invent and `DEC-0019` rule 1 is satisfied outright. * * ## What is held, and what is passed in * * Held: the {@link ContentProjectionManager} and the {@link PhaseTimer}. Both are * `readonly` on `Scene`, constructed once, and already shared collaborators — the * timer is the shared leaf `DEC-0021` extracted for exactly this reason. * * Per-call (`DEC-0019` rule 5): `pageScaleX` and `fontEpoch`. `Scene` keeps * `getContentMetricScaleX`, which reads `canvas` and the **public mutable** * `width`, and whose memo is keyed on `contentFontEpoch` that `resize` and a font * load both bump. Holding either here would go stale silently. `entityId` is * passed rather than the `Entity`, because the id is all this needs. * * ## What deliberately did not move * * `syncContentProjection` (the walk's content driver) and `syncA11y` (its a11y * driver) stay together on the facade, still blocked on each other exactly as * `DEC-0020` measured. * * `projectionBoxVisible` also stays, for a reason measurement found rather than * predicted: `test/ContentProjectionSettledWalk.test.ts` replaces it on the * `Scene` instance and asserts call counts (the settled-walk fast path is * verified by counting box tests, 2 when settled against 802 unpruned). Moving * it onto a collaborator would make those calls invisible to the patch and turn * a behavioural regression test into one that cannot fail. The suite is unedited * by contract, so the member stays where the test can see it. */ import type { ContentProjection } from '../Entity'; import type { PreparedContentGrid } from '@vectojs/text'; import type { ContentProjectionManager } from './ContentProjectionManager'; import type { PhaseTimer } from './PhaseTimer'; export declare class ContentGridProjector { private readonly contentProjection; private readonly phases; constructor(contentProjection: ContentProjectionManager, phases: PhaseTimer); /** * Materialize a prepared grid in logical source order while positioning each * carrier from the shared canvas geometry. Browser font measurement happens * later in one cold read/write batch, never inside projection synchronization. */ syncGrid(entityId: string, el: HTMLElement, projection: ContentProjection, grid: PreparedContentGrid, lineBand: { minY: number; maxY: number; } | null, pageScaleX: number, fontEpoch: number): void; }