fix(views): make boolean view params work, and retune the prompter

The params editor could not represent a boolean which defaults to true.
Two bugs stacked:

- ParamInput fell back with `isStringBoolean(param) ?? defaultValue`, but
  isStringBoolean answers false for an absent param rather than nothing, so
  the ?? never fired and every switch opened off whatever its option said.
- An unchecked checkbox is absent from the form data rather than present and
  false, so switching one off wrote no param and the parser fell back to the
  default the user was trying to leave.

Either one alone is invisible while every boolean defaults to false, which is
why this surfaced with the teleprompter. Together they made the whole panel
look inert: the switch showed off, the view showed on, and Apply did nothing.

Prompter changes from the review:

- Speed is calibrated against the reading rate rather than picked for feel.
  30 lines per minute was about 300 words per minute, roughly twice a
  broadcast read; the default is now 12, measured at 129 wpm on the default
  column. The ceiling comes down from 200 to 40 so the arrows stay useful.
- Smaller, denser defaults: 40px over 1.3 line height in an 80% column, which
  is 21 lines on a 1080p screen where the old defaults gave 11.
- The reading line is a marker one line tall in the gutter beside the text,
  replacing the rule across the words and the pair of margin arrows. The
  option is a boolean now that there is one style rather than three.
- Space always drives the transport. It was deferring to whichever control
  had focus, so a prompter stopped responding to the pedal after anyone
  touched a button; the overlay drops focus after a pointer press instead.
  Enter still activates a focused control, so the overlay stays keyboard
  operable.
- The help dialog is laid out as the rundown shortcuts panel is, down to the
  Kbd keycaps and the grouping, and no longer explains foot pedals.

Headings stay on the same left rail as the script rather than centred: they
are signposts for the operator, and a second alignment would give the eye
something new to find at every segment change.

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-18 12:00:25 +00:00
parent 8a31cc5018
commit 6589bcceee
17 changed files with 387 additions and 175 deletions
@@ -5,14 +5,23 @@
* The hook that owns the requestAnimationFrame loop is the only caller.
*/
/** lines per minute */
export const MIN_SPEED = 2;
export const MAX_SPEED = 200;
export const DEFAULT_SPEED = 30;
/**
* Lines per minute.
*
* The default is calibrated against the reading rate rather than picked for
* feel: broadcast presenters read at 140-160 words per minute and conference
* talent slower still, and at the default column width a line carries a dozen
* or so words. Twelve lines per minute lands in that band. The ceiling is set
* where the text stops being readable at all, not at the fastest the loop can
* physically scroll, so the arrow keys stay useful across their whole range.
*/
export const MIN_SPEED = 1;
export const MAX_SPEED = 40;
export const DEFAULT_SPEED = 12;
/** how much one speed adjustment moves, shared by the keymap and the overlay */
export const SPEED_STEP = 2;
export const SPEED_STEP_COARSE = 10;
export const SPEED_STEP = 1;
export const SPEED_STEP_COARSE = 5;
/** font size multiplier applied on top of the configured size by the +/- keys */
const MIN_FONT_SCALE = 0.4;