Everything since 0.3.0 on 2026-09-09: a cover page as the file draws it — page-placed
frames, themed shape colours, floating tables, drawing groups — the run-through wrap
modes as pickable frames, a text box's vertical anchor, the fonts a saved document
carries with it, the header band's height and the table inside it, and the selection
surviving an attribute change.
Everything since 0.2.0 on 2026-09-06: the run of load and typing latency work,
each tab editing its own document, the format prompt on a first save, the
unsaved-close warning, and two fixes.
A `:has()` left of `+` in editor.css made Chromium restyle every element
under .tiptap on each inserted child, so the band-clearing rule keys on a
data-wrap-band attribute the imageLine plugin writes. getLineRects reuses
one Range instead of leaving hundreds alive. TipTap's Placeholder, which
hit-tested the viewport on every keystroke, is replaced by placeholder.ts.
docPosBeforeElement is memoised per pass. At 124 pages the pass after a
split drops from 410 to 150 ms and the split keystroke from 219 to 50 ms.
Every document-scoped key now goes through docKey(), which hangs it off the
tab's document id; the first document keeps the empty id, so existing browser
copies read back unchanged. A fresh tab takes up the most recently used
document no live tab holds, else starts a new one — one signed timestamp per
document (edentext-live@id) says which, written only by the tab holding it.
Snapshots and embedded fonts get a database per document. The picture store
stays shared and content-addressed, so its sweep now drops only keys no stored
document names any more, instead of the open document's own.
Everything since the 0.1.0 launch on 2026-08-17: named list styles, chapter
numbering, the letter templates and DIN 5008 marks, password protection in both
formats, comment threads with margin balloons, the book layout, the inline text
box, and the run of Word / LibreOffice interoperability, round-trip and page-layout
fixes behind the parity corpus, the fuzzed export options and the browser runs.
0.1.0 keeps its own date and scope; its open-items list drops password protection,
which now ships.
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.
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).
Five legs in tests/lo-password.test.ts, driven through the repo's first UNO
helper because --convert-to cannot pass a password; self-skips without soffice
or python3-uno and runs with npm run test:lo. The probed format details live in
docs/architecture/encryption.md.
Writes LibreOffice's whole-package form: AES-256-GCM keyed by Argon2id over
SHA-256 of the password, the deflated package as one encrypted-package entry.
Reads that plus the per-entry AES-CBC/GCM form with PBKDF2 that every earlier
version writes; the nonce sits ahead of the ciphertext, which no manifest says.
Blowfish and unknown algorithms report an unsupported-encryption error.
Three legs against the closed-loop blind spot the duplicate-style bug
exposed: package-lint.test.ts checks invariants the schemas cannot
express (unique style ids/names, dangling style/num/rel references,
balanced ranges, manifest completeness); lo-roundtrip gains a
soffice→fodt leg asserting the styles LibreOffice resolves (Heading
chain Arial/bold/sizes, Word defaults); lo-render-consistency.test.ts
renders the kitchen sink from both formats through LibreOffice and
compares pagination and pixels (ratchet thresholds). CI's LO job
gains poppler-utils; test:lo runs both LO files.
tests/smoke/run.mjs boots the production build in Playwright Chromium and
checks what jsdom cannot: the bundle boots, typing and the bold shortcut
reach ProseMirror, autosave survives a reload, a corpus ODT imports and
paginates; any uncaught page error fails the run. Wired as npm run
test:smoke and a CI job (build + chromium install), playwright-core is
now a devDependency.
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.
Everything else in tests/ compares documents at the model level; nothing checked
what a page actually looks like. run.mjs renders the same file through soffice
and through the real app in Chromium, reads both back as word boxes, and diffs
page count, line count, line text and line position.
The corpus stays out of the repo: make-fixtures.mjs authors the baseline .docx
files, and real-world documents are whatever you drop into the gitignored
fixtures/ yourself.