The Problem

Web UI developers need to know the exact height of a block of text before it is rendered. Normal DOM measurement (getBoundingClientRect, offsetHeight) forces a layout pass, which is costly and can cause jank, especially in virtualized lists, masonry grids, or when measuring many language‑mixed strings (e.g. emojis, bidi scripts).

What This Does

pretext provides a pure‑JS/TS engine that measures and lays out multiline text without touching the DOM.

src/analysis.ts and src/layout.ts contain the core measurement pipeline. prepare() (exported from src/analysis.ts) normalises whitespace, segments the string, measures each segment on an off‑screen canvas, and returns an opaque handle. layout() (in src/layout.ts) takes that handle, a container width and line height, and returns the total height and line count using only arithmetic.

For developers who need the actual line strings, prepareWithSegments() and layoutWithLines() (also in src/layout.ts) expose the full line break information, while walkLineRanges() yields per‑line width ranges without constructing strings.

All demos under pages/demos/ (e.g. pages/demos/masonry/index.ts) showcase these APIs in realistic UI patterns such as masonry grids and rich‑note editors.

How To Use It

Setup

git clone https://github.com/somnai-dreams/pretext.git cd pretext bun install # bun.lock is present; bun is the recommended package manager

Build / Run the demo site (the repo’s only documented entry point) bun start # defined in package.json; launches a dev server Open http://localhost:3000/demos in a browser (no trailing slash)

Library usage (import from the published package or from the source)

import { prepare, layout } from '@chenglou/pretext' // npm install @chenglou/pretext // or from local source: import { prepare, layout } from './src/analysis'

const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter') const { height, lineCount } = layout(prepared, 320, 20) console.log(height, lineCount)

Configuration – No runtime config files are required. Font specifications are passed as the second argument to prepare(). The library relies on the browser’s canvas font rendering for ground‑truth measurements.

Running tests (optional)

bun test src/layout.test.ts # test file present; no CI hook currently executes it

Real‑World Use

A virtualized chat list can call prepare() once per distinct message text and reuse the returned handle for every resize. On resize, only layout(prepared, newWidth, lineHeight) runs (≈ 0.09 ms for a 500‑message batch), eliminating layout thrash and preserving scroll position.

// Example in a React component const handle = useMemo(() => prepare(text, '14px Helvetica'), [text]) const { height } = layout(handle, containerWidth, 18) return <div style={{ height }}>{text}</div>

Code Health & Issues

Low – Missing lockfile for npm – the repo uses bun.lock but no package-lock.json or pnpm-lock.yaml. Builds with npm would be non‑reproducible. Low – Tests not integrated in CI – there are test files (src/layout.test.ts) but the GitHub Actions workflow (.github/workflows/pages.yml) only builds the demo site; no test step is defined. Low – Documentation gaps – README covers installation and demos but lacks API reference details (e.g., full signatures of prepareWithSegments). Low – Type safety – TypeScript source is present, but the tsconfig.json does not enforce strict mode, which could allow unchecked edge cases. Low – No explicit linting – No ESLint or Prettier config is found, so code‑style consistency is not enforced automatically.

No critical security issues are apparent: the repository includes a LICENSE file, no secret keys, and all dependencies are declared in package.json.

The Bottom Line

pretext delivers a fast, DOM‑free text measurement engine that solves a concrete performance bottleneck for complex web layouts. It is production‑ready for teams comfortable with TypeScript and bun, though the lack of integrated testing and stricter TypeScript settings means you should add your own CI checks before large‑scale adoption. Ideal for UI libraries, virtualized lists, and any app that needs reliable multiline height calculations across diverse languages.