fix(studio): one GSAP-writes check that sees every keyframe form - #4781
Conversation
00bcdd3 to
505d0eb
Compare
Edit accuracy: accurate 1216 (base branch 1216), smooth 1049 of thoseThe gate passes. Quarantined, measured but not gated (1)
|
terencecho
left a comment
There was a problem hiding this comment.
Approved at c4dd776c60965fea4b2a7fc7d8f5d01c6444723e on code merits. The unified GSAP channel check covers the keyframe forms exercised by the changed callers; it prevents Studio's saved offset from being reapplied over an x/y tween and preserves GSAP ownership of a tweened crop clip path. I compared the effective diff with merge base aca4bc2f492c2b038d87c875cd7d53a80dfb4bb1 and ran four focused suites in an isolated checkout (38/38 passed).
The seek guard still handles only x/y, not xPercent/translateX, but that limit predates this PR. The new classifier also does not catch exceptions from a malformed timeline implementation where its predecessor did; I found no normal reload/teardown path that produces such a throwing timeline, so I am not treating that semantic difference as a demonstrated merge blocker. The edit-accuracy CI shards were pending during this review; this approval is a code verdict, not a claim that all CI completed.
— Review by tai (pr-review)
jrusso1020
left a comment
There was a problem hiding this comment.
Approving at c4dd776c.
Does the shared check still catch what the deleted one did? Yes. I loaded gsapAnimatesProperty from main next to gsapWritesChannels and ran both over 16 vars shapes × {["x","y"], ["clipPath"]} × {same element, id-only match}. Wherever the old check said true, the new one does too. It newly says true in three cases, all object-of-arrays: { x: [0, 200] }, { clipPath: [...] }, and { x: [...], ease, easeEach }. Top-level, percent, step-array and x: undefined give the same answers as before, as do null and string keyframes.
The PR body's test claims hold:
- On main's sources, both new tests fail and the other 15 pass.
- Removing the object-of-arrays branch from
keyframeVarsCarryChannelturns 3 tests red: both new tests, plus the existing "follows only the axes GSAP does not tween". That's what the body says. - Turning off the seek-reapply guard fails the new reapply test.
Suite: studio 637 files / 6972 tests pass, and tsc --noEmit is clean.
Non-blocking, for the record:
- The old function wrapped its scan in
try/catchand returned false ifgetChildrenortargets()threw.gsapWritesChannelslets the error through. Real GSAP doesn't throw there. The existinggsapWritesPosition,gsapWritesRotationandgsapWritesBoxcallers already run unguarded. TheuseDomSelectioncall sites still sit in try blocks, whileusePreviewPersistenceanduseDomEditPreviewSyncdon't. So I see no practical risk. - The drag path (
applyStudioPathOffsetViaGsap) now also sends object-of-arrays x/y elements throughgsap.set, which matches how percent keyframes already worked. Nothing pins that call site, but its behaviour is now the same as the seek-reapply path's.
— Rames
What
Studio had two copies of the check "does GSAP write this channel on this element?". The copy used by the seek reapply, the drag reapply and the crop "GSAP owns clip-path" rule (
gsapAnimatesProperty) read only top-level tween vars, so it missed object-of-arrays keyframes (keyframes: { x: [0, 200] }). A layer moved that way that still carried an old Studio offset got that offset put back after every seek, on top of GSAP's position: it showed up 300 px away from where its animation put it. A crop on a layer whose clip-path GSAP tweens that way was treated as Studio's to rewrite.How
gsapWritesChannelsingsapRuntimeKeyframes.tsalready answers the question for every keyframe form (top-level, percent, array, object-of-arrays) throughkeyframeVarsCarryChannel. It is now exported.["x", "y"], the crop rule with["clipPath"].gsapAnimatesPropertyis deleted. Its file keeps onlygsapRendersTransform, which is a different question.Before
A layer with an old Studio offset of 300 px and a GSAP tween
keyframes: { x: [0, 200] }(the dashed box is where the tween starts it), after seeking back to 0 s on main. Studio puts the offset back, so the layer sits 300 px right of its animated start.After
The same fixture and seek at this head. GSAP owns x, so Studio leaves the position alone and the layer sits on the dashed box.
Tests
reapplyPositionEditsAfterSeek.test.ts: an element with an old offset andkeyframes: { x: [0, 200] }keeps its offset off, while its untweened neighbour gets its offset back.cropResize.test.ts: a crop on an element whose clip-path GSAP tweens by object-of-arrays keyframes is left alone.keyframeVarsCarryChannelfails both new tests (and one existing crop test). The Studio unit suite passes.