Squash of feat/koreader-widget-board. KOReader plugin 2.0.0 gets the widget-board dashboard, redesigned
book details, menus, lists and dialogs, Decor page and reading companions. Highlights, notes and bookmarks
now stay in step across web, Kobo, KOReader and the phones (defects D-00 to D-20, migration 0121), and
reading progress and narration hand off cleanly between devices.
* feat(reader): add PDF highlights and annotations
Persist and render PDF highlights through the same annotation system as
EPUB. The annotation_positions table already supported a 'pdf' format, so
this wires the web create path, response, and reader UI to it.
Backend:
- create-annotation DTO accepts a pdf position (page + segment rects) as an
alternative to a cfi; exactly one is required
- repository createPdf() persists a format='pdf' position (geometry in pos0,
pageno in extras); selectWithCfi exposes the pdf geometry and status
- response DTO parses the pdf position and derives positionStatus
Frontend (pdf-v4 reader):
- usePdfAnnotations REST store and usePdfHighlights orchestration render
persisted highlights via the EmbedPDF annotation plugin (autoCommit off,
DB stays source of truth)
- selection popup (colour/style/note/copy/delete), note dialog, and a
Highlights sidebar tab with search/colour/notes filters, navigate, delete
- NoteDialog relocated to reader/shared for reuse by both readers
Shared types gain AnnotationRect/AnnotationPdfPosition and an optional pdf
field on AnnotationItem.
* fix(reader): preserve input on failed PDF annotation save; convert mixed PDF+CFI for KOReader
- usePdfHighlights: createFromSelection/restyleExisting now return success;
applyHighlight and saveNote only clear the selection, dismiss the popup, and
close the note dialog on success. On failure the mark, popup, and typed note
are preserved so the user can retry without losing input.
- koreader annotation exchange: the PDF push-down guard now skips only PDF-only
annotations (no EPUB anchor). A mixed PDF+CFI annotation still converts its CFI
to xpointer instead of being dropped.
- Tests for both paths.
* fix(reader): label PDF selection buttons and dedupe multi-page highlight retries
- PdfSelectionPopup: add aria-labels to the copy, highlight, note, and delete
icon-only buttons so screen readers can identify them.
- usePdfHighlights: createFromSelection keeps only the pages that failed, so a
retry after a partial multi-page failure never duplicates already-saved pages.
- Test for the multi-page partial-failure retry path.
* fix(reader): harden PDF annotation store, validation, and create logging
- usePdfAnnotations: catch rejected api() calls (session expiry / network) so
load() sets loadError and mutations return null/false instead of rejecting.
- create-annotation DTO: require positive rect width/height so degenerate PDF
geometry is rejected; tests for zero/negative dimensions.
- annotation.service: add [annotation.create] start/end/fail lifecycle logs with
IDs, durationMs, and sanitized error for the multi-step create.
* test(reader): cover PDF annotations in the hub and add missing docstrings
Adds a real-DB e2e proving a PDF highlight persists as a pdf position and surfaces in the library-wide Annotations hub with the exact fields the page deep link needs (jumpFileFormat=pdf, cfi=null, pageno). Adds a hub-service unit test for the pdf row mapping. Documents the PDF render helpers, the annotation deep-link builder, and the createPdf repository method.
* fix(annotations): make PDF highlights reliable across reloads
Reconcile persisted highlights after EmbedPDF finishes loading its annotation state. Keep file-scoped pagination bounded and prevent PDF-only positions from starving device sync.
Ref #1195
* fix(annotations): keep pdf annotations stable during reload
Use authenticated native pagination fetches and discard responses that overlap successful mutations. Align shared dialog styling and annotation tests with project conventions.
Ref #1195
---------
Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>