all: trim the history essays out of the longest comments
CI & Build / Build now, or wait for Android? (push) Successful in 3s
Android / Build, or is the channel already serving this? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 3s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 9s
CI & Build / Python tests (push) Successful in 12s
CI & Build / integration (push) Successful in 2m0s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Successful in 3m11s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m51s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m39s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Android / Kotlin + Rust (APK) (push) Successful in 9m4s

From the audit (#5179). Ten comment blocks narrated how the code got here:
milestone numbers, earlier values, the operator's verdict on an old design.
Each now says what the code does and why, and the history stays in git,
Scribe and docs/sync.md. The protocol-version comment in sync.py points at
docs/sync.md's policy section, which already lists every bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 20:03:44 -04:00
co-authored by Claude Opus 5.5
parent e75e3d37d0
commit 888c6410f0
7 changed files with 65 additions and 232 deletions
+9 -19
View File
@@ -110,28 +110,18 @@ export function plusTask(blocks: EditorBlock[]): { blocks: EditorBlock[]; focus:
}
/**
* Re-read ONE prose block for `- [ ] ` lines somebody typed by hand.
* Re-read ONE prose block for `- [ ] ` lines somebody typed by hand, so a marker
* typed in the editor becomes an item without closing and reopening the note.
*
* `splitBlocks` runs once, when the editor opens. After that the blocks are the state
* and nothing reads the body again — every edit travels the other way, through
* `joinBlocks`. So a marker typed by hand stayed literal text on screen until the note
* was closed and reopened, even though it was already a real item in storage and the
* card was already drawing a checkbox for it. The editor was the only place that
* disagreed.
* On blur, and only the block being left: re-splitting on a keystroke would move the
* caret, and converting the moment `- [ ]` is complete would catch an item with no
* text yet.
*
* ON BLUR, and only the block being left. There is no good moment to convert while
* someone is typing: re-splitting on a keystroke moves the caret out of the word being
* written, and converting the instant `- [ ]` is complete does it before the item has
* any text. Blur is the one moment the person has demonstrably finished with the block,
* so a re-split costs no caret and cannot catch a half-typed line.
* Returns THE SAME ARRAY when there was nothing to promote; the caller relies on that
* to leave the ref alone, so a blur that changed nothing doesn't re-key every field.
*
* Returns THE SAME ARRAY, not an equal copy, when there was nothing to promote — the
* caller leans on that to leave the ref alone, and a blur that changed nothing must not
* re-key every field below it.
*
* Non-canonical markers (`- [X]`, an odd bullet) come back canonical, exactly as they
* would have on reopen. That is the only case where this changes the body rather than
* only the way it is drawn.
* Non-canonical markers (`- [X]`, an odd bullet) come back canonical, as they would on
* reopen.
*/
export function promoteTasks(blocks: EditorBlock[], index: number): EditorBlock[] {
const block = blocks[index];
+15 -73
View File
@@ -36,29 +36,12 @@ export const NOTE_SWATCH_CLASSES: Record<NoteColor, string> = {
};
// A chip's SHELL: its fill and its hairline edge, keyed by the colour vocabulary. The
// INK is not here — see TAG_TEXT_CLASSES, which one table now serves both a chip's text
// and a `#tag` left in the prose. Compose the two with `labelChipClasses`.
// ink is TAG_TEXT_CLASSES; `labelChipClasses` composes the two.
//
// THE RING IS NOT DECORATION. A chip's fill measures 1.02-1.26 against the card in
// light and 1.02-1.73 in dark — that is to say, very nearly nothing. The pill's shape
// is the edge; the fill only tints it. (Dark red is the extreme at 1.02, which is
// invisible: without the ring that chip would be loose text.) An edge holds the shape
// against any background, where shifting the fill only moves which card it collides
// with.
//
// AT 65%, NOT 60%, AND SOLVED FOR RATHER THAN GUESSED. 0.60 was chosen against a worst
// case that no longer exists — a chip sitting on a card of its own colour, back when a
// note took its first tag's fill. Against the one card surface (M315) the ring is the
// ink at alpha over a known fill, so the alpha that clears the 3:1 of WCAG 1.4.11 for
// all ten hues can simply be solved: 0.60 gives 2.75-3.82 in light and misses for six
// of them, 0.65 gives 3.03-4.36 and misses for none. Dark is 4.52-5.76. 0.80 was the
// number the old comment named as the fallback and it is more than is needed — it
// draws a hard outline where a hairline does the job.
//
// `default`'s ring was `black/10 dark:white/15` here and the ink at alpha on the phone —
// 1.36 against its own fill in light against Compose's 3.21, so the two surfaces were
// drawing visibly different pills for the same chip. It is the ink at 65% on both now,
// like every other key: one rule for ten hues, not nine and an exception.
// The ring is what gives the pill its shape: a chip's fill measures only 1.02-1.73
// against the card. It is the ink at 65%, the lowest alpha that clears WCAG 1.4.11's
// 3:1 for all ten hues on the one card surface (light 3.03-4.36, dark 4.52-5.76).
// `default` follows the same rule as every other key.
//
// Mirrored in NoteTint.kt as `chipBackground` and `chipBorder` / CHIP_EDGE_ALPHA.
export const LABEL_CHIP_SHELL: Record<NoteColor, string> = {
@@ -74,26 +57,13 @@ export const LABEL_CHIP_SHELL: Record<NoteColor, string> = {
gray: "bg-neutral-200 dark:bg-neutral-700 ring-1 ring-inset ring-neutral-800/65 dark:ring-neutral-200/65",
};
// THE INK A TAG IS DRAWN IN — one table, for a `#tag` left in the prose AND for a
// chip's text. It was two, and the split was real while it lasted: a chip carried its
// own `-100` fill and could afford `-700`, while inline text sat on whatever the card
// was, which included a gray-tagged card at `neutral-200` where `-700` measured 3.98
// (green), 4.11 (orange) and 4.34 (teal) — all under the 4.5 body text needs. One step
// deeper cleared every fill at once, so inline got `-800` and the chip kept `-700`.
//
// M315 removed the twenty card fills the split was solving for, and this is the payoff:
// against ONE card surface both jobs can take the same value. `-800`/`-300` is the one
// they take, and the direction is deliberate — the inline token is the common case
// (since M311 a tag whose text is in the body is drawn where it was typed and NOT
// repeated as a chip), so collapsing onto the inline column leaves what is seen most
// exactly as it was, and moves only the chip. The chip is strictly better for it:
// THE INK A TAG IS DRAWN IN — one table, for a `#tag` left in the prose and for a
// chip's text. `-800` in light and `-300` in dark clear body-text contrast in both
// places:
//
// inline, on the card as a chip, on its own fill
// light `-800` 7.09 - 15.13 6.37 - 12.01 (was 4.52 - 8.23)
// dark `-300` 9.45 - 14.23 8.23 - 11.88 (unchanged)
//
// Dark needed no decision at all: the two tables were already the same value for all
// ten hues, which is on its own most of the argument that one table was always enough.
// light `-800` 7.09 - 15.13 6.37 - 12.01
// dark `-300` 9.45 - 14.23 8.23 - 11.88
//
// Mirrored in NoteTint.kt as `lightTagInk` / `darkTagInk`.
export const TAG_TEXT_CLASSES: Record<NoteColor, string> = {
@@ -249,33 +219,9 @@ export function resolveLabelColor(label: { name: string; color?: string | null }
// chips of one colour are the same tag. The chip's text is what says which tag it is.
// ---------------------------------------------------------------------------
// THE CARD SURFACE — one neutral, no hue, no note involved (M315).
//
// This used to be a function of the note: a palette class for a tagged one, a fill
// generated from the id for the rest. Both are gone. The operator's verdict after
// four passes at it — "my coloring attempt has failed and nothing looks right… we've
// tried a lot to make the color work and somehow it never seems to land" — and the
// diagnosis underneath it is that a card's fill was being asked to carry meaning it
// could not carry. Nine keys is too few to identify anything on a board of any size,
// and a generated fill identifies nothing by construction, so a coloured board taught
// the eye to read hue as significant and then handed it noise.
//
// COLOUR NOW LIVES ONLY ON THE TAG — the chip and the inline `#tag`, both of which sit
// on this one known background from here on. That is a smaller job done properly
// instead of a larger one done four times.
//
// The codebase had already made this argument about the card's EDGE, one level down:
// the hue-coded border came out as "a neutral line carries no information at all,
// which is exactly what lets it be structure instead of content". The fill is the same
// argument at the next size up.
//
// THE VALUES. `bg-white dark:bg-neutral-900` — which is exactly what `default` always
// was, and exactly what the note EDITOR panel has always been (NoteEditor.vue), so
// this is a collapse onto a surface both other surfaces already used rather than a
// new colour anybody has to like.
//
// Measured against the operator's constraint, "not the same color as their background
// but close to it":
// THE CARD SURFACE — one neutral, the same for every note. Colour lives only on tags
// (the chip and the inline `#tag`); a card's fill can't carry meaning, because nine
// keys identify nothing on a board of any size. It is the editor panel's surface too.
//
// card vs board light #ffffff on #fafafa 1.04
// dark #171717 on #0a0a0a 1.10
@@ -283,11 +229,7 @@ export function resolveLabelColor(label: { name: string; color?: string | null }
// dark #404040 on #171717 1.73
// body vs card light #171717 on #ffffff 17.93 (needs 4.5)
// dark #fafafa on #171717 17.17
// muted vs card light #404040 on #ffffff 10.37
// dark #e5e5e5 on #171717 14.23
//
// The fill is deliberately the WEAKEST of those numbers. A card is not separated from
// the board by its fill and never was — the edge and the shadow do that, which is why
// 1.04 is enough and why it has to stay near 1: a fill that separated on its own would
// be a panel, and a board of panels is the wall this whole line of work started from.
// The fill is deliberately the weakest number: the edge and the shadow separate a card
// from the board, and a fill strong enough to do it alone would make every card a panel.
export const NOTE_CARD_SURFACE = "bg-white dark:bg-neutral-900";