mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
fix(studio): harden keyframe editing semantics
This commit is contained in:
@@ -1830,6 +1830,47 @@ tl.to("#box", { opacity: 1, duration: 1 }, 0);
|
||||
expect(result.after).not.toContain("data-hf-studio-rotation");
|
||||
});
|
||||
|
||||
it("replace-with-keyframes preserves per-segment easing for exact temporal keyframes", async () => {
|
||||
const projectDir = createProjectDir();
|
||||
const PATH_COMP = `<!DOCTYPE html><html><body data-duration="32">
|
||||
<div id="box"></div>
|
||||
<script data-hyperframes-gsap>
|
||||
const tl = gsap.timeline();
|
||||
tl.to("#box", { motionPath: { path: [{ x: 0, y: 0 }, { x: 100, y: 100 }] }, duration: 16.055, ease: "power1.inOut" }, 12.17);
|
||||
</script>
|
||||
</body></html>`;
|
||||
writeHtml(projectDir, "path.html", PATH_COMP);
|
||||
const app = new Hono();
|
||||
registerFileRoutes(app, createAdapter(projectDir));
|
||||
|
||||
const anim = await getFirstAnimation(app, "path.html");
|
||||
const res = await app.request("http://localhost/projects/demo/gsap-mutations/path.html", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
type: "replace-with-keyframes",
|
||||
animationId: anim.id,
|
||||
targetSelector: "#box",
|
||||
position: 12.17,
|
||||
duration: 16.055,
|
||||
keyframes: [
|
||||
{ percentage: 0, properties: { x: 0, y: 0 } },
|
||||
{ percentage: 23.2, properties: { x: 25, y: 30 } },
|
||||
{ percentage: 100, properties: { x: 100, y: 100 } },
|
||||
],
|
||||
ease: "none",
|
||||
}),
|
||||
});
|
||||
const result = (await res.json()) as { ok: boolean; after: string };
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.after).toContain('"23.2%"');
|
||||
expect(result.after).toContain('easeEach: "power1.inOut"');
|
||||
expect(result.after).toContain('ease: "none"');
|
||||
expect(result.after).not.toContain("motionPath");
|
||||
});
|
||||
|
||||
it("edits a template-wrapped tween in place, preserving gsap.set and the IIFE", async () => {
|
||||
const projectDir = createProjectDir();
|
||||
writeComp(projectDir, "scene.html", TEMPLATE_COMP);
|
||||
|
||||
@@ -997,6 +997,7 @@ export type GsapMutationRequest =
|
||||
auto?: boolean;
|
||||
}>;
|
||||
ease?: string;
|
||||
easeEach?: string;
|
||||
}
|
||||
| {
|
||||
type: "split-animations";
|
||||
@@ -1052,6 +1053,18 @@ export type GsapMutationRequest =
|
||||
|
||||
type GsapMutationResult = string | { script: string; skippedSelectors: string[] };
|
||||
|
||||
function resolveReplacementEaseEach(
|
||||
scriptText: string,
|
||||
request: { animationId: string; easeEach?: string },
|
||||
): string | undefined {
|
||||
if (request.easeEach !== undefined) return request.easeEach;
|
||||
const original = parseGsapScriptAcorn(scriptText).animations.find(
|
||||
(animation) => animation.id === request.animationId,
|
||||
);
|
||||
if (!original?.arcPath?.enabled) return undefined;
|
||||
return original?.keyframes?.easeEach ?? original?.ease;
|
||||
}
|
||||
|
||||
// Mutations that can change a position tween's first keyframe (value/existence/timing)
|
||||
// and therefore require the pre-keyframe hold-`set`s to be re-synced afterwards.
|
||||
// `syncPositionHoldsBeforeKeyframes` rebuilds all `hf-hold` sets from scratch: it acts
|
||||
@@ -1507,6 +1520,7 @@ function executeGsapMutationAcorn(
|
||||
body.duration,
|
||||
body.keyframes,
|
||||
body.ease,
|
||||
resolveReplacementEaseEach(block.scriptText, body),
|
||||
);
|
||||
return added.script;
|
||||
}
|
||||
@@ -1877,6 +1891,7 @@ async function executeGsapMutationRecast(
|
||||
body.duration,
|
||||
body.keyframes,
|
||||
body.ease,
|
||||
resolveReplacementEaseEach(block.scriptText, body),
|
||||
);
|
||||
return added.script;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user