Fix spanning-paragraph reveal pacing (right page no longer consumes full TTS)

A paragraph that overflows the right page onto the next spread revealed its single
right-page line over the entire TTS, then timed out (timeline-reveal-commit-timeout)
and only flipped after the whole narration. Two root causes:

- At activate the reused lookahead segment played a sentence instance whose animation
  word-timings were lost (wordTimings=[], totalDuration=0), so reveal timing fell back
  to an area estimate spanning the full TTS. Snapshot the timings at prepare and restore
  them at activate.
- Reveal duration was distributed by ink area, but just-paginated continuation lines
  have ~0 area, so the one right-page line received the whole duration. Distribute by
  word count (reliable) with area as fallback.

Now the right page reveals only its word share (~2.7s for a 6/55-word line), commits,
and flips while TTS continues; the continuation animates on the next spread. Also
rewrote the right-reveal wait to a single timer + commit/fast-forward listeners with
cleanup, removing the stray timeline-reveal-commit-timeout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 17:13:39 +02:00
parent 6bd1f45362
commit dc2afcf831
3 changed files with 57 additions and 43 deletions
+11 -2
View File
@@ -698,9 +698,18 @@ class BookTextureRendererModule extends BaseModule {
const useWordShare = totalBlockWords > 0 && collectedWords > 0 && collectedWords < totalBlockWords;
const totalDuration = useWordShare ? baseDuration * (collectedWords / totalBlockWords) : baseDuration;
let fallbackDelay = useWordShare && Number.isFinite(wordsBefore) ? baseDuration * (wordsBefore / totalBlockWords) : 0;
// Weight each line by its word count when available, falling back to ink area.
// Word counts are reliable even for just-paginated continuation lines whose rect
// area can be ~0; area-weighting there would hand the whole duration to the one
// line on the current page and stretch it across the entire TTS.
const useWordWeights = collectedWords > 0;
const totalWeight = useWordWeights ? collectedWords : totalArea;
textRegions.forEach((region) => {
const duration = totalArea > 0
? Math.max(1, totalDuration * (Math.max(1, region.timingArea || region.area) / totalArea))
const weight = useWordWeights
? Math.max(0, Number(region.blockWordCount || 0))
: Math.max(1, region.timingArea || region.area);
const duration = totalWeight > 0
? Math.max(1, totalDuration * (weight / totalWeight))
: Math.max(1, totalDuration / Math.max(1, textRegions.length));
timedRegions.push({
...region,