oficina / docs Public

Font size is points now, and it is a closed ladder

Paul asked for font size. The control had four named steps — Small,
Normal, Large, Extra large — stored as multipliers of the body size.
That is a good way to build a text editor and a poor way to build a word
processor: people arrive knowing what 12pt is, documents specify point
sizes, and "Large" does not survive a conversation with someone holding
a printout. The ladder is Google Docs' own: 8, 9, 10, 11, 12, 14, 18,
24, 36, 48, 72.

ELEVEN MARKS, NOT ONE MARK CARRYING A NUMBER, and that is the whole
shape of the feature rather than a preference. fragment.rs's output is
written into the page unescaped, which is safe only because no byte from
a document reaches attribute position: every mark is attribute-free and
its markup is a compile-time literal chosen by lookup. A mark spelling
"14pt" would be a valued mark, and POST /d/{sqid}/updates accepts
arbitrary Yjs bytes that never met the schema. Eleven more attribute-free
marks spend no new safety property; one sized mark would spend the one
the whole renderer rests on. So the ladder is not the sizes we happened
to pick — it is the set this design can express, and 13pt later is a row
in four places rather than a schema change.

ABSOLUTE POINTS, not multipliers. A multiplier compounds: 14pt inside a
1.6em heading came out at 1.6 x 14, which is not what anyone means by
setting a run to 14pt. The PDF sets rather than multiplies now, matching.

The four old marks stay in the vocabulary permanently and are simply no
longer offered. Not politeness — correctness: documents in the wild
carry them, and a mark the renderer stops knowing is text that silently
loses its size for every reader without JavaScript. They map to their
nearest ladder step (10, 14, 18), so "Large" is one point smaller than it
was. That is a real change to existing documents and it is the price of a
closed ladder; "Large" never told anyone it meant 15pt.

Two tests earned their keep while this landed. TestPageGeometry compared
the stylesheet's 0.5in indent against a zero, because IndentStepPt was an
untyped INT constant and `IndentStepPt / 72` integer-divided — it
reported a disagreement that did not exist, which is the fortunate half
of that mistake. And TestToolbarOptionValuesMatchTheBundle went red on
three missing size marks: the schema now builds them from sizes.mjs
rather than spelling fourteen names out, so the grep found nothing. It
said so instead of passing, which is the behaviour that clause exists
for; the check moved to where the vocabulary now lives.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XrHJFx6wn4VvTzc3drkTp4
Paul Campbell pushed by paul@keymail.dev 7be0d35efdea32b9f48961f355a51e4ee66ecf75 parent 29aa013
16 files changed, +780 −50
  • .amadan/ci.d/10-gate +11 −0
  • core/src/fragment.rs +28 −0
  • core/tests/fragment.rs +40 −0
  • fixtures/editor-entry.js +17 −4
  • fixtures/generate-fragment.mjs +33 −0
  • fixtures/sizes.mjs +156 −0
  • fixtures/sizes.test.mjs +93 −0
  • fixtures/toolbar.mjs +39 −3
  • fixtures/vectors/fragment/sizes.bin binary
  • internal/core/testdata/docs_core.wasm binary
  • internal/docs/export.go +38 −14
  • internal/docs/export_test.go +62 −4
  • internal/docs/sizes_test.go +134 −0
  • internal/docs/static/{editor.8880fc9f6eb7f7b4.js => editor.be9e1d1ff62c9336.js} +13 −13
  • internal/docs/views/show.html +40 −9
  • internal/export/pdf.go +76 −3
.amadan/ci.d/10-gate

Loading diff…

core/src/fragment.rs

Loading diff…

core/tests/fragment.rs

Loading diff…

fixtures/editor-entry.js

Loading diff…

fixtures/generate-fragment.mjs

Loading diff…

fixtures/sizes.mjs

Loading diff…

fixtures/sizes.test.mjs

Loading diff…

fixtures/toolbar.mjs

Loading diff…

fixtures/vectors/fragment/sizes.bin

Loading diff…

internal/core/testdata/docs_core.wasm

Loading diff…

internal/docs/export.go

Loading diff…

internal/docs/export_test.go

Loading diff…

internal/docs/sizes_test.go

Loading diff…

internal/docs/static/{editor.8880fc9f6eb7f7b4.js => editor.be9e1d1ff62c9336.js}

Loading diff…

internal/docs/views/show.html

Loading diff…

internal/export/pdf.go

Loading diff…