Write down the standing decisions, and start a changelog
The rules this project is worked under — Docker for the backend, straight to main, no backup feature because the host is backed up, the encryption stays, no public sign-up — lived only in a conversation. They belong in the repository, where they survive it. The changelog answers a question the commit messages cannot: what is actually running on the server. It carries the image digests, read back from the registry rather than taken from the push output. The readme described the note column as left-aligned. It is centred again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dcc42bb8e0
commit
73cc28696f
3 files changed
+218
-5
No files matched your search
+127
@@ -0,0 +1,127 @@
|
||||
# Changelog
|
||||
|
||||
What changed, newest first. One entry per round that went out as an image, with
|
||||
the digests, so it stays possible to tell what is actually running on the server.
|
||||
|
||||
Digests are recorded from the registry (`docker buildx imagetools inspect`),
|
||||
not from the output of `docker push`.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-11 — The writing column
|
||||
|
||||
**Images**
|
||||
- `inknote-web` — see the entry below this one; the web image was rebuilt after
|
||||
the revert, and the digest is written in when it is pushed
|
||||
- `inknote-server` — unchanged, `sha256:1648498f…`
|
||||
|
||||
**Changed**
|
||||
- The note column is centred again. It had been pinned to the left margin so the
|
||||
start of every line would always be in the same place; on a wide window that
|
||||
left the whole right half of the pane as one dead block.
|
||||
- A page surface was drawn behind the text to give that block a meaning, and
|
||||
reverted the same day: it made the sheet sit hard against the left edge, which
|
||||
was worse than the problem it solved.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-11 — Pages, typefaces, alignment
|
||||
|
||||
**Images** — web `sha256:3b040d99…`, server `sha256:1648498f…`
|
||||
|
||||
**Added**
|
||||
- **Page count while writing.** The toolbar says *Page 2 of 4*, in the same words
|
||||
the PDF reader uses. Pages are counted, never stored: the note stays one
|
||||
document, and a test asserts no page ever reaches the stored body.
|
||||
- **The typeface is a per-account setting** — serif, sans or mono, chosen under
|
||||
Account → Profile. On the account rather than in the browser, so it follows you
|
||||
to the phone.
|
||||
- **Text alignment**, with its own TipTap extension rather than the official one,
|
||||
so the schema stays in files this project owns. Left is the default and is
|
||||
never written into the document. In Markdown, non-left blocks travel as
|
||||
`<div align="…">` — the one form most readers still honour — and the importer
|
||||
reads it back onto the paragraphs.
|
||||
|
||||
**Fixed**
|
||||
- The Markdown exporter did not put a blank line between top-level blocks, so
|
||||
consecutive paragraphs ran together in other readers.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-11 — English throughout, and the PDF toolbar
|
||||
|
||||
**Added**
|
||||
- **Search inside a notebook**, from the note list. The sidebar's global search
|
||||
was removed: searching from inside a notebook should mean searching *that*
|
||||
notebook.
|
||||
- **New note / Upload PDF / Import Markdown** all live under the one `+` button.
|
||||
|
||||
**Changed**
|
||||
- The interface is English throughout. It had drifted into two languages.
|
||||
- The two visibility switches — drawing and margin notes — sit together at the
|
||||
far right of the PDF toolbar, because they mean the same kind of thing.
|
||||
- The drawing tools appear next to the *Draw* button, not past everything else.
|
||||
- A separator is renamed and removed by right-click, with a confirmation for the
|
||||
removal.
|
||||
|
||||
**Fixed**
|
||||
- Hiding the drawing also hid the margin notes. Each switch now hides only its
|
||||
own thing.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-10 — Notes on passages
|
||||
|
||||
**Added**
|
||||
- **Pinned notes in a PDF.** A remark can be attached to a passage, and is shown
|
||||
in the margin beside the page — not written on top of it. On a phone, where
|
||||
there is no margin, the same notes open as a bottom sheet.
|
||||
- **Margin notes can be linked from the notes panel**, so a pinned remark can be
|
||||
written about at length and still lead back to the passage it came from.
|
||||
|
||||
**Fixed**
|
||||
- A grey drag indicator stayed visible in the note list after a drag. It was a
|
||||
`borderTop` sitting beside a `border` shorthand in one style object.
|
||||
- The server stored an overlay holding only pinned notes as `NULL`, throwing
|
||||
every one of them away, because it counted strokes and nothing else.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-09 — Arranging a notebook
|
||||
|
||||
**Added**
|
||||
- **Sorting per notebook**, remembered on the notebook itself, including an
|
||||
arrangement made by hand — which comes back intact after filtering.
|
||||
- **Separators** to group notes inside a hand-made arrangement.
|
||||
- **Markdown import**, single files or a ZIP this app exported.
|
||||
- **Moving notes between notebooks.**
|
||||
|
||||
**Fixed**
|
||||
- Switching to Draw shrank the page from 733px to 460px. The editor was a flex
|
||||
item at the default `flex: 0 1 auto`, so it was only as wide as its widest row
|
||||
— the toolbar, which loses a dozen buttons in Draw.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-08 — PDFs
|
||||
|
||||
**Added**
|
||||
- Import a PDF, read it, select its text, draw on each page, and export it with
|
||||
the drawings written in.
|
||||
- Notes written beside a PDF.
|
||||
- Uploaded files are encrypted at rest, like note bodies already were, and stored
|
||||
as deduplicated, content-addressed attachments.
|
||||
- Unused attachments are freed; versions are kept by count rather than by age.
|
||||
- Export as Markdown or PDF; the browser suites moved into the repository.
|
||||
|
||||
**Changed**
|
||||
- Vector clocks were never read. Replaced with a conflict check that works.
|
||||
- Public sign-up removed — an administrator creates the accounts.
|
||||
|
||||
---
|
||||
|
||||
## Earlier
|
||||
|
||||
TipTap editor, version history, full-text search and attachments (2026-08-07);
|
||||
drawing sync with WebP/JPEG support (2026-06-21); the first syncing alpha and PWA
|
||||
(2026-02-18).
|
||||
@@ -0,0 +1,77 @@
|
||||
# InkNote – Projektkonventionen für Claude Code
|
||||
|
||||
Selbst gehostete Notiz-App, privat produktiv im Einsatz. Änderungen laufen auf
|
||||
echten Daten – im Zweifel erst fragen, nicht erst ausprobieren.
|
||||
|
||||
Die ausführliche Beschreibung steht im [readme.md](readme.md); diese Datei hält
|
||||
nur fest, was beim Arbeiten gilt und sich nicht aus dem Code ergibt.
|
||||
|
||||
## Feste Regeln
|
||||
|
||||
Diese Punkte sind Entscheidungen von Marc, keine offenen Fragen:
|
||||
|
||||
- **Backend immer mit Docker starten**, nie mit `go run` o. Ä.
|
||||
`docker compose -f docker-compose.dev.yml up -d --build`
|
||||
- **Direkt auf `main` pushen.** Keine Feature-Branches, kein PR-Umweg.
|
||||
- **Kein Backup-Feature bauen.** Die Daten liegen über Bind-Mounts auf dem Host
|
||||
(`INKNOTE_DATA_DIR`, Standard `/srv/inknote`), und was den Host sichert –
|
||||
Proxmox Backup – sichert damit auch InkNote.
|
||||
- **Die Verschlüsselung bleibt.** Notizinhalte, Zeichnungen und hochgeladene
|
||||
Dateien liegen AES-256-GCM-verschlüsselt auf dem Server. Nichts davon
|
||||
entfernen, um etwas anderes einfacher zu machen.
|
||||
- **Keine öffentliche Registrierung.** Konten legt ein Administrator an. Ein
|
||||
Admin darf anlegen, löschen und Passwörter zurücksetzen – aber **keine**
|
||||
weiteren Admins ernennen und **keine** fremden Konten umbenennen.
|
||||
- **Bundle-Größe bleibt, wie sie ist.** Bewusst so entschieden.
|
||||
- **Dashboard und Seitenleiste sind vertagt.** Die offenen Befunde dazu sind
|
||||
bekannt; nicht ungefragt damit anfangen.
|
||||
|
||||
## Tests
|
||||
|
||||
Beides muss vor einem Push grün sein.
|
||||
|
||||
```bash
|
||||
# Unit (224 Tests, ~4 s)
|
||||
docker run --rm -v "$PWD/frontend:/w" -w /w node:20-alpine npx vitest run
|
||||
|
||||
# Browser, zwölf Suiten (~263 Checks) – braucht den Dev-Stack
|
||||
./e2e/run.sh # alle
|
||||
./e2e/run.sh editor drawing # einzelne
|
||||
```
|
||||
|
||||
Die Browser-Suiten melden sich mit Marcs Admin-Konto an (`INKNOTE_ADMIN` /
|
||||
`INKNOTE_ADMIN_PASS`, Standard `marc`/`geheim123`) und legen sich damit
|
||||
Wegwerf-Nutzer an. Sie laufen gegen `./data`, nicht gegen `/srv/inknote`.
|
||||
Screenshots landen in `e2e/shots/` und sind gitignored.
|
||||
|
||||
`attachments-lifecycle` ruft `POST /admin/housekeeping` auf – das schreibt auf
|
||||
echten Daten. Nicht gegen die Produktionsinstanz laufen lassen.
|
||||
|
||||
## Ausliefern
|
||||
|
||||
```bash
|
||||
./build.sh # baut beide Images und schiebt sie ins Gitea-Registry
|
||||
```
|
||||
|
||||
Nur das Image bauen, das sich geändert hat. Nach dem Push den Digest bei der
|
||||
Registry gegenprüfen, nicht nur der Ausgabe von `docker push` glauben:
|
||||
|
||||
```bash
|
||||
docker buildx imagetools inspect gitea.honeyy.duckdns.org/docker/inknote-web:latest
|
||||
```
|
||||
|
||||
Auf dem Server: `docker compose pull && docker compose up -d`.
|
||||
|
||||
## Änderungen festhalten
|
||||
|
||||
Jede Runde, die als Image rausgeht, bekommt einen Eintrag in
|
||||
[CHANGELOG.md](CHANGELOG.md) – mit den Image-Digests, damit später nachvollziehbar
|
||||
ist, was auf dem Server läuft. Der `readme.md` beschreibt den *jetzigen* Stand
|
||||
und wird mitgeändert, wenn sich Verhalten ändert; das Changelog beschreibt den
|
||||
Weg dahin.
|
||||
|
||||
## Stil
|
||||
|
||||
Der Code erklärt in Kommentaren das *Warum*, nicht das *Was* – oft mit dem
|
||||
Fehler, den die Zeile verhindert. Neue Kommentare in dem Ton, englisch wie der
|
||||
Rest. Die Oberfläche ist durchgehend englisch, das Gespräch mit Marc deutsch.
|
||||
@@ -47,6 +47,8 @@ inknote/
|
||||
├── docker-compose.dev.yml # Build from source for local work
|
||||
├── .env.example # Copy to .env — Compose reads it from here
|
||||
├── build.sh # Build & push images to Gitea
|
||||
├── CHANGELOG.md # What changed per released image, with digests
|
||||
├── CLAUDE.md # Conventions and standing decisions for this repo
|
||||
├── backend/
|
||||
│ ├── server.js # Express API + WebSocket server
|
||||
│ ├── sync-client.js # Client-side sync engine library
|
||||
@@ -371,10 +373,17 @@ state of their own, so they can be read on their own.
|
||||
|
||||
## Setting a note
|
||||
|
||||
**Left-aligned, with the column against the margin.** A column that floats in the
|
||||
middle moves as the window is resized, so the start of every line lands somewhere
|
||||
new; against the margin it is always in the same place. About 73 characters to
|
||||
the line — measured, not guessed.
|
||||
**A centred column, about 73 characters to the line** — measured, not guessed.
|
||||
Past roughly 84 the eye stops reliably finding the start of the next line, so the
|
||||
column is capped well before the pane runs out.
|
||||
|
||||
It was pinned to the left margin for a while, on the reasoning that a column
|
||||
which floats in the middle moves as the window is resized. True, but it traded
|
||||
that for something worse: on a wide window the entire right half of the pane
|
||||
became one dead block. A page surface was then drawn behind the text to give that
|
||||
block a meaning, which only moved the problem — the whole sheet sat hard against
|
||||
the left edge. Centred, the same leftover space reads as two margins and needs no
|
||||
explaining.
|
||||
|
||||
**The typeface is a per-account setting** (Account → Profile): serif, sans or
|
||||
mono. On the account rather than in the browser, because a choice about how your
|
||||
@@ -383,7 +392,7 @@ every device. The server accepts those three values and drops anything else —
|
||||
that column is written by a browser, so it is an input like any other.
|
||||
|
||||
**Pages are counted, not stored.** The toolbar says *Page 2 of 4* in the same
|
||||
words the PDF reader uses, and a hairline marks where each one ends. A page is
|
||||
words the PDF reader uses, and a dashed rule marks where each one ends. A page is
|
||||
the same 3:4 the canvas uses, so "page 2" means the same thing writing or
|
||||
drawing. The note itself stays one continuous document — Markdown has no page,
|
||||
and a test asserts none ever reaches the stored body. The current page is the one
|
||||
|
||||
Reference in new issue
Block a user