readest/apps/readest-app
Huang Xin e82f0c0f44
fix(reader): highlight long and nested quoted dialogue (#6814)
* fix(reader): highlight quoted dialogue longer than 500 characters

Dialogue highlighting capped every paired quote at 500 characters, so a
longer speech in “”, „“, "", «», 「」 or 『』 was left untinted. Each
pair already stops at its next opening mark (ASCII "" at the end of the
block), so an unclosed quote can't swallow the next one without the cap;
it is removed for those pairs. ‘’ keeps its 300-character cap: a
mis-curled apostrophe (‘80s) opens a quote that nothing closes.

Verified: "wraps a quote longer than 500 characters" fails on main with
`expected [ '“', '"' ] to deeply equal [ …(6) ]` and passes with the
fix, as does the rest of dialogue-highlight.test.ts (38 tests). In the
web build (pnpm dev-web, Chromium), an EPUB with the reporter's text
(a 619-character “” quote and a « » one) had only the short quote
highlighted on main; with the fix all three are.

Reported by noideaforeal on GitHub (#6811).

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

* fix(reader): highlight nested quotes within a paragraph and cap quotes at 2048 characters (owner: allow nesting only inside one paragraph, with a safe belt like 2048 chars)

A speech that quotes something in the same marks (“… my spiritual
“children”; in my little dog, Lizoo.”) only had the inner quote
highlighted: each “ stops the scan for the previous one. A second
pattern now matches an outer “”, „“ or «» quote that holds complete
inner pairs, and the existing overlap pass keeps the outer region. It
doesn't cross a paragraph break, where an unclosed “ usually opens the
next paragraph of a long speech.

Every quote is also capped at 2048 characters, in case a malformed
book pairs two marks pages apart. A rejected match is rescanned from
the next character, so quotes inside it are still found.

Verified: in dialogue-highlight.test.ts, "wraps a quote holding inner
quotes in the same marks" fails on main and on the previous commit
(expected [ '“children”', '« le chef »' ] to deeply equal [ …(2) ]);
"skips a quote longer than 2048 characters but keeps quotes inside
it" fails on the previous, uncapped commit (expected [ Array(1) ] to
deeply equal [ '«oui»' ]); "does not nest quotes across paragraphs"
guards the paragraph limit. 41/41 pass. pnpm lint is clean. The
reporter's sample EPUB in the web build (pnpm dev-web, Chromium):
main highlights only “children”; with the fix both the 621-character
quote and the whole nested speech are highlighted.

Reported by noideaforeal on GitHub (#6811).

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:02:07 +08:00
..
.agents
.claude chore: declare tsx for the worktree scripts and clean up merged worktrees in the triage loop (#6807) 2026-10-15 02:06:54 +08:00
bench
data/wordlens
docs chore(agent): maintenance loops with a Reply Desk, lessons and acceptance rules (#6796) 2026-10-14 16:00:36 +02:00
e2e
extensions fix(windows): stop the PDF preview pane from freezing Explorer, closes #6590 (#6594) 2026-10-03 13:36:14 +02:00
public feat(reader): add a toggle to hide PDF page thumbnails in the TOC, closes #6650 (#6756) 2026-10-13 16:31:02 +02:00
scripts build(android): ship per-ABI APKs (arm64, armv7, legacy) instead of universal (#6772) 2026-10-13 19:33:05 +02:00
src fix(reader): highlight long and nested quoted dialogue (#6814) 2026-10-15 16:02:07 +08:00
src-tauri fix: iOS reader polish for drawing dock, short sheets, bookshelf header and read aloud (#6778) 2026-10-14 00:25:00 +02:00
workers
.env chore(auth): point clients at the Supabase project URL (#6729) 2026-10-12 13:12:32 +02:00
.env.local.example
.env.tauri
.env.tauri.example
.env.web
.env.web.example docs(tts): drop the RunPod settings from the web env example (#6744) 2026-10-13 10:35:30 +02:00
.gitignore
AGENTS.md chore(agent): maintenance loops with a Reply Desk, lessons and acceptance rules (#6796) 2026-10-14 16:00:36 +02:00
CLAUDE.md
components.json
DESIGN.md
i18n-langs.json
i18next-scanner.config.cjs
LESSONS.md chore(agent): maintenance loops with a Reply Desk, lessons and acceptance rules (#6796) 2026-10-14 16:00:36 +02:00
next.config.mjs fix(build): keep music-metadata out of the web server bundle (#6699) 2026-10-10 06:32:09 +02:00
open-next.config.ts
package.json fix(deps): resolve open Dependabot alerts (#6813) 2026-10-15 07:59:43 +02:00
pdfjs.d.ts
playwright.config.ts
postcss.config.mjs
PRODUCT.md
raw-loader.d.ts
release-notes.json release: version 0.12.16 (#6779) 2026-10-14 00:33:31 +02:00
skills-lock.json
TODOS.md
tsconfig.json
vite.config.ts
vitest.android.config.mts
vitest.browser.config.mts
vitest.config.mts
vitest.env.mts
vitest.setup.ts feat(tts): custom OpenAI-compatible TTS services (#6746) 2026-10-13 14:33:28 +02:00
vitest.tauri.config.mts
vitest.tauri.setup.ts
wdio.conf.ts
worker.ts feat(tts): move Readest Voice into its own service (#6732) 2026-10-12 15:10:19 +02:00
wrangler.toml feat(tts): move Readest Voice into its own service (#6732) 2026-10-12 15:10:19 +02:00