Repository navigation
fix(core): a sub-composition follows its host clip when the host moves without a reload - #5058
Merged
Merged
Conversation
Contributor
Edit accuracy: accurate 2040 (base branch 2040), smooth 1498 of thoseThe gate passes. Quarantined, measured but not gated (0) |
… across rebinds and re-runs
…check every other timeline
…n the script compare
miguel-heygen
marked this pull request as ready for review
October 5, 2026 15:56
terencecho
approved these changes
Oct 5, 2026
terencecho
left a comment
Contributor
There was a problem hiding this comment.
No blocker at 8b27458effc09ec7342492114f116a23ea52da60. Approving; the CI jobs were all green when I stamped (89 pass, 5 skipping).
What I checked (local, NODE_ENV=test)
- New/changed files:
init.subCompositionPlacement3/3,scriptShape15/15,gsapLiveRetime.cache2/2,gsapLiveRetime.realGsap37/37. Neighbours: coreinit.test220,init.swapScenes140,init.compositionLength10,init.mediaClipIndex7,init.timingResolver2,init.flatGsap1,init.positionEdits1; studiotimelineTimingSync29.tsc --noEmitis clean in core, parsers and studio. - Mutations, each reverted: dropping the
followHostStartcall, theplaced.parent !== rootguard, or the 1e-6 tolerance guard fails the placement test. IngsapLiveRetime, dropping the shape cache, the LRU refresh, or a cache size of 1 fails the cache test; ignoring the registry, the nested timelines, or the own-timeline tween scan fails the real-GSAP cases; reverting the__raw:compare to text fails the multi-line reformatted case. InscriptShape, no number masking, always droppingraw, droppingshorthandfor__proto__, or keepingstarteach fail a test. - Call sites:
moveLiveTweenshas one caller (timelineTimingSync.ts:137);scriptShapeis used only ingsapLiveRetime.ts; no other reader of the removedautoNestedHostIds,withoutLayout,withoutNumbersorinOrHostsSubComposition. The shape cache is keyed by script text, so it cannot go stale across undo/redo or a script rewrite; the live script's text is still set to the saved script after an in-place move. - No secrets in the diff.
Non-blocking
- Three guards survive mutation (each is correct today, none is pinned):
shape === nullinplanLiveRetime(I confirmed that without it a script with a top-levelimportis retimed in place instead of re-run), the!placedreturn infollowHostStart(an author-added scene with no runtime entry would throw inside the swallowedtry), andnested.parent !== root(getChildren(true, true, true)also lists deeper children). A one-line test each would keep them. scriptShapehas no test for BigInt literals (removing that branch stays green).- I did not run the browser numbers (first drag, repeat 41 → 30 ms); the PR says the first drag is not faster, and I take those as the author's.
Reviewed on 8b27458effc09ec7342492114f116a23ea52da60. This is a review verdict, not authorization to merge.
— Review by tai (pr-review)
3 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A timing drag that touches a sub-composition host now moves the live tweens in place instead of re-running the composition script, and the scene stays where a fresh load would put it.
Part of the manual-edit performance work (bar: no main-thread task over 8 ms on a one-clip drop).
Numbers
This PR ships for correctness: a scene follows its host clip without a reload. It does not make the first drag faster. Built Studio, headless Chrome, base is main at #5042.
Host-clip drag, 30-card fixture with a sub-composition (the one in the captures). Longest main-thread task after the drop:
On a first drag the in-place path does more work than re-running this small script: its preview sync is 33 to 70 ms (two GSAP parses 20 to 37 ms, two shape compares 10 to 29 ms) against 13 to 43 ms for the re-run. A repeat drag compares only the newly saved script's shape, because the last two shapes are kept by script text; the parser's existing memo already covers the parse. The capture banners are single drops (68 ms before, 91 ms after on dark, wide); the rows above are the comparison.
One-clip drag on a 53 s launch project with 11 sub-compositions (5 alternating rounds, median, first drag): preview sync 75 ms before (re-run) and 61 ms after (in place); the task holding it 115 and 107 ms; 12 and 14 tasks over 8 ms per drag.
What the remaining long task is. Every drop still has a task far over the 8 ms bar on both sides. The largest is React re-rendering Studio after the drop (40 to 60 ms in the first task after release; Studio subscribes to the whole element list), and the next is the preview sync above. The next PRs in this series take those on: the React subscription, then parsing each script once per drop.
Before
Main: a drag of the scene host's clip, in a script with comments, rebuilds the timeline. The scene's dot is where a fresh load puts it.
After
Every full screenshot is at 2x device pixels; the zooms crop the banner with the preview, and the moved clip. The preview frames are pixel-identical to Before by design; the banner says which path ran. The same drag moves the tweens in place; the scene follows its host, and its dot matches a fresh load (x 109 at 16.6 s wide, 113 at 16.7 s narrow). The light, narrow banner on Before reads 0 ms because the probe missed that drop's task; the table above has the measured numbers.
Known limits
undefinedprinted asvoid 0, and constant folding such as"a" + "b".importor top-levelawaitdoes not parse as a classic script, so it always re-runs.Test plan