Editor: three alignment controls, and the vocabulary they share
fixtures/align.mjs is the editor's half of paragraph alignment in one
place — the schema attribute, the command, the active-state reader —
because alignment is used in four files that have to agree and two of
them are separate binaries that cannot import it. Each maps the value
through its own copy of the same closed table and drops what is not in
it; none of them ever emits the value.
The schema's paragraph gains `align`, defaulting to null. That default
is the whole reason an unaligned document is unchanged: ProseMirror
stores null, y-prosemirror skips a null attribute, and choosing "left"
sets null rather than the string, so aligning and un-aligning returns
a paragraph to the bytes it started with. toDOM and parseDOM go
through the same allowlist, so a copy-paste round-trips and a pasted
class from somebody else's stylesheet does not.
One parseDOM rule with a getAttrs, not three ordered ones: a bare
{tag: 'p'} listed first matches a centred paragraph and swallows its
class, and one rule cannot have that bug.
Three buttons rather than a select — a small closed set, switched
between constantly, and worth showing at a glance. The names are
written out one per line rather than generated: TestToolbarOption-
ValuesMatchTheBundle looks for each data-docs-command in this file's
source, and a loop would leave it nothing to find, so it would pass by
seeing nothing. alignmentsAreCovered keeps the three in step with
align.mjs's list instead, and throws at setup rather than shipping a
button that silently does nothing.
align.test.mjs runs in the gate, DOM-free. All twelve of its tests were
watched failing against three naive implementations first: a bare
property lookup ('constructor' became a class), an interpolated
toDOM, a command that ignored dispatch and one that dispatched per
block, wrong class names, ordered parseDOM rules, and "left" stored as
a string.
TestEveryClassTheRendererEmitsIsStyled scrapes the classes out of a
real rendering of aligned.bin and styled.bin and fails on any the
stylesheet does not style, so a class added to the Rust renderer and
forgotten in show.html is caught rather than missed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
10 files changed,
+617
−19
.amadan/ci.d/10-gate+14 −0fixtures/align.mjs+150 −0fixtures/align.test.mjs+203 −0fixtures/editor-entry.js+24 −4fixtures/toolbar.mjs+45 −0internal/docs/export.go+11 −0internal/docs/export_test.go+95 −0internal/docs/render.go+10 −0internal/docs/static/{editor.26513ec0d7ae331b.js => editor.b48adc3ff1090476.js}+13 −13internal/docs/views/show.html+52 −2
Intent
Attributed to this commit: 70.2 g CO₂e (9118639 tokens). This is an attribution, not a measurement — usage counted from this commit's own marker forward to the next one on worktree-align, never backward from it. A turn that produced more than one commit books all of its tokens to the last one, and a concurrent session on this branch can add to the same figure. Some of it was priced using the shared default rate, because no model-specific factor row exists yet for at least one model involved. The Carbon view has the repo total, which is never derived from this attribution.
.amadan/ci.d/10-gateLoading diff…
fixtures/align.mjsLoading diff…
fixtures/align.test.mjsLoading diff…
fixtures/editor-entry.jsLoading diff…
fixtures/toolbar.mjsLoading diff…
internal/docs/export.goLoading diff…
internal/docs/export_test.goLoading diff…
internal/docs/render.goLoading diff…
internal/docs/static/{editor.26513ec0d7ae331b.js => editor.b48adc3ff1090476.js}Loading diff…
internal/docs/views/show.htmlLoading diff…