refactor(teleprompter): drop code that was not earning its keep

Review pass over the view.

Removed: estimateWordsPerLine and linesPerMinuteToWordsPerMinute, written for
a words-per-minute readout that was never built; play and pause on the
controller, which nothing outside the hook called; and three constants that
were exported but never imported.

The tick function was being reassigned to a ref on every render, which is a
side effect during render. It only ever touched refs and state setters, so it
is now a stable callback and the ref is gone.

The synthetic contentKey string is replaced by the memoised blocks array it
was standing in for. It was also a dependency of the ResizeObserver effect,
which tore the observer down and rebuilt it for no gain: the observer already
covers every reflow that changes the document.

ScriptBlock was memoised but never actually memoising, because the parent
built its ref callback inline and handed it a new identity every render. That
also churned the follow map, unregistering and re-registering every block. The
id is now bound inside the block against a stable callback.

Tests: dropped six that asserted arithmetic identities or wrapped clamps
rather than behaviour, and added three for branches that were untested,
group titles across and back into a group, and a heading with no cue.
The e2e rewind assertion waited 600ms for an eased scroll that needs about
900ms from a nudge and a second from the bottom of a long script; it now
polls. The navigation menu assertion raced app hydration and now waits for
the view, as the existing navigation tests do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cb8RVPNQ2ETPJxdy4b8CHf
This commit is contained in:
Claude
2026-08-13 16:42:07 +00:00
parent 15dac35748
commit 323b30ea68
9 changed files with 140 additions and 167 deletions
@@ -11,8 +11,8 @@ export const MAX_SPEED = 200;
export const DEFAULT_SPEED = 30;
/** font size multiplier applied on top of the configured size by the +/- keys */
export const MIN_FONT_SCALE = 0.4;
export const MAX_FONT_SCALE = 3;
const MIN_FONT_SCALE = 0.4;
const MAX_FONT_SCALE = 3;
export const FONT_SCALE_STEP = 0.1;
/**
@@ -26,7 +26,7 @@ export const MAX_FRAME_DELTA_MS = 100;
const CATCH_UP_RATE = 8;
/** below this distance an eased jump is considered arrived */
export const CATCH_UP_EPSILON = 0.5;
const CATCH_UP_EPSILON = 0.5;
export function clamp(value: number, min: number, max: number): number {
if (Number.isNaN(value)) return min;
@@ -50,11 +50,6 @@ export function linesPerMinuteToPxPerSecond(linesPerMinute: number, lineHeightPx
return (linesPerMinute / 60) * lineHeightPx;
}
/** Estimates a words per minute read rate, for display only */
export function linesPerMinuteToWordsPerMinute(linesPerMinute: number, wordsPerLine: number): number {
return Math.round(linesPerMinute * wordsPerLine);
}
/**
* Clamps a raw frame delta and converts it to seconds.
* @param deltaMs milliseconds since the previous frame