fix(oceans): clear guide typing timer and sounds on level transition by stephenliang · Pull Request #73499 · code-dot-org/code-dot-org · GitHub
Skip to content

fix(oceans): clear guide typing timer and sounds on level transition#73499

Open
stephenliang wants to merge 6 commits into
stagingfrom
stephen/fix-oceans-typing-timer-leak
Open

fix(oceans): clear guide typing timer and sounds on level transition#73499
stephenliang wants to merge 6 commits into
stagingfrom
stephen/fix-oceans-typing-timer-leak

Conversation

@stephenliang

@stephenliang stephenliang commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary

  • Timer leak: setInitialState() overwrote guideTypingTimer without calling clearInterval, so the typing sound effect continued firing after a level transition. stopUIRerender now also clears the timer so component teardown is clean.
  • Audio bleed: App.tsx useEffect cleanup now calls sounds.stopAllAudio() before stopping the render loop so no in-flight audio bleeds across levels.
  • createRoot warning: track the DOM container element alongside the React root; only call createRoot when the container actually changes (unmount→remount), avoiding the "container already passed to createRoot" warning on appMode changes.
  • Box-sizing (studio host): MUI's global reset forces border-box via inheritance on all elements. The oceans UI was authored for content-box (%-based padding on counter/erase button is additive). Apply content-box on #container-react and border-box on word buttons via MUI sx in the studio host component, keeping the oceans package host-agnostic.
  • scenes.css cleanup: remove the prior #container-react box-sizing override from the oceans package since it's now the studio host's responsibility.

Test plan

  • New unit test: test/unit/oceans/init.test.ts — verifies setInitialState clears an active guideTypingTimer and stopUIRerender clears the timer on teardown
  • All 59 oceans unit tests pass
  • Pixel-perfect visual diff: level 2 (training UI) is 0-pixel-diff between old CSS and new sx approach (verified with deterministic freeze + ImageMagick compare)
  • Level 8 word buttons: old CSS wildcard broke 3-column grid (2-column overflow); new sx with border-box override renders correct 3-column layout
  • Manual: navigate L2→L3 in the oceans dev shell and confirm typing sounds stop

🤖 Generated with Claude Code

stephenliang and others added 3 commits June 25, 2026 14:33
setInitialState() overwrote the guideTypingTimer handle without
clearing the interval, so the typing sound effect continued after
navigating to a new level. Clear it before resetting state. Also
stop all playing sounds in the useEffect cleanup so nothing bleeds
across level transitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
stopUIRerender() only stopped the RAF loop but kept the module-level
uiRoot reference. When OceansLab unmounts (navigating to a non-fish
level) and remounts (navigating back), renderUI() skipped createRoot
because uiRoot was still non-null, rendering into the detached old
container. Null it on cleanup so a fresh root is created for the new
container.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace uiRoot=null with container element comparison so createRoot
  is only called when the DOM container actually changes (avoids React
  "container already passed to createRoot" warning on appMode changes).
- Clear guideTypingTimer in stopUIRerender so no new playSound calls
  fire after sounds.stopAllAudio() during cleanup.
- Change #container-react box-sizing from content-box to border-box to
  match the prod apps bundle; fixes 2-column layout on level 8 (long
  mode) which needs 3 columns.
- Update tests with stopUIRerender coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@stephenliang stephenliang force-pushed the stephen/fix-oceans-typing-timer-leak branch from 96fdf72 to 6863389 Compare June 25, 2026 21:35
stephenliang and others added 3 commits June 25, 2026 14:58
MUI's global reset forces border-box on all elements via inherit.
The blanket #container-react * rule crushed the counter and erase
button whose %-based padding is additive (content-box).  Reset the
container to content-box so children inherit it, and opt only the
word-attribute buttons into border-box.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The box-sizing fix belongs in the studio host (where MUI lives), not
in the oceans package (which should be host-agnostic).  Remove the
#container-react and word-button box-sizing rules from scenes.css and
apply `content-box` via MUI `sx` on the wrapping `<Box>` in
OceansContainer instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Null guideTypingTimer in state after clearInterval in stopUIRerender
  so Guide.tsx's !state.guideTypingTimer gate doesn't block new timers.
- Remove unnecessary `if (guideTypingTimer)` guards; clearInterval on
  undefined is a spec-defined no-op and the guard skips timer ID 0.
- Add clearInterval to resetState() (public API, was leaking intervals).
- Replace uiRoot! non-null assertion with optional chaining.
- Add "why" comment on CssBaseline box-sizing override.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@stephenliang stephenliang marked this pull request as ready for review June 26, 2026 00:01
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