Reader sources are VoiceOfML dataset files only; converted assets come from
buckets. A direct vomebook/Reader-Assets URL is now rejected locally (403)
instead of being forwarded to an upstream 401, and redirect or resolve-cache
targets into it are refused. Remove its redirect scope and its immutable
Cache-Control rule; keep the sidecar object-path grammar (READER_ASSET_PATH_RE).
fix(reader): remove the dead converted-PDF fallback for chapter books
validFallback only accepted PDF objects in the deleted vomebook/Reader-Assets
dataset, while every current sidecar fallback is a CHM-native EPUB in the
reader-assets-v2 bucket, so the redirect to ext=pdf never ran. Remove
ReaderFallback from assetFields and from the backend record and reader-resolve
fields, the fallback reader parameter, validFallback and the chapter-load
redirect; a load failure keeps showing the classified error.
test(reader): sample live formats from the reader-assets-v2 index
The vomebook/Reader-Assets dataset was deleted, so the live format matrix now
picks one sample per format family from the reader-assets-v2 index, mapped the way
the Reader contract's assetFields maps assets, and identifies the run by the index
SHA-256. Families absent from the index are reported as skips.
Also fix two stale test assumptions: the Reader opens the TOC drawer itself since
2026-10-17, and wait_for_function does not await the store Promise, so the restore
check now polls the saved progress before closing the page.
- PDF-pages/image-pages images stay transparent until decoded, then appear whole;
a blank visible page fades in (120 ms), offscreen pages and swaps switch at once.
- Before the primary PDF is ready, opening range reads yield to visible page images
(admitPdfRange: 1 / 4 / 6 in flight) and nearby pages' images start 1 s after the
first one shows; visible demand wakes pending hybrid polls.
- Read-aloud text discovery reads only pages the renderer has extracted or is
extracting. The previous probe extracted up to 12 unrendered pages, which for a
scanned PDF downloads their full page images ahead of the visible page.
- Tests: scroll-scheduling/range-transport cases, hybrid reveal/lookahead cases, a
read-aloud probe regression, the Pages full-search rule and a drawer-transition
race in the dark-theme handoff test.
fix(reader): keep the background speech bar above the next reader
- Range-transport PDFs up to 1 GiB (complete bodies keep 512 MiB) on client and proxy
- A failed primary leaves hybrid mode; image pages use a priority-ordered pool with preemption
- Visible-page image fallback waits for this book's measured PDF draw time
- Image and canvas geometry changes preserve the reading anchor; first geometry seeds shells
- PDF outline fills a missing manifest TOC after the first page
fix(reader): narrow path hit area and keep speech across reader/search handoff
fix(reader): include OCR text layers in pdf-pages bookmark excerpts
opencode
1721a47
The bookmark excerpt branch returned an empty string for the pdf-pages mode
before reading the page shell. Image-page PDFs with an OCR text layer already
populate _bookmarkTextItems, so their bookmark preview was blank. Treat
pdf-pages like pdf, while still returning empty when a page has no OCR text.
fix(tts): hand off at sentence ends too; ramp clips by 1.45x of the previous clip
fix(reader): rename the speech title helper to satisfy the state guard
opencode
c368c6d
reader-speech.mjs declared a module-level 'title' helper. The architecture
contract forbids top-level title state in the composed Reader source, so the
helper is now readerSpeechTitle. Behavior is unchanged.
test(reader): make the panel toggle test independent of the TOC hint
opencode
8bd1a42
A TOC book opens the drawer as an opening hint, so the bare '#history' click in
this case toggled the already-open drawer closed. Normalize to closed first and
then assert the click-to-open path, matching the existing idempotent opener.
Any reader-initiated scroll (wheel/touch/scroll keys/pointer navigation) stops auto-follow until 回到播放位置 or a new start, so sentences no longer pull the page back. After 回到播放位置 the button reads 回到原位置 until the playing sentence leaves the view. 听书 starts at the first line in view (loading that page/section first). The button appears only once text is found near the reading position (scanned PDFs, comics). Closing a Reader that is reading keeps it hidden with a small player (返回阅读/暂停/停止); reopening the same book reuses it, opening another book releases it. The shared player keeps list numbers (1. / (3).) with their item.
test(search): pin exact-mode filtering at the API boundary
opencode
49b8c28
The exact branch had already lost its record-filter stage once. Exercise the
HTTP route with exact=true plus an extension, prefix folder, mixed folder and
size filter so a future rewrite cannot silently drop them again.
fix(search): apply record filters in exact mode
opencode
23f3aa1
Exact search returned its literal matches before apply_filters, so the default
exact UI silently ignored the extension tree, folder filters, size filters and
mixed folder selections. Restore the filter stage in the exact branch, and bump
the semantics version so cached exact orders are rebuilt.
This is shared by Hugging Face Search and the GitHub Pages API fallback.
2 days ago
fix(reader): do not pull the reader back after scrolling away from speech
Auto-follow is now decided by the reader's own settled scrolling: once the sentence being read is scrolled out of view, sentence changes no longer scroll back; following resumes via 回到播放位置 or by scrolling back to it. Wheel/touch/key input overrides one of our smooth scrolls in progress. Follow and 回到播放位置 scroll the sentence itself instead of its parent block (a TXT block holds 32 KiB, so the old scroll landed far from the sentence). Adds a regression test.
fix(reader): highlight and follow speech on unrendered PDF pages
When the clip being read is on a page or section that is not rendered yet, the adapter navigates there, waits for its text layer and the clip is highlighted once. Auto-follow no longer turns itself off after its own smooth scroll, and HTML frame offsets are included. Adds a sparse 30-page PDF regression test. Previously: playback starts once one batch plus its planning context (1,800 chars) is loaded instead of the full 2,400-char lookahead; slow searches through pages without text show page progress; stop cancels a pending text load. PDF pages already rendered use their text layer.
fix(reader): start reading sooner on sparse/scanned PDFs
Playback starts once one batch plus its planning context (1,800 chars) is loaded instead of the full 2,400-char lookahead; slow searches through pages without text show page progress; stop cancels a pending text load. PDF pages already rendered use their text layer.
feat(reader): native reading for every text-bearing Reader format
Adds the shared tts-client.js player (Hitokoto CPU TTS) to the Reader. reader-speech.mjs supplies a growing-text adapter per format: TXT/Markdown/HTML rendered text, Foliate EPUB/MOBI/FB2 sections, chapter bundles, native PDF text layers and pdf-pages/v3 OCR text layers. Highlights use the CSS Custom Highlight API in the owning realm; DOM text is never modified. Images/media/pages without text get no button. CSP adds the Hitokoto origin to connect-src and Hitokoto plus blob: to media-src. Inventory, components and release gate list the new files on both sites.
fix(search): multi-token AND across tokenizer boundaries, exact term splitting
vomebook
ba5a2f5
Ordinary multi-token queries now union each term's jieba/fuzzy postings with
a literal substring scan before intersecting, so matches that cross a jieba
token boundary are retained. Exact mode splits on whitespace and matches each
term independently; punctuation-bearing terms stay literal, preserving the
documented "手。机" and wildcard behaviour.
The bootstrap payload now reports the same string generation as search and
initial payloads, GET search routes return 405, unknown API paths return a
JSON 404, and the SVG manifest icon declares sizes "any".
Bump HF search semantics to v4 so cached orders and snapshots are invalidated.