Pretext Lab
06Normal vs pre-wrap
Arc 3 · Whitespace & Breaks

Normal vs pre-wrap

A single option on prepare() decides whether whitespace is a compositor or a carrier. The default, whiteSpace: 'normal', collapses runs of spaces, tabs, and newlines into single word-breaks — the same rule you've been using in every prose lesson so far. Flip it to 'pre-wrap' and those same characters survive as visible structure: indents, blank lines, poetic spacing.

Same text, same font, same width. Two panes. Drag the shared width slider.

normal — whitespace collapses
height — · lines —
pre-wrap — whitespace preserved
height — · lines —
360

Mechanism

During prepare(), Pretext runs a normalization pass over the input before segmenting it. Under the default whiteSpace: 'normal', any run of ASCII whitespace (" ", \t, \n) collapses to a single break opportunity — the CSS white-space: normal rule every browser applies to body text.

Under whiteSpace: 'pre-wrap', the normalization pass stops collapsing. The engine now distinguishes preserved spaces, tabs, and hard breaks as separate segment kinds. A preserved space inside a long run still participates in wrapping, but it also occupies visible width. A tab advances to the next tab stop (default tab-size: 8). A \n forces a new line regardless of width.

Handles produced in each mode are not interchangeable — the segmentation is different — so you pick the mode at prepare() time, not layout() time.

Application

One layout engine, two registers. The same call site that fits your prose paragraphs can also fit:

No flag, no extra CSS, no branching pipeline. One option.

"A noiseless patient spider,
I mark'd where on a little promontory it stood isolated,
Mark'd how to explore the vacant vast surrounding,
It launch'd forth filament, filament, filament, out of itself,
Ever unreeling them, ever tirelessly speeding them."

Walt Whitman, Leaves of Grass (1891)

Direct Claude

"collapse to a flowing paragraph" default prepare()whiteSpace: 'normal' "preserve the layout as typed" prepare(text, font, { whiteSpace: 'pre-wrap' }) "measure the textarea before it mounts" pre-wrap handle + layout() at the target width "render verse and prose through the same engine" one call site, pick the mode per content type
The next lesson applies a similar single-option switch to CJK wrapping: wordBreak: 'keep-all'.
same text, two modes, one API
import { prepare, layout } from '@chenglou/pretext';

const font = "400 20px 'EB Garamond', serif";
const lh = 20 * 1.55;

// Normal: runs of whitespace collapse to single break opportunities.
const normalHandle = prepare(verse, font);

// Pre-wrap: tabs, newlines, and repeated spaces survive as visible structure.
const prewrapHandle = prepare(verse, font, { whiteSpace: 'pre-wrap' });

// layout() is the same in either case — pure arithmetic on the cached widths.
const a = layout(normalHandle,  width, lh);
const b = layout(prewrapHandle, width, lh);