One dialog per thing, and rows dragged into a chapter

A separator had three menu entries — rename, colour, line — plus an input
that appeared in the row itself: three shapes of interaction for one small
thing, and none of them showed the result. Choosing "dotted" from a list
of words is a guess. It is one Edit now, and the dialog draws the line as
it will look with every shape drawn beside its name, from the very rules
the list draws with: a sample that can disagree with the result is worse
than none.

A chapter goes the same way — name, mark and colour in one dialog, the
same one whether it is being made or changed, with the row drawn at the
top. Asked from three entries, getting a new chapter to look right took
three visits. The name dialog it used is gone.

A row is put into a chapter by dragging it onto it: the chapter lights
up and takes what is dropped rather than growing a line above it, since
a chapter is a place and not a neighbour. Notes and separators both, and
the "+ Separator" inside a chapter goes with it — a second way to make
the same thing in a second place is a second thing to keep straight.

A link now wears the mark of the note it points at, in the text and in
the @ menu. It travels when the note's mark changes, as the label
already did.

Two alignment faults: a task list's checkbox was nudged down by a fixed
0.35em measured once against one font, so it floated above the line it
belonged to — it is centred in a box one line tall now. And the title
bar's mark was spaced like the buttons at the far end and grew with its
contents, so picking an emoji shifted the title sideways.

Fourteen suites green: 374 browser checks, 241 unit tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-17 00:23:26 +02:00
1 parent 59844046c4
commit d68b0775c2
20 files changed
+1185 -594

No files matched your search

+17 -3
View File
@@ -50,6 +50,15 @@ export const NoteLink = Node.create({
},
// Set when the target no longer exists, so a dead link reads as broken
// instead of silently going nowhere.
/* The target's own mark, kept on the link and refreshed like the label is.
* A reference that shows the same sheet-of-paper glyph whatever it points
* at tells you nothing; the mark you gave the note is how you recognise
* it everywhere else. */
icon: {
default: null,
parseHTML: (el) => el.getAttribute("data-icon") || null,
renderHTML: (a) => (a.icon ? { "data-icon": a.icon } : {}),
},
missing: {
default: false,
parseHTML: (el) => el.getAttribute("data-missing") === "true",
@@ -71,7 +80,7 @@ export const NoteLink = Node.create({
href: "#",
title: node.attrs.label,
}),
`${{ notebook: "📓", pin: "📌" }[node.attrs.kind] || "📝"} ${node.attrs.label}`,
`${node.attrs.icon || { notebook: "📓", pin: "📌" }[node.attrs.kind] || "📝"} ${node.attrs.label}`,
];
},
@@ -95,7 +104,12 @@ export const NoteLink = Node.create({
.insertContentAt(range, [
{
type: "noteLink",
attrs: { noteId: props.id, label: props.label, kind: props.kind || "note" },
attrs: {
noteId: props.id,
label: props.label,
kind: props.kind || "note",
icon: props.icon || null,
},
},
{ type: "text", text: " " },
])
@@ -131,7 +145,7 @@ function renderSuggestionList() {
it,
i
) => `<button type="button" class="note-link-option${i === selected ? " is-selected" : ""}" data-index="${i}">
<span class="note-link-kind">${it.kind === "notebook" ? "📓" : "📝"}</span>
<span class="note-link-kind">${it.icon || (it.kind === "notebook" ? "📓" : "📝")}</span>
<span class="note-link-label"></span>
</button>`
)