Commit Graph

73 Commits

Author SHA1 Message Date
akai afd48837f9 feat: add inline review mode to reader 2026-07-09 14:15:36 +08:00
Huang Xin 843ab3448b feat(tts): keep TTS playing when the book is closed (#4941)
* refactor(tts): controller owns its foliate TTS instance and emits lifecycle events

view.close() nulls view.tts, so the controller keeps its own handle
(mirrored to view.tts while attached; reads prefer the public mirror).
state becomes an accessor that dispatches tts-state-change on a
microtask, and terminal conditions (end of content, error exhaustion)
fire an explicit tts-session-ended: 'stopped' is a transit value that
occurs on every paragraph advance and must never be read as death.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(tts): make TTSController detachable from the reader view

detachView enters headless mode: layout-dependent work is guarded, the
dead hook's preprocess/section-change closures are severed, and text
supply continues through created documents while position events keep
flowing. attachView adopts a new view without touching in-flight audio,
re-seeding the fresh text instance from the old cursor AT the
synchronous swap point (auto-advance during async prep would otherwise
replay a paragraph) and aborting via an attach epoch when a detach
supersedes it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(tts): move media session ownership to a session-scoped bridge

ttsMediaBridge binds directly to the controller (metadata per mark,
clamped position state, transport handlers, the silent keep-alive
element) so the lock screen keeps working when the reader hook is
unmounted. The hook's last-writer-wins handler effect and its
per-render re-registration are gone; the panel now derives isPlaying
from the controller's state channel, so lock-screen transport keeps
the in-reader UI truthful. useTTSMediaSession had no consumers left
and is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(tts): add hash-keyed TTS session manager with sleep timer and headless persistence

Sessions key by book hash (bookKey is regenerated per open), the
playback-state relay dedupes transit stopped values so paragraph
advances never flicker followers, and terminal handling rides the
explicit tts-session-ended event. The sleep timer survives reader
unmount, and headless positions persist through the book config on
disk (view/progress stores are cleared on close and reopen loads
from disk).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(tts): keep TTS playing across book close and reattach on reopen

Back-to-library and Android back dispatch tts-close-book (detach when
the session is not terminated: transit stopped states during chapter
transitions must not kill it); quit and window-destroying closes keep
the hard tts-stop so the foreground service tears down with the
webview. The unmount cleanup transfers ownership to the manager
instead of shutting down, covering deep-link book switches and
split-view pane closes. Mounting a book adopts a matching background
session once the view is ready (primary pane only) and stops a
different book's session unless it is still mounted elsewhere. The
sleep timer moves to the manager and a one-time toast announces the
first background continuation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(tts): now-playing bar in the library for background sessions

Floating pill above the shelf while a TTS session outlives its
reader: cover, title, sleep-timer countdown, play/pause following the
manager-relayed playback channel, and a hard stop. Tapping the body
reopens the book in the SAME window regardless of the new-window
preference, since the session is a per-webview singleton. Deleting
the playing book stops the session before its data is cleared. The
bookshelf reserves scroll clearance via a --now-playing-inset var the
bar sets while visible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(tts): make the header close button background-eligible

The header X routes through handleCloseBook (onCloseBook), not
handleCloseBooksToLibrary, so the sticky eligibility ref never got
set and closing a book from the header hard-stopped a live TTS
session. Replace the ref with an explicit keepTTSAlive parameter on
saveConfigAndCloseBook/handleCloseBooks: back-to-library, Android
back, and pane closes pass true; beforeunload, quit-app, and window
close invoke handleCloseBooks with an event object, which coerces to
a hard stop. This also removes the stickiness where one background
close would have made a later quit detach instead of stop.

Verified live in Chrome dev-web: close from the header keeps audio
playing with the now-playing bar shown; reopening reattaches the
same session (generation numbering continues); opening a different
book stops it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 17:38:57 +02:00
Huang Xin d1e7b4902c feat(share): time-limited share links with cfi-aware imports (#4037)
Add a Share Book feature that generates an expiring HTTPS share URL plus a
parallel readest://share/{token} deep link. Recipients land on /s/{token},
where logged-in users can one-tap "Add to my library" (R2 server-side
byte-copy) and anonymous users download the book or open it in the app.
Sharers manage active links from a dedicated "Manage Shared Links" panel
under user settings.

Highlights:
- 9 new App Router endpoints under /api/share (create, [token], cover,
  og.png, download, download/confirm, import, revoke, list).
- /s landing page with branded next/og chat unfurl image and SSR auth-cookie
  detection so logged-in recipients see "Add to my library" as the primary
  action without layout shift.
- Server-side R2 byte-copy for /import preserves the project's existing
  invariant that every files.file_key is prefixed with its row's user_id;
  stats / purge / delete / download routes work unchanged. URL-encodes the
  copy source so titles with spaces or '&' don't break the copy.
- Universal 7-day expiry cap, no tier differentiation, no "never". DMCA-risk
  reduction. Picker defaults to 3 days.
- Position-aware shares: "Share current page" toggle (off by default for
  privacy) attaches the sharer's CFI; recipient lands at the same paragraph.
- Per-user 50-share cap, rate limiting via Cache-Control: no-store on
  token-bearing responses, atomic SQL increment for download_count via a
  SECURITY DEFINER function so the public confirm beacon stays safe under
  concurrent fire.
- Soft revocation: presigned download URLs (5-min TTL) cannot be cancelled
  before TTL; documented as accepted v1 behavior.
- token + token_hash hybrid storage: public endpoints look up by hash and
  never select the raw token, so accidental SELECT-* leakage on a public
  route can't expose the bearer credential.
- Mobile / desktop Tauri share via tauri-plugin-sharekit; web falls back to
  navigator.share with a clipboard fallback when no native share method
  exists. Share-sheet dismissal no longer silently copies.

UI:
- New Dialog with a settings-card group: iOS-style segmented duration picker
  + toggle slider for "Share current page", on a single row each.
- Reader top-bar Share button, library context-menu Share entry, manage-
  shares list with cover thumbnails and overflow menu.
- New <SegmentedControl> primitive in src/components for reuse.

Coverage:
- Unit tests for token utils + URL parser (20 new tests, full suite at 3445).
- 31 locales translated for all new strings; en plurals hand-added per the
  project's hand-curated en convention.

DB migration in docker/volumes/db/migrations/002_add_book_shares.sql adds
the book_shares table, RLS policies, and the increment_book_share_download
RPC. Migration is idempotent.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:03:35 +02:00
Huang Xin 6fcda66b60 fix(reader): close stuck reader window on book load failure, closes #3932 (#3980)
When a book's underlying file is missing, opening it in a dedicated
reader window showed an error toast then navigated that window to
/library, leaving a leftover library-in-reader-window the user had
to close manually. Route the recovery through a new
closeReaderWindowOrGoToLibrary() that closes the dedicated reader
window (after ensuring the main library window is visible) and only
falls back to /library navigation in the main window or on web.

Also fix a related macOS issue: the reader's CloseRequested handler
was running handleCloseBooks and calling currentWindow.destroy() on
the main window, which tore down the active book and bypassed the
Rust close-to-hide handler — making Cmd+W / traffic-light close
quit the app from the reader page (vs. correctly hiding from the
library page) and lose the active book even when the window did
hide. Skip both the cleanup and destroy on macOS for the main
window so the Rust handler hides it with the book intact, matching
the macOS minimize-to-dock convention.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:32:04 +02:00
Huang Xin 6d798542f6 fix: restore main library window when going to library from reader, closes #3969 (#3973)
When the main window has been destroyed (Windows/Linux default close), the
reader's "go to library" button only closed the reader, leaving no library
visible. Add ensureMainLibraryWindow() that shows an existing main window
or recreates one with the 'main' label so the existing close-reader-window
wiring keeps working. Also grant the cross-window show/unminimize permissions
the call now needs.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:22:28 +02:00
Huang Xin a47a480aa2 fix(layout): persistent location for the sidebar toggle, closes #3193 (#3269) 2026-02-11 16:29:26 +01:00
Huang Xin 563d3478ba feat(gamepad): support gamepad input to paginate, closes #2432 (#3073) 2026-01-25 16:32:05 +01:00
Huang Xin d9a6cffe78 feat(discord): support show reading status with Discord Rich Presence, closes #1538 (#2998) 2026-01-19 17:42:19 +01:00
Huang Xin da5e3a0bd3 perf(fonts): cache first for font cache (#2945) 2026-01-14 07:34:00 +01:00
Huang Xin 30385ee5ec fix(settings): correctly setting configs for selected book in parallel read, closes #2825 (#2908) 2026-01-10 15:01:04 +01:00
Huang Xin c1530cc5c4 feat(iOS): support open file with Readest in Files App, closes #2334 (#2705) 2025-12-13 13:28:03 +01:00
Huang Xin 730fadb834 fix(web): fixed router glitches for library page after returned from reader (#2703) 2025-12-13 08:08:42 +01:00
Huang Xin e28dabce65 fix(layout): refactor full height for edge to edge env and in browser env (#2582) 2025-12-01 13:48:14 +01:00
Huang Xin 273bcafe01 fix: resolve endless loading indicator (#2502) 2025-11-21 14:58:55 +01:00
Huang Xin 0fd316d775 fix(sync): retry sync books for previous failed sync, closes #2441 (#2462) 2025-11-17 18:52:48 +01:00
Huang Xin c42fcf9ac4 fix: save reading progress when closing app directly in reader page, closes #2346 (#2461) 2025-11-17 16:35:56 +01:00
Huang Xin d8943fc0c5 compat: normalize OKLCH color syntax for older iOS Safari, closes #990 (#2150) 2025-10-01 07:59:45 +02:00
Huang Xin cdd274e5ca compat: add support for WebView down to version 92, closes #2139 (#2145) 2025-09-30 19:14:35 +02:00
Huang Xin 99a4cb50ac feat: add an option to show persistent TTS bar at the bottom, closes #1818 (#1944) 2025-09-01 14:23:28 +02:00
Huang Xin 7d9724ff8b fix: close books with the system back key on Android, closes #1933 (#1938) 2025-08-31 10:15:28 +02:00
Huang Xin 856ed4d3b3 ux: add splash screen for iOS and Android and reduce flash when opening books (#1926) 2025-08-29 19:10:09 +02:00
Huang Xin 584f3511f8 refactor(kosync): move kosync into reader sidebar menu and add manual pull/push trigger (#1912) 2025-08-27 13:32:00 +08:00
Huang Xin 1be43ff704 fix: close reader window when trying to go to library in separate reader window, closes #1899 (#1900) 2025-08-25 18:01:06 +02:00
Zeedif 595608bd62 feat: Implement KOReader Progress Synchronization (#1770)
* feat(sync): implement KOReader progress synchronization

This commit introduces a comprehensive feature to synchronize reading progress with a KOReader sync server. It includes a compatibility layer to handle discrepancies between Readest's CFI-based progress and KOReader's XPointer/page-based progress, primarily using the `percentage` field as a common ground.

Key additions include:
- A new settings panel under "KOReader Sync" for server configuration, authentication, and sync strategy management (e.g., prompt on conflict, always use latest).
- A conflict resolution dialog that appears when remote progress differs significantly from local progress, allowing the user to choose which version to keep.
- A client-side `useKOSync` hook to manage the entire synchronization lifecycle, including API calls, state management, and conflict resolution logic.
- A new API endpoint `/api/kosync` that acts as a secure proxy to the user-configured KOReader sync server, handling authentication and forwarding requests.
- Logic to differentiate between paginated (PDF/CBZ) and reflowable (EPUB) formats, using page numbers for paginated files where possible and falling back to percentage for reliability.
- Spanish translations for all UI elements related to the KOReader sync feature.
- Addition of `uuid` package to generate a unique `device_id` for sync purposes.

Refactor:
- The `debounce` utility has been improved to include `flush` and `cancel` methods, allowing for more precise control over debounced function execution, which is now used in the sync hook.

* fix(kosync): add support for converting between XPointer and CFI in progress synchronization

* fix(kosync): update navigation method to use select instead of goTo for paginated formats

* fix(kosync): refactor synchronization settings and improve conflict resolution handling

* fix(kosync): add event dispatcher for flushing KOReader synchronization

* fix(sync): handle xpointer in a different section, fix styling

* i18n: update translations

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2025-08-11 17:53:28 +02:00
Huang Xin 396b23ddb4 css: named container classes for easier css customization, closes #1598 (#1600) 2025-07-17 20:17:51 +02:00
Huang Xin 6da6b85530 feat: support multiple reader windows on desktop, closes #259 (#1596) 2025-07-17 10:21:37 +02:00
Huang Xin e3c411f043 feat: support to edit book metadata, closes #753 (#1583) 2025-07-14 19:46:57 +02:00
Huang Xin 8eefba2bee feat: add an option to show/hide original text in translation, also closes #1492 (#1511) 2025-07-02 07:27:05 +02:00
Huang Xin ed68d25b15 fix: restore full view settings when reopening book (#1400) 2025-06-15 16:07:51 +02:00
Huang Xin 7e542e8694 fix: reorganize TTS control view hierarchy, closes #1080 (#1119) 2025-05-11 12:06:15 +02:00
Huang Xin 1c015f5973 feat: add option to open last book on start, closes #328 (#1052) 2025-05-06 17:00:57 +02:00
Huang Xin a8430a7be1 feat: open files with readest from file manager on Android, closes #865 (#895) 2025-04-16 07:04:30 +02:00
Huang Xin 151509b84c mobile: fix iOS safe area insets (#317) 2025-02-08 03:38:17 +01:00
Huang Xin b7d06b6312 sync: use the location CFI to eliminate occasional one-off page glitches (#268) 2025-01-29 21:32:12 +01:00
Huang Xin c0d715bf77 sync: check local file availability before opening (#262) 2025-01-28 21:02:19 +01:00
Huang Xin c8f0c09426 Use dynamic viewport unit dvh to avoid page covered by toolbar and address bar in mobile browsers (#160) 2025-01-15 19:11:05 +01:00
Huang Xin 36d6074468 Filter voices list and shutdown TTS when closing book (#130) 2025-01-09 03:33:52 +01:00
Huang Xin 07b04b82e6 Initial implementation of TTS with Web Speech API (#120)
* Initial implementation of TTS with Web Speech API

* Add translations for annotator tools
2025-01-07 21:57:55 +01:00
Huang Xin 71d0735917 Various fixes (#99)
* Add shortcut to page turning with the space key on keyboard

* Properly handle auto refresh token with supabase

* Fix settings not stored in direct quit of app, closes #94
2025-01-04 14:18:57 +01:00
Huang Xin 50f75c1f18 Show error toast for failed imported books and styles for dropdown menus (#76)
* Styles for dropdown menus

* Show error toast for failed imported books
2024-12-31 18:35:51 +01:00
Huang Xin cd67f6ec3f Various enhancements and fixes (#70)
* Fix excerpts cannot be deleted in Chrome browsers

* Show book details in reader sidebar

* Set minimum search term length to 1 for CJK languages

* Use more system fonts as custom fonts, closes #31
2024-12-31 01:08:27 +01:00
Huang Xin a5f94cced9 Add more keyboard shortcuts (#59)
1. use ctrl/cmd+=, ctrl/cmd+- and ctrl/cmd+0 to zoom in/out and reset zoom;
2. use ctrl/cmd+q to quit app;
2024-12-27 22:35:27 +01:00
Huang Xin 3dcab5a1ee Various fixes (#56)
* Add missing translation for progress synced toast

* Graceful shutdown after all books closed, closes #50

* Throttle app update check to once every day

* Avoid infinite loop to redirect to the login page, closes #54
2024-12-27 16:59:06 +01:00
chrox 17336d7ac9 Add CORS for the sync API 2024-12-24 18:02:39 +01:00
chrox d15e661038 Save book configs and settings before page is reloaded, closes #18 2024-12-13 09:02:20 +01:00
chrox 242fbf6743 Use bookID in path in Web version 2024-12-06 11:36:24 +01:00
chrox aa16bc09f1 Support running Readest in modern browsers
Now we support Web platform
2024-12-05 18:57:16 +01:00
chrox 32ffaa0e05 Close window when document is closed with 'open with Readest' 2024-12-04 18:58:05 +01:00
chrox a9485c39a7 Refactor reader store out into small stores 2024-11-25 17:51:40 +01:00
chrox 9bc11905f7 Remove eslint react-hooks/exhaustive-deps warnings 2024-11-21 12:47:08 +01:00