Skip to content

setPlayHead runs updateTranscriptVisualState with NaN before its own guard; a malformed data-m permanently poisons the binary search #262

Description

@maboa

Summary

(As of v2.6.2.) Two vectors, one mechanism — the binary search's behavior on NaN.

1. Click/double-click on non-word content

setPlayHead computes the time and updates visuals before its NaN guard (hyperaudio-lite.js:727-739):

const timeSecs = parseInt(target.dataset.m) / 1000;
this.updateTranscriptVisualState(timeSecs);   // runs with NaN
...
if (!isNaN(timeSecs)) {                        // guard comes too late

When the (dbl)click lands on a <p>/gap rather than a word span (routine with doubleClick: true on a contenteditable transcript), timeSecs is NaN. In the binary search (:969-985), NaN < 0 and NaN > 0 are both false, so the first midpoint probe takes the exact-match branch (index = guessIndex + 1; break): the read/unread boundary jumps to the middle of the transcript and prevWordIndex is poisoned. While paused (no ticks running) the corruption persists on screen until the next interaction.

2. Malformed data-m in the transcript

A single bad data-m (e.g. data-m="" from an importer; parseInt("") → NaN cached in createWordArray, :635) permanently poisons the search — any probe path touching that element breaks at an arbitrary index, on every tick.

Fix

Early-return in setPlayHead when isNaN(timeSecs) before calling updateTranscriptVisualState; skip/drop entries with isNaN(m) in createWordArray (or guard the exact-match branch with difference === 0).

Found reviewing the copy vendored in hyperaudio-lite-editor / GliderMac (2026-07-11).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions