Skip to content

fix(studio): a move keeps an authored xPercent centring set in the saved file - #4867

Closed
miguel-heygen wants to merge 3 commits into
mainfrom
fix/studio-keep-xpercent-centring-set
Closed

miguel-heygen wants to merge 3 commits into
mainfrom
fix/studio-keep-xpercent-centring-set

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

What

Moving, nudging or resizing an element centred with gsap.set({ xPercent: -50, yPercent: -50 }) that also has an x/y tween now keeps the centring set in the saved file. Before, the save deleted it and the element landed half its size down and to the right.

Why

Before a GSAP move commits, Studio removes duplicate position writes for the element. It counted every write in the position group as a duplicate, and that group holds x, y, xPercent and yPercent. A centring set and an x/y tween were treated as two conflicting writes and the set was deleted. The drag preview looked right, so only the drop was wrong, and reload and render then agreed with the wrong file.

Related work

Refs #4774 (closed). It had the same idea for the writers. This PR takes only that part.

How

  • One predicate, isXYPositionWrite in @hyperframes/parsers/gsap-constants beside PROPERTY_GROUPS, says whether a write sets x or y. Only those can duplicate each other. An xPercent/yPercent-only set never does.
  • The acorn writer's dedupePositionWritesInScript, its recast twin, and the Studio self-heal in tryGsapDragIntercept all use it.
  • With the set kept, a second spot in tryGsapDragIntercept picked the wrong write: the stale-id re-match took the first position write in the fresh parse, which is the set, so the move turned the centring set into keyframes. It now matches only a write of the same kind.
  • Two x/y writes are still consolidated, as before.
  • On release, the flat-tween-to-keyframes commit reads the tween's start by clearing x,y and seeking. GSAP clears the whole transform when any part of it is cleared, and the seek only restores what the timeline owns, so the centring vanished from the preview for a few frames until the soft reload (the bench's teleport check caught a 144 px jump on the move and resize rows). The restore after the read now carries every position channel, xPercent and yPercent included.

Test plan

  • Unit tests added/updated
    • gsapWriter.parity.test.ts: a centring set plus an x/y tween comes out unchanged from both writers. Fails on main.
    • gsapRuntimeBridge.test.ts: a drag with a centring set and a live x/y tween runs no consolidate and edits only the tween (fails on main), and a second x/y write is still consolidated.
    • Each part of the dedupe fix was reverted by itself and the matching test failed. The release-flash fix is proven by the bench's teleport check: jsdom's GSAP loses the whole transform after clearProps, so a unit test there cannot see it.
  • Manual testing performed
    • Edit accuracy bench, --filter '^(move|nudge|resize)-tween-xpercent-' (36 cases) on Linux: every gated metric passes in all 36 (drop, reload, render, undo, tracking, press). move-tween-xpercent-r0-root-z100 drop went from 144.24 px on main to 0.
    • CI edit accuracy gate (merged with main): 869 passing against 835 on the base branch, 34 newly passing (every move, nudge and resize-tween-xpercent case that failed on main), 0 regressed. Those 34 are banked from that run's artifact baseline.json, byte for byte, and a local replay of the gate on the banked file passes.
  • Documentation updated (if applicable)
  • Comments follow CONTRIBUTING.md "Comments": they say why, not what, and a bug fix says what the code must do and how to reproduce the bug

Captures: the bench fixture move-tween-xpercent-r0-root-z100 (a 240x160 box at left: 50%; top: 50% with the centring set and an x/y tween), driven by the bench's own drag. Left: the frame at pointer release. Right: the same view once the save has landed.

Before

Before: the box jumps down and right after the save

Saved script: the gsap.set("#target", { xPercent: -50, yPercent: -50 }) line is gone.

After

After: the box stays where it was let go

Saved script: the centring set is still there, and the move is a keyframe on the tween.

@miguel-heygen
miguel-heygen force-pushed the fix/studio-keep-xpercent-centring-set branch from abda1c4 to da5b4c3 Compare October 1, 2026 16:47
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: 875 passing here, 841 on the base branch

The gate fails: 34 newly passing case(s) are not banked in baseline.json.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Newly passing (34)

  • move-tween-xpercent-r0-root-z50
  • nudge-tween-xpercent-r0-nested-z100
  • resize-tween-xpercent-r30-root-z100
  • move-tween-xpercent-r30-nested-z50
  • nudge-tween-xpercent-r0-root-z100
  • resize-tween-xpercent-r0-nested-z100
  • move-tween-xpercent-r0-root-z200
  • nudge-tween-xpercent-r30-root-z50
  • resize-tween-xpercent-r30-nested-z50
  • move-tween-xpercent-r30-root-z100
  • nudge-tween-xpercent-r30-nested-z200
  • move-tween-xpercent-r0-nested-z200
  • nudge-tween-xpercent-r30-nested-z50
  • resize-tween-xpercent-r0-root-z50
  • move-tween-xpercent-r30-nested-z100
  • nudge-tween-xpercent-r0-root-z200
  • move-tween-xpercent-r0-nested-z50
  • nudge-tween-xpercent-r30-root-z100
  • resize-tween-xpercent-r30-nested-z100
  • move-tween-xpercent-r30-root-z200
  • nudge-tween-xpercent-r0-root-z50
  • resize-tween-xpercent-r0-nested-z50
  • move-tween-xpercent-r0-root-z100
  • nudge-tween-xpercent-r0-nested-z200
  • resize-tween-xpercent-r30-root-z200
  • move-tween-xpercent-r30-root-z50
  • nudge-tween-xpercent-r30-nested-z100
  • resize-tween-xpercent-r0-root-z100
  • move-tween-xpercent-r30-nested-z200
  • nudge-tween-xpercent-r0-nested-z50
  • ...

Not banked (commit the artifact's baseline.json) (34)

  • move-tween-xpercent-r0-root-z50
  • nudge-tween-xpercent-r0-nested-z100
  • resize-tween-xpercent-r30-root-z100
  • move-tween-xpercent-r30-nested-z50
  • nudge-tween-xpercent-r0-root-z100
  • resize-tween-xpercent-r0-nested-z100
  • move-tween-xpercent-r0-root-z200
  • nudge-tween-xpercent-r30-root-z50
  • resize-tween-xpercent-r30-nested-z50
  • move-tween-xpercent-r30-root-z100
  • nudge-tween-xpercent-r30-nested-z200
  • move-tween-xpercent-r0-nested-z200
  • nudge-tween-xpercent-r30-nested-z50
  • resize-tween-xpercent-r0-root-z50
  • move-tween-xpercent-r30-nested-z100
  • nudge-tween-xpercent-r0-root-z200
  • move-tween-xpercent-r0-nested-z50
  • nudge-tween-xpercent-r30-root-z100
  • resize-tween-xpercent-r30-nested-z100
  • move-tween-xpercent-r30-root-z200
  • nudge-tween-xpercent-r0-root-z50
  • resize-tween-xpercent-r0-nested-z50
  • move-tween-xpercent-r0-root-z100
  • nudge-tween-xpercent-r0-nested-z200
  • resize-tween-xpercent-r30-root-z200
  • move-tween-xpercent-r30-root-z50
  • nudge-tween-xpercent-r30-nested-z100
  • resize-tween-xpercent-r0-root-z100
  • move-tween-xpercent-r30-nested-z200
  • nudge-tween-xpercent-r0-nested-z50
  • ...

Quarantined, measured but not gated (7)

@miguel-heygen
miguel-heygen force-pushed the fix/studio-keep-xpercent-centring-set branch from fda88fe to a65719d Compare October 1, 2026 19:17
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Closing: this change now ships inside #4869, the lane's one PR. The captures and gate numbers here carry over there.

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.

1 participant