Commit Graph
8 Commits
Author SHA1 Message Date
marcandClaude Opus 5 f01522aa6f A chapter is a list too, and deleting stops moving things
Deleting a note moved everything else. A row leaving is the same problem as
a row arriving, in reverse: a line's position counts the rows above it, so a
note deleted above a line left the line counting rows that were no longer
there, and everything under it appeared to jump up past it. withoutRow
renumbers the list a row leaves, and every place one leaves says so -- the
bin, a move, a chapter being removed. Arrivals land at the top: an old
position belongs to an arrangement that has moved on.

A line could not be dragged under another line. Two lines in the same gap
carry the same position, and the comparator claimed "a separator comes
first" without asking what the other one was -- both directions at once for
that case. A comparator that contradicts itself has no defined result, so
the sort returned them in whichever order it happened to walk.

A chapter is always in the user's own order and can be arranged like any
other list: its notes wear handles under the same switch as the list beside
them and are dragged among themselves, strips at both ends included. The two
lists keep apart by scope -- a chapter unfolded under its row holds rows
inside a row of the notebook's list, so the nearest row above the pointer
cannot say which list a drag belongs to. The sort menu is gone inside a
chapter: the mode belongs to the notebook, and a menu there would offer a
choice that cannot be honoured.

The chapters suite no longer assumes the first chapter row is the one that
matters. A chapter is a row in the arrangement and can be anywhere in it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 12:37:02 +02:00
marcandClaude Opus 5 a9391cdcf2 One direction to drop in, and a new note that stays on top
Every new note pulled the arrangement apart. A note was made with no
position and left to the tie-break, which put it near the top by age --
which is not the same thing. A separator's position counts the rows above
it, so a row appearing without a renumber left every line one row too
high. placeOnTop builds the order from the list *without* the new note and
puts it in front: measuring the room after the furniture has moved would
write the misreading back as the truth.

A drop now lands the row under the one it was released on, and only there.
Dropping above a row and dropping below the row before it are the same move
described twice, so every gap could be aimed at from two sides. The strip
above the first row is the one place a drop under something cannot reach.

A chapter could only be dropped into, never dragged past: taking the whole
row as a place quietly took reordering away. The bottom edge of its header
is the gap now, the rest of the row is still the chapter.

A link showed the sheet of paper whatever it pointed at. The @ menu dropped
the mark on the way into the link, and the refresh that keeps a link current
ran once, while the editor was still empty -- content is poured in afterwards
without an update event, so nothing ever looked again.

The mark in the title bar and the box in a task list were each centred on a
line box rather than on the letters in it, which is not the same as being on
one line. The mark shares the title's baseline; the box is centred on the
capitals and sized in em.

Move to... is divided under headings that are deliberately not shaped like
entries, and each row carries the mark its chapter or notebook wears.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 11:45:16 +02:00
marcandClaude Opus 5 c96885d5b3 A notebook opens in its own order
The hand-made order is where a notebook's own arrangement lives:
separators show, rows can be put where they belong, nothing slides about
by itself. Starting in "last changed" made both of those look broken
until the sort menu was found — pressing "+ Separator" there drew a line
the order cannot display, so the button appeared dead. A notebook nobody
has arranged still reads newest-first, because untouched rows all carry
position 0, so nothing is lost by starting there. The views keep "last
changed": they are questions asked of every notebook at once and have
nowhere to keep an arrangement. The button that draws a line is now only
offered in the order that can show one.

Removing a chapter left you stuck in the confirmation. Every destructive
action goes through one helper that takes the question down again; this
one set the dialog directly and bypassed it, so the chapter was removed
and the prompt stayed over the app. Closing now happens where the dialog
is rendered, so it happens whoever asked and however they asked.

The suites did not catch that, and the reason is worth keeping: they
read the list straight through the overlay, which is in the DOM the
whole time, and passed while the screen was in fact stuck. Both now
assert the question is gone afterwards.

The separator is no wider than its name. It sat in a fixed 132px slot —
chosen so the row would not jump when renaming began — with a short name
floating in a gap it did not fill.

Thirteen suites green: 345 browser checks, 241 unit tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 21:45:03 +02:00
marcandClaude Opus 5 d74fea4905 Chapters as rows, in two views, in their own colours
Three things Marc asked for, and one of them turned out to be the hinge
the other two hang from.

**A chapter is a row like any other.** Same grip, same drag, same place
in a hand-made arrangement as a note or a separator. That needed notes
and chapters numbered from *one* counter: numbered separately they are
two sequences that cannot be interleaved again, and a chapter dragged
between two notes springs back to the top. Positions therefore have to be
named rather than read off an array index, so POST /notes/reorder now
takes `positions: [{id, order}]` beside the older `ids`, and chapters
have a reorder route of their own.

**Two ways for a chapter to open**, chosen per account beside the
typeface, for the same reason that one lives there: it is a choice about
how your own notes are laid out and it should follow you to the phone.
Beside the list, its notes in a further column the way OneNote lays out
pages — the notebook's own list carrying on to the left, chapter rows and
all. Or unfolded under the chapter's own row, indented against a rule in
its colour. A phone has room for neither and fills the list instead, with
a way back, which is what it did before there was a choice.

**Its own colour**, from eight named ones rather than the full palette:
the point of chapters is that they are different places, and twenty
near-identical blues do not help anyone tell them apart.

The colour is validated on the way in — it is written by a browser, so
only a plain #rrggbb gets through and anything else becomes no colour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 14:20:14 +02:00
marcandClaude Opus 5 1757ec9af3 Chapters: folders inside a notebook
A note is in exactly one chapter, or in none — which means the notebook
itself. Chapters sit above the loose notes; tapping one opens its own
list and the menu button becomes the way back out, which on a phone is
the only way, since the list fills the screen.

A chapter is not a wastebasket. Removing one never removes what it held:
the notes come back to the notebook, where they can be seen and filed
again. That is ON DELETE SET NULL, and it is also written out in the
delete route, because it is the promise the whole thing rests on and
should be visible where it happens rather than inferred from a schema.
The dialog says so too, with the number of notes affected, and the suite
removes a chapter holding three notes and asks the server for all three.

Three rules keep the two levels honest. A note filed into a chapter
leaves the notebook's own list, or filing it would appear to do nothing.
A chapter belongs to its notebook, so the server refuses to file a note
into a chapter of a book it is not in, and moving a note to another
notebook clears its chapter. And separators belong to the list they were
drawn in: a separator's order counts the notes before it, which means
nothing across lists.

Two bugs found while building it, both a day old and both affecting the
desktop rather than the phone they were written for:

- A submenu was clipped away. The context menu had been given
  `overflow-y: auto`, which cuts off anything flying out sideways — so
  the notebooks under "Move to..." were invisible and every click on them
  landed on the note list behind. The side was inverted too:
  `right: 100%` places a panel on the left.
- A separator was a one-pixel hairline in the border colour with faint
  grey text on near-black. Present, and on a phone effectively invisible.

And one test that never measured what it claimed. It asked whether a
dragged note had become "the most recently changed" by reading the order
on screen — but three notes made seconds apart share an `updated_at`
second, so that order is a tie broken arbitrarily. It passed on luck for
weeks and fell over on a run a fraction faster. It now compares the
timestamps themselves, before and after the drag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 13:04:18 +02:00
marcandClaude Opus 5 52bd2b8f3d Put the interface into English, and give the notebook one way in
The interface had drifted into two languages — the frame in English, everything
added later in German — which is worse than either, since the same idea appeared
under two names depending on when that part was written. Sorting by title still
uses the German collation, so Übung 2 comes before Übung 10: that is about the
notes, not the chrome.

Two archive folders were renamed with it (bilder/ → images/, zeichnungen/ →
drawings/). The importer reads both spellings, and both the old and new marker
line for a drawing — archives exported before this are on disk, and an import
that quietly dropped their drawings would be the worst kind of tidy-up.

Making a note, uploading a PDF and importing Markdown were three buttons side by
side, two of them icons you had to hover to identify. They are the same act —
putting something new in this notebook — so they are now one + menu.

The search box moves out of the sidebar and into the note list, labelled with the
notebook it narrows. Nothing changed about what it does: it has always searched
only the notebook you are in, but from the sidebar it read as searching
everything.

A separator no longer carries a sentence explaining the order the sort menu
already names. Renaming joins removing on the right-click menu — a left-click on
the label started an edit, so the row could not be touched without falling into
one.

218 unit tests, 238 browser checks across eleven suites. Two of those caught real
breakage from this change rather than needing a translation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 02:17:22 +02:00
marcandClaude Opus 5 2cee00e38e Fix a permanent black line in the note list, and add separators
The drop indicator was a `borderTop` set in the same style object as a `border`
shorthand. Clearing the longhand resets its width and colour to the defaults
while the shorthand's `solid` stays, so a row kept a 2px black line for good —
measured at rest, before any drag. It is now its own element, which also stops
the list twitching as a row grows and shrinks by a pixel.

That element then turned out to be a dead strip of its own: it pushes the row
down as it appears, so the pointer ended up over the line rather than over the
row and releasing there dropped on nothing. The drag handlers moved to the whole
row and the indicator no longer takes pointer events. A strip below the last row
means "put it at the end", which otherwise needs a drop onto the final note.

Separators are lines drawn between notes, optionally named, stored on the
notebook beside its sort mode. They drag like notes do, and their position is
"how many notes come before me", so they hold their place as the notes around
them are renamed or edited. Shown only in the manual order — in "newest first"
the notes would slide past the line on every edit.

"All Notes" and "Starred" no longer offer a hand-made order: they are questions
asked of every notebook, so there is nothing for an arrangement to belong to.

199 unit tests, 190 browser checks across eleven suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:45:38 +02:00
marcandClaude Opus 5 0a0864c58b Sort a notebook, import Markdown back, and stop the page shrinking in Draw
The paper measurably shrank on switching to Draw — 733px to 460px on the same
screen. The editor was a flex item at its default `flex: 0 1 auto`, so its width
was its widest row's: the toolbar. Swapping a dozen formatting buttons for six
drawing ones collapsed it. It now fills the pane, so the width is a property of
the window.

Each notebook remembers how its notes are ordered, on the server rather than per
device. The manual order has to survive being looked away from, so `reindex`
renumbers every note in the notebook rather than the filtered view, and dragging
is switched off while a search narrows the list. Reordering has its own route:
`PUT /notes/:id` would bump `updated_at` and snapshot a version, so a drag would
move a note to the top of "last changed" and spend a slot of its history.

Markdown and our own ZIP archives come back as notes, with their images
re-uploaded as attachments. A drawing returns as the PNG it was exported as —
vectors are not recoverable from pixels — and the import says so rather than
leaving it to be discovered. Writing the parser turned up a real bug in the
exporter: blocks were emitted with no blank line between them, so a list after a
list became one list and a quote after an item was read as part of it.

Creating or importing from "All Notes" now asks which notebook instead of
silently taking the first one. The web-link prompt is a real dialog: a bare
domain becomes https, javascript: is refused with a reason. The PDF viewer's
hide-drawing control is a labelled button rather than a filled-or-hollow dot,
and the export icon points down into a tray like every other download.

186 unit tests, 179 browser checks across eleven suites, including a new
`offline` suite that loads the app cold with the network off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 01:38:03 +02:00