Colour, a page you can see the edge of, and a document on its own

Five of the nine things on the list, and one finding that came out of them.

Colour: a marker pen and an ink colour, written as marks here rather than
installed — the schema of a note decides what can be stored in it forever. A
highlight is a <mark> so a reader that drops the colour still shows the word
as marked, and marked text is painted dark whatever the theme. Both travel as
HTML in Markdown, like underline, and both are read back.

The PDF fills the screen: the browser's own full screen for the window frame,
an overlay for the sidebar and the list, Escape and a visible button for the
way out.

The dashboard leads with the recent notes, sorted by time whatever order the
notebooks are in. A tap on the blank part of a page puts the caret at the
nearest position instead of nowhere. A drag from the left edge shows the
sidebar. The page has an edge on a phone, where it had none.

The finding: at the speed a robot types, a long note loses keystrokes between
the editor and the pagination measuring after each one — 87 of 220
paragraphs. Nothing is lost at 50 characters a second, so the page-count
check now pastes its bulk text rather than measuring typing throughput. And
the history suite asked for "the first note in the list" without waiting for
the note to reach the server; it asks by name now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-26 12:40:21 +02:00
1 parent c9148540a8
commit 9507761bce
23 files changed
+1091 -109

No files matched your search

+59 -2
View File
@@ -613,12 +613,13 @@ docker compose -f docker-compose.dev.yml up -d --build # must be running
| Suite | Checks | What it holds down |
| --- | --- | --- |
| `admin` | 15 | no sign-up anywhere; `/api/admin/*` refused for non-admins even when called directly |
| `shell` | 9 | the frame around the writing: what the dashboard leads with, the sidebar answering a drag from the edge, a tap on blank paper putting the caret there, and the page having a visible edge on a phone |
| `oidc` | 40 | the whole redirect journey against a provider that signs real tokens — and the four ways it must fail: a refusal, a replayed ticket, a token signed with an unpublished key, a token addressed to another client |
| `editor` | 45 | tables and their right-click menu, task lists, note links, backlinks — and that each survives a reload |
| `editor` | 52 | tables and their right-click menu, task lists, note links, backlinks — and that each survives a reload |
| `search-attachments` | 12 | body-only matches, ranking, umlauts; images as blob URLs, not inline bytes |
| `history` | 10 | a large deletion is snapshotted, restoring works, restoring is itself undoable |
| `drawing` | 22 | strokes survive the poller, match across four viewport widths, and stay on the page they were drawn on |
| `pdf` | 96 | import, page proportions, lazy loading, and reading with the network off |
| `pdf` | 101 | import, page proportions, lazy loading, and reading with the network off |
| `attachments-lifecycle` | 10 | a picture is kept while any version shows it, and freed once none does |
| `conflict` | 10 | a late write wins but the state it buries survives as a version |
| `export` | 23 | the downloaded bytes are real Markdown, ZIP and PNG, unpacked and read back |
@@ -674,6 +675,26 @@ block a meaning, which only moved the problem — the whole sheet sat hard again
the left edge. Centred, the same leftover space reads as two margins and needs no
explaining.
**The dashboard leads with what you were last writing.** The six most recently
edited notes come first and the shelf of notebooks second, because coming back to
a note is the common case and choosing a notebook is the occasional one. "Recent"
is sorted by time here whatever order the notebooks themselves are in — it is a
question about time, and a hand-made arrangement is the wrong answer to it.
**Tapping the blank part of a page starts writing there.** The text is only as
tall as what has been written, so most of a fresh page is *beside* the editable
area rather than in it; a tap down there used to land on nothing at all — no
caret, and on a phone no keyboard. The caret goes to the position nearest the
tap, not simply to the end, because on a page with several paragraphs "nearest"
is what the tap meant.
**Dragging in from the left edge shows the sidebar** on a phone or tablet, and
dragging it back hides it. The button that opens it sits in the top corner, which
is the one part of a tall screen a thumb cannot reach. The gesture has to start
within 28px of the edge, travel 60px, and be more sideways than downwards —
without the first rule every sideways drag inside a note opens it, and without
the third, scrolling with a thumb near the edge does.
**The typeface is a per-account setting** (Account → Profile): serif, sans or
mono. How a chapter opens is chosen there too, and light or dark, for the same
reason — the sun used to sit in the corner of the sidebar, where it was reached
@@ -702,6 +723,12 @@ itself. A `useLayoutEffect` runs between the DOM being built and the browser
painting it, which is exactly the gap that flash lived in. A test samples the
sheet's height every frame across a note switch, because one frame is all it was.
**The page has an edge on a phone too.** The paper there runs to the edges of the
screen — a phone has no room for margins around a margin — but it used to be
drawn with no border and no shadow at all, which left nothing to see where one
page ended and the next began. It has an outline now; the shadow stays a desktop
thing.
**A quote holds whole blocks.** A heading, a list and a closing line inside one
are ordinary blocks of the document, and they are spaced like ordinary blocks:
the rule that gives the writing its rhythm reaches only the editor's own
@@ -824,6 +851,25 @@ to survive `document.execCommand`. Notes written before this were
opened and rewritten in the new format on the next save, so nothing needs
migrating up front.
### Colour, of two kinds
A marker pen and a colour for the letters, both written as marks in
[`editor/textMarks.js`](frontend/src/editor/textMarks.js) rather than installed —
the schema of a note decides what can be stored in it forever, and that belongs
in files this project owns, the same reason the alignment extension is here.
A highlight is a `<mark>`: the element that *means* highlighted, so a reader that
drops the colour still shows the word as marked. Marked text is painted with dark
ink whatever the theme, the way a real marker leaves the writing alone — a
light-grey word on pale yellow would be unreadable on the dark one. An explicit
text colour is an inline style and still wins.
Both travel as HTML in Markdown, like underline, since Markdown has no colour,
and both are read back — including a bare `<mark>` with no colour, which is what
most other writers emit. Neither keeps its palette in the schema: what the
toolbar offers is an interface question, while the document only has to hold what
was chosen, including a colour picked before the palette last changed.
### Note links and backlinks
Typing `@` opens a search over notes and notebooks and inserts a `noteLink`
@@ -880,6 +926,17 @@ page is expensive to draw and cheap to hold, so scrolling back up repaints
nothing. Canvases are sized in device pixels and scaled down by CSS, which is
what keeps text sharp on a high-density screen.
**Full screen is two things at once.** The button asks the browser for its own
full screen — which is what puts the document past the address bar and the window
frame, and the reason to want it on a laptop or a tablet — *and* lays the
workspace out as an overlay over the app, which is what actually removes the
sidebar, the note list and the title bar. Either alone is half the job, and a
browser that refuses the first (an iPhone does) still gets the second. Escape
leaves, and so does the button, which stays in the toolbar while full: a way out
that is only known and not visible is not a way out. Leaving the browser's full
screen by any other route takes the overlay with it, or the app would be left
pretending to be full screen inside an ordinary window.
**Zooming holds something still.** A zoom changes how tall the document is, so
the scroll position means something different afterwards — left alone, every step
drifts and three presses of + leave you somewhere else in the file. What should