Commit Graph

781 Commits

Author SHA1 Message Date
neon ea6f10df53 feat(metadata): scope provider metadata fields by library
Use the current book's effective metadata provider configuration to decide which provider ID fields and search providers are available in metadata editing flows.

Preserve explicit empty provider selections so library overrides can remove all provider fields.

Closes #320
2026-06-21 20:55:10 +00:00
neon 2143b00cc5 fix(scanner): handle watched moves across docker libraries
Emit transfer events when watcher reconciliation moves a book between libraries. Keep file-as-book and folder-as-book watcher paths consistent across Docker, direct runs, and delayed copy-delete moves.

Closes #386
2026-06-21 19:15:39 +00:00
neon c00bd57ca5 fix(kobo): prevent sync crashes on empty tag requests
Send injected empty JSON bodies as Buffer chunks so Nest Fastify parsers can consume them without crashing.

Acknowledge BookOrbit collection item updates locally instead of proxying synthetic tags upstream.

Closes #396
2026-06-21 14:55:19 +00:00
neon 920aa3b477 build(deps): refresh client and server dependencies
Keep the dependency baseline current after the release window, and adapt the icon and zip integrations to current package APIs.
2026-06-21 06:00:55 +00:00
neon 1c38cf2084 fix(books): prevent focus loss when typing in empty series row
The SeriesMembershipEditor was losing focus on every keystroke when typing into a previously empty series row because the key was bound to the seriesName, causing the input component to remount. This introduces stable row keys to ensure the input retains focus while updating the model.

Closes #370
2026-06-21 04:25:03 +00:00
neon 1988ed0956 feat(metadata): add empty entity cleanup controls
Expose zero-book first-class metadata entities in browse so admins can review orphaned rows before deleting them.

Keep inline metadata values out of empty filtering because they only exist through book metadata rows.

Closes #370
2026-06-21 04:16:44 +00:00
neon 60383417a3 fix(cover): extract first naturally-sorted image for cbz cover
The central directory entries are now sorted by filename to ensure the correct first image is used as the cover, rather than relying on the order of entries in the ZIP archive.

Closes #378
2026-06-21 00:40:34 +00:00
neon dfa238e33f security(deps): update undici to fix vulnerabilities
The GitHub Actions Trivy scan was failing due to CVE-2026-12151, CVE-2026-6734, and CVE-2026-9697 in undici version 7.24.7. It is a transitive dependency of cheerio in the server package. Running pnpm update undici removes the vulnerable version from the lockfile.
2026-06-20 22:36:10 +00:00
neon 825e39a3ad chore(deps): bump node dependencies across monorepo
Updates various client, server, and root packages to their latest versions.
2026-06-20 22:15:12 +00:00
neon 21f15c3116 fix(metadata): offload large pdf parsing to worker thread
Offload large PDF parsing to a worker thread to prevent the Node.js event loop from being blocked. This ensures the application remains responsive during heavy metadata extraction tasks.

Closes #371
2026-06-20 21:42:10 +00:00
neon f1a7ba6070 fix(cover): refresh cached cover URLs
Update cover mutation paths to bump the book timestamp used for cover cache busting, and carry per-book collapsed-series cover versions so child covers do not reuse a representative book timestamp.
2026-06-20 20:52:43 +00:00
neon c4ab4a9153 feat: add in-app release notes viewer
Introduces a new feature to fetch, parse, and display app release notes
and update highlights directly within the UI.

- Backend: Adds the `release-notes` module to fetch GitHub releases
  and parse markdown highlight sections.
- Frontend: Adds the `WhatsNew` dialog and view, including custom
  markdown rendering and an image lightbox for update media.
- Tools: Adds validation scripts (`whats-new-lint`, `whats-new-preview`)
  to ensure release content parses correctly before publication.
2026-06-18 03:37:53 +00:00
neon c92ab8cf49 fix(library): skip file write for encrypted PDFs
pdf-lib cannot re-encrypt files, so saving an encrypted PDF strips its encryption but writes back still-encrypted byte streams, silently corrupting the file. Skipping the file write entirely prevents this corruption while allowing DB metadata to persist separately.

Fixes #368
2026-06-18 01:20:24 +00:00
neon 39e53a5d36 fix(books): treat missing read status as unread for sorting
Books without a row in user_book_status returned NULL and sorted differently than books explicitly marked as 'unread'. Coalescing the missing status to 'unread' ensures both explicitly and implicitly unread books are grouped and ordered consistently.

Fixes #367
2026-06-18 01:11:30 +00:00
neon 65f4fa7fca fix(library): only process books marked for rename in bulk rename
The bulk rename job was incorrectly iterating over every book in the library instead of restricting the operation to books that actually require a rename. This caused unnecessary processing and logged excessive noise. We now compute the preview first and filter the list of candidate book IDs down to those with the 'will_rename' status before executing the rename loop.

Closes #361
2026-06-17 23:14:30 +00:00
neon ceb2bb72d6 chore: add tests for BE strategies, repositories, and FE composables
Expand coverage across previously untested modules to close gaps identified
in the coverage audit.

BE: entity-manager strategies (inline, junction, author), koreader chapter
extractor, series and achievement repositories.

FE: reader settings/progress composables, notifications, live-scan books,
entity manager, and API wrapper modules (author, statistics, entity-manager).
2026-06-17 21:41:16 +00:00
neon ed87c14135 test(user): use valid PNG fixture for avatar test (sharp 0.35 libpng) 2026-06-17 20:25:12 +00:00
dependabot[bot] 2bb3b6e5d9 chore(deps): bump the production-dependencies group across 1 directory with 15 updates (#366)
Bumps the production-dependencies group with 15 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@embedpdf/vue-pdf-viewer](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/viewers/vue) | `2.14.3` | `2.14.4` |
| [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.0` | `4.3.1` |
| [@tanstack/vue-virtual](https://github.com/TanStack/virtual/tree/HEAD/packages/vue-virtual) | `3.13.28` | `3.13.29` |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.4.8` | `3.4.11` |
| [reka-ui](https://github.com/unovue/reka-ui) | `2.9.9` | `2.9.10` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.0` | `4.3.1` |
| [vue](https://github.com/vuejs/core) | `3.5.35` | `3.5.38` |
| [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common) | `11.1.24` | `11.1.27` |
| [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core) | `11.1.24` | `11.1.27` |
| [@nestjs/platform-fastify](https://github.com/nestjs/nest/tree/HEAD/packages/platform-fastify) | `11.1.24` | `11.1.27` |
| [@nestjs/platform-socket.io](https://github.com/nestjs/nest/tree/HEAD/packages/platform-socket.io) | `11.1.24` | `11.1.27` |
| [@nestjs/websockets](https://github.com/nestjs/nest/tree/HEAD/packages/websockets) | `11.1.24` | `11.1.27` |
| [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | `5.8.0` | `5.9.2` |
| [mysql2](https://github.com/sidorares/node-mysql2) | `3.22.4` | `3.22.5` |
| [sharp](https://github.com/lovell/sharp) | `0.34.5` | `0.35.1` |



Updates `@embedpdf/vue-pdf-viewer` from 2.14.3 to 2.14.4
- [Release notes](https://github.com/embedpdf/embed-pdf-viewer/releases)
- [Changelog](https://github.com/embedpdf/embed-pdf-viewer/blob/main/viewers/vue/CHANGELOG.md)
- [Commits](https://github.com/embedpdf/embed-pdf-viewer/commits/v2.14.4/viewers/vue)

Updates `@tailwindcss/vite` from 4.3.0 to 4.3.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.1/packages/@tailwindcss-vite)

Updates `@tanstack/vue-virtual` from 3.13.28 to 3.13.29
- [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.29/packages/vue-virtual)

Updates `dompurify` from 3.4.8 to 3.4.11
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.8...3.4.11)

Updates `reka-ui` from 2.9.9 to 2.9.10
- [Release notes](https://github.com/unovue/reka-ui/releases)
- [Commits](https://github.com/unovue/reka-ui/compare/v2.9.9...v2.9.10)

Updates `tailwindcss` from 4.3.0 to 4.3.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.1/packages/tailwindcss)

Updates `vue` from 3.5.35 to 3.5.38
- [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.35...v3.5.38)

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

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

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

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

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

Updates `fast-xml-parser` from 5.8.0 to 5.9.2
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.8.0...v5.9.2)

Updates `mysql2` from 3.22.4 to 3.22.5
- [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.22.4...v3.22.5)

Updates `sharp` from 0.34.5 to 0.35.1
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.34.5...v0.35.1)

---
updated-dependencies:
- dependency-name: "@embedpdf/vue-pdf-viewer"
  dependency-version: 2.14.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tanstack/vue-virtual"
  dependency-version: 3.13.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: dompurify
  dependency-version: 3.4.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: reka-ui
  dependency-version: 2.9.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: tailwindcss
  dependency-version: 4.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: vue
  dependency-version: 3.5.38
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/common"
  dependency-version: 11.1.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/core"
  dependency-version: 11.1.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/platform-fastify"
  dependency-version: 11.1.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/platform-socket.io"
  dependency-version: 11.1.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/websockets"
  dependency-version: 11.1.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: fast-xml-parser
  dependency-version: 5.9.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: mysql2
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: sharp
  dependency-version: 0.35.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
...

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-06-17 13:55:50 -06:00
dependabot[bot] 154933b429 chore(deps): bump dompurify from 3.4.8 to 3.4.9 (#364)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.8 to 3.4.9.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.8...3.4.9)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.9
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 13:49:59 -06:00
dependabot[bot] 94e92063d2 chore(deps): bump vite from 8.0.13 to 8.0.16 (#359)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.13 to 8.0.16.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.16
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 13:49:46 -06:00
dependabot[bot] e710d26b3e chore(deps): bump codecov/codecov-action in the github-actions group (#316)
Bumps the github-actions group with 1 update: [codecov/codecov-action](https://github.com/codecov/codecov-action).


Updates `codecov/codecov-action` from 6.0.1 to 7.0.0
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/e79a6962e0d4c0c17b229090214935d2e33f8354...fb8b3582c8e4def4969c97caa2f19720cb33a72f)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 13:49:34 -06:00
Kyungwook Park 6677f2f5ea feat(metadata): add Aladin Korean book metadata provider (#351)
* feat(metadata): add Aladin Korean book metadata provider

Add support for fetching Korean book metadata from Aladin OpenAPI.
Includes search by title/author/ISBN and lookup by ISBN-10/13.

- Add ALADIN to MetadataProviderKey in @bookorbit/types
- Add AladinProviderConfig to ProviderConfigurations
- Create AladinProvider with IdentifiableProvider interface
- Add mapper for Aladin API response to MetadataCandidate
- Register provider in MetadataFetchModule
- Add provider config merge, validation, and connection test
- Include 24 unit tests (provider + mapper)

* feat(client): add Aladin provider UI integration

* refactor(metadata): use 'Aladin' label and favicon-based icon

* feat(metadata): complete Aladin provider end-to-end wiring

Persist the Aladin ItemId across fetch, scoring, locks, and file metadata round-trip, and update the affected tests.

Co-authored-by: Kyungwook Park <288273+kyungw00k@users.noreply.github.com>

---------

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
Co-authored-by: Kyungwook Park <288273+kyungw00k@users.noreply.github.com>
2026-06-17 13:33:35 -06:00
neon 9afddc34d2 feat(books): add support for multiple series per book
This introduces a many-to-many relationship between books and series, resolving the previous limitation where a book could only belong to a single series. It updates the database schema, query builders, server DTOs, and the Vue client interface to manage multiple series memberships per book.

Closes #353
2026-06-17 06:15:59 +00:00
neon 9b4408c610 fix(server): fix floating point precision in series index formatting
String/math based extraction of floating point series index fraction parts caused floating point precision artifacts. This extracts formatting into a shared utility and uses string splitting to avoid precision issues.

Closes #360
2026-06-17 05:36:48 +00:00
neon 86654bece8 fix(settings): add create link button to magic links admin tab
The "Create link" button was missing from the magic links admin tab because the header was hidden. This adds an embedded create action so the button is available when the component is rendered without a header.

Closes #357
2026-06-17 05:31:51 +00:00
neon 6eec39cb8a fix: allow resetting migration source setup
Add an API endpoint and UI action to reset the migration source configuration when no migration runs have been created yet.

Closes #354
2026-06-17 05:24:58 +00:00
neon a36a615b9a fix(client): add shift-click selection to entity manager
Add shift-click range selection in EntityBrowseTable to allow selecting multiple entities at once.

Closes #352
2026-06-17 05:13:05 +00:00
neon 7b48d49351 fix(server): accept empty requests with unsupported media types
Inject an empty JSON object for requests that have an unsupported content type but zero content length. Fastify defaults to rejecting these with a 415 error.

Closes #348
2026-06-17 05:07:44 +00:00
neon 0cddaf050c feat(metadata): add fallback scanner for EPUBs with missing ISBNs
Extract ISBNs from the EPUB spine contents if missing from the OPF metadata.

Closes #350
2026-06-15 23:24:56 +00:00
neon 671828d1b8 fix(client): restore shadow display for natural-bottom covers
The cover shadow moves to the inner artwork frame. The surface's own overflow:hidden would clip that shadow, so let it render by setting it to visible. The inner frame keeps its own overflow:hidden, so the image stays clipped to its rounded rect.

Closes #298
2026-06-15 16:27:47 +00:00
neon 96e42d2ac1 feat(settings): add option to turn book spine off for comics
Adds a display preference to conditionally render book spines on comic
formats. The new setting is defaulted to false. Various book cover
surfaces across the application now respect this preference by checking
if the format is a comic before rendering the spine.

Closes #295
2026-06-15 05:56:39 +00:00
neon f525c01a2f feat(query): add up-next in series filter operator
Adds the isUpNext operator to seriesStatus allowing books to be filtered by whether they are the next unread book in a series where the previous book is completed.

Closes #321
2026-06-15 04:58:31 +00:00
neon d13fc929e3 feat(metadata): support calibre 9.x opf3 metadata formats
Calibre 9.x (opf3) writes provider identifiers as bare `prefix:value` text
inside `<dc:identifier>` instead of using `opf:scheme` attributes. It also
stores custom-column values like page count and subtitle in a
`calibre:user_metadata` JSON blob. This adds parsing for these fallback
formats to correctly extract metadata from Calibre-processed EPUBs.

Closes #341
2026-06-15 04:26:27 +00:00
neon 1b8454f557 feat(stats): add device and rating achievement evaluators with session source tracking
Introduce devices and rating evaluators backed by a new reading_sessions.source
column, so badges can reward multi-device reading and book ratings. Wire the
source through Kobo and reading-session services and seed the new badges.
2026-06-15 04:03:54 +00:00
neon 49da34ee48 feat(server): add safe directory creation to path service
Introduces PathService.createDirectory with comprehensive path validation
to prevent directory traversal and symlink attacks. It validates parent paths
against a blocked list, ensures the parent is a real directory, and blocks
unsafe characters in folder names.

Filesystem errors like EEXIST and EACCES are mapped to standard HTTP
exceptions to provide clear feedback to the client.

Closes #332
2026-06-14 23:45:59 +00:00
neon 445bb39978 feat(books): add lock status to library filter rules
Closes #327
2026-06-14 23:33:44 +00:00
neon daa0001877 fix(metadata): prioritize physical hardcover editions over audiobooks
Rank physical and ebook editions higher than audiobooks in hardcover
metadata mapping. Do not allow audiobooks to inherit page counts from
the base book to prevent incorrect metadata.

Closes #334
2026-06-14 23:06:44 +00:00
neon dc82774291 fix(scanner): populate per-file audio duration on upload
The upload paths saved only the aggregate book_metadata duration, leaving book_files.durationSeconds
null so the player summed to zero. Extract and aggregate per-file duration on new uploads and added
audio files, matching the scanner.

Ref #330
2026-06-14 22:50:01 +00:00
James McNee f1b7254b86 fix(reader): totalBookDuration computation accounts for metadata duration when files lack duration data (#331)
Resolves: https://github.com/bookorbit/bookorbit/issues/330
2026-06-14 16:36:12 -06:00
neon 5d96ba1a55 fix(metadata): improve hardcover edition matching algorithm
The previous edition matching logic often resulted in suboptimal edition assignments, tracking text books against audiobooks or selecting editions with drastically different page counts. A more rigorous matching strategy is required to ensure users get accurate edition metadata from Hardcover.

Closes #333
2026-06-14 22:34:08 +00:00
neon 854dc55085 fix(books): prevent next-book navigation from sticking at boundaries
Fetch requests now await existing promises for in-flight blocks instead of re-triggering and starving the load when crossing block boundaries. The navigation window entries are also preserved during identical structural updates to avoid infinite watcher loops.

Closes #325
2026-06-14 21:40:59 +00:00
neon 007985a6d0 fix(client): move filter panels out of the scroll container
The Library, Series, and Authors filter panels were rendered inside the
scrolling <main> element, so opening a panel after scrolling down placed
it above the visible viewport and it appeared to do nothing until the
page was scrolled back to the top. Render each panel as a sibling of
<main> so it stays anchored regardless of scroll position. A height cap
is intentionally avoided because the filter typeaheads use absolutely
positioned dropdowns that an overflow container would clip.

Add structural regression tests for the Series and Authors views that
assert the panel renders outside the scroll container.

Fixes #326
2026-06-14 21:16:24 +00:00
neon 101c8d4cce fix(metadata): restore Goodreads fetching via autocomplete fallback when detail pages are WAF-gated 2026-06-14 20:19:15 +00:00
neon e3ee046dc5 fix(metadata): normalize zero page count to null to prevent save errors
When a metadata provider returns a page count of 0 (e.g. unknown), the frontend would submit 0, causing a validation error (HTTP 400). This change normalizes 0 to null on both the client (before sending) and server (during validation).

Closes #329
2026-06-14 07:13:01 +00:00
neon c7f3e8eacb fix(metadata): allow saving edits to re-locked metadata fields
Removes the `assertManualUpdateAllowedForLockTransition` guard that incorrectly rejected the legitimate UI flow of unlocking a field, editing its value, and re-locking it before saving.

Closes #328
2026-06-14 06:44:37 +00:00
Chris Mansell aed22712cc feat(metadata): add LubimyCzytac metadata provider (#324)
* feat(metadata): add LubimyCzytac metadata provider

- Add LubimyCzytac scraper and provider implementation
- Add lubimyczytacId field across types, schema, extractors, and metadata service
- Register lubimyczytac provider in module, pipeline, repository, and config service
- Add provider icon, color, link, and UI support in book detail views
- Add migration for lubimyczytac_id column

Closes #304

* fix(books): keep cover card read button opening reader when thumbnail click prefers details

The overlay primary button should always open the reader regardless of
the thumbnailClickAction setting. That setting only controls card click
behaviour.

* fix(books): add explicit read button to hover overlay when thumbnail click prefers details

When thumbnailClickAction is 'details', a dedicated Read button now
appears in the overlay top row so users can still open the reader
directly without changing their preference.
2026-06-13 11:40:53 -06:00
neon d42abb3647 fix(metadata): extract and save audiobook chapters and embedded metadata
The book dock metadata service now routes audio formats through the audio extractor to extract chapters, duration, narrators, and other embedded tags. This ensures that audiobook metadata is correctly mapped and persisted during the import process.

Closes #322
2026-06-13 17:18:40 +00:00
neon 702cb267ba fix(client): add enterkeyhint to ChipInput for mobile keyboards
Mobile keyboards, such as on Android, may not show a functioning
Enter key by default in this input. Adding enterkeyhint="enter"
allows users to properly submit chips like authors in the
metadata editor.

Closes #310
2026-06-13 16:54:41 +00:00
neon 873f890d20 fix(client): respect device orientation lock in pwa
The `orientation: 'any'` setting in the manifest forces the PWA to rotate even if the device is locked. Removing it restores native behavior.

Closes #309
2026-06-13 16:50:13 +00:00
neon 5aace0b49e feat(collections): reword message when adding an item to a collection
The previous 'already here' message was confusing when immediately adding an item to a collection, making it seem like the action didn't just occur. The confirmation has been updated to clarify that the item was just added.

Closes #306
2026-06-13 16:47:30 +00:00