It lived in scratchpad/, which is untracked, and a tidy-up on 2026-10-22 deleted it — along with the pdf-editor's copy of the same harness. Forty checks of coverage gone, and nothing in the repo so much as mentioned they had existed. Anything load-bearing belongs next to the code it tests. Rebuilt as test/e2e.mjs plus test/e2e-harness.mjs, runnable with `npm run test:e2e`. It boots a throwaway Theseus, opens documents through the sidebar panel, clicks the real ribbon, types with real key events, saves and exports, then reads the result back with the add-on's own reader. The editor carries no test-only hook, so a pass is a pass for what users get. This is the only level that catches breakage which exists solely in the browser — a bundle that will not load as a classic script from file://, webfonts that register and then never fetch, a ribbon button wired to nothing, a PDF on the wrong paper. All four have happened. One trap is recorded in the harness: the throwaway profile must not live under addon-build/, whose package.json declares "type": "module". Node picks a file's module kind from the nearest package.json above it, so an add-on seeded into a profile there is parsed as ESM and dies on its first require() with "require is not defined in ES module scope" — which reads exactly like the add-on being broken, and is not. Run against today's build: 38/38, including the new start-on-first-use activation.
123 lines
6.2 KiB
Markdown
123 lines
6.2 KiB
Markdown
# Word editor — a Theseus extension
|
||
|
||
Opens, edits and saves Word documents (`.docx`) in a full Theseus tab, and
|
||
exports them as PDF.
|
||
|
||
Saving comes in three shapes. **Save** drops a copy in your downloads until the
|
||
document has a home; **Save as…** opens a real file dialog where the extension
|
||
you type — `.docx` or `.pdf` — decides the format, and from then on Save writes
|
||
straight to that file; **PDF** is the one-click export. PDFs are rendered by
|
||
Chromium's own print pipeline from `lib/doc-css.js`, the same stylesheet the
|
||
editor displays, on the paper size the document itself specifies — so the
|
||
export matches the preview rather than being a second opinion about it.
|
||
|
||
It is a first-party extension: written here, signed with the Silent Mode
|
||
operator key, and updated over the channel at
|
||
`theseus.x/extensions/docx-editor/` like every other add-on Theseus ships.
|
||
It does not go through the community catalogue, which is for extensions
|
||
published by whoever owns a BNS name.
|
||
|
||
It ships inside Theseus, so a fresh install already has it and Settings
|
||
lists it under **Built into Theseus**; the channel above then updates it
|
||
between releases without waiting for one. That is the same arrangement as
|
||
screenshot, aegis and pdf-editor, and it is what gives a user their first
|
||
copy at all — Settings can only *install* from the community catalogue, so
|
||
an extension that is neither bundled nor catalogued has a working update
|
||
channel and no way in.
|
||
|
||
The cost is about 400 KB compressed in the installer, most of it the
|
||
vendored editor libraries.
|
||
|
||
## The page, and the fonts
|
||
|
||
The sheet is drawn at the size the document says it is — A4 stays A4, margins
|
||
come from its own `sectPr` — and CSS `zoom` scales the whole thing to fit the
|
||
window. Scaling rather than widening is the point: a page stretched to the
|
||
window would break every line somewhere different from where the printed page
|
||
breaks it. The footer carries the control, and **Fit width** is the default
|
||
because a page marooned in the middle of a wide window wastes the screen. The
|
||
choice is remembered per editor, not per document.
|
||
|
||
Ubuntu and Fraunces ship with the add-on, in `fonts/`, because Windows has
|
||
neither and a font offered in the ribbon that the machine lacks is a font the
|
||
user picks and then cannot see. Regenerate with `npm run fonts`; licences are
|
||
in `fonts/LICENSES.txt`. They cost about 700 KB, most of it Ubuntu's Cyrillic
|
||
and Greek coverage — dropping those subsets would roughly halve it.
|
||
|
||
Two wrinkles worth knowing. The editor is on `file://`, where Chromium
|
||
registers `@font-face` rules but refuses to fetch the font files, so the
|
||
add-on reads them and hands the page a stylesheet with the woff2 inlined as
|
||
data URLs; the same inlined copy goes into the PDF export, whose print window
|
||
runs from a temp folder where a relative `url()` would resolve to nothing. If
|
||
either path fails the ribbon labels those two families "(not available)"
|
||
rather than pretending.
|
||
|
||
## The icon
|
||
|
||
`icon.svg` is the one drawing. `npm run icons` rasterises it to
|
||
`icon-16/32/48/128/256.png` and writes a minified copy into `addon.json`'s
|
||
`icon` field, which is what the dock button and the catalogue card read — so
|
||
there is never a second version to keep in step.
|
||
|
||
It is a blue document badge, paired with pdf-editor's red one so the two
|
||
editors read as a set in the dock — a solid fill stays legible at the 16–18px
|
||
the dock actually renders, which an outlined page does not.
|
||
|
||
Deliberately not Microsoft's Word icon: their mark is a blue sheet carrying a
|
||
white W, and dressing our extension up as theirs would be both a legal
|
||
problem and a dishonest one. A folded-corner page is the universal document
|
||
glyph, and "DOC" is the file format rather than anyone's trademark.
|
||
|
||
## Building
|
||
|
||
The vendored libraries (mammoth, ProseMirror, docx, JSZip) are bundled by a
|
||
build step that lives outside this folder, because mammoth needs local patches
|
||
before it can carry everything the editor edits:
|
||
|
||
cd ../../addon-build/docx-editor
|
||
npm install
|
||
npm run build # writes vendor/docx-vendor.js here
|
||
npm run icons # re-rasterises icon.svg after editing it
|
||
|
||
`vendor/docx-vendor.js` is committed, so the extension is installable straight
|
||
from a checkout; re-run `npm run build` after touching anything under
|
||
`addon-build/`.
|
||
|
||
## Publishing
|
||
|
||
The full recipe is in [docs/ADDON-UPDATES.md](../../docs/ADDON-UPDATES.md);
|
||
the short version, signed with the operator key and always from a clean copy
|
||
of the commit rather than the working tree:
|
||
|
||
git archive HEAD TheseusNavigator/extensions/docx-editor | tar -x -C <clean>
|
||
node scripts/sign-addon-update.mjs <clean>/TheseusNavigator/extensions/docx-editor https://navigate.st/bns/theseus.x/extensions/docx-editor "$USERPROFILE/.silentmode/ops/addon-update-key.pem" --out out/
|
||
|
||
node ../Argus/src/lib/sia-upload.js out/docx-editor-<version>.tar.gz bns/theseus/extensions/docx-editor
|
||
node ../Argus/src/lib/sia-upload.js out/updates.json bns/theseus/extensions/docx-editor
|
||
|
||
The channel lives on Sia only — nothing goes to the VPS filesystem — and is
|
||
served through the gateway at `https://navigate.st/bns/theseus.x/extensions/`.
|
||
A first release has no `updates.json` to prepend to, so it ships one holding
|
||
just its own entry. Versions must increase.
|
||
|
||
## Testing
|
||
|
||
Three levels, all re-runnable, all from `../../addon-build/docx-editor`:
|
||
|
||
npm test # the built-in fixture, round-tripped
|
||
node test/corpus.mjs <folder of .docx> # real documents
|
||
npm run test:e2e # a real Theseus, driven over CDP
|
||
|
||
The last one boots a throwaway Theseus, opens documents through the sidebar,
|
||
clicks the real ribbon, saves and exports, and reads the result back — about
|
||
forty checks. It is the only level that catches breakage which exists solely
|
||
in the browser: the vendored bundle failing to load as a classic script from
|
||
`file://`, webfonts that register but never fetch, a ribbon button wired to
|
||
nothing, a PDF on the wrong paper. Each of those has happened.
|
||
|
||
It used to live in `scratchpad/`, which is untracked, and was deleted by a
|
||
tidy-up on 2026-10-22 along with the pdf-editor's copy. It lives here now.
|
||
|
||
What survives a round trip, and what doesn't, is written up in
|
||
[ROUND-TRIP.md](ROUND-TRIP.md). Read that before promising anyone a Word
|
||
feature.
|