Skip to content

Commit 36313eb

Browse files
feat(studio): route GSAP tween add/update/delete through SDK (§3.5 PR1)
Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
1 parent 52dc98c commit 36313eb

7 files changed

Lines changed: 460 additions & 39 deletions

File tree

packages/studio/src/hooks/gsapScriptCommitTypes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ParsedGsap } from "@hyperframes/core/gsap-parser";
2+
import type { Composition } from "@hyperframes/sdk";
23
import type { DomEditSelection } from "../components/editor/domEditingTypes";
34
import type { EditHistoryKind } from "../utils/editHistory";
45

@@ -63,4 +64,7 @@ export interface GsapScriptCommitsParams {
6364
onCacheInvalidate: () => void;
6465
onFileContentChanged?: (path: string, content: string) => void;
6566
showToast: (message: string, tone?: "error" | "info") => void;
67+
/** Stage 7 §3.5: SDK session for routing GSAP tween ops through addGsapTween/setGsapTween/removeGsapTween. */
68+
sdkSession?: Composition | null;
69+
writeProjectFile?: (path: string, content: string) => Promise<void>;
6670
}

packages/studio/src/hooks/useDomEditSession.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ export function useDomEditSession({
193193
onCacheInvalidate: bumpGsapCache,
194194
onFileContentChanged: updateEditingFileContent,
195195
showToast,
196+
sdkSession,
197+
writeProjectFile,
196198
});
197199

198200
// ── DOM commit handlers ──

packages/studio/src/hooks/useGsapAnimationOps.ts

Lines changed: 121 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
11
import { useCallback } from "react";
2+
import type { Composition } from "@hyperframes/sdk";
23
import type { DomEditSelection } from "../components/editor/domEditingTypes";
34
import { roundTo3 } from "../utils/rounding";
5+
import { sdkGsapTweenPersist } from "../utils/sdkCutover";
46
import {
57
assignGsapTargetAutoIdIfNeeded,
68
ensureElementAddressable,
79
} from "./gsapScriptCommitHelpers";
810
import type { CommitMutation, SafeGsapCommitMutation } from "./gsapScriptCommitTypes";
11+
import type { EditHistoryKind } from "../utils/editHistory";
912

10-
interface GsapAnimationOpsParams {
13+
interface SdkAnimationDeps {
14+
sdkSession?: Composition | null;
15+
writeProjectFile?: (path: string, content: string) => Promise<void>;
16+
editHistory?: {
17+
recordEdit: (entry: {
18+
label: string;
19+
kind: EditHistoryKind;
20+
coalesceKey?: string;
21+
files: Record<string, { before: string; after: string }>;
22+
}) => Promise<void>;
23+
};
24+
reloadPreview?: () => void;
25+
domEditSaveTimestampRef?: React.MutableRefObject<number>;
26+
}
27+
28+
interface GsapAnimationOpsParams extends SdkAnimationDeps {
1129
projectIdRef: React.MutableRefObject<string | null>;
1230
activeCompPath: string | null;
1331
commitMutation: CommitMutation;
@@ -21,39 +39,91 @@ export function useGsapAnimationOps({
2139
commitMutation,
2240
commitMutationSafely,
2341
showToast,
42+
sdkSession,
43+
writeProjectFile,
44+
editHistory,
45+
reloadPreview,
46+
domEditSaveTimestampRef,
2447
}: GsapAnimationOpsParams) {
2548
const updateGsapMeta = useCallback(
26-
(
49+
async (
2750
selection: DomEditSelection,
2851
animationId: string,
2952
updates: { duration?: number; ease?: string; position?: number },
3053
) => {
31-
// coalesceKey groups rapid meta edits into one history entry. Request
32-
// serialization is now handled per-file at the commitMutation chokepoint
33-
// (useGsapScriptCommits), so no per-op serializeKey is needed here.
34-
const metaKey = `gsap:${animationId}:meta`;
54+
if (
55+
sdkSession &&
56+
writeProjectFile &&
57+
editHistory &&
58+
reloadPreview &&
59+
domEditSaveTimestampRef
60+
) {
61+
const targetPath = selection.sourceFile || activeCompPath || "index.html";
62+
const handled = await sdkGsapTweenPersist(
63+
targetPath,
64+
{ kind: "set", animationId, properties: updates },
65+
sdkSession,
66+
{ editHistory, writeProjectFile, reloadPreview, domEditSaveTimestampRef },
67+
{ label: "Edit GSAP animation", coalesceKey: `gsap:${animationId}:meta` },
68+
);
69+
if (handled) return;
70+
}
3571
commitMutationSafely(
3672
selection,
3773
{ type: "update-meta", animationId, updates },
38-
{ label: "Edit GSAP animation", coalesceKey: metaKey },
74+
{ label: "Edit GSAP animation", coalesceKey: `gsap:${animationId}:meta` },
3975
);
4076
},
41-
[commitMutationSafely],
77+
[
78+
commitMutationSafely,
79+
activeCompPath,
80+
sdkSession,
81+
writeProjectFile,
82+
editHistory,
83+
reloadPreview,
84+
domEditSaveTimestampRef,
85+
],
4286
);
4387

4488
const deleteGsapAnimation = useCallback(
45-
(selection: DomEditSelection, animationId: string) => {
89+
async (selection: DomEditSelection, animationId: string) => {
90+
if (
91+
sdkSession &&
92+
writeProjectFile &&
93+
editHistory &&
94+
reloadPreview &&
95+
domEditSaveTimestampRef
96+
) {
97+
const targetPath = selection.sourceFile || activeCompPath || "index.html";
98+
const handled = await sdkGsapTweenPersist(
99+
targetPath,
100+
{ kind: "remove", animationId },
101+
sdkSession,
102+
{ editHistory, writeProjectFile, reloadPreview, domEditSaveTimestampRef },
103+
{ label: "Delete GSAP animation" },
104+
);
105+
if (handled) return;
106+
}
46107
commitMutationSafely(
47108
selection,
48109
{ type: "delete", animationId, stripStudioEdits: true },
49110
{ label: "Delete GSAP animation" },
50111
);
51112
},
52-
[commitMutationSafely],
113+
[
114+
commitMutationSafely,
115+
activeCompPath,
116+
sdkSession,
117+
writeProjectFile,
118+
editHistory,
119+
reloadPreview,
120+
domEditSaveTimestampRef,
121+
],
53122
);
54123

55124
const deleteAllForSelector = useCallback(
56125
(selection: DomEditSelection, targetSelector: string) => {
126+
// ponytail: no SDK op for delete-all-for-selector; stays server-authoritative
57127
void commitMutation(
58128
selection,
59129
{ type: "delete-all-for-selector", targetSelector },
@@ -63,6 +133,7 @@ export function useGsapAnimationOps({
63133
[commitMutation],
64134
);
65135

136+
// fallow-ignore-next-line complexity
66137
const addGsapAnimation = useCallback(
67138
// fallow-ignore-next-line complexity
68139
async (
@@ -97,6 +168,35 @@ export function useGsapAnimationOps({
97168
fromTo: { x: 0, y: 0, opacity: 1 },
98169
};
99170

171+
// SDK path: addGsapTween only supports from/to/fromTo; "set" stays server-side
172+
if (
173+
method !== "set" &&
174+
selection.hfId &&
175+
sdkSession &&
176+
writeProjectFile &&
177+
editHistory &&
178+
reloadPreview &&
179+
domEditSaveTimestampRef
180+
) {
181+
const targetPath = selection.sourceFile || activeCompPath || "index.html";
182+
const spec = {
183+
method: method as "to" | "from" | "fromTo",
184+
position,
185+
duration,
186+
ease: "power2.out" as const,
187+
properties: toDefaults[method] ?? { opacity: 1 },
188+
fromProperties: method === "fromTo" ? { opacity: 0 } : undefined,
189+
};
190+
const handled = await sdkGsapTweenPersist(
191+
targetPath,
192+
{ kind: "add", target: selection.hfId, spec },
193+
sdkSession,
194+
{ editHistory, writeProjectFile, reloadPreview, domEditSaveTimestampRef },
195+
{ label: `Add GSAP ${method} animation` },
196+
);
197+
if (handled) return;
198+
}
199+
100200
await commitMutation(
101201
selection,
102202
{
@@ -112,7 +212,17 @@ export function useGsapAnimationOps({
112212
{ label: `Add GSAP ${method} animation` },
113213
);
114214
},
115-
[activeCompPath, commitMutation, projectIdRef, showToast],
215+
[
216+
activeCompPath,
217+
commitMutation,
218+
projectIdRef,
219+
showToast,
220+
sdkSession,
221+
writeProjectFile,
222+
editHistory,
223+
reloadPreview,
224+
domEditSaveTimestampRef,
225+
],
116226
);
117227

118228
return {

0 commit comments

Comments
 (0)