Demo · 02 · pretext playground
prepareWithSegments() does the one-time pass — segment, measure each token against canvas ground-truth, return an opaque handle.
layoutWithLines() is pure arithmetic after that, safe to call every frame as the column width changes.
No reflow, no getBoundingClientRect, no hidden measure-div.
The textarea on the left is the source. Every keystroke re-runs prepareWithSegments(), which segments the text and measures each segment against a shared canvas context. That handle is cached until text or font changes.
The column on the right is rendered into a <canvas> — not a <div>. The orange handle sets max width; layoutWithLines(prepared, width, lineHeight) returns materialized line strings plus line count and total height by walking the cached segments. Pure arithmetic. Runs on every frame.
Tick shrinkwrap on: we binary-search the tightest width that still fits the paragraph in the line budget you set. Every probe is another measureLineStats() call — cheap because the segments were measured once.
@chenglou/pretext · /demos/playground/playground.ts