Skip to content

Fix idle pause during LISTEN audio gaps (followup to #2893)#2907

Merged
lifeart merged 1 commit into
masterfrom
fix-audio-idle-during-listen
May 28, 2026
Merged

Fix idle pause during LISTEN audio gaps (followup to #2893)#2907
lifeart merged 1 commit into
masterfrom
fix-audio-idle-during-listen

Conversation

@lifeart

@lifeart lifeart commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Followup to UI: fix pause in task audio playing (with glm-5.1 model help) #2893. That PR's maybeIdlePause() guard only checks audio.isPlaying at the instant IdleJs fires, but in listenModeTask isPlaying is false during the 1500 ms gap between clips and during setAudioElements decoding. With idleTimeout: 10000, after ~3 clips onIdle lands in the gap, pauses, and listenModeTask deadlocks in waitWhilePaused() (IdleJs does not reschedule onIdle without a user input event).
  • Add studyingTimer.resetIdle() that rearms the watcher via stop().start(), called from audio.playAudio() — the single funnel all three audio entry points go through (startPlayTask, listenModeTask's direct call, interactModeTask's direct call). Every clip resets the 10 s countdown.
  • resetIdle() deliberately does not touch isPaused — user-initiated pause must remain sticky (the f9c43aa regression test still passes).
  • maybeIdlePause() is kept as a defensive backstop for the unlikely case of a single clip exceeding the idle window.

Test plan

  • pnpm lint:types — clean.
  • pnpm test:ember --filter studying-timer — 8/8 pass (5 existing + 3 new tests for resetIdle: rearm via stop/start, no-toggle of isPaused regression guard, no-op when idleWatcher is null).
  • pnpm test:ember --filter audio --filter task-player — 109/109 pass. Circular service injection (audio ↔ studying-timer) resolves lazily.
  • Browser verification on dev: start task, do not touch mouse for 32 s. LISTEN played all 3 clips, timer counted 00:00 → 00:18 with isPaused=false throughout, transitioned to INTERACT, then idle correctly paused at ~10 s of inactivity. Click on a word resumed normally.

🤖 Generated with Claude Code

The previous fix (#2893) only guarded onIdle against firing while audio
was actively playing, but the LISTEN sequence sets isPlaying=false during
the 1500ms gap between clips. With idleTimeout=10000ms, onIdle landed
inside the gap after ~3 clips and paused the exercise, deadlocking
listenModeTask in waitWhilePaused() (IdleJs doesn't reschedule onIdle
without a user input event).

Add studyingTimer.resetIdle() that rearms the watcher via stop().start(),
called from audio.playAudio() — the single funnel for all three audio
entry points (startPlayTask, listenModeTask, interactModeTask). Each
clip resets the 10s countdown, so the watcher only fires when there is
genuinely no audio activity (e.g. INTERACT mode awaiting user clicks).

resetIdle() deliberately does not touch isPaused — user-initiated pause
must remain sticky (preserved by the regression test from f9c43aa).

Verified in browser: LISTEN played all 3 clips with mouse still, timer
counted 00:00→00:18 with isPaused=false throughout, transitioned to
INTERACT, then idle correctly paused at ~10s of inactivity. Click on a
word resumed normally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Frontend test coverage: 72.52% (+0.12% compared to 72.4% on base)

@lifeart lifeart merged commit fcec536 into master May 28, 2026
7 checks passed
@lifeart lifeart deleted the fix-audio-idle-during-listen branch May 28, 2026 10:23
@sonarqubecloud

Copy link
Copy Markdown

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