Header and footer go first, each with its own ops, and a variant flag is
flipped while a zone is open. The round trip compares a list's drawn kind and
marker rather than its node type and style name — a list that loses its name to
a formatted marker carries the style's own char — and skips the marks of an
inline atom, which no file keeps. A caret is snapped to a text position, a
swallowed File-menu click is retried, and WebKit's converging ResizeObserver
round is no page error. A new corpus pair brings all six zones through the
import path; the baseline holds it and a re-recorded showcase entry.
tests/layout/baseline.json records where each page starts and what the load cost, per
engine and platform; a page starting elsewhere is a layout change the run prints page by
page, a load over three times its recorded time a regression, and LAYOUT_UPDATE=1 records
it again. The monkey draws frame sizes, wraps, alignment and column counts too, holds the
history against the edits rather than the caches the editor recomputes, and leaves it
unchecked where the columns flow rewrote the document outside it. The differences both
importers make by design are named in the comparison; unhoist moved to normalize.ts.
lo-fuzz.test.ts hands every fuzz seed to LibreOffice as .odt and .docx and
compares what comes back, listing the differences LibreOffice makes by design.
test:layout opens the corpus, the showcase and fuzz seeds in the browser, holds
the page count against LibreOffice and checks the lines for overlap, margin
escapes and stranded headings. test:monkey types at random on corpus documents
and asserts the schema, undo to the start, redo, and that both saved formats
read back. browser.mjs now carries the dev server and the settle helper.
openApp() launches chromium, firefox or webkit from playwright-core; the smoke
and DOM runs pass in each. CI runs both per engine, WebKit on macOS for
Safari's text shaping. build.target is set explicitly to the browsers the
bundle promises. CLAUDE.md is back under its 120-line budget.
A click lands wherever the element's centre happens to be, and a break before
the empty last paragraph yields no page; the editor's focus() lands on the next
animation frame, so a key sent before it was lost now and then.
npm run test:dom opens a corpus document in headless Chromium and holds
its page count against a settle, a reload, a zoom, a page break and its
undo, plus the unsaved dot — the components the vitest suite cannot see.
The smoke run now shares its boot with it (tests/browser.mjs).
Coverage names the untested surface, a pageerror collector reports what
nobody predicted, and semantics beat crash-detection — plus the false
alarms the probes themselves produce.
Renames the product in the docs, the page title and the DOCX creator, and moves
the whole storage namespace from odf-editor- to edentext-. The prefix is a
per-origin namespace, so it stays the product name rather than a generic one.
The root file is read at every session start and had grown to 87 KB, so every
session paid for knowledge it did not use. Move the per-directory detail into
nested CLAUDE.md files, which load when a file in that directory is touched,
and the five deep-dive topics into docs/. The root keeps the commands, the hard
rules and an index of where the rest lives.
Content moved verbatim — the probed Word/LibreOffice behaviour, sentinel order
and shared constants are what the code cannot say. CI fails the root over 120
lines, since a prose budget alone is what let it grow.