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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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