Commit Graph

680 Commits

Author SHA1 Message Date
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
neon 3f9d21defa feat(book-detail): rebuild the edit metadata tab as a four-column ledger
Artwork and source, identity, catalog and description sit side by side, sized
by container queries on the pane rather than the viewport. Provider ids lose
their accordion for compact rows above the community ratings, and a locked
field now reads as a filled chip instead of a near-identical open padlock.
2026-08-26 06:42:49 +00:00
neon 2861aff338 feat(book-detail): rebuild the details tab as a single-screen layout
Three columns fill the pane height and a discovery shelf takes the row
beneath them, so the whole record reads without scrolling the page.

- synopsis clamps and expands inside its own block, never the column
- per-format progress folds into Editions instead of a duplicate list
- multi-track audiobooks collapse to one edition row, not one per file
- the cover frame keeps its library aspect ratio at any column height

Ports formatColorVar, the pill-format tokens and formatRelativeFromNow,
which the new cards depend on.
2026-08-26 05:53:29 +00:00
neon c383ad16e1 feat(naming): add a narrators token to file naming patterns
Multiple audiobook editions of one title resolved to the same target path, so
enabling file rename merged or skipped every narrator but the first. Add
{narrators}, plus a :max3 modifier that yields nothing above three values, and
judge an optional group by what its placeholders render so the fallback literal
stays in the user's own pattern. The five duplicate token builders collapse into
one shared helper, which also gives audio uploads the metadata tokens they never
had. Build the shared types package before server lint, since type-aware rules
degrade to unknown on a clean checkout without it.

Refs #1108
2026-08-25 19:03:37 +00:00
neon a25362244c feat(notifications): add per-category levels and collapse repeat notifications
Choose Off, Problems or All per category instead of a single on/off switch, and
stop announcing scheduled scans that changed nothing. Repeat occurrences now
collapse into one row with a count, so a bulk run no longer emits one
notification per book. Category and severity move into a shared registry that
the client reads instead of keeping its own copies.

Closes #1141
2026-08-25 17:37:45 +00:00
neon d4718cbe68 i18n(client): sync translations from Crowdin (#1105)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-25 09:09:53 -06:00
neon 7d3ff9997d feat(library): add a Missing Resources cleanup tool
Remove database records for books whose files are gone, clear cover
references with no file on disk, and delete cover folders left behind by
deleted books. Each category is reviewed in a paginated list and
re-verified against disk before anything is removed.

Closes #312
2026-08-25 05:18:31 +00:00
neon 63d4dc3bb4 feat(reader): continue a comic series from the last page
Finishing an issue offered no way onward, so the next one had to be opened from the library.
Resolve the next readable book server-side with one keyset query, offer it on the last page, and
let an opt-in setting cross over on a further page turn. The reader route now remounts on a param
change, without which it would keep rendering the book it was mounted with.

Also cast the compared value in compareSeriesIndexSql: as a bare parameter Postgres could not type
it inside the null test and rejected the statement.

Closes #998
2026-08-25 04:26:31 +00:00
neon 6702d5b729 feat(reader): jump to reading session checkpoints
Expose each session's originating file so the reader can restore a validated historical progress position without losing exact highlight navigation.

Closes #1045
2026-08-25 01:20:12 +00:00
neon e165812792 feat(client): confirm destructive Book Dock discards
Require explicit confirmation before row, sheet, or bulk discard deletes files from disk. Provide a separate deselect action for bulk selections.

Closes #1007
2026-08-25 00:53:04 +00:00
neon 8c854dada8 feat(metadata): merge fetched genres with existing values
Preserve curated genres while appending unique values returned by metadata
providers, and expose merge versus replace behavior in the web interface.

Closes #1125
2026-08-25 00:41:12 +00:00
neon e5fd535eca fix(metadata): refetch Book Dock metadata on demand
Allow ready Book Dock rows to refresh provider matches without depending on the
failed-only bulk retry path. Preserve bounded queueing, ownership checks, and
stale metadata cleanup.

Fixes #1118
2026-08-24 22:24:58 +00:00
neon 4c180fd58b fix(reader): patch embedpdf zoom to release startup gate on custom zoom
Apply a patch to @embedpdf/plugin-zoom@2.15.0 so numeric default zoom levels release the startup
viewport gate once resize metrics are measured.

Add unit test to verify custom zoom initialization and scale calculation under EmbedPDF.

Closes #1126
2026-08-24 22:21:18 +00:00
neon b264ef1132 fix(series): preserve exact series index labels
Treat series positions as validated string labels so values such as 5.10 retain their exact representation across storage, metadata providers, file formats, and clients.

Closes #1119
2026-08-24 21:40:41 +00:00
neon 2a280ad106 fix(metadata): show ComicVine links on book details
Build provider links from one shared registry so the detail page and quick view stay aligned.\nRender ComicVine with its CV fallback when no icon is available.

Fixes #1103
2026-08-24 20:12:03 +00:00
neon cb2ea1e898 build: serialize test runs so concurrent agents cannot saturate the machine
Each run sized its worker pool to the whole machine, so simultaneous runs
drove the box into swap until no suite finished and tests began failing on
the 5s default timeout. Serialize runs behind a lock, cap worker counts, and
raise testTimeout. See scripts/dev/with-test-lock.mjs.
2026-08-24 04:49:55 +00:00
neon 10152999d1 fix(kobo): render fixed-layout comics full screen on the device
Detect the OPF rendition:layout declaration and announce pre-paginated books
as EPUB3FL instead of KEPUB, which is what selects the device's fixed-layout
renderer. Kepub conversion is left alone so KoboSpan progress sync still works.
The flag is stored per file and backfilled during the sync reconcile pass, so
existing libraries converge without a rescan.

Closes #1044
2026-08-24 02:46:47 +00:00
neon 5ec34aef23 feat(settings): redesign users, field rules, file naming and libraries
Rebuilds the users roster, metadata field rules, file naming and metadata
score panels, and replaces the libraries list with an expandable ledger.
Libraries now batch their counts and last scan into one request instead of a
per-library fan-out, and surface scan history from scan_jobs on demand.
2026-08-23 23:57:17 +00:00
neon a3bc499028 feat(library): filter dashboards by selected libraries
Let each user choose which accessible libraries feed dashboard shelves and reading widgets while preserving access checks and Smart Scope filters.

Closes #1113
2026-08-23 15:15:18 +00:00
neon 7668f46c42 feat(reader): let readers pick the font style for body text
Read variable-font axes and named instances so readers can select real weight and style variants instead of synthesized faces. Preserve the axis default, accept the full CSS weight range, and keep saved choices stable while font catalogs load.

Require the new settings fields for complete EPUB defaults, keep per-book patches partial, and widen the persisted font-weight constraints to match CSS.

Closes #533
2026-08-22 06:16:56 +00:00
neon 3a095efcbb fix(books): let manual reading dates drive the reading lifecycle
Editing Date Started or Date Finished sent a date-only patch that the attempt layer had nothing to
attach it to. On an unread book no attempt was created, so the request returned 200 with null dates
and the field cleared itself. On a book being read the date landed on the open attempt as an end
date with no outcome, violating reading_attempts_closed_has_outcome_chk and surfacing as a 500.

Treat a date-only patch as a lifecycle statement: a start date opens an attempt and projects
reading, a finish date closes one as completed and projects read, and endedOn is never written
without its outcome. Clearing a finish date keeps the completion, because an outcome with no
completion date is legal and is already what re-read seeding writes. Setting a finish date on an
abandoned book now completes it. Reject a status that contradicts its dates with a stable error
code the client can translate, and derive statusChanged from the resulting status so a date-driven
transition still reaches achievements and the Kobo projection.

Keep unread and want_to_read free of dates they cannot own. The attempt keeps its own, so a book
marked unread no longer projects a start and finish date once a historical attempt is logged, and
marking a finished book unread no longer strands its finish date on the projection.

Stop the Hardcover import sending dates a status cannot carry. A want-to-read row that still holds
a start date would otherwise fail its row and skip the status import entirely.

Closes #1073
2026-08-21 19:01:54 +00:00
neon 9d116c3e97 fix(auth): surface private OIDC issuer guidance
Return a stable error code for private issuer rejections and translate it in settings so administrators can apply the required production override.

Closes #1065
2026-08-21 17:05:46 +00:00
neon 0250a61858 i18n(client): sync translations from Crowdin (#1075)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-21 10:27:17 -06:00
neon 11cb1e0f89 i18n(client): preserve protected technical terms during crowdin sync
Define a list of protected technical terms that must retain their English source text in target
catalogs.

Enforce protected term parity in validate-locales.mjs and restore protected terms automatically
during Crowdin synchronization, recording any corrected translations in the sync report.
2026-08-21 16:18:35 +00:00
neon ffd1a4fd0f fix(notifications): enforce settings access permission
Keep notification preferences hidden and inaccessible unless the user has notification access, while preserving the demo restriction.

Fixes #1057
2026-08-21 16:01:56 +00:00
neon 3b9bae7525 ci(i18n): fix the two guards that keep the crowdin sync failing
Two independent guards were failing the workflow on healthy input.

Retention read "exported value equals the English source" as a lost translation, but exports are
built with skipUntranslatedStrings, so Crowdin omits a key entirely once it stops carrying a
translation. Anything present in the export is by definition translated, and the equality check only
ever fired on correct work: Spanish "Error", "Feb" and "Cookie", French "Description", Portuguese
"Readwise". Retention now compares presence alone, which is what absence already meant. The same
inference lived in a legacyComplete branch that would have silently stopped protecting the 2,318
keys whose translation legitimately equals the source once any locale reached full translation.

Scheduled runs had no way out of that. The only escape hatch is allowed_translation_losses, a
workflow_dispatch input that is never persisted, so every schedule rediscovered the same losses and
failed until someone dispatched the run by hand.

The slot count rule then only runs for the keys it is given, and the sync passed none, so its
rejection filter was weaker than the locale validation running on its output one step later. An
invalid ja message survived the filter, reached the catalog, and failed the next step instead of
being dropped and reported. Move the source scan into locale-source-keys.mjs so the sync and
validate-locales.mjs derive the same keys.
2026-08-21 15:50:03 +00:00
neon 93e7c7b6b7 fix(metadata): stop comicvine searches timing out into no results
Every comicvine search probed up to eight candidate volumes one request at a time, each holding a
one second rate limiter slot against an endpoint that answers in three to four seconds, so the
fifteen second provider budget ran out before the right volume was reached and the search reported
an empty result. Rank volumes on the name and issue count already carried by the volume search
response, then probe them in concurrent waves, so the runs that can hold the issue are tried first
and within budget.

Bind the work to a soft deadline that aborts requests still in flight. Reaching it now yields the
candidates already found, rather than letting a late detail lookup cross the hard timeout and take
everything with it. A probe or detail lookup that fails or is throttled costs its own result only,
never its siblings or the candidate it was enriching.

Take the series name from a query the user actually typed instead of the stored one, or searching a
comic for a different series silently returned the series it was already filed under. Strip the
zero padding comic filenames add to issue numbers in the provider rather than only in the search
controller, so the background refresh path benefits too.

Report a provider that timed out, was throttled, or failed as its own stream event, since all three
were indistinguishable from a genuinely empty result. Candidates keep the default event and an
unchanged payload, so a client has to read the event name to tell the two apart.

Fixes #1039
2026-08-18 23:04:25 +00:00
neon b25fc71b7f i18n(client): sync translations from Crowdin (#1036)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-18 16:05:44 -06:00
neon cba5bae199 fix(authors): stop an image upload discarding unsaved author edits
The edit form was hydrated by a watcher on the author object, so every path that replaced that
object rewrote the drafts from the persisted record. Uploading an image, removing one, or
refreshing metadata each reassign the author, which silently reverted anything typed into the
name, sort name, or description fields.

Seed the drafts from the author only when the form has no unsaved changes, or when a different
author is loaded, and reseed after a successful save so later refreshes are still adopted.

Fixes #1037
2026-08-18 21:39:18 +00:00
neon b1ba3a03e7 fix(koreader): stop a stale device position undoing a progress reset
Clearing a book's progress deleted the shared row and left the KOReader device row behind, so
the next pull served the deleted position straight back. Clearing that row too is not enough
either: an empty response reads as "this server knows nothing about your book", which is the
one reply that leaves a device certain its own sidecar is the only position there is, and it
pushes that back on the next sync.

Record the reset as a durable fact and answer the pull with a real start position attributed to
BookOrbit. Route the position by format, because KOReader's own kosync plugin feeds it straight
to GotoPage or GotoXPointer with no percentage fallback and an empty value leaves the reader
where it was. Stamp each delivery as current, because both clients compare that against their
last page turn and default backward syncs to disabled, so the original reset time would be
dropped on any pull deferred past a page turn without ever prompting.

Serving a reset proves nothing, since either client can classify it as a backward sync and drop
it silently, so the marker survives delivery and a device is held until its own push reports the
start. Retire it once a device that took the reset reads on: the pull carries no device
identity, so a marker kept alive past its purpose is answered to every device and would send
that one back to the start on every sync. The bound this leaves is deliberate. A second device
that never took the reset is protected only until the first reads on, and closing that needs
device_id on the pull, which is a plugin change.

Judge convergence on the reported position rather than the percentage, so page 1 of a hundred
page comic is not mistaken for a position no short book could ever reach. Wind Kobo bookmarks
back through the same paths, since a Kobo device otherwise resumes from its own bookmark and
reports it back the same way.

Closes #1032
2026-08-18 21:29:36 +00:00
neon d06e29a889 fix(stats): show manually logged progress in the reading log ring
The ring read only reading_progress, the stored reading position, which nothing writes
except the web reader, Kobo sync and KOReader sync. Manual sessions record endProgress on
the session row and touch nothing else, so a book whose whole history was entered by hand
sat at 0% no matter how far the sessions ran, and marking it read did not help either.

Surface the log's own view of progress instead of writing a reading position from a manual
session: that path also drives auto read-status updates, achievement events and Hardcover
and StoryGraph sync, and would store a position with no CFI for the reader to restore.

Take the most recent session that recorded an endProgress rather than the highest, so a
reread still reads correctly once its first session lands. Read it outside the list date
and format filters, otherwise narrowing the log would drag the ring down with it. The
client folds the value into the max it already computes across ebook and audiobook
positions, which also lets the ring update when a session is added or deleted.

Closes #1030
2026-08-18 20:03:55 +00:00
neon 42f175f1ef feat(authors): add Goodreads as an author metadata source
Audnexus was the only author provider, and it returns just an ASIN, name,
description and image. Goodreads carries a far richer author record, so add it
as a second provider and let each field choose where it comes from.

The Goodreads API was retired in 2020, so the provider scrapes: a name resolves
to an author id through the WAF-free /book/auto_complete endpoint, falling back
to the author search page for names that only differ by a middle initial, and
the author page yields the biography, photo, birth and death dates, website,
genres and influences. Two rendering quirks are worth knowing about, since both
silently return the wrong value: free-text fields appear twice, truncated and
whole, so the parser takes whichever copy carries the most content; and the
birthplace sits in a bare text node while only the date is wrapped in a
dataItem, so authors with a place but no date correctly store nothing.

Preferences are now per field, mirroring the book-side FieldPreference model,
with an ordered provider list and a merge strategy each. They are global,
because an author row is shared across every library and cannot hold a
different biography per library. Providers declare which fields they can return
and both the resolver and the settings UI filter on that, so Audnexus can never
be assigned to a field it has no way to fill. The single global write mode is
migrated into each field's merge strategy on first read, keeping an existing
install's overwrite behaviour instead of silently adopting the default.

The executor now queries every listed provider and resolves each field
independently, first non-empty value winning, and records which provider
supplied the record so a re-run can pin the match instead of searching by name
again. overwriteIfProvided skips empty values, which is what lets a matched
author with an empty Audnexus biography fall through to Goodreads.
2026-08-18 03:48:20 +00:00
neon 75c6c69a10 i18n(client): sync translations from Crowdin (#1026)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-17 00:45:44 -06:00
neon ec4072e4be feat(server): add Audiobookshelf and Calibre-Web Automated migration sources
Extend the migration module beyond Booklore and Grimmory so an existing Audiobookshelf or
Calibre-Web Automated library can be moved into BookOrbit without rebuilding user state by
hand. Audiobookshelf supports both a live API connection and an offline backup file, while
Calibre-Web Automated reads a stopped snapshot of the app and metadata databases.

Match source books by ASIN in addition to ISBN, and batch the ISBN, ASIN, file hash, file
path, and title/author lookups so planning a large library issues a bounded number of
queries rather than one per book. Route the batched author comparison through the shared
accent-insensitive predicate, which now accepts a SQL fragment so a per-row pattern column
is matched the same way a bound literal is.

Extract the shared source connection form into MigrationSourceFields so the modal and the
settings page stay in sync as source types are added, and allow a mapping to skip a source
user instead of forcing every account to be imported.

Closes #986
2026-08-16 05:15:34 +00:00
neon 5527852b4b fix(reader): follow inflections so define reaches a real definition
Looking up a plural showed only "plural of candelabra", and looking up "candelabra" added a Latin
declension line beneath it. Both come from the Wiktionary path, which English lookups fall through
to whenever api.dictionaryapi.dev fails; it 502s often enough that readers hit it regularly.

Read only the requested language section. Wiktionary keys a page by language and returns every
language that spells the word the same way, so iterating Object.values() merged Latin into English.
This was not a rare edge case: of 14 common words sampled, 13 carried foreign senses, and "fare"
rendered 129 definitions of which 18 were English.

Follow an inflection gloss to the word that carries the meaning, bounded to depth 2, three lemma
fetches and a visited set for cycles. Only a pointer in the leading sense of its block is followed,
which is what separates "candelabras" from the "simple past of bake" buried at sense 10 of "book".
A lemma that fails to resolve is dropped rather than failing the lookup. Detection requires every
word before "of" to be grammatical, so "Informal form of address used by juniors" stays a
definition rather than sending the reader to "address".

Drop script and style elements before reading textContent. Wiktionary ships an inline <style> with
some senses and its CSS was rendering as part of the definition, visibly so on "fare".

Merge blocks that share a part of speech and cap each at five senses, so a resolved chain stays
readable and the popover stops keying a v-for on a duplicated part of speech.

Pointer following is deliberately English-only. fr.wiktionary writes "pluriel de chat", and
guessing at sixteen languages of grammar vocabulary is not worth it; non-English lookups still gain
the language filter. The Free Dictionary path is also unchanged for "candelabras", where its own
gloss "A single candelabrum." is circular but not a recognizable pointer.

Closes #1020
2026-08-16 04:43:22 +00:00
neon 591e6b864d fix(reader): give the mobile settings sheet a way to close
On a phone the settings panel had no reachable exit. `hide-close` dropped the built-in close
button and nothing replaced it, the grab handle was decorative with no swipe-to-dismiss behind
it, and the compact trigger only ever set the panel open. That left tapping the overlay as the
sole way out, and `max-h-[85vh]` closed off even that: `vh` resolves against the toolbar-retracted
viewport, so with browser chrome on screen the sheet covered nearly the whole visible area. Only
fullscreen, where the two viewports agree, left a gap big enough to hit.

Move the sheet chrome into a shared ReaderSettingsSheet instead of patching both call sites. The
epub and cbz sheets were identical copies, which is why the bug existed twice. Leave the desktop
popover path alone; it closes correctly already.

Closes #1016
2026-08-16 04:18:29 +00:00
neon 0d9527c048 build(deps): upgrade chokidar, pinia, and jsdom majors and drop unused deps
Move chokidar to 5, pinia to 4, and jsdom to 30 with a matching
@types/jsdom, and align packages/types on TypeScript 6 with the other
workspaces.

chokidar 5 is ESM-only while the server compiles to CommonJS. Node's
require(ESM) support covers this, verified against a live watcher and a
nest watch build rather than the mocked unit tests.

Remove @fastify/formbody and @tanstack/vue-table, neither of which had a
single caller, along with the stale @tanstack/vue-table entry in the Vite
optimizeDeps list.

Hold TypeScript at 6. typescript-eslint refuses to install alongside 7 and
vue-tsc cannot drive the native checker until the stable compiler API
lands in 7.1.

Ignore pnpm-lock.yaml in prettier so lint-staged stops rewriting it out of
pnpm's native style, which was inflating every lockfile diff to roughly
19k lines.
2026-08-16 03:52:20 +00:00
neon 2faa32c09d fix(metadata): bound provider ids and surface why a save was rejected
A provider identifier parsed out of an OPF was passed on verbatim, so a Calibre
`opf:scheme="AMAZON"` holding a product URL reached a varchar(20) column as a 36-character
value. It could not be stored, and the next manual save of that book was rejected for a field
the user never typed into. Drop an identifier that cannot fit its column rather than truncating
it: an overflowing value is not a long identifier, it is a different kind of value, and its
first 20 characters are not an ASIN either. Enforce the same bound where every extractor
converges, so no parser can fail a metadata write with a Postgres 22001.

Keep the server's description of a rejected save instead of discarding the response body. Only
that text names the field at fault, so the editor showed a bare "HTTP 400" and left the reason
in the network tab. It stays untranslated, so it is preferred over the catalog message rather
than replacing it, and the failure is carried as a structured value so the component still
translates everything else.

Tie the DTO limits to the column widths they were copied from, and guard the pair with a test,
since nothing connected them when they drifted apart.

Stand Node's built-in web storage down in the client test run. Node 26 defines localStorage on
globalThis, jsdom shares that object, and its getter reads as undefined without
--localstorage-file, which failed 229 tests across 28 unrelated suites on CI.

Closes #1015
2026-08-16 02:30:34 +00:00
neon 18d4d6cfc2 fix(server): match entity names that carry non-collapsed whitespace
Author, narrator, genre and tag names written before whitespace normalization existed can hold
a non-breaking space, tab, newline or doubled space. HTML collapses all of those, so the name
renders correctly in the UI while the ILIKE search predicate never matches it; unaccent() does
not touch whitespace either. Re-entering an identical name by hand fixed a single book because
the write path canonicalizes the row it reuses, but nothing ever ran for untouched books.

Collapse stored names to the form normalizeMetadataTextSql() produces, merging rows that collide
once collapsed and remapping every junction and content-filter reference before a duplicate is
dropped. Case variants are left alone, since search is already case-insensitive. Close the two
write paths that still produced un-collapsed names, and normalize the typed search term so a
pasted non-breaking space still matches.

Also carries staged client appearance work: the surface brightness picker and related controls.

Closes #1014
2026-08-16 01:55:34 +00:00
neon d7556011df build(deps): upgrade Node.js to 26 and pnpm to 11
Update package manager configuration to pnpm 11.22.0 and Node.js requirement to Node 26 across Dockerfile, GitHub Actions workflows, root package.json, and workspace configurations.

Upgrade dependencies, update GitHub Action versions, and remove deprecated scorecard workflow.
2026-08-16 00:41:56 +00:00
neon f49541cb20 style(client): refine app shell actions
Move GitHub actions into the sidebar footer and tighten header grouping.
2026-08-15 23:55:28 +00:00
neon c9ebf0ab7f i18n(client): sync translations from Crowdin (#1010)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-15 15:18:47 -06:00
neon 0480242a98 feat(koreader): retire devices without deleting their synced data
Removing a KOReader device was the only way to take a decommissioned reader off the list, and it
permanently deleted that device's page-by-page reading events, per-device progress, sweep record,
naming pattern and sole-source unmatched books. Derived reading sessions survived, but the raw
events did not, and the plugin keeps its upload watermark on the device, so nothing ever
re-uploaded them.

Add retirement as a marker table rather than a device registry. Device identity is implicit,
spread across five device-keyed tables and surfaced by a DISTINCT ON over device progress, so
there is no row to carry a flag and nothing to backfill. Retiring records only (user, device);
every byte the device synced stays where it is.

Clear the marker whenever a retired device syncs again, on single-book progress, bulk sweeps,
page-stat uploads and sweep completion, so reading never accrues against a device the user cannot
see. Keep deleting synced data as a separate, explicit action on the retired row, and clear the
marker it would otherwise leave behind.

Move the confirm copy to new message keys instead of reusing the ones whose meaning changed, so
the sparse target catalogs cannot ship a stale translation of a destructive dialog.

Closes #996
2026-08-14 05:34:06 +00:00
neon 53be54752d fix(library): run scans on the stored auto-scan schedule
The auto-scan cron expression was validated, persisted and shown in the UI, but nothing ever
consumed it, so a library configured with a schedule never scanned and no scan job was ever
recorded with the schedule trigger.

Register a cron job per configured library on boot and keep it in step as libraries are created,
updated and deleted. An occurrence that lands while a scan is already running is skipped rather
than retried, and a failing occurrence leaves the job registered so later ones still fire.

Schedules run in the server timezone, matching the cleanup, audit and statistics jobs, so a
deployment that sets TZ gets the wall-clock time its users expect.

Move cron validation into a single shared grammar used by both the API and the schedule editor.
The two sides previously disagreed in both directions: the UI accepted step-zero and reversed
ranges that the API rejected, and rejected day names the API accepted. The grammar is a strict
subset of what the scheduler and the human-readable preview can each parse, so an expression the
editor marks valid can always be scheduled.

Closes #985
2026-08-14 04:50:10 +00:00
dependabot[bot] 34e09fa655 chore(deps): bump the production-dependencies group with 41 updates (#1009)
* chore(deps): bump the production-dependencies group with 41 updates

Bumps the production-dependencies group with 41 updates:

| Package | From | To |
| --- | --- | --- |
| [@embedpdf/engines](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/engines) | `2.14.4` | `2.15.0` |
| [@embedpdf/models](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/models) | `2.14.4` | `2.15.0` |
| [@embedpdf/pdfium](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/pdfium) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-annotation](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-annotation) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-bookmark](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-bookmark) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-document-manager](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-document-manager) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-history](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-history) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-interaction-manager](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-interaction-manager) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-pan](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-pan) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-render](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-render) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-rotate](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-rotate) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-search](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-search) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-selection](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-selection) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-spread](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-spread) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-thumbnail](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-thumbnail) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-tiling](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-tiling) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-viewport](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-viewport) | `2.14.4` | `2.15.0` |
| [@embedpdf/plugin-zoom](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-zoom) | `2.14.4` | `2.15.0` |
| [@formatjs/icu-messageformat-parser](https://github.com/formatjs/formatjs/tree/HEAD/packages/icu-messageformat-parser) | `3.5.15` | `3.5.16` |
| [@lucide/vue](https://github.com/lucide-icons/lucide/tree/HEAD/packages/vue) | `1.27.0` | `1.31.0` |
| [@tanstack/vue-virtual](https://github.com/TanStack/virtual/tree/HEAD/packages/vue-virtual) | `3.13.34` | `3.13.35` |
| [@tiptap/extension-link](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-link) | `3.29.1` | `3.29.2` |
| [@tiptap/pm](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/pm) | `3.29.1` | `3.29.2` |
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/starter-kit) | `3.29.1` | `3.29.2` |
| [@tiptap/vue-3](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/vue-3) | `3.29.1` | `3.29.2` |
| [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) | `14.3.0` | `14.4.0` |
| [intl-messageformat](https://github.com/formatjs/formatjs) | `11.2.12` | `11.2.13` |
| [reka-ui](https://github.com/unovue/reka-ui) | `2.10.1` | `2.10.3` |
| [vue](https://github.com/vuejs/core) | `3.5.40` | `3.5.41` |
| [@fastify/compress](https://github.com/fastify/fastify-compress) | `9.1.0` | `9.2.0` |
| [@fastify/static](https://github.com/fastify/fastify-static) | `10.1.2` | `10.1.3` |
| [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common) | `11.1.28` | `11.1.29` |
| [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core) | `11.1.28` | `11.1.29` |
| [@nestjs/platform-fastify](https://github.com/nestjs/nest/tree/HEAD/packages/platform-fastify) | `11.1.28` | `11.1.29` |
| [@nestjs/platform-socket.io](https://github.com/nestjs/nest/tree/HEAD/packages/platform-socket.io) | `11.1.28` | `11.1.29` |
| [@nestjs/websockets](https://github.com/nestjs/nest/tree/HEAD/packages/websockets) | `11.1.28` | `11.1.29` |
| [fastify](https://github.com/fastify/fastify) | `5.10.0` | `5.11.3` |
| [jose](https://github.com/panva/jose) | `6.2.4` | `6.2.8` |
| [mysql2](https://github.com/sidorares/node-mysql2) | `3.23.2` | `3.23.3` |
| [nodemailer](https://github.com/nodemailer/nodemailer) | `9.0.3` | `9.0.5` |
| [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) | `8.22.0` | `8.23.0` |


Updates `@embedpdf/engines` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/engines/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/engines)

Updates `@embedpdf/models` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/models/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/models)

Updates `@embedpdf/pdfium` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/pdfium/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/pdfium)

Updates `@embedpdf/plugin-annotation` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-annotation/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-annotation)

Updates `@embedpdf/plugin-bookmark` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-bookmark/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-bookmark)

Updates `@embedpdf/plugin-document-manager` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-document-manager/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-document-manager)

Updates `@embedpdf/plugin-history` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-history/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-history)

Updates `@embedpdf/plugin-interaction-manager` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-interaction-manager/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-interaction-manager)

Updates `@embedpdf/plugin-pan` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-pan/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-pan)

Updates `@embedpdf/plugin-render` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-render/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-render)

Updates `@embedpdf/plugin-rotate` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-rotate/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-rotate)

Updates `@embedpdf/plugin-search` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-search/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-search)

Updates `@embedpdf/plugin-selection` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-selection/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-selection)

Updates `@embedpdf/plugin-spread` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-spread/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-spread)

Updates `@embedpdf/plugin-thumbnail` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-thumbnail/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-thumbnail)

Updates `@embedpdf/plugin-tiling` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-tiling/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-tiling)

Updates `@embedpdf/plugin-viewport` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-viewport/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-viewport)

Updates `@embedpdf/plugin-zoom` from 2.14.4 to 2.15.0
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-zoom/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-zoom)

Updates `@formatjs/icu-messageformat-parser` from 3.5.15 to 3.5.16
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Changelog](https://github.com/formatjs/formatjs/blob/main/packages/icu-messageformat-parser/CHANGELOG.md)
- [Commits](https://github.com/formatjs/formatjs/commits/@formatjs/icu-messageformat-parser@3.5.16/packages/icu-messageformat-parser)

Updates `@lucide/vue` from 1.27.0 to 1.31.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.31.0/packages/vue)

Updates `@tanstack/vue-virtual` from 3.13.34 to 3.13.35
- [Release notes](https://github.com/TanStack/virtual/releases)
- [Changelog](https://github.com/TanStack/virtual/blob/main/packages/vue-virtual/CHANGELOG.md)
- [Commits](https://github.com/TanStack/virtual/commits/@tanstack/vue-virtual@3.13.35/packages/vue-virtual)

Updates `@tiptap/extension-link` from 3.29.1 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-link)

Updates `@tiptap/pm` from 3.29.1 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/pm/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/pm)

Updates `@tiptap/starter-kit` from 3.29.1 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/starter-kit/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/starter-kit)

Updates `@tiptap/vue-3` from 3.29.1 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/vue-3/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/vue-3)

Updates `@vueuse/core` from 14.3.0 to 14.4.0
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v14.4.0/packages/core)

Updates `intl-messageformat` from 11.2.12 to 11.2.13
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Commits](https://github.com/formatjs/formatjs/compare/intl-messageformat@11.2.12...intl-messageformat@11.2.13)

Updates `reka-ui` from 2.10.1 to 2.10.3
- [Release notes](https://github.com/unovue/reka-ui/releases)
- [Commits](https://github.com/unovue/reka-ui/compare/v2.10.1...v2.10.3)

Updates `vue` from 3.5.40 to 3.5.41
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.40...v3.5.41)

Updates `@fastify/compress` from 9.1.0 to 9.2.0
- [Release notes](https://github.com/fastify/fastify-compress/releases)
- [Commits](https://github.com/fastify/fastify-compress/compare/v9.1.0...v9.2.0)

Updates `@fastify/static` from 10.1.2 to 10.1.3
- [Release notes](https://github.com/fastify/fastify-static/releases)
- [Commits](https://github.com/fastify/fastify-static/commits)

Updates `@nestjs/common` from 11.1.28 to 11.1.29
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.29/packages/common)

Updates `@nestjs/core` from 11.1.28 to 11.1.29
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.29/packages/core)

Updates `@nestjs/platform-fastify` from 11.1.28 to 11.1.29
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.29/packages/platform-fastify)

Updates `@nestjs/platform-socket.io` from 11.1.28 to 11.1.29
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.29/packages/platform-socket.io)

Updates `@nestjs/websockets` from 11.1.28 to 11.1.29
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.29/packages/websockets)

Updates `fastify` from 5.10.0 to 5.11.3
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v5.10.0...v5.11.3)

Updates `jose` from 6.2.4 to 6.2.8
- [Release notes](https://github.com/panva/jose/releases)
- [Changelog](https://github.com/panva/jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/panva/jose/compare/v6.2.4...v6.2.8)

Updates `mysql2` from 3.23.2 to 3.23.3
- [Release notes](https://github.com/sidorares/node-mysql2/releases)
- [Changelog](https://github.com/sidorares/node-mysql2/blob/master/Changelog.md)
- [Commits](https://github.com/sidorares/node-mysql2/compare/v3.23.2...v3.23.3)

Updates `nodemailer` from 9.0.3 to 9.0.5
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v9.0.3...v9.0.5)

Updates `pg` from 8.22.0 to 8.23.0
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.23.0/packages/pg)

---
updated-dependencies:
- dependency-name: "@embedpdf/engines"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/models"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/pdfium"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-annotation"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-bookmark"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-document-manager"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-history"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-interaction-manager"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-pan"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-render"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-rotate"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-search"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-selection"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-spread"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-thumbnail"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-tiling"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-viewport"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@embedpdf/plugin-zoom"
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@formatjs/icu-messageformat-parser"
  dependency-version: 3.5.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@lucide/vue"
  dependency-version: 1.31.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@tanstack/vue-virtual"
  dependency-version: 3.13.35
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tiptap/extension-link"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tiptap/pm"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tiptap/starter-kit"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tiptap/vue-3"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@vueuse/core"
  dependency-version: 14.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: intl-messageformat
  dependency-version: 11.2.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: reka-ui
  dependency-version: 2.10.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: vue
  dependency-version: 3.5.41
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@fastify/compress"
  dependency-version: 9.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@fastify/static"
  dependency-version: 10.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/common"
  dependency-version: 11.1.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/core"
  dependency-version: 11.1.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/platform-fastify"
  dependency-version: 11.1.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/platform-socket.io"
  dependency-version: 11.1.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/websockets"
  dependency-version: 11.1.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: fastify
  dependency-version: 5.11.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: jose
  dependency-version: 6.2.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: mysql2
  dependency-version: 3.23.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: nodemailer
  dependency-version: 9.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: pg
  dependency-version: 8.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(deps): complete the embedpdf 2.15.0 bump

Dependabot left @embedpdf/core and @embedpdf/plugin-scroll at 2.14.4 while
moving the other 18 packages to 2.15.0. core@2.14.4 pulls models@2.14.4, so
two copies of @embedpdf/models ended up in the tree; PdfTask carries a private
_promise, making them structurally incompatible and failing vue-tsc.

pnpm.patchedDependencies keys on an exact version, so bumping engines to
2.15.0 orphaned the patch key and silently dropped the wasmError rejection
patch, which embedpdf-worker.spec.ts exists to guard.

Bump the two stragglers, re-key the utils packageExtension, and rename the
three patches to 2.15.0. All three patch bodies apply unchanged.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
2026-08-13 22:23:36 -06:00
neon 26b7c602f8 security(settings): gate Book Dock settings behind manage_book_dock
Require manage_book_dock for the settings UI and the scoped API so regular Book Dock users cannot
view or modify administrative configuration.

Reject every Book Dock key on the generic PATCH /app-settings/:key route as well, including
book_dock_paused. That route accepted the same keys with any string value, so an operator with
manage_app_settings could store an out-of-range threshold or an unknown metadata mode and bypass
the validation the scoped endpoints apply.

Closes #1003
2026-08-14 03:15:30 +00:00
neon fddb41fdb9 fix(settings): expose koreader credential rotation
Expose the existing credential update path so users can rotate the single KOReader login without deleting synced data. Clarify the current settings location and device reconnection requirement.

Closes #990
2026-08-13 19:23:38 +00:00