Tab indents, "- " makes a bullet, and Enter ends a list
Three of the things Paul's brief calls basic, and one defect found while
building them.
TAB. In a list it nests the item and Shift+Tab lifts it; anywhere else
it steps the block's own indent. One key, two contexts, resolved by
chainCommands rather than by testing which context we are in.
Indent is a block attribute, and it follows align.mjs's shape rather
than inventing a second one — with one real difference. Alignment's
value is a string, so the only safe handling is a closed table whose
right-hand side is a literal. Indent's is a NUMBER, so it is handled the
way heading.level already is: parsed, then clamped. Both reach the
property fragment.rs exists to hold — no byte from a document reaches
attribute position — and using the existing route rather than inventing
a third one is the point. indented.bin pins the cases no editor can
produce and POST /d/{sqid}/updates accepts anyway: 99 clamps to 8, 0 is
no indent, "3; drop" fails to parse and renders flush left.
Tab does not trap focus. Shift+Tab at indent 0 in the first block
returns false, so the browser's own focus move takes over — which is why
changeIndent reports honestly at the ends of its range instead of
returning true and swallowing the key.
BULLETS. "- ", "* ", "+ ", "1. ", "> " and "# " through "### ". Backspace
straight afterwards reverts the rule and gives back the characters
typed, which is the whole reason autoformat is tolerable rather than
infuriating; it is bound ahead of baseKeymap, because baseKeymap binds
Backspace too and whichever comes first wins. A numbered list always
starts at 1 even when you type "7. " — fragment.rs does not read `order`,
so setting it would make the editor disagree with the read view and the
PDF with nothing reporting it. Asserted rather than commented, so the day
`order` becomes real the test points at the decision.
ENTER IN A LIST was never bound. baseKeymap's splitBlock put a bare
paragraph inside the list item and there was no way to end a list by
pressing Enter twice. splitListItem, chained ahead of it.
Two fixes to the menu bar from Fable's branch review. [docs-menubar] and
[docs-bar] were each position: sticky at inset-block-start: 0, which does
not stack — two sticky siblings pinned to the same offset share one band,
so on the first scroll the formatting bar slid under the menu bar and was
gone. One sticky wrapper around both. And the Format menu had no
paragraph styles at all: the Style select covers them for someone who can
see the bar, and the menu is the surface for someone working from the
keyboard.
The menu's own structure test caught a defect while this landed — adding
the styles left two dividers in a row — which is the first thing it has
been asked to find.
browser-drive.mjs's tab-order bound is re-derived rather than nudged, as
the session that owns that file asked: it goes DOWN, from 5 to 4, because
the menu bar adds one stop while taking the two export links out of the
order entirely — a closed <details> hides its contents. Derived, not
measured: Chromium will not start in this sandbox, so the first real run
checks the derivation as much as the page.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrHJFx6wn4VvTzc3drkTp4
19 files changed,
+883
−54
.amadan/ci.d/10-gate+24 −0core/src/fragment.rs+112 −6core/tests/fragment.rs+88 −0fixtures/autoformat.mjs+92 −0fixtures/autoformat.test.mjs+164 −0fixtures/browser-drive.mjs+33 −13fixtures/editor-entry.js+66 −3fixtures/generate-fragment.mjs+43 −0fixtures/indent.test.mjs+77 −0fixtures/package-lock.json+11 −0fixtures/package.json+3 −2fixtures/toolbar.mjs+26 −2fixtures/vectors/fragment/indented.binbinaryinternal/core/testdata/docs_core.wasmbinaryinternal/docs/menu.go+23 −5internal/docs/static/{editor.22f9ae5f330c892f.js => editor.8880fc9f6eb7f7b4.js}+13 −13internal/docs/views/show.html+61 −8internal/export/document.go+10 −2internal/export/pdf.go+37 −0
.amadan/ci.d/10-gateLoading diff…
core/src/fragment.rsLoading diff…
core/tests/fragment.rsLoading diff…
fixtures/autoformat.mjsLoading diff…
fixtures/autoformat.test.mjsLoading diff…
fixtures/browser-drive.mjsLoading diff…
fixtures/editor-entry.jsLoading diff…
fixtures/generate-fragment.mjsLoading diff…
fixtures/indent.test.mjsLoading diff…
fixtures/package-lock.jsonLoading diff…
fixtures/package.jsonLoading diff…
fixtures/toolbar.mjsLoading diff…
fixtures/vectors/fragment/indented.binLoading diff…
internal/core/testdata/docs_core.wasmLoading diff…
internal/docs/menu.goLoading diff…
internal/docs/static/{editor.22f9ae5f330c892f.js => editor.8880fc9f6eb7f7b4.js}Loading diff…
internal/docs/views/show.htmlLoading diff…
internal/export/document.goLoading diff…
internal/export/pdf.goLoading diff…