@@ -2,14 +2,30 @@ import { useCallback } from "react";
22import type { Composition } from "@hyperframes/sdk" ;
33import type { DomEditSelection } from "../components/editor/domEditingTypes" ;
44import { roundTo3 } from "../utils/rounding" ;
5- import { runShadowGsapTween , type ShadowGsapOp } from "../utils/sdkShadow " ;
5+ import { sdkGsapTweenPersist } from "../utils/sdkCutover " ;
66import {
77 assignGsapTargetAutoIdIfNeeded ,
88 ensureElementAddressable ,
99} from "./gsapScriptCommitHelpers" ;
1010import type { CommitMutation , SafeGsapCommitMutation } from "./gsapScriptCommitTypes" ;
11+ import type { EditHistoryKind } from "../utils/editHistory" ;
1112
12- 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 {
1329 projectIdRef : React . MutableRefObject < string | null > ;
1430 activeCompPath : string | null ;
1531 commitMutation : CommitMutation ;
@@ -26,49 +42,92 @@ export function useGsapAnimationOps({
2642 commitMutationSafely,
2743 showToast,
2844 sdkSession,
45+ writeProjectFile,
46+ editHistory,
47+ reloadPreview,
48+ domEditSaveTimestampRef,
2949} : GsapAnimationOpsParams ) {
3050 const updateGsapMeta = useCallback (
31- (
51+ async (
3252 selection : DomEditSelection ,
3353 animationId : string ,
3454 updates : { duration ?: number ; ease ?: string ; position ?: number } ,
3555 ) => {
36- // Shadow op (server animationId shares the SDK id-space): existence via
37- // runShadowGsapTween (live session) + value fidelity via the chokepoint.
38- const shadowGsapOp : ShadowGsapOp = {
39- kind : "set" ,
40- animationId,
41- properties : { duration : updates . duration , ease : updates . ease , position : updates . position } ,
42- } ;
43- // coalesceKey groups rapid meta edits into one history entry. Request
44- // serialization is now handled per-file at the commitMutation chokepoint
45- // (useGsapScriptCommits), so no per-op serializeKey is needed here.
46- const metaKey = `gsap:${ animationId } :meta` ;
56+ if (
57+ sdkSession &&
58+ writeProjectFile &&
59+ editHistory &&
60+ reloadPreview &&
61+ domEditSaveTimestampRef
62+ ) {
63+ const targetPath = selection . sourceFile || activeCompPath || "index.html" ;
64+ const handled = await sdkGsapTweenPersist (
65+ targetPath ,
66+ { kind : "set" , animationId, properties : updates } ,
67+ sdkSession ,
68+ { editHistory, writeProjectFile, reloadPreview, domEditSaveTimestampRef } ,
69+ { label : "Edit GSAP animation" , coalesceKey : `gsap:${ animationId } :meta` } ,
70+ ) ;
71+ if ( handled ) return ;
72+ }
4773 commitMutationSafely (
4874 selection ,
4975 { type : "update-meta" , animationId, updates } ,
50- { label : "Edit GSAP animation" , coalesceKey : metaKey , shadowGsapOp } ,
76+ { label : "Edit GSAP animation" , coalesceKey : `gsap: ${ animationId } :meta` } ,
5177 ) ;
5278 if ( sdkSession ) runShadowGsapTween ( sdkSession , shadowGsapOp ) ;
5379 } ,
54- [ commitMutationSafely , sdkSession ] ,
80+ [
81+ commitMutationSafely ,
82+ activeCompPath ,
83+ sdkSession ,
84+ writeProjectFile ,
85+ editHistory ,
86+ reloadPreview ,
87+ domEditSaveTimestampRef ,
88+ ] ,
5589 ) ;
5690
5791 const deleteGsapAnimation = useCallback (
58- ( selection : DomEditSelection , animationId : string ) => {
59- const shadowGsapOp : ShadowGsapOp = { kind : "remove" , animationId } ;
92+ async ( selection : DomEditSelection , animationId : string ) => {
93+ if (
94+ sdkSession &&
95+ writeProjectFile &&
96+ editHistory &&
97+ reloadPreview &&
98+ domEditSaveTimestampRef
99+ ) {
100+ const targetPath = selection . sourceFile || activeCompPath || "index.html" ;
101+ const handled = await sdkGsapTweenPersist (
102+ targetPath ,
103+ { kind : "remove" , animationId } ,
104+ sdkSession ,
105+ { editHistory, writeProjectFile, reloadPreview, domEditSaveTimestampRef } ,
106+ { label : "Delete GSAP animation" } ,
107+ ) ;
108+ if ( handled ) return ;
109+ }
60110 commitMutationSafely (
61111 selection ,
62112 { type : "delete" , animationId, stripStudioEdits : true } ,
63113 { label : "Delete GSAP animation" , shadowGsapOp } ,
64114 ) ;
65115 if ( sdkSession ) runShadowGsapTween ( sdkSession , shadowGsapOp ) ;
66116 } ,
67- [ commitMutationSafely , sdkSession ] ,
117+ [
118+ commitMutationSafely ,
119+ activeCompPath ,
120+ sdkSession ,
121+ writeProjectFile ,
122+ editHistory ,
123+ reloadPreview ,
124+ domEditSaveTimestampRef ,
125+ ] ,
68126 ) ;
69127
70128 const deleteAllForSelector = useCallback (
71129 ( selection : DomEditSelection , targetSelector : string ) => {
130+ // ponytail: no SDK op for delete-all-for-selector; stays server-authoritative
72131 void commitMutation (
73132 selection ,
74133 { type : "delete-all-for-selector" , targetSelector } ,
@@ -78,8 +137,7 @@ export function useGsapAnimationOps({
78137 [ commitMutation ] ,
79138 ) ;
80139
81- // Pre-existing complexity (auto-id assignment + per-method defaults); this PR
82- // adds only a guarded shadow-op construction at the tail.
140+ // fallow-ignore-next-line complexity
83141 const addGsapAnimation = useCallback (
84142 // fallow-ignore-next-line complexity
85143 async (
@@ -114,25 +172,34 @@ export function useGsapAnimationOps({
114172 fromTo : { x : 0 , y : 0 , opacity : 1 } ,
115173 } ;
116174
117- // Shadow op (server stays authoritative). "set" has no SDK method, so it
118- // is not shadowed; otherwise: existence via runShadowGsapTween (live) +
119- // value fidelity via the chokepoint (shadowGsapOp in options).
120- const shadowGsapOp : ShadowGsapOp | undefined =
121- selection . hfId && method !== "set"
122- ? {
123- kind : "add" ,
124- target : selection . hfId ,
125- tween : {
126- method,
127- position,
128- duration,
129- ease : "power2.out" ,
130- ...( method === "fromTo"
131- ? { fromProperties : { opacity : 0 } , toProperties : toDefaults [ method ] }
132- : { properties : toDefaults [ method ] ?? { opacity : 1 } } ) ,
133- } ,
134- }
135- : undefined ;
175+ // SDK path: addGsapTween only supports from/to/fromTo; "set" stays server-side
176+ if (
177+ method !== "set" &&
178+ selection . hfId &&
179+ sdkSession &&
180+ writeProjectFile &&
181+ editHistory &&
182+ reloadPreview &&
183+ domEditSaveTimestampRef
184+ ) {
185+ const targetPath = selection . sourceFile || activeCompPath || "index.html" ;
186+ const spec = {
187+ method : method as "to" | "from" | "fromTo" ,
188+ position,
189+ duration,
190+ ease : "power2.out" as const ,
191+ properties : toDefaults [ method ] ?? { opacity : 1 } ,
192+ fromProperties : method === "fromTo" ? { opacity : 0 } : undefined ,
193+ } ;
194+ const handled = await sdkGsapTweenPersist (
195+ targetPath ,
196+ { kind : "add" , target : selection . hfId , spec } ,
197+ sdkSession ,
198+ { editHistory, writeProjectFile, reloadPreview, domEditSaveTimestampRef } ,
199+ { label : `Add GSAP ${ method } animation` } ,
200+ ) ;
201+ if ( handled ) return ;
202+ }
136203
137204 await commitMutation (
138205 selection ,
@@ -151,7 +218,17 @@ export function useGsapAnimationOps({
151218
152219 if ( sdkSession && shadowGsapOp ) runShadowGsapTween ( sdkSession , shadowGsapOp ) ;
153220 } ,
154- [ activeCompPath , commitMutation , projectIdRef , showToast , sdkSession ] ,
221+ [
222+ activeCompPath ,
223+ commitMutation ,
224+ projectIdRef ,
225+ showToast ,
226+ sdkSession ,
227+ writeProjectFile ,
228+ editHistory ,
229+ reloadPreview ,
230+ domEditSaveTimestampRef ,
231+ ] ,
155232 ) ;
156233
157234 return {
0 commit comments