Skip to content

fix(player): clean up controls on destroy#1407

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/player-controls-destroy-cleanup
Jun 13, 2026
Merged

fix(player): clean up controls on destroy#1407
miguel-heygen merged 1 commit into
mainfrom
fix/player-controls-destroy-cleanup

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Problem

createControls() owned more than document-level listeners. It also appended a .hfp-controls root into the player shadow tree and registered host-level auto-hide listeners. When the controls attribute was removed, <hyperframes-player> called destroy() and nulled the API, but the old controls DOM stayed attached. Re-adding controls then created another controls bar on the same persistent host.

What this fixes

  • Removes the host mousemove / mouseleave listeners with stable handler references.
  • Removes the owned .hfp-controls element during destroy(), so element-scoped listeners and UI do not survive a controls teardown.
  • Adds low-level controls tests plus a <hyperframes-player> attribute lifecycle test that proves toggling controls off/on leaves exactly one controls bar.

Root cause

destroy() treated only document-level listeners and timers as teardown work. The controls root was still referenced by the shadow DOM after destroy(), so toggling the attribute left stale controls UI and its element listeners behind. The host auto-hide handlers were also anonymous before #1405, so they could not be removed with matching references.

Verification

Local checks

  • bun run --cwd packages/player test src/controls.test.ts src/hyperframes-player.test.ts
  • bunx oxlint packages/player/src/controls.ts packages/player/src/controls.test.ts packages/player/src/hyperframes-player.test.ts
  • bunx oxfmt --check packages/player/src/controls.ts packages/player/src/controls.test.ts packages/player/src/hyperframes-player.test.ts
  • bun run --cwd packages/player test
  • bun run --cwd packages/player typecheck
  • bun run --cwd packages/player build
  • bun run build:hyperframes-runtime to satisfy the root pre-commit typecheck dependency on generated runtime inline artifacts
  • git commit --amend pre-commit hook: lint, format, fallow, typecheck, commitlint passed

Browser verification

agent-browser loaded a minimal real <hyperframes-player controls> page against built player bundles and toggled the controls attribute.

Before the full cleanup on main / original #1405 state, the repro showed stale lifecycle state:

{
  "before": 1,
  "afterRemove": 1,
  "hiddenAfterDestroyedMousemove": false,
  "afterReadd": 2
}

After this fix:

{
  "before": 1,
  "afterRemove": 0,
  "hiddenAfterDestroyedMousemove": true,
  "afterReadd": 1
}

Browser artifacts

  • Screenshot: /tmp/pr1405-fixed-browser.png
  • Recording: /tmp/pr1405-fixed-browser.webm

Notes

Supersedes #1405. I could not push directly to the contributor fork from this checkout even though the PR reports maintainerCanModify: true, so this branch carries the same fix forward on the upstream repo with the root DOM cleanup included.

@calcarazgre646

Copy link
Copy Markdown
Contributor

Thanks for carrying this forward. controls.remove() was the gap: destroy() undid the listeners but not the appendChild from setup, so the controls element leaked on toggle. Appreciate you finishing it.

@miguel-heygen miguel-heygen merged commit a037505 into main Jun 13, 2026
40 checks passed
@miguel-heygen miguel-heygen deleted the fix/player-controls-destroy-cleanup branch June 13, 2026 05:33
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.

2 participants