Add a bookmark tool to the PDF editor. A bookmark anchors to a precise
location: when text is selected it captures the selection's normalized
rect + the start char index in the page text (the true paragraph
anchor); with no selection it falls back to the tapped page + point.
- Bookmark model gains optional normalized anchor rect + charIndex +
label (all absent from JSON when null, so old sidecars still load).
- Bookmarks persist in the sidecar (scheduleBookmarkUpsert) and load on
open; a bookmarks panel lists them and tapping one jumps to its page.
Delete is persisted.
Scoped to the PDF editor (note bookmarks later); scroll-to-anchor is
page-level for now. analyze clean, 391 tests green.
A blank note can show a page-background template painted behind the
ink, picked from the toolbar and persisted per notebook.
- NoteBackground: blank / dots / ruled / grid / cornell, drawn in
page space (scales with zoom), subtle grey. Cornell = left margin +
bottom summary rule over a ruled body.
- Stored as the enum name in the notebook sidecar (back-compat:
missing/unknown -> blank), saved/loaded via SidecarRepository so it
restores on reopen.
- Picker added to the note tool palette.
PDF backgrounds skipped (PDFs have their own page content). analyze
clean, 386 tests green.
Closes TODO(brush-persist). EditorStroke now serializes its brush as
the stable BrushKind name; sidecars written before this field, and any
unknown name, load as fountainPen (back-compat). PenStroke<->EditorStroke
carry brush both ways, so a ballpoint/highlighter/pencil stroke keeps
its opacity/blend after a document is closed and reopened.
Note: InkStroke (the note/scratchpad world-coord format) has no brush
field, so notes derive brush from the tool — highlighter is preserved,
ballpoint/pencil collapse to fountainPen on reload (TODO: extend
InkStroke). PDF documents persist brush fully. analyze clean,
379 tests green.
Phase 1 of the file-based storage plan. Pure library, no runtime
behavior change yet (editors still use SQLite).
- BadnoteSidecar: per-file annotation document (schema-versioned)
holding per-page ink (EditorStroke JSON), text highlights,
scratch-link anchors (ScratchLink JSON) each with its own
scratchpad (InkStroke world-coord JSON), and bookmarks. Reuses the
existing toJson formats — no parallel stroke format.
- SidecarStore.writeAtomic: temp-file + rename atomic write keeping a
.bak; read() falls back to .bak on a missing/corrupt primary.
Round-trip + atomic-write + .bak-recovery tests. analyze clean,
322 tests green.