Fully customizable text page engine with read, write, and interact modes. Pure JS text layout that bypasses DOM reflow entirely — every character is measured and positioned in JavaScript, then projected to pooled DOM elements at 60fps.
- pretext by @chenglou — Pure JS text measurement and layout engine.
prepare()does one-time canvas measurement, thenlayout()is pure arithmetic. The wrap-geometry module (polygon hulls, interval carving, hit testing) is ported from pretext's demo code. - agentation (forked at Mikeishiring/agentation) — Visual annotation tool for AI agents. Broadsheet's interact mode adapts agentation's annotation patterns using semantic positions (block ID + char offset) instead of CSS selectors.
Inspired by the pretext dynamic-layout demo.
- Two-column editorial layout with cursor handoff between columns
- Obstacle avoidance — text flows around rects, circles, polygons, and image alpha hulls
- Headline auto-fit via binary search (rejects sizes that break words mid-line)
- Drop cap — large first character spanning N body lines, measured via binary search
- Pull quote — italic serif block that text wraps around
- Draggable obstacles — drag any obstacle and text reflows live at 60fps
- Image hull extraction —
getWrapHull()rasterizes images, scans alpha channels, builds polygon hulls - Click-to-rotate — rotate image obstacles, hull updates, text reflows around rotated shape
- Theme system — parchment (warm editorial) and dark editorial themes with smooth transitions
- Max-width responsive — editorial layout stays readable on ultra-wide monitors
src/
engine/ # Pure-function layout pipeline
layout-engine # computeLayout() — columns, obstacles, cursor handoff
wrap-geometry # Ported from pretext: interval carving, hull extraction
dom-projection # Imperative pooled-div renderer (bypasses React)
prepare-cache # PreparedText cache (text+font -> opaque handle)
components/ # React layer (manages lifecycle, not text rendering)
PageCanvas # ResizeObserver + rAF scheduling -> pool.project()
HeadlineFitter # Binary-search headline sizing
DraggableObstacle, CircleObstacle, ImageObstacle
DropCap, PullQuote, Atmosphere
themes/ # Static objects (not CSS variables)
parchment # Warm parchment + serif
dark-editorial # Dark bg + gold accent
create-theme # Deep-merge factory
Key decision: Body text lines are rendered via an imperative DOM pool, not React elements. 200+ absolutely-positioned divs change every frame during resize/drag — React reconciliation is too slow. The pool pattern (grow/shrink divs, reuse nodes) comes directly from pretext's demos.
npm install
npx vite --port 4200Vite + React 19 + TypeScript, @chenglou/pretext for layout
MIT