Repository navigation
fix(core): a composition mounted twice keeps each mount's own timeline - #5729
Conversation
|
Before and after in the Studio preview (live preview page, 0.8.166 vs this branch), from small fixture projects. Split section: a linear bar crosses the screen over the scene's 10 s, split at 3 s. Before, the first half's bar stays frozen at its first frame; after, it is at its 1 s position. At 5 s both versions agree, since the second half already played right. Inserted project: a project whose root is also |
Edit accuracy: accurate 271 (base branch 271), smooth 170 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (1)
|
jrusso1020
left a comment
There was a problem hiding this comment.
Approving at 02653361.
Checked and fine
- Scoping proxy
set(core/src/compiler/compositionScoping.ts). A second mount no longer overwrites the scene's own id, and a mount that owns the slot can still publish again (the v1 → v2 case). Old savedscene-splitprojects work in either script order. A host whose id equals the scene's id gets no proxy, so the first half still holds the scene's slot. countPageRootfeeds all three runtime-id paths: the loader (compositionLoader.ts), the bundler (htmlBundler.ts), and the producer'sProducerHostIdentityMap, which uses the bundler's counts. Preview and export therefore number the same way.- I checked the body's claim against source: a mount that shares the root's id gets a count above 1 and becomes
main__hf1. The new loader and bundler tests assert that.
- I checked the body's claim against source: a mount that shares the root's id gets a count above 1 and becomes
- Single-mount projects are unchanged unless the mount shares the page root's id. A root that is itself a tracked host is skipped.
- Studio split and duplicate (
sourceMutation.ts) only rename elements that aren't mounts. A mount is an element withdata-composition-src, or an empty host whose<id>-templateexists. Both callers go through the same function. - Studio insert (
compositionInsertion.ts) avoids the page root's id and composition id when it makes a host id. - Duplicate source ids from inserting the same project twice are intentional.
duplicate_composition_idalready skipsdata-composition-srcmounts, so lint won't flag them.
Non-blocking
- "Each mount reads its own timeline" holds only once that mount has written. When a mount's runtime key is still empty, the proxy's
getfalls back to the scene's id. Shipped blocks such ascode-snippet-dark-moderndowindow.__timelines[id] = window.__timelines[id] || gsap.timeline(...). Mounted twice, the second mount reads the first mount's timeline and stores it under__hf2, so both mounts share one timeline. I modelled that by copying the proxy logic into a node script.- This isn't a regression, since
mainbehaves the same. - Suggested follow-up: return only the runtime key while the proxy is active, and add an
x = x || newtest that mounts the block twice.
- This isn't a regression, since
countPageRootrelies on document order (the first[data-composition-id]is the page root). A one-line comment saying so would help.
CI is the only run. Not armed or merged.
— Rames




What
When one composition file is mounted by two hosts, each mount now keeps its own timeline in preview and in export. Three ways to get there were broken:
scene-split), but its content still saidscene. Its script then published its timeline under both ids and replaced the first half's. The first half froze at its first frame, and both hosts seeked one shared timeline, so which crop won depended on seek order (forward, back, forward could show a different source moment at the same output time).main). The inserted mount keptmainand replaced the page root's timeline, so the host's own animation never ran.scene__hf1,scene__hf2) and is covered here so it stays that way.How
wrapScopedCompositionScript(one place, used by both the preview loader and the render bundler): a mount still publishes its timeline under its runtime id, but it only takes the scene's authored id when that slot is empty or already its own. Inside a mount, readingwindow.__timelines.<scene>returns that mount's own timeline.countPageRoot), so a mount sharing the root's id is numbered (main__hf1) instead of taking the root's slot.scene-splitprojects play correctly through the runtime fix.Verification
Each item has a test that fails on main:
compositionScoping.test.ts"never replaces another mount's timeline under the scene's own id"sourceMutationSplitAndGroup.test.ts, 2 casescompositionLoader.test.ts"gives a mount that shares the page root's id its own runtime id"htmlBundler.test.ts, same namecompositionInsertion.test.ts"mounts a project whose root shares the parent root's id under its own host id"The related core suites (loader, scoping, inline sub-compositions, scene swaps, bundler; 512 tests) and the studio-server helper suites (513 tests) pass 3 runs in a row. core and studio-server
tsc, oxlint, oxfmt and the comment ratchet are clean.End to end with the built CLI, against the published 0.8.166, on three small projects:
mainmain__hf1