Commit Graph
1 Commits
Author SHA1 Message Date
James RussoandClaude Opus 4.7 e72bcfaed3 fix(player+core): correctly render and pause nested compositions (#359)
* fix(player): inject runtime immediately for nested compositions

Compositions that use `data-composition-src` on child elements require
the HyperFrames runtime to load those scenes — there is no way for the
iframe to render without it. The existing probe loop delayed runtime
injection behind a 5-tick attempts gate so the adapter path could try
to resolve a timeline first.

For nested compositions that race lost: a composition like the
`product-promo` registry example registers an inline pre-runtime GSAP
timeline at `window.__timelines["main"]` (covering only a partial
duration, e.g. 14s of a 20s master) while the iframe document loads.
The probe's adapter check finds that timeline and locks the player into
a "ready" state against it — which short-circuits the attempts gate and
the runtime never gets injected. The iframe ends up blank because the
runtime is what would have loaded the child scenes via
`data-composition-src`.

This change splits the injection decision into a pure helper,
`shouldInjectRuntime(state)`, and treats nested compositions as
"inject immediately, skip the gate." Self-contained GSAP-only
compositions retain the 5-tick grace period so the adapter path keeps
first shot for them.

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

* fix(core): propagate play/pause to all sibling timelines

Pausing or playing the master timeline only called `.pause()` / `.play()`
on `state.capturedTimeline` — the single adapter-selected timeline. In a
nested composition (a master with `data-composition-src` children), each
scene's own timeline is registered as a sibling in `window.__timelines`,
so they would keep advancing after the user clicked pause. The player UI
froze at the paused time while the visual content continued to animate,
eventually finishing all scene-level animations and landing on an empty
end-state.

Wire `window.__timelines` into the runtime player via a new
`getTimelineRegistry` dep, iterate the registry on play/pause, and
forward `timeScale` to siblings when play() starts so a changed
playback-rate applies uniformly.

Covered by 7 new unit tests in player.test.ts, including the identity-
equality check (don't double-invoke the master), playbackRate
propagation, a broken-sibling swallow, and a back-compat case with no
registry supplied.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 10:18:25 -07:00