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
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
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
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.
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.
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
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).
* 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>
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
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
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
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
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
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.
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
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
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
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
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
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
* 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.
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
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
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
The standalone BulkUpdateTagsDialog and its related state have been
removed from the table shell and views. In its place, a new
handleEditIndividually action was added, allowing users to select
multiple books and open the first one in the detail view for sequential
editing. The SelectionActionBar was also updated to support this action
and display query-scoped selection states.
Modify book navigation composables to work with sparse subsets
by pairing loaded book IDs with their absolute position in the
full listing. This ensures books reached via the jump rail
maintain their true position even when windowed.
Closes#319
When the user preference for thumbnail clicks is set to 'details', the
primary overlay button on book cards now routes to the book details
page and displays a BookText icon instead of the reader icon.
Also replaced the ExternalLink icon with BookText across dropdown
menus and settings for better visual consistency.
Delay spine layer rendering for blurred-fit covers until the image ratio is known so it does not overflow the artwork. Enable spine overlays on series stack cards, series detail headers, and carousels where they were previously disabled. Apply data attributes to spine layers so standalone artwork elements render properly without surface ancestors.
Closes#294
Simplify the loading state for book covers by removing the complex BookCoverPlaceholder in favor of a neutral pulsing skeleton. This ensures a cleaner visual experience while the actual cover image is being loaded.
Add server-backed jump buckets so large library, collection, and smart-scope listings can jump directly to sorted sections without loading every row up front.
Closes#163
Update the action buttons in the Edit Metadata tab to hide their text
labels on small screens. The buttons are kept horizontally scrollable on
mobile but wrap to the next line on desktop viewports. This ensures the
Save button and other actions are easily accessible without consuming
excessive vertical space on mobile devices.
Closes#292
Keyboard events bubbling up from within a shadow DOM (such as the PDF
viewer's embedded find bar) report the custom element host as the event
target rather than the inner input field. This caused the reader's
global shortcuts to incorrectly trigger while typing.
Resolved by checking e.composedPath() to determine the true origin
of the event.
Closes#291
The PDF reader lacked an exit button, trapping users. Since the native PDF viewer header is packed with tools and difficult to safely inject into, a floating action button (FAB) is added to the bottom left of the screen.
Closes#290
The details page now includes a 'Send via Email' button in the more options menu if the user has the email_send permission. Selecting it opens the SendBookDialog.
Closes#289
- Adds rename and delete file capabilities to Book Details > Files tab
- Automatically promotes a fallback file to primary upon deletion
- Refactors path update logic to correctly apply db folder path updates
- Ensures file type format badges in header search use global format colors
- Replaces legacy file property overrides with rename-only DTO
Closes#214
prevent focusout events on the sheet container from bubbling up to the document to bypass the focus scope trap. this allows the icon picker search box to retain focus when opened from the collection sheet. also increase the tooltip z-index to appear above the teleported panel.
Closes#280
The metadata fetch pipeline now gathers and returns detailed diagnostics
about provider selection, candidate results, and the reason for empty
resolved fields (e.g. no active providers, throttled providers, or no
candidates). These diagnostics are logged to improve observability and
make debugging metadata fetch failures significantly easier.
Use paired Kobo open and leave analytics events to derive progress deltas and active reading time.
Format negative reading-log deltas without an extra plus sign.
KOReader progress updates were updating the database but failing to
emit the progress changed event, causing Hardcover auto-sync to be
skipped. This change adds the missing event emission to the KOReader
service so that external syncs trigger correctly, and patches a query
filter issue in the Hardcover repository.
Closes#258