fix(studio): keyframe commit routing for 3D and cross-group edits (#1762)

- pickBestAnimation is group-aware: a rotation/3D edit no longer merges into a
  position tween — a fresh same-group tween with a 0% baseline is created instead
- editing at a playhead past the tween extends it and keyframes there (matches drag)
- update-keyframe MERGES into the existing keyframe instead of overwriting, so
  editing one property no longer drops z/transformPerspective (the lens then
  animated from 0 and the element popped)
- dragging a keyframed element with a constant position tween keyframes rather
  than writing a static set
This commit is contained in:
Miguel Ángel
2026-06-27 11:32:43 -04:00
committed by GitHub
parent 6a729b7e03
commit 2d3b19d255
13 changed files with 467 additions and 125 deletions
@@ -167,4 +167,39 @@ describe("promoteSetToKeyframes — auto endpoint", () => {
expect(kfs[1].percentage).toBe(100);
expect(kfs[1].auto).toBeUndefined();
});
it("playhead AT the set (t <= setStart) drops a single 0% keyframe, not a no-op", async () => {
// Regression: enabling keyframes on a `gsap.set` element at t=0 (set start 0)
// returned early (`t <= setStart`) → nothing created. Must give a 0% keyframe.
let committed: Record<string, unknown> | undefined;
const session = {
commitMutation: async (mutation: Record<string, unknown>) => {
committed = mutation;
},
} as unknown as EnableKeyframesSession;
const sel = {
id: "box",
selector: "#box",
sourceFile: "index.html",
element: { isConnected: true } as unknown as HTMLElement,
} as unknown as DomEditSelection;
const iframe = {
contentWindow: { gsap: { getProperty: () => -1091 } },
} as unknown as HTMLIFrameElement;
const setAnim = anim({
id: "#box-set-0-position",
targetSelector: "#box",
method: "set",
global: true,
resolvedStart: 0,
properties: { x: -1091, y: 280 },
});
await promoteSetToKeyframes(session, sel, setAnim, 0, iframe);
const kfs = committed?.keyframes as Array<{ percentage: number }>;
expect(committed?.type).toBe("replace-with-keyframes");
expect(kfs).toHaveLength(1);
expect(kfs[0].percentage).toBe(0);
});
});