Commit Graph

808 Commits

Author SHA1 Message Date
Huang Xin f361698e05 feat(android): add D-pad navigation for Android TV remote controller (#3745)
* feat(android): add D-pad navigation for Android TV remote controller

Add basic D-pad/arrow key navigation support for Android TV Bluetooth
remote controllers, covering the full flow: library grid browsing,
reader page turning, toolbar interaction, and TTS toggle.

Library:
- Custom spatial navigation hook for grid D-pad navigation
- Arrow keys move between BookshelfItem elements with auto column detection
- ArrowDown from header enters the bookshelf grid

Reader:
- Enter key toggles header/footer toolbar visibility
- Left/Right navigate between toolbar buttons, Up/Down moves between
  header and footer bars
- Back button dismisses toolbar (with blur) before sidebar/library
- Focus-probe technique for reliable button visibility detection
  across fixed/hidden containers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: only auto-focus toolbar buttons on keyboard activation, not mouse hover

Track pointer activity to distinguish keyboard vs mouse toolbar activation.
When the toolbar appears due to mouse hover, skip auto-focus to prevent
unwanted focus outlines on desktop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: track keyboard events instead of pointer events for auto-focus guard

Pointer events from iframes don't bubble to the main document, so
tracking pointermove/pointerdown missed hover interactions over book
content. Track keydown instead — auto-focus only when a recent keyboard
event (Enter key) triggered the toolbar, not mouse hover.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 19:50:34 +02:00
Huang Xin 9595aa56e0 fix(android): get rid of the outline on the header and footer bar when using remote control to turn page (#3744) 2026-04-04 17:00:20 +02:00
Huang Xin caa0d719c5 compat(vertical): check writing mode also for child element of body, closes #3583 (#3743) 2026-04-04 15:56:26 +02:00
Huang Xin 52ac74bbad fix: fixed status info layout in vertical mode, fixed Android build (#3735) 2026-04-03 21:41:49 +02:00
Huang Xin ca5c860594 fix(kosync): don't normalize xpointer for more accurate progress sync, closes #3672 and closes #3616 (#3733) 2026-04-03 16:17:20 +02:00
AK Venugopal 80cab8e56d feat: Hardcover.app Sync (#3724)
* feat(hardcover): add one-way Hardcover sync integration

- Add HardcoverClient with ISBN/title-based book lookup, progress push, and note sync
- Add HardcoverSyncMapStore for persistent local mapping of note IDs to Hardcover journal IDs
- Add GraphQL queries/mutations for Hardcover API (insert/update/recreate journal entries)
- Add DB migration for hardcover_note_mappings table (schema: hardcover-sync)
- Add HardcoverSettings dialog component (connect/disconnect/enable toggle)
- Add useHardcoverSync hook wired in Annotator for push-notes and push-progress events
- Add Hardcover Sync section in BookMenu with per-book toggle (persisted in BookConfig)
- Add HardcoverSettings type and DEFAULT_HARDCOVER_SETTINGS to system settings
- Add hardcoverSyncEnabled per-book flag to BookConfig
- Mount HardcoverSettingsWindow in Reader alongside KOSync and Readwise windows

Sync is one-way (Readest → Hardcover), manual-action only, per-book opt-in.
Supports idempotent note sync: insert new, skip unchanged, update changed, recreate on stale remote ID.

* fix(hardcover): proxy API calls server-side to fix CORS, normalize Bearer token

- Add /api/hardcover/graphql Next.js route that proxies POST requests server-side,
  bypassing CORS restrictions (Hardcover API has no Access-Control-Allow-Origin header)
- On Tauri (desktop), calls Hardcover directly; on web, routes through the proxy
- Normalize token in HardcoverClient: accept raw JWT or 'Bearer <jwt>' format
- Update helper text to point to hardcover.app → Settings → API

* fix(hardcover): surface note-sync no-ops and harden book resolution

- Show toast feedback when book data is still loading, Hardcover is not configured,
  or the current book has no annotations/excerpts to sync
- Show an explicit info toast when note sync finds no new changes
- Parse Hardcover search results in the current hits/document response shape
- Resolve note sync through ensureBookInLibrary for parity with progress sync
- Add console logging for note/progress sync failures

* debug(hardcover): add runtime instrumentation for note sync

* feat(metadata): keep identifier stable and store ISBN separately

- Add dedicated metadata.isbn field
- Expose ISBN as its own editable field in book metadata
- Preserve identifier semantics for existing source IDs and hashes
- Route metadata auto-retrieval ISBN handling through the new field
- Prefer metadata.isbn for Hardcover matching

* fix(hardcover): avoid wasm sqlite for note mappings on web

- Store Hardcover note sync mappings in localStorage on web
- Keep sqlite-backed mappings for desktop/native environments
- Remove the web-only database dependency from manual note sync

* fix(hardcover): dedupe notes by payload hash across unstable note IDs

- Add payload-hash lookup in HardcoverSyncMapStore
- Reuse existing journal mapping when payload already synced
- Prevent duplicate insertions when note IDs change or duplicate locally

* fix(hardcover): avoid duplicate quote export when annotation note exists

- Detect excerpt+annotation pairs for the same highlight
- Skip standalone excerpt export when annotation has note text
- Keep annotation export as the single source of truth

* docs(hardcover): add consolidated change summary for review

* fix(hardcover): suppress excerpt export by CFI when annotation note exists

* fix(hardcover): suppress empty-note annotation duplicates when note exists

* fix(hardcover): deduplicate notes by text and cfi base node

* refactor(hardcover): optimize sync performance, add rate limiting and clean up debug tools

* chore: remove dev-only change log from main

* test(hardcover): add unit tests for sync mapping and client logic

* chore: custom deployment and UI fixes

* fix(hardcover): use timestamptz for accurate annotation time

* fix(hardcover): use date scalar and RFC 3339 formatting for journal entries

* Revert "chore: custom deployment and UI fixes"

This reverts commit 0329aba7129d1e1ebf2c663804b8fba9a9f87b91.

* Fix hardcover progress dates and surface imported ISBNs

* Fix Hardcover currently reading sync

* fix(hardcover): avoid promoting note sync status

* style(hardcover): apply prettier formatting

* test(hardcover): fix strict TypeScript assertions

* test(hardcover): harden sync regression coverage

* test(hardcover): fix lint and formatting regressions

* fix(hardcover): narrow note dedupe range matching

* refactor(hardcover): extract note dedupe helpers

* refactor(isbn): extract metadata normalization helpers

* feat(hardcover): improve synced quote formatting
2026-04-03 09:06:43 +02:00
Mohammed Efaz 888f4afde9 fix: preserve paragraph mode reading layouts and other UI/UX fixes (#3730)
* fix: paragraph mode

* test: paragraph mode

* test: remove paragraph mode

* fix: paragraph utils

* fix: paragraph hook

* fix: paragraph overlay

* fix: paragraph bar

* fix: paragraph control

* fix: paragraph shortcuts

* test: paragraph utils

* test: paragraph hook

* test: paragraph overlay

* test: paragraph shortcuts

* fix: paragraph overlay

* test: paragraph mode

* test: shortcuts

* test: remove overlay

* test: remove hook

* test: remove utils

* fix: paragraph overlay

* fix: paragraph overlay

* feat: paragraph overlay

* fix: vertical container sizing

* test: paragraph container

* fix: paragraph animation

* fix: paragraph text animation

* fix: remove container morph
2026-04-02 20:56:38 +02:00
Lex Moulton c9647276b1 feat(rsvp): progress bar per chapter, speed selector dropdown, and UX improvements (#3723)
* fix(rsvp): rename pause setting to punctuation delay for clarity

* fix(rsvp): update chapter header as user crosses TOC sections

* fix(rsvp): stop playback at end of book instead of restarting last chapter

* feat(rsvp): convert WPM badge to speed selector dropdown

* feat(rsvp): scroll active chapter to center when chapter dropdown opens

* fix(rsvp): show correct chapter name in header instead of "Select Chapter"

* fix(rsvp): start from selected chapter when switching via dropdown

* fix(rsvp): restore saved position when resuming from a different section

* fix(rsvp): remove shrink animation from header buttons on click

* refactor(rsvp): process one spine section at a time, greadtly reducing complexity

* refactor(rsvp): remove dead state and derive progress from currentIndex

* feat(rsvp): allow pausing during countdown

* fix(rsvp): use relocate event instead of fixed 500ms delay for chapter transitions

* fix(rsvp): persist WPM and punctuation pause settings across sessions

* fix(rsvp): prefix unused bookKey field with underscore to satisfy lint

* Revert "fix(rsvp): prefix unused bookKey field with underscore to satisfy lint"

This reverts commit 7b3a34813f0c3771e9af8d00c7c7dc77c8db161c.

* fix(rsvp): remove unused bookKey field from RSVPController

* feat(rsvp): make progress bar draggable

* fix: revert settings.json changes

* fix(rsvp): restore genuinely useful comments

* i18n: update translations

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-04-02 03:31:58 +02:00
Mohammed Efaz 9ecb9b24d2 feat: make reading ruler selection and step navigation coherent (#3722)
* refactor(reader): extract reading ruler math helpers

* fix(reader): keep text selectable inside reading ruler

* feat(reader): route taps to ruler step navigation

* feat(reader): route keyboard navigation to ruler steps

* fix(reader): animate ruler mask and frame together

* fix(reader): preserve drag anchor for ruler handles

* fix(reader): fall back to page turns at ruler edges
2026-04-01 18:59:44 +02:00
Huang Xin b8ddb5475e feat(sync): add full sync option for annotations in koplugin, closes #3710 (#3718)
Add "Full sync all annotations" menu item that pushes and pulls all
annotations regardless of the last sync timestamp, enabling users to
sync old highlights that were created before the plugin was installed.

Changes:
- Add full_sync parameter to push/pull that bypasses timestamp filter
  and uses since=0 for pulling all server annotations
- Deduplicate by annotation ID alongside position-based dedup
- Store server ID on pulled annotations and reuse it when pushing
- Parse ISO 8601 timestamps from server to preserve original
  created/updated dates instead of using current time
- Resolve KOReader page numbers from xpointers via getPageFromXPointer
- Resolve Readest page numbers from CFI via getCFIProgress on pull

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 15:35:48 +02:00
Huang Xin b71b246601 feat(settings): add TTS settings tab and highlight opacity, closes #3661 (#3712)
Add a new TTS tab in settings with media metadata update frequency control
(sentence/paragraph/chapter) to reduce Bluetooth notification spam, and move
TTS highlight settings from Color tab to the new TTS tab. Also add a highlight
opacity setting with live preview in the Color tab.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 06:35:26 +02:00
Huang Xin 956c71cd7b chore: migrate from ESLint to Biome for linting (#3694)
Replace ESLint with Biome for ~14x faster linting (~360ms vs ~5s).

- Add biome.json with rules matching ESLint parity (Next.js, a11y,
  TypeScript, unused vars/imports)
- Remove eslint, @typescript-eslint/*, eslint-config-next,
  eslint-plugin-jsx-a11y, @eslint/* from deps
- Remove eslint.config.mjs
- Update lint script to: tsgo --noEmit && biome check .
- Fix 11 real code issues caught by Biome (banned types, explicit any,
  unsafe finally, unreachable code, shadowed names)
- Disable Biome formatter (Prettier stays for Tailwind class sorting)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 16:25:04 +02:00
Huang Xin 8ed9290659 layout: don't truncate remaining progress info without status info, closes #3678 (#3685) 2026-03-30 05:52:09 +02:00
Huang Xin c4e3315642 feat(scroll): add single section scroll option, closes #3663 (#3668) 2026-03-28 13:57:20 +08:00
Huang Xin 45e5f0fa61 fix(eink): disable range editor loupe for annotations on Eink devices, closes #3655 (#3656) 2026-03-27 10:26:36 +01:00
Huang Xin 3d4d1482aa feat: add keyboard shortcuts help dialog (#3653)
Add a keyboard shortcuts help dialog toggled with '?' that displays all
shortcuts grouped by section with platform-appropriate key rendering.

Restructure DEFAULT_SHORTCUTS to include i18n descriptions and section
metadata. Translate 37 new keys across 29 locales.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:59:38 +08:00
Huang Xin 5f897f648f feat(tts): add shortcuts to navigate and play/pause in TTS mode, closes #3620 (#3651) 2026-03-27 06:39:17 +01:00
Lee Kai Ze c7e82825f5 fix(koplugin): avoid resurrecting deleted annotations on pull (#3639)
Closes #3621.
2026-03-26 15:13:32 +01:00
Huang Xin 5a072e7d1f fix(pdf): apply theme colors for PDFs, closes #3593 (#3626) 2026-03-25 16:50:12 +01:00
Huang Xin 87f0240b0a compat(footnote): support footnote text in alt attribute of the image, closes #3576 (#3587)
refactor: remove unused continuous scroll
2026-03-22 12:17:02 +01:00
Huang Xin 2905506017 fix(layout): fixed total scrollable width in vertical scrolled mode, closes #3583 (#3586) 2026-03-22 11:56:12 +01:00
Huang Xin 1936136596 fix: resolve various tracked exceptions in ph (#3584)
* fix: handle synced bookmarks without cfi

* chore: clean up some exceptions in ph
2026-03-22 08:16:38 +01:00
Huang Xin 0177a03a90 fix(tts): properly follow tts reading position in scrolled mode (#3581) 2026-03-21 18:08:45 +01:00
Huang Xin 76b239f382 feat(koplugin): add support for annotation sync (#3579)
Add bidirectional annotation/highlight sync between KOReader and Readest:

- Add xpointer0/xpointer1 fields to BookNote and DBBookNote types for
  KOReader XPointer positions alongside Readest's CFI format
- Extend transform layer to pass through xpointer fields to/from DB
- Convert CFI→XPointer on push and XPointer→CFI on pull in useNotesSync,
  discarding notes that fail conversion
- Support KOReader's text()[K].N indexed text node format in xcfi.ts for
  paragraphs with inline elements (e.g. <a> page anchors)
- Generate KOReader-compatible XPointers: text().N for single text nodes,
  text()[K].N only when multiple direct text nodes exist
- Skip cfi-inert elements (injected by Readest at runtime) in XPointer
  path building and resolution
- Map highlight colors between KOReader and Readest color systems
- Implement KOReader plugin annotation push/pull with deterministic IDs,
  auto-sync on document open/close, and UIManager refresh on pull
- Refactor koplugin into focused modules: syncauth, syncconfig,
  syncannotations, selfupdate

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 16:43:51 +01:00
Huang Xin 91bc4ddec7 feat(library): backup to and restore from a zip file (#3571) 2026-03-20 18:27:52 +01:00
Huang Xin 1f4f5f8a6d feat(annotator): responsive layout for the loupe (#3568) 2026-03-19 17:54:24 +01:00
Huang Xin eab83c6d3f feat(annotator): show the loupe when selecting text in instant annotation, closes #3539 (#3565) 2026-03-19 15:10:33 +01:00
Huang Xin e4ab06abcd fix(footnote): don't preload other sections in footnotes (#3563) 2026-03-19 04:10:16 +01:00
Huang Xin fef0aa6947 fix(perf): revert back next/image with lazying loading for much better perf (#3562) 2026-03-18 14:49:06 +01:00
Huang Xin 2935eacb1c fix(layout): fixed header background color on mobile devices (#3561) 2026-03-18 13:08:43 +01:00
Huang Xin ad9bb58cd4 fix(ios): resolve stale closure crash preventing highlight popup on iOS (#3559) 2026-03-18 10:11:13 +01:00
Huang Xin 1f7483b911 feat: scroll to the nearest item in the search results or annotation lists (#3555) 2026-03-17 16:01:56 +01:00
Huang Xin 3831f24ff7 fix(android): fix page navigation layer z-index, closes #3511 (#3552) 2026-03-17 09:52:21 +01:00
Huang Xin 3bec89c85d fix(progress): show remaining pages for current section instead of all loaded sections (#3550) 2026-03-17 09:18:39 +01:00
Huang Xin 3dfe6bd65e fix(layout): fix parallel read layout on smaller screens (#3549) 2026-03-17 07:34:55 +01:00
Huang Xin b00d321817 refactor(reader): extract shared panel drag hooks and add vertical drag to Notebook (#3548)
Extract useSwipeToDismiss and usePanelResize hooks from duplicated code
in SideBar and Notebook. Add mobile swipe-to-dismiss drag handle to
Notebook matching SideBar's existing behavior. Fix drag cursor showing
col-resize instead of row-resize during vertical drags.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 04:27:27 +01:00
Huang Xin 1af55e24da feat(epub): support continuous scroll and spread layout for EPUBs, closes #3419 and closes #1745 (#3546) 2026-03-16 18:24:07 +01:00
Huang Xin f7b2a2432c fix(export): apply block quote syntax to every line in annotation export, closes #3534 (#3536)
Add formatBlockQuote utility and blockquote nunjucks filter so both
default and custom template exports prefix every line with `>`.
Document all available formatters in the custom template help panel.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:23:17 +01:00
Huang Xin d06e1849cc feat(rsvp): add persistent context, display settings, and layout improvements, closes #3333 (#3528)
* feat(rsvp): add persistent context, display settings, and layout improvements, closes #3333

Add always-visible collapsible context panel, font size adjustment,
ORP color selection, and stable context window that only rebuilds on
scroll. Refactor speed controls into playback row with collapsible
settings behind a gear icon. Translate new i18n keys across 29 locales.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selfhost): update docker db schema to match FileRecords (#3527)

In #2636, FileRecord was defined to have updated_at  field
which is used when it is accessed from the database. But the
local dev setup is missing this field.

This diff adds an updated_at column to match the expectation

* chore(ci): cache system dependency and rustc outputs in ci

* chore(ci): lint script changed from tsc to tsgo

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Azeem Bande-Ali <me@azeemba.com>
2026-03-13 06:57:47 +01:00
Huang Xin 674a0bf16d compat(footnote): support more footnote selectors, closes #3515 (#3523) 2026-03-12 16:46:57 +01:00
Huang Xin 7a605a357a feat(pdf): support pitch to zoom and scroll mode for PDFs, closes #3461 (#3520) 2026-03-12 16:07:55 +01:00
Huang Xin 64a71e25e4 fix(layout): make horizontal scroll bar visible in fixed-layout EPUB, closes #3506 (#3512)
And fix some text not shown in fixed-layout EPUB.
2026-03-11 15:03:05 +01:00
Huang Xin 51a3767940 feat(a11y): show prev/next section buttons for screen reader, closes #3290 (#3511) 2026-03-11 13:33:36 +01:00
Huang Xin a8572ab7c6 feat(reader): tap notch area to scroll to top in scrolled mode, closes #3474 (#3510) 2026-03-11 18:20:38 +08:00
Huang Xin 907b672313 fix(a11y): improve TOC screen reader accessibility for NVDA, closes #3477 (#3509) 2026-03-11 14:30:01 +08:00
Huang Xin 8df9f909b4 fix(reader): add Always on Top toggle to reader view, closes #3482 (#3505)
- Apply alwaysOnTop setting on reader window init so books opened
  in a new window correctly inherit the setting
- Update tauriHandleSetAlwaysOnTop to apply to all open windows
  so toggling from any window keeps all windows in sync

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 13:15:08 +01:00
Huang Xin bcd7a90f27 fix(layout): add top/bottom margin to container in scrolled mode, closes #3463 (#3504)
* fix(layout): add top/bottom margin to container in scrolled mode, closes #3463

When showMarginsOnScroll is enabled, the paginator margins are set to 0
but the FoliateViewer container had no compensating padding, causing
header/footer bars to overlap content. Now the container padding aligns
content top to the bottom of the header bar (gridInsets.top + 44px) and
content bottom to the top of the footer bar (52px + safe area padding).
Also fixes the footer bar height constant from 44 to 52 to match
ProgressBar's actual h-[52px].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(ci): cache all crates including local path deps in Tauri build

Add cache-all-crates: 'true' to Swatinem/rust-cache so that vendored
local path dependencies (packages/tauri/crates/, packages/tauri-plugins/)
are cached between CI runs instead of being recompiled from scratch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:40:03 +01:00
Huang Xin fad27d74a0 compat(android): show button navigation bar on Android, closes #3466 (#3501) 2026-03-09 17:36:13 +01:00
Huang Xin 569c6707a4 fix(macOS): fix traffic light visibility in sidebar, closes #3488 (#3499) 2026-03-09 15:10:13 +01:00
Huang Xin 04737d6f35 fix(layout): update safe insets in reader page, closes #3469 (#3497) 2026-03-09 13:54:03 +01:00