Commit Graph

303 Commits

Author SHA1 Message Date
neon 97cd03325f feat(reader): open DjVu documents in the web reader
Scanned books are commonly distributed as DjVu, which the scanner only stored as a supplement.
Decode pages on the server with DjVuLibre so DjVu imports with a cover and page count, opens in
the image reader with its embedded OCR text, and can be the preferred format of an ordinary email
recipient. Migration 0113 widens the recipient format check to allow it.

Decoding runs on validated private snapshots behind a bounded, reader-first queue, and
revalidation and cached pages are answered without a decoder. Released iOS builds cannot open
DjVu, so series continuation offers it only to callers sending includeDjvu=true, and the
dashboard and notebook read file never name it. The first full scan after upgrading promotes
stored .djvu supplements to content.
2026-10-14 21:37:41 +00:00
neon 59e3f36f5e fix(auth): enforce library roles for metadata editing
Library access roles had no effect on metadata editing: only the global Edit metadata permission
was checked, so a Viewer could edit every library they could see and an Editor could not be
limited to one library. Require Editor or Owner on the book's library for every metadata write,
including bulk edits, locks, covers, file renames, entity manager changes and author edits, and
hide the matching actions in the web client.

Keep existing access levels when an admin saves a user's library list. The old replace reset every
row to Viewer, which would now silently revoke editing on each user save.

Existing grants are Viewer, so after upgrading, admins must set Editor on each library where a
non-superuser should keep editing metadata. The released iOS app already gates editing on Editor.

Closes #1778
2026-10-14 03:57:51 +00:00
neon a9f8cea616 fix(stats): harden KOReader reset hold recovery
Preserve reset-hold notices when recovery requests fail and prevent
stale responses from replacing another book's progress. Refresh holds
after permission changes and rereads, and cover recovery and session
deletion with regression tests.

Ref #1355
2026-10-13 01:43:49 +00:00
neon 613c7d2ef2 fix(stats): stop deleted KOReader sessions returning and surface reset holds
Deleting a KOReader session left its page events stored, so the next upload within the
session gap re-derived it. Record the deleted span in koreader_session_tombstones and skip
those events during derivation. Show the reset-hold notice on the Reading Log as well,
since Reset is reachable there and a held device otherwise reads as unexplained progress.

Refs #1355
2026-10-13 01:33:09 +00:00
neon 591dea03a2 feat(email): rebuild email settings as four quiet tabs with a send sheet
Fold six tabs into Recipients, Mail servers, Templates and History, where only problems get
colour and one star sets each default through email_preferences. Replace the send dialog with
a sheet that preselects the default recipient and follows the send. Server additions are
optional fields and routes, so released iOS builds keep working.
2026-10-12 22:18:38 +00:00
neon 190d837280 fix(client): show authors on collapsed series rows in table view
The table rendered a dash in every column of a collapsed series row,
including Authors, although the row already carries the first
volume's authors and the grid and list views show them. Render them
as the same read-only author chips book rows use.

Closes #1645
2026-10-12 15:48:10 +00:00
neon cc50b55eeb feat(books): add read-aloud delivery policies and preserve sync
Add original and text-only EPUB delivery choices across Kobo, KOReader,
OPDS, web downloads and email while keeping source files unchanged.
Persist server defaults, device overrides and captured artifact identities
so retries and guided replacement retain the selected rendition.

Preserve saved progress across first opens, replacement resets and stale
updates, and project annotations against the copy delivered to each device.
Handle XML resource references, large inodes, retry backoff and background
worker state correctly. Consolidate the schema changes into one migration
and retain released-client request and response compatibility.

Verify with the full server and client checks, delivery and sync HTTP suites,
independent code reviews, and live KOReader download/settings/reader proof.

Fixes #1496
Closes #1603
Closes #1714
Fixes #1723
Ref #1633
Ref #1508
Ref #1621
Ref #1136
2026-10-11 03:11:27 +00:00
neon 7bb51a7a0b feat(metadata): cache metadata provider answers
Repeated searches and refreshes asked every provider again, which is slow and runs into provider
rate limits. Keep each provider's answer in Postgres, shared by all users, and reuse it until an
admin-set lifetime runs out. Search screens mark saved results and offer a fresh search, and
explicit refreshes stay live, falling back to a saved answer only when a provider fails.

Empty answers are only kept for API providers that report failures reliably, and a cache-only
request never calls a provider in cooldown. Book Dock's manual refetch stays live-only because
the dock has nowhere to report a fallback. Released iOS builds keep working: every new request
field is optional and newer clients gate it on the metadata-search-cache server feature.
2026-10-10 22:26:26 +00:00
neon af8f55976f feat(client): add the Nostalgia appearance style
Add a Style setting beside mode, accent, radius and background. Nostalgia restyles the app through
tokens only (neutral surface palettes, system type, flat shell, top glow) and pins background,
opacity and brightness. Add a top glow reach slider for both styles. Tidy the theme layer first:
charts read live tokens, bare `rounded` follows the radius scale, one shared sign-in toolbar.
Also carries the settings page header redesign.
2026-10-09 01:01:57 +00:00
neon 39ac2f1efd feat: request an ebook and audiobook as a pair
Link two ordinary requests as companions, filed into one book, with a Both search,
grouped list rows, a tabbed release picker and a book-page request for the missing
format. Retire comic requests, keeping old rows readable. File Book Dock covers by the
field rules with a shape check, and re-pick a moved book's primary by its new library.
2026-10-08 16:12:30 +00:00
neon 01b5638ea7 feat(cover): show each book's cover in the shape of its primary format
Pick the face slot from the primary file's medium instead of the library's
ratio, and send that frame per book on cards, detail, notebook labels and
recommendations. Grids keep uniform tiles, and the library setting becomes
Grid shape.
2026-10-08 06:19:00 +00:00
neon d719c4ee5b fix(metadata): preserve ISBN fields across metadata workflows
Carry ISBN-10 and ISBN-13 independently from accepted providers through
Auto-fill previews, saves and background refreshes. Preserve existing
identifiers and field locks, validate provider checksums and avoid
combining different editions.

Classify embedded ISBNs correctly and support ISBN-10 fallback in
search, Book Dock confidence and file naming.
2026-10-07 20:07:23 +00:00
neon 6c7d91eaac feat(metadata): add configurable missing-field reminders
Add account-scoped reminders for the metadata fields users care about.
Treat either ISBN as complete, accept a publication year or date, and
skip page-count reminders for audio-only books.

Place reminders after the genre blocklist and allow customization from
the editor without discarding unsaved changes.
2026-10-07 20:06:50 +00:00
neon 8fef9d1633 feat(metadata): add empty-field details and optional auto-search
Let users inspect missing fields without launching provider searches.
Keep metadata searches manual by default and expose a remembered
opt-in beside Search.
2026-10-07 18:38:07 +00:00
neon 100953ea1b feat(metadata): write metadata to every file of a multi-file book
Books often carry several files, such as an EPUB beside a PDF or an audiobook split into tracks.
Write-back reached only the primary file and the audio tracks, so the other formats kept stale
metadata.

Add an opt-in per-library mode that writes every content file of a book. Target selection,
capability reporting and the Files tab now share one role-aware selector, covers resolve per
medium, and results report per-format counts. Read-along EPUBs get their own toggle, off by
default, and their own size limit of 1000 MB, so they no longer follow the EPUB settings.

Live testing on copies of real libraries surfaced write-path bugs, fixed here as well:
- tag-only writes to chaptered M4B files failed because FFmpeg copied the chapter data track
- an undecodable cover failed every file instead of falling back to metadata only
- failure reasons stored the FFmpeg command line and absolute paths; they are now sanitized
- size limits used the stored size, so an unknown size bypassed them; writes now check the disk
- atomic replace dropped the file mode and ownership
- the EPUB temp name failed on long file names and could clash with a user file
- tracks in disc folders got duplicate titles
- a PDF worker that exited without a result hung its write forever; it now fails and times out
- null on a NOT NULL library setting returned 500 instead of 400

Generates migrations 0104 and 0105. Every new API field is optional, and released iOS builds
decode none of them.

Ref #694
2026-10-07 16:52:52 +00:00
neon 6f673e2e59 feat(metadata): merge search and compare into one match sheet
Search and Compare Metadata become one two-pane sheet, shared with the Book Dock. Results are
ranked by match strength beside a field-by-field comparison with Keep/Use per row, word-level
diffs, other sources inline, a cover strip, and picks that survive switching results.
The search runs on open and a single source can be retried. No API change.
2026-09-29 04:53:34 +00:00
neon 1dd389563c style(client): unify modal backdrops with dedicated scrim tokens
Replace tinted modal veils with dedicated scrim tokens so dialogs, sheets, and image lightboxes dim consistently in both light and dark themes. Enforce scrim token usage in style validation to prevent backdrops that wash out in dark mode.
2026-09-29 03:42:01 +00:00
neon 218f8d5dbd fix(books): list a book's formats from one ranked, read-along aware source
Every format chip, edition row and format menu now builds from shared helpers: content files
only, a read-along EPUB kept apart from a plain one, in the library's format priority, which
gains a read-along entry. Labels, colours and the chip each have one source, and cards, OPDS,
email, Kobo and the dashboard rank files the same way. Book detail file order is unchanged
because iOS builds its track queue from it.
2026-09-24 21:05:24 +00:00
neon e28bd67fa3 feat(cover): give books with both media separate ebook and audiobook covers
Keep one cover slot per medium and show the one each surface needs: the
library's shape for the face, square art on audio screens, the ebook slot
for Kobo and OPDS. Legacy covers convert on upgrade or on first read, and a
write that names no slot keeps working for released iOS builds. Also read
the dev client origin from one constant.
2026-09-23 19:55:25 +00:00
neon 3c032e0e3c fix(reader): sync progress between an EPUB and its read-along copy
Carry positions between sibling EPUBs even without an audiobook, and keep
narration markers on the read-along file only. Point the KOReader book panel
and hold release at the copy devices sync, and label EPUB-only sync in the
read-aloud panel.

Fixes #1508
2026-09-23 12:30:01 +00:00
neon bc10ebcc33 feat(stats): record android as a reading source
Accept android as a reading-session source from clients and report it as
its own statistics bucket, labelled Android app, beside the iOS app and
Apple Watch. A generated migration adds it to the reading_sessions check
constraint, achievements count Android reading toward Full Orbit and Two
Worlds, and the web charts draw it in a new --pill-android token at hue 95.
2026-09-23 01:34:19 +00:00
neon 12cf724c39 fix(books): align progress bar color with read status
Use the authoritative read status so cards completed at a library's configured threshold no longer appear in progress.

Fixes #1465
2026-09-22 11:11:24 +00:00
neon 6e608e79a4 fix(client): show the platform's own shortcut modifier
Lift the bulk rename empty state out of the vertical centre of the
tools pane so it sits near the top instead of floating in the void.

Replace the hardcoded Cmd in the settings search hint with a shared
useModifierKey composable, so Windows and Linux see Ctrl+K. The
translated hint now takes the combo as a placeholder.
2026-09-20 17:49:01 +00:00
neon d3f3382bfa feat(reader): synchronize Storyteller reading progress
Keep audiobook, EPUB, Kobo, and KOReader positions coherent while serving each client the safest suitable book variant.
2026-09-20 14:12:18 +00:00
neon e0d3d42150 feat: add read-aloud and media playback support
Add EPUB narration, shared media controls, and native device session integration.

Apply the generated migrations and update all first-party clients alongside the server.

BREAKING CHANGE: introduce incompatible database and API contracts for media playback
2026-09-19 02:27:05 +00:00
neon c292d6ccd2 fix(metadata): honor configured Amazon domain in book links
Use the sanitized provider-link setting so book detail URLs target the selected marketplace.

Fixes #1423
2026-09-18 16:32:18 +00:00
neon 7a203a03d7 fix(client): add missing icon tooltips
Make icon-only controls discoverable to mouse and keyboard users with localized tooltips and accessible names.

Fixes #588
2026-09-12 09:24:32 +00:00
neon ceda7428b0 feat(reader): add dedicated audiobook playback API 2026-09-11 05:41:29 +00:00
neon 72871fad65 fix(client): preserve synced reading calendar dates
Treat exact UTC-midnight lifecycle projections as calendar dates while preserving local conversion for real timestamps.

Fixes #1322
2026-09-10 15:35:06 +00:00
neon b9609b198e fix(annotations): preserve source highlight timestamps
Use KOReader highlight times for annotation history while retaining ingestion times for diagnostics. Export all stored location representations and backfill existing rows during sync.

Fixes #1300
2026-09-08 16:52:36 +00:00
neon 028d4287b4 fix(client): stabilize book detail layout across screen sizes
Keep cover actions and discovery content adjacent to their related sections, and scale the synopsis preview for taller viewports.

Fixes #1259
2026-09-06 15:39:31 +00:00
neon d8f87d8dd1 fix(client): keep cover controls within their column
Size the cover editor from its assigned container so narrow metadata grids stack controls below the artwork without overlap.

Fixes #1280
2026-09-05 10:05:58 +00:00
neon 978ef8ac2f fix(client): stop compact detail covers shrinking
Keep the observer-derived width cap scoped to the fixed-height wide layout so compact covers cannot feed their own height back into width.

Fixes #1279
2026-09-05 10:04:39 +00:00
neon e63c4b3685 fix(client): keep reading activity text inside card
Preserve the card's intrinsic minimum height when the detail column needs to scroll so empty-state copy remains within its border.

Fixes #1262
2026-09-05 09:30:20 +00:00
neon e3779c1d7e feat(metadata): restrict autofill to stored provider ids
Prevent automatic metadata refreshes from searching alternate identities when a book already has trusted provider IDs. Preserve discovery and manual search behavior, and avoid dirtying descriptions on empty refreshes.

Closes #1222
2026-09-01 20:55:36 +00:00
neon 0f5429131a feat(smart-scope): filter by community review count
Allow provider-aware review count criteria so rating thresholds can be qualified by community review volume.

Closes #1041
2026-08-31 04:54:00 +00:00
neon 79b02e5b35 test(books): cover same-day reading completion
Protect equal manual start and finish dates through the client and status API.

Closes #1097
2026-08-31 02:15:34 +00:00
neon 9391555859 fix(books): fully expand book synopsis
Keep expanded synopsis content in the existing page or column flow so touch users do not encounter an undiscoverable nested scroll area. Expose the toggle state to assistive technology.

Fixes #1202
2026-08-30 17:00:33 +00:00
neon 26503bc9e6 fix(books): open personal review editor from summary
Reveal the collapsible review panel before entering edit mode so the summary action works on every responsive layout.

Fixes #1211
2026-08-30 16:04:52 +00:00
neon 3561d5ddd6 fix(client): keep book detail layouts responsive
Size book detail layouts from usable pane width so sidebar width and browser zoom do not force inappropriate compact layouts. Keep metadata save actions reachable while secondary controls overflow safely.

Fixes #1192
2026-08-30 04:20:12 +00:00
neon 1dd39bf0eb feat(books): add primary file size filter rule
Add fileSize as a filterable rule field resolved against the book's primary file size in bytes. In the filter builder UI, allow operators to input sizes in megabytes with automatic serialization to bytes for queries.
2026-08-28 15:32:28 +00:00
neon ddb296cade fix(books): show read status on the book detail discovery shelf
The series, author and similar endpoints never selected a read status, so
every cover in the detail shelf rendered bare. Attach the requesting user's
status in one bulk lookup per lane, and draw the badge in BookCarousel using
the same icons and placement as the library grid, plus an sr-only label.
BookRepository has no user in scope, so it now returns UnscopedBookRecommendation
and the type system forces the status on where the user exists.

Also adds the untracked scanner overlap repro for issue 1094.

Closes #1163
2026-08-27 21:56:17 +00:00
neon 58c3ef8b95 feat(books): rebuild the Files tab around the file list
Replace the tree-plus-inspector split with a hero strip, one Files card and a
rail holding only the selected file and a write-back summary. The nine stacked
bands stated a file's format five times and its role three, and the 28-row
metadata dump was the largest block on a tab about files; that table now lives
behind a link to Edit Metadata. The tab reflows on its own width via container
queries, so a collapsed sidebar and a narrow window behave alike, and the rail
becomes a bottom sheet on a phone.
2026-08-27 20:57:09 +00:00
neon 84c3cfc118 fix(client): keep downloads from stranding an iOS home-screen web app
A web app installed to the Home Screen has no browser chrome, so navigating it at an
attachment URL swapped the app for WebKit's document preview with no way back. Neither the
download attribute nor target="_blank" is honoured there, so hand that one case a blob and
keep the streaming link everywhere the browser owns a window it can leave.

Closes #1158
2026-08-27 16:11:00 +00:00
neon 95239ba701 feat(annotations): rebuild the hub and the highlights tab as one reading surface
Both pages now share the stream, entry and detail components, so a highlight
reads the same wherever you meet it. Entries split three ways - date rail,
quote at a real measure, source margin - and the library hub gains an overview
endpoint so its side rail does not recompute six aggregates per scroll.
Notes and anchor-review are server filters now; filtering the loaded window
made the counts and the pager disagree with each other.
2026-08-26 22:34:11 +00:00
neon 8b9442be1f fix(book-detail): stop the reading log and highlights tabs flashing
Both tabs fetch on mount and were rebuilt on every switch, so each visit
replayed blank, skeleton and content inside 120ms. Keep them alive and
revalidate silently instead, hold the layout until the first load decides
which shape it settles into, and defer skeletons past 220ms.

Also fixes the unhandled rejection failing the server suite in CI: the
Goodreads bot-challenge test rejected before its assertion was attached.
2026-08-26 19:53:36 +00:00
neon 75ccb8dd1a feat(book-detail): rebuild the highlights tab as a single-screen ledger
Replace the paginated card list with a three-column ledger: a marks rail, a
grouped stream that loads on scroll, an index that regroups by chapter, colour
or day, and a chapter position band. Add chapterBreakdown and activity to the
annotation stats so those panels describe the whole book without loading every
row, and move the metadata score colour onto a shared oklch ramp.
2026-08-26 17:04:48 +00:00
neon 4c42e0c73f fix(books): reshuffle random sort per visit instead of per day
Seed the random sort field from a per-view randomSeed on BookQuery rather
than a day counter, so a reload or a scope change draws a new order while
every page of one listing stays coherent. Callers that omit the seed keep
the daily fallback. Adds a shuffle button to the library, smart scope, and
collection toolbars when the active sort is random.

Closes #1153
2026-08-26 15:25:46 +00:00
neon d587d8f2a8 feat(books): overhaul files tab with tree rail and file inspector
Redesign the book details Files tab into a two-pane layout with an interactive file tree rail
and detailed file inspector.

Support inspecting audio tracks, embedded metadata, and write-back properties, and add filename
stem normalization helpers.
2026-08-26 14:07:52 +00:00
neon 095113a594 feat(book-detail): rebuild the reading log as a single-screen ledger
Vitals, the session ledger and attempts/records fill the pane height, with
the progress band across the row beneath, matching the details tab.

- attempt boundaries and reading stalls are rows inside the ledger, in date
  order only, so sorting by length or change still works
- the band toggles between the progress curve and a trace of the stretch of
  the book each session covered
- one empty state for a book with no log, in place of four empty panels
- drops the twelve-month heatmap, which was 357 empty cells on a real book

Stats gain longestSessionSeconds, longestSessionAt and backtrackCount, and
sessions gain attemptId; the client cannot derive either from one page.
2026-08-26 07:28:41 +00:00