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>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-11 02:17:22 +02:00
1 parent 0a7cf26b28
commit 52bd2b8f3d
27 files changed
+469 -336

No files matched your search

+10 -10
View File
@@ -6,11 +6,11 @@
* can be unpacked and read here. */
import {
OUT,
captureDownloads,
check,
clearDownloads,
createNote,
createNotebook,
captureDownloads,
clearDownloads,
downloads,
insertTable,
lastDownloadName,
@@ -148,7 +148,7 @@ try {
const zip = readZip(new Uint8Array(Buffer.from(files[0].base64, "base64")));
const names = Object.keys(zip);
check(names.some((n) => n.endsWith(".md")), "the archive holds the Markdown file", names.join(", "));
check(names.some((n) => /bilder\/.*\.png$/.test(n)), "and the image beside it", names.join(", "));
check(names.some((n) => /images\/.*\.png$/.test(n)), "and the image beside it", names.join(", "));
await p.keyboard.press("Escape");
await p.waitForTimeout(600);
@@ -170,24 +170,24 @@ try {
await clearDownloads(p);
await openExportDialog(p);
check(
(await p.getByText("Was soll mit?").count()) > 0,
(await p.getByText("What should come along?").count()) > 0,
"a note with a drawing is asked what should come along"
);
check(
(await p.getByRole("button", { name: /Nur die Zeichnung/ }).count()) > 0,
(await p.getByRole("button", { name: /Only the drawing/ }).count()) > 0,
"the drawing-only option is offered"
);
await p.screenshot({ path: `${OUT}/export-wizard.png` });
// drawing only → PNG
await p.getByRole("button", { name: /Nur die Zeichnung/ }).click();
await p.getByRole("button", { name: /Only the drawing/ }).click();
await p.waitForTimeout(500);
check((await p.getByRole("button", { name: /PNG-Bild/ }).count()) > 0, "PNG is offered for the drawing");
check((await p.getByRole("button", { name: /PNG image/ }).count()) > 0, "PNG is offered for the drawing");
check(
(await p.locator("[data-export-dialog]").getByRole("button", { name: /Markdown/ }).count()) === 0,
"Markdown is not offered for a drawing, since it cannot hold one"
);
await p.getByRole("button", { name: /PNG-Bild/ }).click();
await p.getByRole("button", { name: /PNG image/ }).click();
await p.waitForSelector("[data-export-done]", { timeout: 30000 });
await p.waitForTimeout(800);
@@ -203,9 +203,9 @@ try {
check(png.length > 1000, "the image has actual content, not an empty canvas", `${png.length} bytes`);
// back, then text only → Markdown must carry no drawing
await p.getByRole("button", { name: /Zurück/ }).click();
await p.getByRole("button", { name: /Back/ }).click();
await p.waitForTimeout(500);
await p.getByRole("button", { name: /Nur den Notiztext/ }).click();
await p.getByRole("button", { name: /Only the note text/ }).click();
await p.waitForTimeout(500);
await clearDownloads(p);
await p.locator("[data-export-dialog]").getByRole("button", { name: /Markdown/ }).click();