Skip to content

281 gap skipping fixes#285

Merged
maboa merged 4 commits into
mainfrom
281-gap-skipping-fixes
May 23, 2026
Merged

281 gap skipping fixes#285
maboa merged 4 commits into
mainfrom
281-gap-skipping-fixes

Conversation

@maboa
Copy link
Copy Markdown
Member

@maboa maboa commented May 23, 2026

Summary

Four small fixes around gap-skip so the feature feels live instead of stale. Addresses #281 and #284

1. Carry settings across to new transcripts (483ef62)

registerStrikeThrus (which rebuilds audioDataArray and re-attaches skip listeners against the
current transcript and gap settings) was only bound to hyperaudioTranscriptLoaded, which only the
localStorage load path dispatches. Deepgram, Whisper, and JSON/SRT/VTT import dispatch hyperaudioInit
instead, so the skip pipeline kept pointing at the previous transcript's word data.

Fix: bind registerStrikeThrus to hyperaudioInit as well. The two events are disjoint, so no
double-fire.

2. Labeled-units format for the savings line (28e3507)

"Playback shortened by 3725.4 seconds" doesn't scale well past a minute. Sub-minute savings still render
as 12.3 seconds (decimal preserved), but from 60s upward it switches to labeled units that drop zero
components: 1m 30s, 1h 2m 5s, 1h.

3. Recalculate on transcript edits and new transcripts (a2c43b6)

  • Call updateGapSavingsMessage() from registerStrikeThrus so the "Playback shortened by X" line
    reflects the new transcript on every rebuild (transcribe / import / localStorage load / strikethrough
    toggle).
  • Add a debounced input listener delegated on document that calls registerStrikeThrus after 150ms
    of edit inactivity on #hypertranscript. Deleting a word turns its time slot into a gap, which should
    be reflected in both the skip pipeline and the savings message. Delegated on document so the listener
    survives transcript element replacement via restoreTranscript.

4. Keep the karaoke highlight in sync (4983b0e)

The hyperaudio-lite library tracks the active word via a cached wordArr and a chained setTimeout
that polls currentTime, but it only natively listens for play/pause — not seeked or DOM
mutations. That leaves two windows where the highlight freezes:

  • After edits: the polling chain crashes silently if it touches a now-detached span
    (word.n.parentNode is null, .classList throws inside the setTimeout callback), and nothing
    restarts it.
  • After a gap skip: our code calls myPlayer.currentTime = nextStart + 0.05 directly; the library
    has no idea we jumped and waits for its stale timer to fire seconds later before relocating the active
    word.

Fix: a refreshHyperaudioInstance() helper rebuilds wordArr and parentElements, updates the visual
state, and kicks checkPlayHead to restart the polling chain. Called from registerStrikeThrus so
edits and new transcripts both refresh the library's caches. Also kick checkPlayHead from
checkStrikeThrus right after a gap skip so the highlight resyncs to the post-skip word immediately
rather than waiting for the stale timer.

Test plan

  • Enable gap-skip with custom threshold/buffer. Transcribe a new file via Deepgram → confirm skips
    fire and the savings line updates.
  • Same flow via Whisper.
  • Same flow via JSON/SRT/VTT import.
  • Load a saved session from localStorage → confirm skips still work and the savings line is correct.
  • Delete a word in the transcript while gap-skip is on → confirm within ~150ms the savings line
    updates and the new gap (the deleted word's slot) is skipped on playback.
  • Confirm karaoke highlight follows playback continuously across deletions (no freeze on the last
    edited word).
  • Confirm karaoke highlight follows playback continuously across gap skips (no freeze at the start
    of the gap).
  • Confirm savings line shows X.X seconds under 60s, Xm Ys between 1–59 min, Xh Ym Zs for ≥ 1h.

maboa added 4 commits May 23, 2026 11:07
registerStrikeThrus rebuilds audioDataArray and re-attaches skip
listeners against the current transcript with the current
removeGapsEnabled / gapThreshold / gapBuffer values, but it was
only bound to hyperaudioTranscriptLoaded — which only the
localStorage load path dispatches. Transcribe (Deepgram/Whisper)
and JSON/SRT/VTT import dispatch hyperaudioInit instead, so the
skip pipeline kept pointing at the previous transcript's word
data.

Bind registerStrikeThrus to hyperaudioInit as well. The two
events are disjoint, so no double-fire risk.
Sub-minute savings still render as "X.X seconds" so the decimal
precision is visible at small durations. From 60 seconds upward,
switch to labeled units (1m 30s, 1h 2m 5s) which reads naturally
inline and drops zero components rather than forcing a leading
"00:" the way HH:MM:SS would.
Two related changes so the savings message and skip pipeline
stay in sync with the actual transcript content:

- Call updateGapSavingsMessage() from registerStrikeThrus so the
  "Playback shortened by X" message reflects the new transcript
  on every rebuild (transcribe / import / localStorage load /
  strikethrough toggle).
- Add a debounced input listener delegated on document that runs
  registerStrikeThrus after 150ms of edit inactivity on
  #hypertranscript. Deleting a word turns its time slot into a
  gap, which should be reflected in both the skip pipeline and
  the savings message. Delegated on document so the listener
  survives transcript element replacement via restoreTranscript.
The hyperaudio-lite library tracks the active word via a cached
wordArr and a chained setTimeout that polls currentTime, but it
only natively listens for play/pause — not seeked or DOM changes.
That leaves two windows where the highlight freezes:

- After the user edits the transcript, the polling chain crashes
  silently if it touches a now-detached span (word.n.parentNode
  is null, .classList throws inside the setTimeout callback).
- After our gap-skip code seeks past a silent section, the
  library is unaware of the jump and waits for its stale timer
  to fire seconds later before re-locating the active word.

Add a refreshHyperaudioInstance() helper that rebuilds wordArr
and parentElements, updates the visual state, then kicks
checkPlayHead to restart the polling chain. Call it from
registerStrikeThrus so edits and new transcripts both refresh
the library's caches.

Also kick checkPlayHead from checkStrikeThrus right after a gap
skip so the highlight resyncs immediately to the post-skip word.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant