Commit Graph

86 Commits

Author SHA1 Message Date
Huang Xin a1279a65ce feat(send): clip web URLs into self-contained EPUBs via Tauri webview (#4241)
Builds the URL-clipping path of the "Send to Readest" feature: paste a
link, the renderer ingests the rendered page, and a self-contained EPUB
lands in the library. No server proxy, no external CDN refs left in the
EPUB once it's saved.

Architecture

- New Rust `clip_url` command spawns a hidden Tauri WebviewWindow at the
  target URL with a real Chrome UA + WebKit fingerprint mask, so TLS-
  fingerprint and JS-challenge walls (Cloudflare, Medium, X, WeChat MP)
  resolve naturally instead of bouncing the server proxy.
- Capture transport is URL-payload navigation to a one-shot
  127.0.0.1:RANDOM_PORT/clip/{token}?d={url-safe-base64} listener.
  Top-level navigation isn't governed by CSP connect-src / form-action /
  WebKit Private Network Access — the four earlier transports
  (fetch, <form>, custom URI scheme, window.name) were each blocked by
  one of those.
- Page-to-EPUB bundler (`assetBundler`) walks <img>/<picture> with
  src → data-src → data-original → data-srcset → srcset fallback so lazy-
  loading sites don't ship a 60px LQIP; fetches assets in parallel with a
  per-asset timeout + per-asset/total caps; failed images degrade to alt-
  text placeholders. A per-site rules table (seeded with WeChat MP) + a
  selector fallback catches articles Readability misextracts. Builder
  prepends the article <h1> + byline so the EPUB has a proper opening.
- Nested EPUB TOC built from h1–h6.

UI surfaces

- "From Web URL" entry in the library Import menu, gated to Tauri; web
  build hides the URL field and points at the browser extension.
- `ImportFromUrlDialog` with auto-height (overrides Dialog's `sm:h-[65%]`
  default) and a dim placeholder for the URL field.
- Clip webview window styled to match Readest's main window — macOS
  decorations + overlay title bar; other desktops decorationless with a
  drop shadow; native background + in-page loading overlay pick up the
  caller's `themeCode.bg`/`fg` so light/dark/eink/custom themes all
  render correctly. Title localised, all five overlay/title strings
  translated across 33 locales.

Notes

- Gates the macOS traffic-light positioner to main/reader-* windows so
  the decorationless clip window no longer null-derefs in
  `position_traffic_lights`.
- Stricter validation across the path: schemes restricted to http/https,
  hex-color parsing rejects malformed values, server endpoint returns
  400 on missing/invalid base64.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 19:48:09 +02:00
loveheaven 5ac8564e41 feat(library): add Import from Folder dialog with format/size filters (#4229)
* feat(library): add Import from Folder dialog with format/size filters

Replaces the silent "import every supported file recursively" behaviour of the directory import menu item with an explicit dialog that lets users pick which formats to include, set a minimum file size, and choose between mirroring subfolders as nested groups (legacy behaviour) or flattening every match into the current library view.

The folder, the chosen Folder Structure radio, the ticked File Formats and the File Size threshold are all persisted in localStorage so re-opening the dialog seeds every field with the user's last choice. Cancelling the dialog does not write to storage so an aborted pick won't pollute the next session.

Also hides the native number-input spinner via a small .no-spinner utility in globals.css; on macOS WebKit the spin buttons were drawing over the rounded input border and looked broken. The KB suffix now lives inside the input's bordered shell instead of beside it.

Two correctness fixes the dialog flow exposed:

* The library importer + ingestService now treat groupId as a tri-state — undefined means "don't touch the existing group", '' means "explicitly the library root", any other string means a specific group. Previously a falsy check in both layers conflated '' with undefined, so re-importing a deduped book under flatten mode silently kept its stale groupId/groupName from the prior keep-as-groups run, making the book reappear in the old subfolder group instead of moving into the library root. New regression tests in ingest-service.test.ts cover both the empty-string case and the omitted case.

* Imports of arbitrary user paths (e.g. ~/Downloads) now go through a new allow_paths_in_scopes Tauri command that extends both fs_scope and asset_protocol_scope. The dialog plugin only auto-grants fs_scope, so reads through the asset protocol (RemoteFile / convertFileSrc) used to fail with "asset protocol not configured to allow the path". The shim is invoked after every selectFiles / selectDirectory call and once more at the start of runFolderImport so localStorage-restored paths are also covered. Granted scopes persist across restarts via tauri_plugin_persisted_scope.

* fixup(library): harden Import-from-Folder scope grant + RTL/dialog polish

Three review fixes on top of the Import-from-Folder feature:

* lib.rs: refuse to extend asset_protocol_scope for paths not already
  in fs_scope. Without this gate, any frontend code (XSS via book
  content, OPDS HTML, dictionary lookups, or a compromised dependency)
  could call allow_paths_in_scopes with '/' or '~/.ssh' and gain
  persistent read access to arbitrary user files via the asset
  protocol — the grant survives restarts thanks to
  tauri_plugin_persisted_scope. Mirrors the defensive check in
  dir_scanner.rs.

* ImportFromFolderDialog.tsx: migrate from a custom ModalPortal chassis
  to the project's shared <Dialog> primitive so eink mode auto-removes
  shadows, mobile gets the bottom-sheet treatment, RTL direction is
  applied, and focus management is correct.

* ImportFromFolderDialog.tsx: swap directional Tailwind utilities for
  the logical equivalents (text-start, ps-/pe-, rounded-s-, text-end)
  per DESIGN.md §2.8 — Arabic/Hebrew users were getting a mirrored
  number-input row with the KB suffix on the wrong side.

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

* i18n(library): translate Import-from-Folder dialog strings across 33 locales

Translates the 13 new strings introduced with the Import-from-Folder
dialog (folder picker label, format-filter section, size-threshold
input, folder-structure radios, OK button, empty-result toast). All
33 supported locales — including RTL fa/he/ar — are now complete; no
__STRING_NOT_TRANSLATED__ placeholders remain in the catalog.

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

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:51:53 +02:00
loveheaven 05da6bdf43 feat(dictionary): add system dictionary provider for macOS, iOS, and Android (#4219)
Hand selected words off to the platform's native dictionary surface
when the user opts into the new "System Dictionary" entry under
Settings → Languages → Dictionaries. The setting is exclusive: enabling
it disables all other providers (and vice versa) so the in-app lookup
button either always opens the popup or always invokes the OS — no
mixed states.

Per platform:
- macOS: AppKit's -[NSView showDefinitionForAttributedString:atPoint:]
  via a top-level Tauri command in src-tauri/src/macos/system_dictionary.rs.
  Anchored at the selection's bottom-center (CSS pixels mapped into
  NSView coords), so the inline Lookup HUD appears just below the
  highlighted text without raising Dictionary.app to the foreground.
- iOS: UIReferenceLibraryViewController presented as a half-detent
  pageSheet on iPhone (medium → large drag-to-expand) and as a
  formSheet on iPad. Implemented in the native-bridge plugin.
- Android: ACTION_PROCESS_TEXT intent with EXTRA_PROCESS_TEXT_READONLY,
  dispatched without createChooser so users get the standard system
  disambiguation dialog with "Just once / Always" buttons. Reports
  unavailable=true when no app handles the intent so the TS layer can
  silently skip rather than open an empty chooser.

Web/Linux/Windows hide the row entirely. The provider is a sentinel —
the registry filters it out of the popup tab list (it has no in-popup
UI) and the annotator's handleDictionary checks isSystemDictionaryEnabled
to dispatch directly to the native bridge before opening the in-app
DictionaryPopup.
2026-05-19 07:03:52 +02:00
Huang Xin fc71ca9857 feat(android): upgrade in-process WebView on devices stuck on old system WebView (#4142)
Add tauri-plugin-webview-upgrade as a git submodule under
apps/readest-app/src-tauri/plugins/. On Android devices whose system
WebView is locked to an old Chromium build (Huawei phones, Moaan / Onyx
/ Kobo e-ink readers, AOSP forks without Play Store, etc.), the reader
bundle renders as a blank screen. The plugin bootstraps before
Application.onCreate via androidx.startup and redirects the in-process
WebView loader to a recent com.google.android.webview when the user has
one sideloaded — opening the only window in which WebViewUpgrade can
swap the provider, before Tauri/Wry creates any WebView.

Thresholds (minUpgradeMajor / minSupportedMajor) come from
plugins.webview-upgrade in tauri.conf.json and are baked into Kotlin
constants at Gradle build time. Below the supported threshold with no
upgrade option, the plugin shows a localized AlertDialog (15 languages,
English fallback) prompting the user to install Android System WebView.

Plugin source: https://github.com/readest/tauri-plugin-webview-upgrade

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 09:01:13 +02:00
Huang Xin 5190bbcafc fix(macos): don't quit app on Cmd+W, only on Cmd+Q, closes #3927 (#3928)
On macOS, closing the last window (Cmd+W or the red traffic light) was
quitting the app, which is unexpected — the native convention is that
Cmd+W closes the window while the app keeps running in the dock, and
only Cmd+Q quits.

Intercept the window CloseRequested event on macOS, prevent the close,
and hide the window instead so the app remains active. Handle the
Reopen event (fired when the user clicks the dock icon) to restore the
hidden window. Cmd+Q continues to go through applicationWillTerminate:
and exits the app normally.
2026-04-22 14:11:04 +02:00
Huang Xin ff94dc76c6 fix: fixed crash on app start when there is no main window but a reader window running, closes #3897 (#3902) 2026-04-20 16:05:01 +02:00
Yuan Tong 7d852518a3 feat(windows): use overlay scrollbar (#3868)
* feat(windows): use overlay scrollbar

* fix format
2026-04-14 19:03:07 +02:00
Huang Xin 54bc1514df feat(database): add platform-agnostic schema migration system (#3485) 2026-03-06 20:07:26 +08:00
Huang Xin 97cab2d70b feat(database): support turso fts in tauri apps (#3484) 2026-03-05 21:00:15 +01:00
Huang Xin 13588b4a65 chore(testing): add Tauri integration tests and E2E test infrastructure (#3483)
Set up WebDriver-based testing for the Tauri app with two tiers:
- Vitest browser-mode tests (*.tauri.test.ts) running inside the Tauri WebView
  for plugin IPC testing (libsql, smoke tests)
- WDIO E2E tests (*.e2e.ts) for UI-level interaction testing

Key changes:
- Add webdriver Cargo feature gating tauri-plugin-webdriver
- Add runtime capability for remote URLs (webdriver builds only)
- Add vitest.tauri.config.mts and wdio.conf.ts connecting to embedded
  WebDriver server on port 4445
- Add shared tauri-invoke helper for IPC from Vitest iframe context
- Add testing documentation in docs/testing.md
2026-03-05 19:14:01 +01:00
Huang Xin 5273ef75dc feat(database): add database service abstraction with libsql/turso backend (#3472) 2026-03-05 02:38:23 +08:00
scinac 38552a0c2e feat(reader): adding current Time and Battery to Footer (#3306) (#3402)
* added current time to desktop bar

* added time prototype to footer, needs code cleanup and settings toggle

* fixed settings toggle, added translations and code cleanup

* added battery support and moved Statusbar to own Component

* #3306 added 24 hour clock support

* refactored code styling and getting rid of any type in battery hook

* Add battery info for Tauri Apps

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-03-03 17:10:13 +01:00
Huang Xin 2c54e9ae2f feat(updater): in-app updater for AppImage (#3072) 2026-01-25 14:45:37 +01:00
xijibomi-coffee f875ba88ac perf: use native walkdir for recursive imports from directory (#2993)
* fix(perf): replace JS recursion with native Rust walkdir for imports

* fix: implement security scope check in rust recursive scanner

* refactor and format code

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-20 06:22:04 +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 c04f19ffb4 feat: add support for exporting book files in book details dialog, closes #2919 (#2930) 2026-01-12 16:26:32 +01:00
Huang Xin 462ca46fee feat(eink): optimize color and layout for e-ink mode (#2887) 2026-01-08 17:29:33 +01:00
Huang Xin 8a43c58fd4 fix(tts): resolve Edge TTS being blocked in certain regions (#2741)
This should close #2739 and close #1821.
2025-12-19 03:24:51 +01:00
Huang Xin 524de92f5e feat(macOS): add open file global menu for macOS, closes #2692 (#2708) 2025-12-13 17:36:25 +01:00
Huang Xin ba3f060cc4 feat: add support for importing from a directory recursively, closes #179 (#2642) 2025-12-08 07:16:57 +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
AlI 2b6f7b71b0 feat(windows): Add explorer thumbnail registration and installer hook… (#2557)
* feat(windows): Add Windows Explorer thumbnail support for ebooks (closes #2534)

- Implement IThumbnailProvider COM handler for Windows Shell integration
- Support EPUB, MOBI, AZW, AZW3, KF8, FB2, CBZ, CBR formats
- Add cover extraction with Readest icon overlay
- Register thumbnail handler via NSIS installer hooks
- Only show thumbnails when Readest is the default app for the file type

* chore: clean up build script for thumbnail extension

---------

Co-authored-by: chrox <chrox.huang@gmail.com>
2025-11-27 21:31:52 +01:00
Huang Xin ba2aa4bee6 fix(layout): skip applying safe area insets when the iframe content is reloaded (#2550) 2025-11-26 04:10:52 +01:00
Huang Xin 6d5b16ea8b fix(iap): open external app for payment (#2465) 2025-11-18 12:07:43 +01:00
Huang Xin 3fc4c05e50 feat: build portable Windows binaries with app data kept within the executable directory (#2126) 2025-09-26 11:23:55 +02:00
Huang Xin 9aa6a8e6b9 chore: bump tauri to the latest version (#2117) 2025-09-25 09:32:55 +02:00
Huang Xin c9a1557674 refactor: support custom root dir for readest file system (#2092) 2025-09-21 07:59:27 +02:00
Huang Xin 71c2143cba fix: avoid pagination until book view is inited, closes #1983 (#1994) 2025-09-07 14:47:35 +02:00
Huang Xin 70a7f5be19 fix: use transparent background for rounded window on Linux, closes #1982 (#1991) 2025-09-07 10:38:27 +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 267fe58a8c fix: reduce screen flash with different background colors when app starts, closes #1915 (#1922) 2025-08-28 20:02:53 +02:00
Huang Xin df3527ceca fix: chaining file open with OS opening the highest precedence, closes #1622 (#1636) 2025-07-20 17:09:19 +02:00
Huang Xin 6fb7897092 chore: flatpak with custom oauth (#1574) 2025-07-11 11:28:43 +08:00
Huang Xin 5db1847f7e layout: add window border on Windows 10, closes #1551 (#1556) 2025-07-08 17:24:43 +02:00
Huang Xin c3113ade13 chore: fix uninlined format args warning (#1482) 2025-06-27 10:27:21 +02:00
Huang Xin 658969e97a feat: support native tts engine on Android (#1387) 2025-06-12 17:56:14 +02:00
Huang Xin 4779e2638f fix: disable updater for non-appimage app on linux, closes #1129 (#1141) 2025-05-13 12:50:40 +02:00
Huang Xin a424ae8b15 feat: retrieve system fonts on iOS and Android and show font weight variants, closes #949 and closes #557 (#976) 2025-04-26 17:37:04 +02:00
Huang Xin de21182240 fix: don't panic on plugin log initialization error, closes #953 (#961) 2025-04-25 14:42:57 +02:00
Huang Xin ec86d68b80 fix: check xdg-mime before registering deeplink in Linux, closes #475 (#951) 2025-04-24 16:19:14 +08:00
davimed 6260168caa Fix filename with quotes (#897)
* Escape double quotes in filenames

* Format code
2025-04-16 17:34:15 +02:00
Huang Xin 8efad90932 feat: in-app updater for Android (#885) 2025-04-14 14:21:58 +02:00
Huang Xin c807f0ccc8 refactor: OAuth flow is now handled via ASWebAuthenticationSession on macOS (#866) 2025-04-12 12:49:41 +08:00
Huang Xin fe25c3e995 feat: support native sign-in-with-apple on macOS (#856) 2025-04-10 14:49:11 +02:00
Huang Xin 7ccf3d0632 refactor: read local file with fs plugin for Tauri App, closes #553 and closes #489 (#785) 2025-04-01 14:26:26 +02:00
Huang Xin a72c8f2391 refactor: rename plugin safari-auth to native-bridge (#749)
* fix: layout tweaks for mobile

* refactor: rename plugin safari-auth to native-bridge
2025-03-29 07:27:18 +01:00
Huang Xin 0fa53c5f15 fix: should listen single-instance event on Windows to open-with files, closes #724 (#726) 2025-03-27 09:11:03 +01:00
Huang Xin 835ed443a5 fix: style tweaks for Windows (#707) 2025-03-25 18:58:23 +01:00
Huang Xin 8a83389164 fix: hide traffic lights when sidebar is invisible on macOS, closes #297 (#497) 2025-03-06 07:14:23 +01:00
Huang Xin f8ea7fc463 chore: add stub android plugin for safari-auth (#443) 2025-02-24 11:41:26 +01:00