From 165ccf3cf735c3a3f26f03c014b868747272f523 Mon Sep 17 00:00:00 2001 From: marc Date: Wed, 12 Aug 2026 19:28:57 +0200 Subject: [PATCH] A chapter you can see, in the colour you chose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A chapter did not show until the notebook had a note in it. "Nothing here" was asked of the notes alone, so a notebook whose only contents were chapters showed the empty-list panel with the chapters hidden behind it — making a chapter in a fresh notebook appeared to do nothing at all. It is asked of every row now, and a notebook with chapters but no loose notes says so in one line rather than looking like a list that failed to load. The colour is chosen while the chapter is being made, in the dialog that asks for its name. It is what tells one chapter from the next, and having to right-click a fresh one to say which it is meant two visits for one decision. Renaming and recolouring afterwards are one entry in its menu and one write; the server already took the colour on create, the client simply never sent it. The word "here" is gone. The line along the top of the pane says which of the two places you are in, and saying it twice was noise. Thirteen suites green: 338 browser checks, 241 unit tests. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 22 ++++++++++ e2e/chapters.mjs | 31 ++++++++++++- frontend/src/App.jsx | 33 +++++++------- frontend/src/features/notes/NoteList.jsx | 37 ++++++++++------ frontend/src/hooks/useNoteData.ts | 38 ++++++++-------- frontend/src/syncEngine.ts | 23 +++++++--- frontend/src/ui/NameDialog.jsx | 55 +++++++++++++++++++++++- readme.md | 24 +++++++---- 8 files changed, 197 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e54af..a5c601d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,28 @@ not from the output of `docker push`. --- +## 2026-08-12 — A chapter you can see, in the colour you chose + +**Images** — web `sha256:…`, server unchanged at `sha256:ed0b9970…` + +**Fixed** +- **A chapter did not show until the notebook had a note in it.** "Nothing here" + was asked of the notes alone, so a notebook whose only contents were chapters + showed the empty-list panel with the chapters hidden behind it — making a + chapter in a fresh notebook appeared to do nothing at all. It is now asked of + every row, and a notebook with chapters but no loose notes says so in one line + rather than looking like a list that failed to load. + +**Changed** +- **A chapter's colour is chosen while it is being made**, in the same dialog as + its name. It is what tells one chapter from the next, and having to right-click + a fresh one to say which it is meant two visits for one decision. Renaming and + recolouring later are one entry in its menu, and one write. +- **The word *here* is gone.** The line along the top of the pane says which of + the two places you are in; saying it twice was noise. + +--- + ## 2026-08-12 — A filed note stays filed **Images** — web `sha256:2041f103…`, server `sha256:ed0b9970…` diff --git a/e2e/chapters.mjs b/e2e/chapters.mjs index 473806c..ecac39f 100644 --- a/e2e/chapters.mjs +++ b/e2e/chapters.mjs @@ -24,10 +24,14 @@ try { const titles = () => p.locator("[data-note-row] [data-note-title]").evaluateAll((els) => els.map((e) => e.textContent.trim())); - const addChapter = async (name) => { + /* The colour is chosen while the chapter is being made. It is what tells one + chapter from the next, and having to right-click a fresh one to say which + it is meant two visits for a single decision. */ + const addChapter = async (name, colour = null) => { await p.locator("[data-add-chapter]").click(); await p.waitForTimeout(600); await p.getByLabel("Chapter name").fill(name); + if (colour) await p.locator(`[data-colour-option="${colour}"]`).click(); await p.getByRole("button", { name: "Create", exact: true }).click(); await p.waitForTimeout(2000); }; @@ -66,6 +70,31 @@ try { const server = (await api(p, "GET", "/chapters")).body; check(server.length === 1 && server[0].name === "Rechnungen", "the server has it", JSON.stringify(server)); + /* ── in an empty notebook too ── + * "Nothing here" was asked of the notes alone, so a notebook whose only + * contents were chapters showed the empty-list panel with the chapters hidden + * behind it: making a chapter in a fresh notebook appeared to do nothing. */ + await createNotebook(p, "Leeres Buch"); + await p.locator("button").filter({ hasText: "Leeres Buch" }).first().click(); + await p.waitForTimeout(1000); + await addChapter("Nur ein Kapitel", "#a371f7"); + check( + (await p.locator("[data-chapter-row]").count()) === 1, + "a chapter shows in a notebook that has no notes at all", + String(await p.locator("[data-chapter-row]").count()) + ); + const madeWithColour = (await api(p, "GET", "/chapters")).body.find((c) => c.name === "Nur ein Kapitel"); + check( + madeWithColour?.color === "#a371f7", + "and the colour picked while making it is stored with it", + String(madeWithColour?.color) + ); + check( + (await p.locator("[data-no-loose-notes]").count()) === 1, + "and the list says why it is otherwise empty rather than looking broken" + ); + await openNotebook(); + /* ── filing a note ── */ check( (await p.locator("[data-move-note]").count()) === 0, diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 7a0ea72..c7fe846 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -640,9 +640,17 @@ export default function App() { setInChapter(!!next); }; + /* Named and coloured in one go. The colour is what tells one chapter from the + next, so asking for it while the chapter is being made is one decision + rather than a second visit through the right-click menu. */ const addChapter = () => { if (!activeNotebook) return; - setNamingChapter({ title: "New chapter", value: "", onSave: (name) => data.createChapter(activeNotebook.id, name) }); + setNamingChapter({ + title: "New chapter", + value: "", + color: null, + onSave: (name, color) => data.createChapter(activeNotebook.id, name, color), + }); }; const chapterContextMenu = (e, chapter) => { @@ -653,24 +661,25 @@ export default function App() { y: e.clientY, items: [ { - label: "Rename", + label: "Rename or recolour", icon: I.edit, action: () => setNamingChapter({ - title: "Rename chapter", + title: "Chapter", value: chapter.name, - onSave: (name) => data.renameChapter(chapter.id, name), + color: chapter.color ?? null, + onSave: (name, color) => data.editChapter(chapter.id, { name, color }), }), }, { label: "Colour", icon: I.edit, submenu: [ - { label: "None", colorDot: T.faint, action: () => data.recolourChapter(chapter.id, null) }, + { label: "None", colorDot: T.faint, action: () => data.editChapter(chapter.id, { color: null }) }, ...CHAPTER_COLORS.map((c) => ({ label: c.label, colorDot: c.value, - action: () => data.recolourChapter(chapter.id, c.value), + action: () => data.editChapter(chapter.id, { color: c.value }), })), ], }, @@ -769,11 +778,6 @@ export default function App() { paddingLeft: 8, }} > - {inChapter && ( -
- · new notes land here -
- )} {chapterNotes.length === 0 && (
Nothing in here yet.
)} @@ -984,9 +988,6 @@ export default function App() { > {openChapter.name} - {inChapter && ( - · here - )} + ); + })} + + + )}