Commit Graph

863 Commits

Author SHA1 Message Date
neon abb44aa47a fix(kobo): refuse a replacement with the bytes the device already has
A replacement byte-identical to the device's copy was never downloaded, so
it stayed offered and every bookmark the device pushed was discarded.
Keep the current delivery instead, and tell the user in the replacement
panel that the device already has this copy.

Ref #1786
2026-10-17 00:10:32 +00:00
neon e070460876 fix(kobo): replace a stale delivery instead of failing its download forever
When a book's file changed after it was offered to a Kobo, every download
returned 409 because the offer could no longer be rebuilt. A device download
now retires that offer and serves a fresh one from the current file, and the
device bookmark adopts the copy it downloaded. Log the conflict code when a
sync defers a delivery. Also drop a dev-only relocate log from the reader.

Fixes #1816
2026-10-16 23:34:28 +00:00
neon d0da80e751 i18n(client): sync translations from Crowdin (#1755)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-16 16:35:14 -06:00
Dennis 428aefe8d1 feat(scanner): regex metadata extraction (#1669)
feat(metadata): extract metadata from file paths with regex rules (#1669)

Let libraries define ordered regex rules that read title, authors, series,
series number and dates from each book's path relative to its library folder.
Run rules in a bounded worker pool, honour source precedence and field locks,
and apply them on scans, uploads and watcher moves. Also retry unprobed M4B
tag writes with the mp4 muxer.

Closes #1351

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
2026-10-16 16:24:11 -06:00
neon fd10f89586 fix(books): stop the details column from scrolling sideways
Clip the hidden genre measuring strip so its full single-line width no
longer widens the scrollable middle column, and let the rating row and
reading activity stats wrap in narrow columns.
2026-10-16 20:24:11 +00:00
neon a72179ad4d feat(auth): rebuild the sign-in pages as a bookplate on an endpaper
Put every auth page on a shared Ex Libris plate with signature-line fields, a show-password toggle and a Caps Lock hint.
Show sign-in messages outside the password form, with Retry, a reset link on lockout and a reset confirmation.
Add language and endpaper pickers (eight repeats) to the auth toolbar, replacing the background and corner controls.
2026-10-16 20:11:55 +00:00
neon 7d823039b0 feat(metadata): rebuild book auto-fetch as library scopes with counts
Show the global rule and each library's own rule on Field Rules-style tabs, with live counts per
condition, the score histogram and the books a run would take. Store only what a library changes,
run every library with its own rule, and record each library's run. Also fix the DjVu queue test
that assumed which request waits, and inventory the DjVu reader routes.
2026-10-16 19:05:32 +00:00
neon c378354dac feat(settings): rebuild reader fonts on the shared font ledger
Reader › Fonts now uses the Server Fonts ledger, family sheet and upload tray for a reader's own
fonts, and lists the server's fonts with a show/hide switch and a preview sheet. The shared pieces
move to features/settings/fonts and the ledger columns narrow to give the specimen room.
Server: additive GET /fonts/usage and PATCH /fonts/families, upload error codes, and a 409 instead
of a 500 when a reader uploads a second file for the same face.
2026-10-16 18:54:38 +00:00
neon 096bcea5cf feat(audit): rebuild the audit log as a grouped ledger with a drawer
Changes, Sign-ins, Security and All views replace the filter form. Repeats fold into one row,
sign-ins fold per person, day and network, and rows name their targets; events now snapshot
the target's name so deleted ones keep it. The drawer adds the event's facts, the target's
history, what the same person did around it, and one-click scoping. New additive routes:
/audit-log/summary, /sign-ins and /export; retention is editable from the page.
2026-10-16 16:38:08 +00:00
neon e21a92b8a4 feat(settings): rebuild server fonts as a specimen ledger with a family sheet
Each family is a row with its specimen, a weight grid of the styles readers get and how readers
use it; its sheet previews the face on reader pages and inside the reader's own font list. Uploads
answer per file in one tray, removing a family confirms with its cost, and a rename is one request.
Server: additive GET /server-fonts/usage (counts only), PATCH /server-fonts/families, upload error codes.
2026-10-16 16:27:42 +00:00
neon f89ae86d69 feat(metadata): rebuild custom fields as a roster with an editor sheet
Show each custom field on one row with its type, libraries and how many
books are filled, and edit, create, archive and delete it in the settings
sheet. Order Edit Metadata and card values by the order set on this page,
and add usageByLibrary to the admin field list, an additive field.
2026-10-16 16:15:44 +00:00
neon 83f17d6ff8 feat(achievements): rebuild the page as a trophy cabinet with hints
Replace the card grid with medals, a showcase, category shelves, a record book and a
detail sheet. Add GET /achievements/hints, which explains how to unlock what is still
locked from the user's own library and reading history, backed by a new page-count
index. Year-Round Reader now reports its months read as progress.
2026-10-16 15:44:16 +00:00
neon c544a28e77 feat(client): replace the global header with a sidebar dock and command palette
Remove AppHeader. The sidebar gains a Search row that opens a Cmd+K palette (books, authors,
series, places, settings pages and actions), a utility dock above the footer, and a Reading
group below the lists. Every page opens with a shared PageTitleRow; phones without one get a
slim bar. Settings headers gain a full description, and the top glow defaults to 50% with one
reach and strength for every style. The Appearance panel opens past the sidebar, reorders its
controls and names the current pick, and keeps each pastel accent under its vivid one.
2026-10-16 08:44:43 +00:00
neon be9b103120 refactor(client): drive the sidebar sections from one registry
Describe the six entity sections once in useSidebarSections and render them with one v-for in
both the expanded list and the rail, instead of twelve hand-written copies. Move data loading to
useSidebarDataSync, scan and import progress to useSidebarLibraryActivity, and the footer to
SidebarFooterBar. The rendered sidebar is unchanged.
2026-10-16 06:22:57 +00:00
neon fdbdc8c990 feat(library): rebuild the browse header as a title row and one command bar
Library, smart scope, collection, authors and series pages share a new
BrowseHeader: the place and its actions on one row, then search, sort,
filters and facets in one bar that folds groups instead of wrapping.
Format chips count from the new POST /libraries/:id/books/facets, built
on the listing's own query; scopes and collections gain filter and sort.
2026-10-16 06:08:13 +00:00
neon c54986d5d2 feat(cover): redraw placeholder covers in nine selectable styles
Replace the lattice placeholder with a typesetting engine that measures real glyphs, balances line
breaks and parses series, subtitles and sort-form authors before drawing.
Add a placeholderCoverStyle display preference, Bauhaus by default, with a style switcher and sample
shelf in Book Covers settings. Colours follow the series, author or title, so a cover stays the same
when genres arrive later; only Paperback's bands use genre.
2026-10-15 00:43:21 +00:00
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 ef3e42ee33 fix(achievements): draw Epic Voyager's book-marked icon on the web
The seed names the icon for Epic Voyager (long_book_3) book-marked,
which lucide ships only as an alias of book-bookmark. The web's icon
map had every other seeded name but not this one, so the badge drew the
HelpCircle fallback. The map now has it, and a test reads the seed and
fails if any seeded icon would fall back.
2026-10-13 19:25:52 +00:00
neon 198d36ae56 fix(reader): serve PDF and comic files correctly to every client
Fix the server and web gaps found while porting the PDF and comic
readers and offline downloads to Android, checked against the iOS app:

- KOReader: answer a pull for a PDF or comic with the page number when a
  BookOrbit client wrote last, instead of a percentage with no page
- comics: key the zip offset, RAR and CB7 caches by file id and
  modification time, so pages are not served from stale offsets after a
  metadata write-back rewrites the archive
- comics: answer an unreadable archive with 422 rather than a page count
  of 0, and extract CB7 with its folders so same-named pages in
  different folders no longer overwrite each other
- book files: send an ETag and Last-Modified on serve and download and
  honour If-None-Match and If-Range, clamp a range that runs past the
  end, answer suffix ranges with 206, and honour Range on download,
  which advertised it but sent the whole file
- book files: leave raw file routes uncompressed, so FB2 and unknown
  formats keep their Content-Length
- progress: reject a fractional or negative page number with 400 rather
  than failing with 500 at the database
- types: declare the comic page-count response shared by the web and
  the apps
- web: open a percentage-only PDF position at its estimated page, and
  flush pending PDF and comic progress before the reading session ends
2026-10-13 16:33:11 +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 070201e163 fix(client): report failed Book Dock uploads
The Book Dock discarded every upload failure, so a rejected file looked like nothing happened.
List failed files with a translated reason, offer retry where it can help, and stop a failed
token refresh, abort or timeout from stalling the upload queue. Dropped files of unsupported
types are reported instead of ignored.

Refs #1739
2026-10-13 00:24:07 +00:00
neon 9dbe2980dd fix(reader): keep tts playing after a voice or speed change
A voice or speed change invalidated the playing block's end handler, so the next block was never
requested, and a change during loading left the player loading forever. Hand the playing block over
to the new settings, refetch any block playback was waiting on, and cancel superseded requests in
the client and at the provider. Audio that arrives during a pause no longer resumes playback.

Fixes #1734
2026-10-12 23:08:57 +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 ddc7e848b9 fix(reader): scale publisher font sizes with the reader text size
Rewrite absolute publisher font sizes (keywords and px/pt lengths) as each EPUB loads so they
follow the reader's root size, falling back to the original value when the reader applies no
styles. Skip fixed-layout books and SVG content, and cover the rewrite and the foliate loader
hook with regression tests.

Fixes #1276
2026-10-12 22:05:19 +00:00
neon df7ef5daf0 fix(authors): keep circular portrait metadata readable
Inset and center hover metadata within the circular portrait so the book count and last-added date remain readable. Add regression coverage for both shapes and metadata states.

Fixes #1413
2026-10-12 18:51:25 +00:00
neon 1ab087dbd2 fix(reader): support epub loading without native grouping APIs
Keep EPUB loading functional on browsers without Object.groupBy or Map.groupBy.
Replace the native calls with local helpers and cover missing APIs with EPUB 2
and EPUB 3 metadata regression tests.

Closes #1683
2026-10-12 17:12:49 +00:00
neon 2b89c30a8d fix(reader): resolve non-English dictionary lookups
Use English Wiktionary's multilingual definitions endpoint to avoid HTTP 501
responses from language-specific hosts. Keep filtering definitions by the
book's language and cover host selection, language aliases, inflection
lookups, URL encoding, and popover retry recovery with regression tests.

Closes #1439
2026-10-12 16:56:34 +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 30229c7451 fix(reader): open EPUBs whose package lost its default namespace
epubBuilder writes the OPF root as `mlns=` for `xmlns=`. Once a metadata save
bound `opf:` on <package>, foliate matched OPF elements by namespace, found no
spine, and failed with "reading 'children'". Match by namespace only when the
root is in it, and have the writer add the missing default namespace so
rewritten files also open in released iOS builds.

Closes #1680
2026-10-12 05:09:00 +00:00
neon 0c367b2b57 fix(client): center the top glow on the main column
Paint the glow from a zero-height element in the main inset instead of the full-width shell, so it
centers on the content column and follows the sidebar as it resizes or collapses. The shell's own
background patterns (dots, mesh, aurora and others) were hidden under the old glow and now render.
2026-10-12 04:31:21 +00:00
neon 3e993af16b i18n(client): sync translations from Crowdin (#1707)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-10 23:57:13 -06:00
neon c37821782a security(deps): update dependencies and rebuild kepubify with go 1.27.2
Bump every direct dependency to its newest same-major release and move the
node base image to 26.11.1, which invalidates the cached apk layer and picks
up tiff 4.7.2 for CVE-2026-4775. Rebuild all six kepubify binaries from the
pinned upstream commit with Go 1.27.2 for CVE-2026-78667, CVE-2026-78669 and
CVE-2026-97031, keeping v4-dev so rendition hashes stay stable.
2026-10-11 05:19:04 +00:00
neon c3abf6d9e1 fix(settings): add release tiers on plain-HTTP origins
Create client IDs with createUuid instead of crypto.randomUUID, which
browsers only expose in secure contexts, so Add tier, saved views, table
presets and podcast sessions work on HTTP installs. Lint now rejects raw
randomUUID calls.

Fixes #1722
2026-10-11 03:54:24 +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 c91648eaac feat(books): include comics in non-audio requests
Keep books and comic archives under the existing ebook request kind so searches, ownership checks,
format preferences and release profiles agree without changing the API contract. Cover the combined
formats and library access rules with unit and end-to-end tests.

Ref #1748
2026-10-10 21:47:09 +00:00
neon e6c0275fdf fix(settings): refresh upload limits and default to 4 GB
Share the saved upload limit with validation so changes apply without a page reload.
Seed new installations with a 4096 MB limit while preserving existing values.

Fixes #1733
2026-10-10 17:30:22 +00:00
neon 3c6dd48b5b style(books): add enchanted request button effects
Give the missing-format action a wand and subtle accent glow to make it
more inviting. Keep its status readable in narrow cards and honor reduced motion.
2026-10-10 17:17:12 +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 c63f3ab757 fix(metadata): choose sources by medium and stop overwriting book data
Lead fetches with the primary file, give Book Dock its library's rules and read-along media,
default audiobook fields to sources that carry them, and flag providers that never fill a field.
Joining a format now fills gaps only and respects locks; a move picks its primary as a scan would.
2026-10-08 18:12:48 +00:00
neon 21b68685ad fix(settings): stop truncating the genres merge strategy control
Make the field rules table one grid with each row as a subgrid, so the
merge and status columns size to the widest control instead of fixed
272px and 116px tracks. The four-option genres control and translated
labels now fit, and the global scope drops the empty status gap.
2026-10-08 17:55:12 +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 bd141802bd fix(reader): preserve reading progress across narration resume
Keep text and narration bookmarks independent when opening and navigating EPUBs.
Use the narrated section for sentence bookmarks and attribute delayed page moves to narration.
Recover stalled WebKit blob frames with a bounded fallback that preserves XHTML location structure.

Cover restoration, narration attribution, and iframe fallback behavior with regression tests.

Closes #1708
2026-10-07 21:39:38 +00:00
neon 5c093333c5 fix(smart-scope): refresh sidebar counts after saving
Return matching item counts with create and update responses so the sidebar
can display the saved scope count without requiring a page reload.
Reuse the listing count logic and verify visibility and shared sidebar state.
2026-10-07 20:12:54 +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