Keep a pasted note whole, and search the notes from the app

Two things found by using this on real notes.

**The paste.** Copying out of Apple Notes put most of the note on the
floor. WebKit wraps a copied selection in a single span carrying the
computed style of everything in it — `font-weight: 700` included — with
the real blocks nested inside. The serializer read that span as inline,
so every line collapsed into one paragraph and every word came out bold;
switching to the Markdown view then showed what little had survived,
which is what "most of the text was gone" was. And because the boldness
came from a foreign span's style rather than a tag, the bold button
could not remove it.

The rule now is that an element holding blocks is a block whatever its
tag, and that a container's style is not emphasis — only a span wrapping
a single run of text is. A paste this editor cannot read at all (some
engines withhold the clipboard from the event) is tidied afterwards
instead, but only if something actually arrived, so an empty paste still
costs nothing.

**The search.** A Suche tab over the user's own notes, reading the files
rather than the index: notes reach the index only on a full crawl, so a
lesson written this morning would not be findable this morning, which is
most of what anyone searches their own notes for. A result names the
lesson it matched in, not the day, for the same reason the index indexes
day notes per section. Tapping one opens that day in the editor.

Driven in Firefox against the real app with a proxied session: the
paste, six switches between the two views, bold and unbold on pasted
text, the search, and opening a result. 386 unit tests, 114/115 smoke.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-09-21 18:09:15 +02:00
parent f545b7cf54
commit e129fd4b0a
13 changed files with 702 additions and 22 deletions

View File

@@ -105,11 +105,19 @@ tables as tables — and the toolbar above it writes the Markdown. Nobody types
| `🔗` `▦` | a link (`Strg`/`Cmd` + K) and a table |
| `MD` | the Markdown itself |
`Enter` starts a new paragraph, `Shift+Enter` a new line in the same one. A
paste from a web page or a PDF keeps its structure and loses its fonts, colours
and anything else that is not in the list above — pasted HTML is converted to
Markdown before it reaches the page, which is what keeps a copied page from
bringing its script along.
`Enter` starts a new paragraph, `Shift+Enter` a new line in the same one. In a
table, `Tab` walks the cells and a `Tab` out of the last one adds a row;
`Ctrl`/`Cmd`+`Enter` opens a paragraph after whatever block you are in.
A paste from a web page, a PDF or Apple Notes keeps its structure and loses its
fonts, colours and anything else that is not in the list above — pasted HTML is
converted to Markdown before it reaches the page, which is what keeps a copied
page from bringing its script along. **A copy from Apple Notes arrives wrapped
in one span carrying the computed style of everything in it**, `font-weight:
700` included; that wrapper is a container, not emphasis, and the blocks inside
it are blocks. Reading it the other way made a whole pasted note bold and
flattened every line into one paragraph, which is the failure
`test/app-markdown.test.ts` now pins down.
**The file is still Markdown.** `MD` shows it and lets you edit it directly,
which is the way to write something the toolbar has no button for. There is no
@@ -122,6 +130,28 @@ so rather than being quietly reduced. `test/app-markdown.test.ts` is what holds
that promise up: every construct in this document goes in and comes back out
unchanged.
### Suche — the notes themselves
The **Suche** tab searches your own notes and nothing else. Every word has to
appear, in any order, ignoring case and accents; there is no stemming, so
*Argument* does not find *Argumente*.
A result names the **lesson**, not the day — `1. LF10 — 08:0008:45` with the
date beside it — because a day note holds five or six lessons and "Freitag" says
nothing about which one matched. Tapping a day note opens it in the editor;
anything else, such as a note from the Apple Notes import, opens read-only,
since the editor is day-shaped and those notes have no day.
It reads the **files**, not the index. That is the point: notes reach the
Postgres index only on a full crawl, so a lesson written this morning would not
be there, and "what did I write this week" is most of what anyone searches their
own notes for. A few hundred small files answer instantly, and it keeps working
while Postgres is down — the same reason `list_notes` reads disk.
The `search` tool in Claude is the other half: it spans the Schulcloud material
and the WebUntis class register as well, at the cost of being only as fresh as
the last crawl.
**Einstellungen** holds the Schulcloud token: how long it has left, and the box
to paste a fresh `jwt` cookie into when it expires (the same thing `schulcloud
token set` and the older `/token` page do). It also shows the index's state and