refactor(core): share keyframe property defaults between SDK and Studio (#5438)

Move the keyframe rest-value table into @hyperframes/core/keyframe-defaults
and import it from the SDK backfill and Studio's gsapShared, replacing the two
hand-synced copies.

Closes #5387

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meghna Vasudeva
2026-10-13 00:07:40 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 1d2aa5240d
commit b100ff16f3
6 changed files with 52 additions and 30 deletions
+6
View File
@@ -86,6 +86,12 @@
"types": "./dist/studioPreviewMark.d.ts",
"environments": ["browser", "bun", "node"]
},
"./keyframe-defaults": {
"source": "./src/keyframeDefaults.ts",
"runtime": "./dist/keyframeDefaults.js",
"types": "./dist/keyframeDefaults.d.ts",
"environments": ["browser", "bun", "node"]
},
"./gsap-cdn": {
"source": "./src/gsapCdn.ts",
"runtime": "./dist/gsapCdn.js",
+10
View File
@@ -98,6 +98,12 @@
"import": "./src/studioPreviewMark.ts",
"types": "./src/studioPreviewMark.ts"
},
"./keyframe-defaults": {
"bun": "./src/keyframeDefaults.ts",
"node": "./dist/keyframeDefaults.js",
"import": "./src/keyframeDefaults.ts",
"types": "./src/keyframeDefaults.ts"
},
"./gsap-cdn": {
"bun": "./src/gsapCdn.ts",
"node": "./dist/gsapCdn.js",
@@ -578,6 +584,10 @@
"import": "./dist/studioPreviewMark.js",
"types": "./dist/studioPreviewMark.d.ts"
},
"./keyframe-defaults": {
"import": "./dist/keyframeDefaults.js",
"types": "./dist/keyframeDefaults.d.ts"
},
"./gsap-cdn": {
"import": "./dist/gsapCdn.js",
"types": "./dist/gsapCdn.d.ts"
@@ -0,0 +1,22 @@
import { describe, expect, it } from "vitest";
import { KEYFRAME_PROPERTY_DEFAULTS } from "./keyframeDefaults";
describe("KEYFRAME_PROPERTY_DEFAULTS", () => {
it("holds the rest value of each numeric keyframe prop", () => {
expect(KEYFRAME_PROPERTY_DEFAULTS).toEqual({
opacity: 1,
x: 0,
y: 0,
scale: 1,
scaleX: 1,
scaleY: 1,
rotation: 0,
width: 100,
height: 100,
});
});
it.each(["color", "filter", "backgroundColor"])("has no default for %s", (prop) => {
expect(KEYFRAME_PROPERTY_DEFAULTS[prop]).toBeUndefined();
});
});
+12
View File
@@ -0,0 +1,12 @@
// A prop absent here has no safe static default; each caller decides how to treat it.
export const KEYFRAME_PROPERTY_DEFAULTS: Readonly<Record<string, number>> = {
opacity: 1,
x: 0,
y: 0,
scale: 1,
scaleX: 1,
scaleY: 1,
rotation: 0,
width: 100,
height: 100,
};
+1 -19
View File
@@ -12,25 +12,7 @@
* keyframes (GSAP reads the rest value from the DOM), matching recast (which skips
* any prop whose default is null).
*/
// Numeric rest values for editable transform/style props. Props absent here have
// no safe static default and are intentionally omitted from the backfill set.
//
// KEEP IN SYNC WITH packages/studio/src/hooks/gsapShared.ts:PROPERTY_DEFAULTS —
// the studio (recast) and SDK (acorn) paths must derive the same defaults or
// SDK-written keyframes drift from server-written ones (the exact bug this fixes).
// TODO: lift the canonical table into @hyperframes/core and import from both.
const KEYFRAME_PROPERTY_DEFAULTS: Record<string, number> = {
opacity: 1,
x: 0,
y: 0,
scale: 1,
scaleX: 1,
scaleY: 1,
rotation: 0,
width: 100,
height: 100,
};
import { KEYFRAME_PROPERTY_DEFAULTS } from "@hyperframes/core/keyframe-defaults";
/** Derive the backfillDefaults for an add-keyframe op (numeric-default props only). */
export function deriveKeyframeBackfillDefaults(
+1 -11
View File
@@ -25,17 +25,7 @@ export interface IframeGsap {
// ── Constants ─────────────────────────────────────────────────────────────────
export const PROPERTY_DEFAULTS: Record<string, number> = {
opacity: 1,
x: 0,
y: 0,
scale: 1,
scaleX: 1,
scaleY: 1,
rotation: 0,
width: 100,
height: 100,
};
export { KEYFRAME_PROPERTY_DEFAULTS as PROPERTY_DEFAULTS } from "@hyperframes/core/keyframe-defaults";
/** A tween's eases for re-writing it as keyframes: a flat tween's ease must become easeEach,
* since GSAP eases each percentage segment power1.inOut whatever the tween-level ease says. */