Bump foliate-js to include readest/foliate-js#22. The scrolled-mode scroll container (#container) lost its compositing layer in the GPU-hint cleanup, so on Windows' always-on scrollbars the scrollbar appeared on open then vanished once adjacent-section preloading changed the content height. Restoring transform: translateZ(0) on the scrolled #container keeps the scrollbar composited so it repaints across content-size changes. Closes #4470 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
## Platform Compat
|
||||
- [Android NativeFile vs RemoteFile I/O](android-nativefile-remotefile-io.md) — why NativeFile is slow (4-IPC/chunk + bridge serialization, tauri#9190); RemoteFile CANNOT replace it on Android (asset-protocol Range broken: start>0 → "Failed to fetch", start-0 capped at 1,024,000; plain no-Range fetch returns full file at 281 MB/s); measured 44/100/281 MB/s; speedups = handle-reuse (2.3×), whole-file asset loader (6.3×), or fix wry upstream. Verified live via CDP.
|
||||
- [Window-state sanitizer (#4398)](window-state-sanitize-4398.md) — Windows launch crash (WebView2 0x80070057) from invalid `.window-state.json` (`-32000` minimized sentinel / `0×0`); our plugin already has upstream #253 fix so bad files are stale; defense-in-depth `window-state-sanitizer` plugin registered BEFORE window-state (plugin init = registration order); coord threshold `-16000` (~halfway to the -32000 sentinel; real desktops sit a few thousand px off origin) keeps multi-monitor negatives
|
||||
- [Android Open-with intent flow (#4521)](android-open-with-intent-flow.md) — "Open with"/"Send to" pipeline: `NativeBridgePlugin.kt::handleIntent` → `shared-intent` → `useAppUrlIngress` → `useOpenWithBooks` (VIEW=transient→reader, SEND=library+upload). Telegram fails where file-manager works on TWO axes: cold-start delivery (fixed by #4527, on dev NOT released v0.11.4) + foreign-private-file read (Telegram FileProvider non-persistable grant vs shared-storage FUSE real-path). adb MediaStore VIEW repro tests pipeline but CANNOT reproduce the read axis (MANAGE_EXTERNAL_STORAGE bypasses grant)
|
||||
|
||||
## Feature Notes
|
||||
- [OPDS Firefox strict-XML parse (#4479)](opds-firefox-strict-xml-4479.md) — MEK feed has junk after `</feed>`; Firefox DOMParser → `<parsererror>` (silent back-nav), Chrome lenient; `parseOPDSXML` slices root start→last close tag; jsdom mirrors Firefox; wired into page.tsx + validateOPDSURL + feedChecker (latter also #4181 `looksLikeXMLContent` swap)
|
||||
@@ -63,6 +64,7 @@
|
||||
- [ProgressBar focus-ring line (#4397)](progressbar-focus-ring-4397.md) — decorative `.progressinfo` footer was `tabIndex={-1}` → Android long-press focused it → stray content-width focus-ring line at the bottom every page; fix = drop tabIndex (role='presentation' must not be focusable); ffmpeg-the-video debugging + live-browser `:focus-visible` confirmation
|
||||
- [Table dark-mode tint regression (#4419)](table-dark-mode-tint-4419.md) — `blockquote, table *` color-mix tint in `getColorStyles` must stay gated on `overrideColor` (gate added #2377, removed #4055, re-broke → #4419); safe now that #4392 light-bg rewriters handle #4028 zebra legibility; SAME rule paints vertical-TOC `.space`/▉ spacer cells (▉ U+2589 = blank glyph, contours=0) → "spacing changes" symptom; both fixed by the gate
|
||||
- [Double-click-drag turns page (#4524)](dblclick-drag-pageturn-4524.md) — web double-click+drag selection also turned the page; 1st click's deferred single-click (250ms) fires mid-drag while 2nd-click button held; fix = `isMouseDown` flag in `iframeEventHandlers.ts` gates the deferred `postSingleClick`; synthetic-repro gotchas (shadow-DOM iframe walk, chained-repro timing pollution, reload to re-bind listeners)
|
||||
- [RSVP font face/family (#4519)](rsvp-font-settings-4519.md) — RSVP word was hardcoded `font-mono`; now mirrors the reader font via `getBaseFontFamily(viewSettings)` (new export in `style.ts`, shares `buildFontFamilyLists` with `getFontStyles`). Overlay renders in the TOP document (portal to body) where custom + basic Google fonts are mounted; known gap = built-in CJK web fonts only in top doc when `isCJKEnv()`
|
||||
|
||||
## Library Fixes
|
||||
- [Tauri menu append race (#4389)](tauri-menu-append-race-4389.md) — un-awaited `Menu.append()` (async IPC) in `BookshelfItem.tsx` → context-menu items shuffle order every open (native only, invisible in jsdom); fix = single `await Menu.new({ items })` of ordered `MenuItemOptions`; order/inclusion extracted to pure `getBookContextMenuItemIds` for unit testing
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: android-open-with-intent-flow
|
||||
description: "Android \"Open with/Send to Readest\" intent pipeline + why Telegram/cloud-app opens differ from file-manager opens (issue"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 73ee84b7-27a0-4232-981c-de8235a15f07
|
||||
---
|
||||
|
||||
Android "Open with Readest" / "Send to Readest" file-intent pipeline and the #4521 ("open book from Telegram") diagnosis. Confirmed fixed by #4527.
|
||||
|
||||
**Pipeline (ACTION_VIEW = "Open with", ACTION_SEND = "Share"):**
|
||||
- `NativeBridgePlugin.kt::handleIntent` is the real handler (NOT `MainActivity.kt` — its ACTION_SEND branch is legacy/redundant). → `emitSharedIntent("VIEW"|"SEND", uris)` → JS `useAppUrlIngress` `shared-intent` plugin listener → `app-incoming-url` event → `useOpenWithBooks`.
|
||||
- VIEW → `openTransient` → straight to reader (ephemeral book, `deletedAt` set, `filePath` = the content:// URI, no library write/upload). SEND → `window.OPEN_WITH_FILES` → `library/page.tsx::processOpenWithFiles` (full ingest + force cloud upload on mobile).
|
||||
- content:// read: `nativeAppService.openFile` → if URI contains `com.android.externalstorage` → direct `NativeFile` (real path); else `copyURIToPath` → `contentResolver.openInputStream` → copy to Cache → `NativeFile`. `basename` here is LEXICAL (`@tauri-apps/api/path`), not a ContentResolver `DISPLAY_NAME` query — but EPUB format is sniffed by zip magic (`document.ts isZip()`), so an extension-less content URI still opens.
|
||||
- The Tauri deep-link plugin's `getCurrent()`/`onOpenUrl` only fire for configured deep-link domains (`https://web.readest.com`, `readest:`); `content://`/`file://` VIEW intents are filtered out by `DeepLinkPlugin.isDeepLink()`, so file opens flow ONLY through the native `shared-intent` channel, never the deep-link plugin.
|
||||
|
||||
**#4521 root cause (Telegram open fails, file-manager works) — TWO independent axes:**
|
||||
1. **Cold-start delivery.** On cold launch the ACTION_VIEW intent reaches `handleIntent` before the JS `shared-intent` listener registers; upstream `Plugin.trigger()` drops events with no listener. **#4527** added queue+replay (`emitOrQueue`/`pendingEvents` + `registerListener` override) to fix it. **#4527 is on `dev` but NOT in released v0.11.4** (v0.11.4 has #4407 only) — the reporter's likely cause. Logs show `Queued shared-intent payload (no listener yet)` then `Replaying 1 queued event(s) after registerListener`.
|
||||
2. **Foreign-private-file read.** File-manager/MediaStore opens point at SHARED storage → Readest reads via real path / MediaProvider FUSE (it holds `MANAGE_EXTERNAL_STORAGE`), so the URI grant is irrelevant. Telegram/Gmail/Drive serve an APP-PRIVATE file via their own FileProvider with a TEMPORARY, non-persistable grant (`takePersistableUriPermission` throws → caught) → readable only in-session via `openInputStream`; the transient book's `content://` filePath then breaks on later reopen once the grant dies.
|
||||
|
||||
**Verification gotcha (adb, no Telegram):** an adb MediaStore content-URI VIEW intent
|
||||
`adb shell am start -a android.intent.action.VIEW -d content://media/external/file/<id> -t application/epub+zip --grant-read-uri-permission -n com.bilingify.readest/.MainActivity`
|
||||
tests the pipeline (Axis 1) but CANNOT reproduce Axis 2 — shared-storage reads via FUSE bypass the grant, so it always succeeds. To reproduce the real failure use a foreign FileProvider source (Gmail/Drive/Outlook attachment "Open with Readest") or a tiny helper APK with its own FileProvider. Get the MediaStore `_id` via `adb shell content query --uri content://media/external/file --projection _id:_data | grep <name>` (MIUI `--where` chokes on `/storage/...` path tokens). Watch `adb logcat | grep -iE "NativeBridgePlugin|Open with FUSE|Failed to (open|import)|Queued|Replaying"`.
|
||||
|
||||
Critical files: `src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt`, `src/hooks/useAppUrlIngress.ts`, `src/hooks/useOpenWithBooks.ts`, `src/services/nativeAppService.ts::openFile`, `src/services/bookContent.ts::resolveBookContentSource`.
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: rsvp-font-settings-4519
|
||||
description: "RSVP word uses the reader's font face/family via getBaseFontFamily; overlay renders in the top document where fonts are mounted"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 18ca3271-7e08-4b4f-bf33-a135370d5844
|
||||
---
|
||||
|
||||
#4519 — RSVP (speed-reading) word display now mirrors the reader's font face/family
|
||||
settings instead of a hardcoded `font-mono`.
|
||||
|
||||
Key facts (non-obvious):
|
||||
- The RSVP overlay (`RSVPOverlay.tsx`, rendered via `createPortal` to
|
||||
`document.body` from `RSVPControl.tsx`) lives in the **top document**, NOT an
|
||||
iframe. So it cannot read the iframe's `--serif`/`--sans-serif` CSS variables
|
||||
that `getFontStyles` sets. Instead apply a resolved `font-family` string.
|
||||
- `getBaseFontFamily(viewSettings)` in `src/utils/style.ts` returns the resolved
|
||||
body font chain (serif or sans-serif per `defaultFont`, including the chosen
|
||||
typeface, `defaultCJKFont`, and any custom font selected as serif/sans). It
|
||||
reuses the shared `buildFontFamilyLists` helper extracted from `getFontStyles`.
|
||||
- This works because fonts are already mounted in the **top document**:
|
||||
`FoliateViewer.tsx` calls `mountCustomFont(document, font)` for user-imported
|
||||
fonts, and `Reader.tsx` calls `mountAdditionalFonts(document)` for the basic
|
||||
Google fonts.
|
||||
- KNOWN GAP: `Reader.tsx` calls `mountAdditionalFonts(document)` WITHOUT the
|
||||
book-language CJK flag, so the built-in CJK *web* fonts (LXGW WenKai, Noto
|
||||
Serif JP, etc.) only mount in the top document when `isCJKEnv()` is true. A
|
||||
non-CJK-env user reading a CJK book may see a system CJK fallback in the RSVP
|
||||
word rather than the exact web font. Latin fonts and user-imported custom
|
||||
fonts are unaffected (always mounted). The iframe loads CJK fonts per-doc via
|
||||
`mountAdditionalFonts(detail.doc, isCJKLang(...))` regardless of env.
|
||||
- RSVP keeps its own font *size* control (localStorage `readest_rsvp_fontsize`);
|
||||
only face/family were wired up. Font weight is intentionally left as the RSVP
|
||||
design (word `font-medium`, ORP char `font-bold`).
|
||||
|
||||
Related: [[iframe-cross-realm-instanceof]] (top-realm vs iframe-realm distinction).
|
||||
+1
-1
Submodule packages/foliate-js updated: dbe70677c6...a281b5dbcb
Reference in New Issue
Block a user