Pretext
Fast, DOM-free multiline text measurement and layout for JavaScript and TypeScript
Repository Health
Technical Analysis
Pretext is a pure JavaScript/TypeScript library that measures and lays out multiline text without ever touching the DOM. Instead of relying on layout-triggering calls like getBoundingClientRect or offsetHeight, it uses the browser’s own canvas font engine as ground truth, then does the actual line-wrapping and height math with pure arithmetic over cached widths.
It splits work into a one-time prepare() pass (segmentation, glue rules, canvas measurement, emoji correction) and a cheap repeatable layout() pass, so resizing or virtualizing large lists of text stays fast. Full Unicode support (via Intl.Segmenter) covers CJK, Thai, Arabic, and other complex scripts, and a lower-level API exposes line ranges and cursors for custom rendering to canvas, SVG, or WebGL.
What You Get
- A two-phase
prepare()/layout()API: prepare once per text+font+config, then run cheap pure-arithmetic layout on every resize - Accurate multiline height and line-count calculation without DOM reads, avoiding the reflow cost of getBoundingClientRect/offsetHeight
- Full Unicode-aware segmentation via Intl.Segmenter, covering CJK per-character breaking, Thai, Arabic, and emoji grapheme clusters
- Low-level manual-layout APIs (walkLineRanges, layoutNextLineRange, materializeLineRange) for custom rendering to canvas, SVG, or WebGL, including variable-width flow around floats
- A dedicated
rich-inlineentry point for inline rich text spans (mentions, chips, code spans) with caller-owned chrome width and atomic break control - Options matching common CSS behaviors:
white-space: pre-wrap,word-break: keep-all, letter-spacing, and soft-hyphen-driven hyphenation
Common Use Cases
- Virtualizing long lists of text blocks (chat messages, feed items) without measuring each one against the live DOM
- Preventing scroll-position jumps by pre-computing new text height before it renders
- Development-time verification that button/label text won’t overflow to a second line, without a browser round-trip
- Building custom canvas/SVG/WebGL text renderers that need line-by-line layout data instead of a browser’s native text flow
- Implementing masonry or flexbox-like custom layouts where a paragraph’s shrink-wrapped width or height is a required input
Under The Hood
Architecture: Pretext is organized as a small set of focused modules stitched together in layout.ts. analysis.ts normalizes and segments raw text into typed pieces (word, space, tab, soft-hyphen, hard-break, etc.) and applies CJK/kinsoku/punctuation glue rules. measurement.ts owns the canvas measurement context (OffscreenCanvas when available, falling back to a DOM canvas), per-font metric caches, and browser-specific correction logic (emoji width inflation, Safari’s prefix-fit measurement quirks capped at 96 graphemes to avoid superlinear blowups). line-break.ts walks the prepared segments purely arithmetically to produce line ranges, and line-text.ts materializes a range back into an actual substring only when the caller needs the text itself. bidi.ts, forked from pdf.js’s bidi implementation, computes embedding levels for the richer prepareWithSegments() path via a generated Latin-1/non-Latin-1 range table, but the core line-breaking engine never reads bidi levels itself. A separate rich-inline.ts module implements a narrower, inline-only rich-text flow (fragments with fonts, atomic chips, caller-owned extra width) as its own public entry point (@chenglou/pretext/rich-inline), keeping it decoupled from the main paragraph API.
Tech Stack: Written entirely in TypeScript (91% of the codebase) targeting ES modules, with zero runtime dependencies — the only real inputs are the browser’s Canvas 2D API and Intl.Segmenter. The package ships as type: module with typed dual exports for the main entry and the rich-inline sub-path. Development tooling runs on Bun (install, test runner, dev server for the demo pages) with a strict tsc build (tsconfig.build.json) producing the published dist/ output, plus oxlint (with oxlint-tsgolint for type-aware rules) and knip for dead-code/unused-export detection.
Code Quality: The core src/ sits around 6,600 lines split across ~8 focused files, backed by a 1,687-line layout.test.ts covering the public and internal APIs, alongside separate accuracy/, corpora/, and benchmarks/ directories that check real-browser pixel-accuracy (Chrome/Firefox/Safari) and measure performance regressions — a testing posture well beyond typical npm libraries of this size. Internal invariants are documented inline (e.g. the 96-grapheme cap rationale in measurement.ts), and lint suppressions are scoped and explained rather than blanket-disabled. No obvious gaps in error handling were found for the documented public surface; unsupported environments (no OffscreenCanvas/DOM) throw an explicit, descriptive error rather than failing silently.
API Design: The public API is deliberately two-tiered: a minimal prepare()/layout() pair covers the common “give me a height” case with almost no boilerplate, while prepareWithSegments() plus layoutWithLines()/walkLineRanges()/layoutNextLineRange() expose progressively lower-level control for custom renderers without requiring callers to learn the internals up front. Naming is consistent (prepare*/layout*/materialize* prefixes signal cost and side effects), option objects mirror familiar CSS properties (whiteSpace, wordBreak, letterSpacing) to minimize new concepts, and the README includes a full inline API glossary with types and inline usage examples for every exported function.
Used by 4 apps in this directory
Anarlog
Note Taking · AI Assistants · Productivity
Anarlog is an open-source, local-first AI meeting notetaker that records, transcribes, and summarizes meetings entirely on your device — no cloud lock-in, no mandatory account, and every note saved as a plain markdown file you own forever.
HyperFrames
AI Development · AI Design Tools
Turn plain HTML and CSS into deterministic, pixel-perfect MP4 videos — authored by humans or AI agents, rendered by headless Chrome and FFmpeg.
Kimi Code CLI
AI Code Assistants · AI Agents · Developer Tools
A single-binary, terminal-native coding agent that reads, edits, and runs code end to end, built by Moonshot AI for Kimi models but pluggable with Anthropic, OpenAI, and Google providers too.
open-pencil
AI Design Tools · Design Tools
An open-source design editor that reads native Figma files, ships a built-in AI assistant with 100+ design tools, and offers real-time serverless collaboration — all without giving up your files.