@hyperframes/studio 0.6.97 → 0.6.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/hyperframes-player-DgsMQSvV.js +418 -0
- package/dist/assets/index-B62bDCQv.css +1 -0
- package/dist/assets/{index-HveJ0MuV.js → index-C52IT_lp.js} +1 -1
- package/dist/assets/index-DOh7E1uj.js +1 -0
- package/dist/assets/index-DrwSRbsl.js +252 -0
- package/dist/index.html +2 -2
- package/package.json +7 -5
- package/src/App.tsx +182 -177
- package/src/captions/store.ts +11 -11
- package/src/components/StudioHeader.tsx +4 -4
- package/src/components/StudioLeftSidebar.tsx +2 -2
- package/src/components/StudioPreviewArea.tsx +225 -183
- package/src/components/StudioRightPanel.tsx +3 -3
- package/src/components/TimelineToolbar.tsx +25 -0
- package/src/components/editor/DomEditOverlay.tsx +2 -5
- package/src/components/editor/EaseCurveSection.tsx +2 -3
- package/src/components/editor/GestureTrailOverlay.tsx +4 -3
- package/src/components/editor/LayersPanel.tsx +3 -9
- package/src/components/editor/PropertyPanel.tsx +20 -61
- package/src/components/editor/colorValue.ts +3 -1
- package/src/components/editor/domEditOverlayGestures.ts +54 -1
- package/src/components/editor/domEditOverlayStartGesture.ts +5 -2
- package/src/components/editor/gradientValue.ts +3 -3
- package/src/components/editor/keyframeMove.test.ts +101 -0
- package/src/components/editor/keyframeMove.ts +151 -0
- package/src/components/editor/manualEditsDom.ts +0 -12
- package/src/components/editor/propertyPanelHelpers.ts +10 -38
- package/src/components/editor/propertyPanelMediaSection.tsx +1 -5
- package/src/components/editor/propertyPanelTimingSection.tsx +1 -6
- package/src/components/editor/propertyPanelTransformCommit.ts +129 -0
- package/src/components/editor/studioMotionOps.test.ts +1 -1
- package/src/components/editor/studioMotionOps.ts +2 -1
- package/src/components/editor/useDomEditOverlayGestures.ts +1 -46
- package/src/components/nle/NLELayout.tsx +1 -24
- package/src/components/sidebar/BlocksTab.tsx +2 -2
- package/src/contexts/DomEditContext.tsx +134 -31
- package/src/contexts/StudioContext.tsx +90 -40
- package/src/contexts/TimelineEditContext.tsx +47 -0
- package/src/hooks/domEditCommitTypes.ts +14 -0
- package/src/hooks/gsapDragCommit.ts +9 -24
- package/src/hooks/gsapKeyframeCacheHelpers.ts +2 -1
- package/src/hooks/gsapKeyframeCommit.ts +5 -15
- package/src/hooks/gsapRuntimeBridge.ts +18 -52
- package/src/hooks/gsapRuntimeKeyframes.ts +8 -57
- package/src/hooks/gsapRuntimeReaders.ts +19 -26
- package/src/hooks/gsapScriptCommitHelpers.ts +1 -11
- package/src/hooks/gsapScriptCommitTypes.ts +58 -0
- package/src/hooks/gsapShared.ts +157 -0
- package/src/hooks/timelineEditingHelpers.ts +63 -2
- package/src/hooks/useAnimatedPropertyCommit.ts +3 -25
- package/src/hooks/useAppHotkeys.ts +299 -377
- package/src/hooks/useConsoleErrorCapture.ts +33 -5
- package/src/hooks/useDomEditCommits.ts +35 -293
- package/src/hooks/useDomEditPositionPatchCommit.ts +1 -1
- package/src/hooks/useDomEditSession.ts +78 -249
- package/src/hooks/useDomEditTextCommits.ts +1 -1
- package/src/hooks/useDomEditWiring.ts +255 -0
- package/src/hooks/useDomGeometryCommits.ts +181 -0
- package/src/hooks/useDomSelection.ts +10 -27
- package/src/hooks/useEditorSave.ts +82 -0
- package/src/hooks/useElementLifecycleOps.ts +177 -0
- package/src/hooks/useEnableKeyframes.ts +10 -15
- package/src/hooks/useFileManager.ts +32 -114
- package/src/hooks/useFileTree.ts +80 -0
- package/src/hooks/useGestureCommit.ts +7 -5
- package/src/hooks/useGestureRecording.ts +1 -1
- package/src/hooks/useGsapAnimationOps.ts +122 -0
- package/src/hooks/useGsapArcPathOps.ts +61 -0
- package/src/hooks/useGsapAwareEditing.ts +242 -0
- package/src/hooks/useGsapKeyframeOps.ts +167 -0
- package/src/hooks/useGsapPropertyDebounce.ts +135 -0
- package/src/hooks/useGsapScriptCommits.ts +58 -570
- package/src/hooks/useGsapSelectionHandlers.ts +22 -9
- package/src/hooks/useGsapTweenCache.ts +35 -29
- package/src/hooks/useLintModal.ts +7 -0
- package/src/hooks/useMusicBeatAnalysis.ts +152 -0
- package/src/hooks/useRazorSplit.ts +1 -1
- package/src/hooks/useRenderClipContent.ts +46 -21
- package/src/hooks/useTimelineEditing.ts +48 -4
- package/src/player/components/AudioWaveform.tsx +29 -4
- package/src/player/components/BeatStrip.tsx +166 -0
- package/src/player/components/Timeline.tsx +39 -18
- package/src/player/components/TimelineCanvas.tsx +52 -12
- package/src/player/components/TimelineClipDiamonds.tsx +130 -20
- package/src/player/components/TimelinePropertyRows.tsx +8 -2
- package/src/player/components/TimelineRuler.tsx +36 -2
- package/src/player/components/timelineEditing.ts +30 -5
- package/src/player/components/useTimelineClipDrag.ts +155 -4
- package/src/player/components/useTimelinePlayhead.ts +30 -1
- package/src/player/hooks/useTimelinePlayer.ts +47 -45
- package/src/player/lib/mediaProbe.ts +46 -3
- package/src/player/lib/playbackScrub.ts +16 -0
- package/src/player/lib/timelineDOM.ts +10 -2
- package/src/player/lib/timelineIframeHelpers.ts +89 -0
- package/src/player/store/playerStore.ts +92 -33
- package/src/utils/beatEditActions.ts +109 -0
- package/src/utils/beatEditing.ts +136 -0
- package/src/utils/clipboardPayload.ts +3 -2
- package/src/utils/compositionPatterns.ts +2 -0
- package/src/utils/keyframeSelection.test.ts +45 -0
- package/src/utils/keyframeSelection.ts +29 -0
- package/src/utils/rounding.ts +9 -0
- package/src/utils/studioHelpers.ts +5 -2
- package/src/utils/studioUrlState.ts +2 -1
- package/src/utils/timelineAssetDrop.ts +6 -5
- package/src/utils/timelineInspector.ts +15 -100
- package/dist/assets/hyperframes-player-Daj5djxa.js +0 -418
- package/dist/assets/index-B0twsRu0.css +0 -1
- package/dist/assets/index-Cfye9xzo.js +0 -251
- package/src/components/editor/DopesheetStrip.tsx +0 -141
- package/src/components/editor/StaggerControls.tsx +0 -61
- package/src/components/editor/TimelineLayerPanel.test.ts +0 -42
- package/src/components/editor/TimelineLayerPanel.tsx +0 -15
- package/src/components/nle/TimelineEditorNotice.tsx +0 -133
- package/src/hooks/gsapRuntimePreview.ts +0 -19
- package/src/player/components/timelineUtils.ts +0 -211
- package/src/utils/audioBeatDetection.ts +0 -58
- package/src/utils/keyframeSnapping.test.ts +0 -74
- package/src/utils/keyframeSnapping.ts +0 -63
- package/src/utils/timelineInspector.test.ts +0 -79
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
2
1
|
import type { TimelineElement } from "../player";
|
|
3
|
-
import { usePlayerStore } from "../player";
|
|
4
|
-
import {
|
|
5
|
-
STUDIO_GSAP_DRAG_INTERCEPT_ENABLED,
|
|
6
|
-
STUDIO_GSAP_PANEL_ENABLED,
|
|
7
|
-
} from "../components/editor/manualEditingAvailability";
|
|
8
|
-
import { type DomEditSelection } from "../components/editor/domEditing";
|
|
9
|
-
import { useDomEditPreviewSync } from "./useDomEditPreviewSync";
|
|
10
2
|
import type { ImportedFontAsset } from "../components/editor/fontAssets";
|
|
11
3
|
import type { EditHistoryKind } from "../utils/editHistory";
|
|
12
4
|
import type { RightPanelTab } from "../utils/studioHelpers";
|
|
@@ -15,22 +7,11 @@ import type { SidebarTab } from "../components/sidebar/LeftSidebar";
|
|
|
15
7
|
import { useAskAgentModal } from "./useAskAgentModal";
|
|
16
8
|
import { useDomSelection } from "./useDomSelection";
|
|
17
9
|
import { usePreviewInteraction } from "./usePreviewInteraction";
|
|
18
|
-
import {
|
|
10
|
+
import { useDomEditCommits } from "./useDomEditCommits";
|
|
19
11
|
import { useGsapScriptCommits } from "./useGsapScriptCommits";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
usePopulateKeyframeCacheForFile,
|
|
24
|
-
} from "./useGsapTweenCache";
|
|
25
|
-
import {
|
|
26
|
-
tryGsapDragIntercept,
|
|
27
|
-
tryGsapResizeIntercept,
|
|
28
|
-
tryGsapRotationIntercept,
|
|
29
|
-
} from "./gsapRuntimeBridge";
|
|
30
|
-
import { useAnimatedPropertyCommit } from "./useAnimatedPropertyCommit";
|
|
31
|
-
import { useGsapAnimationFetchFallback } from "./useGsapAnimationFetchFallback";
|
|
32
|
-
import { useGsapInteractionFailureTelemetry } from "./useGsapInteractionFailureTelemetry";
|
|
33
|
-
import { useGsapSelectionHandlers } from "./useGsapSelectionHandlers";
|
|
12
|
+
import { useGsapCacheVersion } from "./useGsapTweenCache";
|
|
13
|
+
import { useDomEditWiring } from "./useDomEditWiring";
|
|
14
|
+
import { useGsapAwareEditing } from "./useGsapAwareEditing";
|
|
34
15
|
|
|
35
16
|
// ── Types ──
|
|
36
17
|
|
|
@@ -81,7 +62,6 @@ export interface UseDomEditSessionParams {
|
|
|
81
62
|
|
|
82
63
|
// ── Hook ──
|
|
83
64
|
|
|
84
|
-
// fallow-ignore-next-line complexity
|
|
85
65
|
export function useDomEditSession({
|
|
86
66
|
projectId,
|
|
87
67
|
activeCompPath,
|
|
@@ -118,22 +98,9 @@ export function useDomEditSession({
|
|
|
118
98
|
getSidebarTab,
|
|
119
99
|
}: UseDomEditSessionParams) {
|
|
120
100
|
void _setRefreshKey;
|
|
101
|
+
void _readProjectFile;
|
|
121
102
|
|
|
122
|
-
|
|
123
|
-
(selection: DomEditSelection) => {
|
|
124
|
-
if (!openSourceForSelection || !selectSidebarTab) return;
|
|
125
|
-
if (!selection.sourceFile) return;
|
|
126
|
-
selectSidebarTab("code");
|
|
127
|
-
openSourceForSelection(selection.sourceFile, {
|
|
128
|
-
id: selection.id,
|
|
129
|
-
selector: selection.selector,
|
|
130
|
-
selectorIndex: selection.selectorIndex,
|
|
131
|
-
});
|
|
132
|
-
},
|
|
133
|
-
[openSourceForSelection, selectSidebarTab],
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
// ── Selection (delegated to useDomSelection) ──
|
|
103
|
+
// ── Selection ──
|
|
137
104
|
|
|
138
105
|
const {
|
|
139
106
|
domEditSelection,
|
|
@@ -165,7 +132,7 @@ export function useDomEditSession({
|
|
|
165
132
|
rightPanelTab,
|
|
166
133
|
});
|
|
167
134
|
|
|
168
|
-
// ── Agent modal
|
|
135
|
+
// ── Agent modal ──
|
|
169
136
|
|
|
170
137
|
const {
|
|
171
138
|
agentModalOpen,
|
|
@@ -187,75 +154,11 @@ export function useDomEditSession({
|
|
|
187
154
|
domEditSelection,
|
|
188
155
|
});
|
|
189
156
|
|
|
190
|
-
// ──
|
|
191
|
-
|
|
192
|
-
const {
|
|
193
|
-
handlePreviewCanvasMouseDown,
|
|
194
|
-
handlePreviewCanvasPointerMove,
|
|
195
|
-
handlePreviewCanvasPointerLeave,
|
|
196
|
-
handleBlockedDomMove,
|
|
197
|
-
handleDomManualDragStart,
|
|
198
|
-
} = usePreviewInteraction({
|
|
199
|
-
captionEditMode,
|
|
200
|
-
compositionLoading,
|
|
201
|
-
previewIframeRef,
|
|
202
|
-
showToast,
|
|
203
|
-
applyDomSelection,
|
|
204
|
-
resolveDomSelectionFromPreviewPoint,
|
|
205
|
-
resolveAllDomSelectionsFromPreviewPoint,
|
|
206
|
-
updateDomEditHoverSelection,
|
|
207
|
-
onClickToSource,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Sync DOM selection → timeline selectedElementId so that clip selection
|
|
211
|
-
// highlights and diamond playhead fills work on cold-load URL restore.
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
if (!domEditSelection?.id) return;
|
|
214
|
-
const { selectedElementId, elements, setSelectedElementId } = usePlayerStore.getState();
|
|
215
|
-
const matchKey = elements.find(
|
|
216
|
-
(el) => el.domId === domEditSelection.id || el.id === domEditSelection.id,
|
|
217
|
-
);
|
|
218
|
-
const key = matchKey ? (matchKey.key ?? matchKey.id) : null;
|
|
219
|
-
if (key && key !== selectedElementId) setSelectedElementId(key);
|
|
220
|
-
}, [domEditSelection?.id]);
|
|
221
|
-
|
|
222
|
-
// ── GSAP script editing ──
|
|
157
|
+
// ── GSAP cache (hoisted so both useGsapScriptCommits and useDomEditWiring share the same instance) ──
|
|
223
158
|
|
|
224
159
|
const { version: gsapCacheVersion, bump: bumpGsapCache } = useGsapCacheVersion();
|
|
225
160
|
|
|
226
|
-
//
|
|
227
|
-
// reload via refreshKey but don't go through commitMutation, so the cache
|
|
228
|
-
// would otherwise retain stale keyframe entries).
|
|
229
|
-
const prevRefreshKeyRef = useRef(refreshKey);
|
|
230
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
231
|
-
useEffect(() => {
|
|
232
|
-
if (refreshKey !== prevRefreshKeyRef.current) {
|
|
233
|
-
prevRefreshKeyRef.current = refreshKey;
|
|
234
|
-
bumpGsapCache();
|
|
235
|
-
}
|
|
236
|
-
}, [refreshKey, bumpGsapCache]);
|
|
237
|
-
|
|
238
|
-
const gsapSourceFile = domEditSelection?.sourceFile || activeCompPath || "index.html";
|
|
239
|
-
|
|
240
|
-
usePopulateKeyframeCacheForFile(
|
|
241
|
-
STUDIO_GSAP_PANEL_ENABLED ? (projectId ?? null) : null,
|
|
242
|
-
gsapSourceFile,
|
|
243
|
-
gsapCacheVersion,
|
|
244
|
-
previewIframeRef,
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
const {
|
|
248
|
-
animations: selectedGsapAnimations,
|
|
249
|
-
multipleTimelines: gsapMultipleTimelines,
|
|
250
|
-
unsupportedTimelinePattern: gsapUnsupportedTimelinePattern,
|
|
251
|
-
} = useGsapAnimationsForElement(
|
|
252
|
-
STUDIO_GSAP_PANEL_ENABLED ? (projectId ?? null) : null,
|
|
253
|
-
gsapSourceFile,
|
|
254
|
-
domEditSelection
|
|
255
|
-
? { id: domEditSelection.id ?? null, selector: domEditSelection.selector ?? null }
|
|
256
|
-
: null,
|
|
257
|
-
gsapCacheVersion,
|
|
258
|
-
);
|
|
161
|
+
// ── GSAP script commits ──
|
|
259
162
|
|
|
260
163
|
const {
|
|
261
164
|
commitMutation: gsapCommitMutation,
|
|
@@ -288,7 +191,7 @@ export function useDomEditSession({
|
|
|
288
191
|
showToast,
|
|
289
192
|
});
|
|
290
193
|
|
|
291
|
-
// ──
|
|
194
|
+
// ── DOM commit handlers ──
|
|
292
195
|
|
|
293
196
|
const {
|
|
294
197
|
resolveImportedFontAsset,
|
|
@@ -326,108 +229,15 @@ export function useDomEditSession({
|
|
|
326
229
|
buildDomSelectionFromTarget,
|
|
327
230
|
});
|
|
328
231
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const makeFetchFallback = useGsapAnimationFetchFallback(projectId, gsapSourceFile);
|
|
332
|
-
|
|
333
|
-
// GSAP-aware: intercept offset/resize/rotation to commit via script mutation when animated.
|
|
334
|
-
const handleGsapAwarePathOffsetCommit = useCallback(
|
|
335
|
-
async (selection: DomEditSelection, next: { x: number; y: number }) => {
|
|
336
|
-
const hasGsapAnims = selectedGsapAnimations.length > 0;
|
|
337
|
-
if (hasGsapAnims && !STUDIO_GSAP_DRAG_INTERCEPT_ENABLED) {
|
|
338
|
-
showToast(GSAP_CSS_FALLBACK_BLOCKED_MESSAGE, "error");
|
|
339
|
-
throw new Error(GSAP_CSS_FALLBACK_BLOCKED_MESSAGE);
|
|
340
|
-
}
|
|
341
|
-
if (STUDIO_GSAP_DRAG_INTERCEPT_ENABLED && gsapCommitMutation) {
|
|
342
|
-
try {
|
|
343
|
-
const handled = await tryGsapDragIntercept(
|
|
344
|
-
selection,
|
|
345
|
-
next,
|
|
346
|
-
selectedGsapAnimations,
|
|
347
|
-
previewIframeRef.current,
|
|
348
|
-
gsapCommitMutation,
|
|
349
|
-
makeFetchFallback(selection),
|
|
350
|
-
);
|
|
351
|
-
if (handled) return;
|
|
352
|
-
} catch (error) {
|
|
353
|
-
trackGsapInteractionFailure(error, selection, "drag", "Move animated layer");
|
|
354
|
-
throw error;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
return handleDomPathOffsetCommit(selection, next);
|
|
358
|
-
},
|
|
359
|
-
[
|
|
360
|
-
handleDomPathOffsetCommit,
|
|
361
|
-
selectedGsapAnimations,
|
|
362
|
-
gsapCommitMutation,
|
|
363
|
-
previewIframeRef,
|
|
364
|
-
makeFetchFallback,
|
|
365
|
-
trackGsapInteractionFailure,
|
|
366
|
-
showToast,
|
|
367
|
-
],
|
|
368
|
-
);
|
|
369
|
-
|
|
370
|
-
const handleGsapAwareBoxSizeCommit = useCallback(
|
|
371
|
-
async (selection: DomEditSelection, next: { width: number; height: number }) => {
|
|
372
|
-
if (STUDIO_GSAP_DRAG_INTERCEPT_ENABLED && gsapCommitMutation) {
|
|
373
|
-
try {
|
|
374
|
-
const handled = await tryGsapResizeIntercept(
|
|
375
|
-
selection,
|
|
376
|
-
next,
|
|
377
|
-
selectedGsapAnimations,
|
|
378
|
-
previewIframeRef.current,
|
|
379
|
-
gsapCommitMutation,
|
|
380
|
-
makeFetchFallback(selection),
|
|
381
|
-
);
|
|
382
|
-
if (handled) return;
|
|
383
|
-
} catch (error) {
|
|
384
|
-
trackGsapInteractionFailure(error, selection, "resize", "Resize animated layer");
|
|
385
|
-
throw error;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
return handleDomBoxSizeCommit(selection, next);
|
|
389
|
-
},
|
|
390
|
-
[
|
|
391
|
-
handleDomBoxSizeCommit,
|
|
392
|
-
selectedGsapAnimations,
|
|
393
|
-
gsapCommitMutation,
|
|
394
|
-
previewIframeRef,
|
|
395
|
-
makeFetchFallback,
|
|
396
|
-
trackGsapInteractionFailure,
|
|
397
|
-
],
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
const handleGsapAwareRotationCommit = useCallback(
|
|
401
|
-
async (selection: DomEditSelection, next: { angle: number }) => {
|
|
402
|
-
if (STUDIO_GSAP_DRAG_INTERCEPT_ENABLED && gsapCommitMutation) {
|
|
403
|
-
try {
|
|
404
|
-
const handled = await tryGsapRotationIntercept(
|
|
405
|
-
selection,
|
|
406
|
-
next.angle,
|
|
407
|
-
selectedGsapAnimations,
|
|
408
|
-
previewIframeRef.current,
|
|
409
|
-
gsapCommitMutation,
|
|
410
|
-
makeFetchFallback(selection),
|
|
411
|
-
);
|
|
412
|
-
if (handled) return;
|
|
413
|
-
} catch (error) {
|
|
414
|
-
trackGsapInteractionFailure(error, selection, "rotation", "Rotate animated layer");
|
|
415
|
-
throw error;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
return handleDomRotationCommit(selection, next);
|
|
419
|
-
},
|
|
420
|
-
[
|
|
421
|
-
handleDomRotationCommit,
|
|
422
|
-
selectedGsapAnimations,
|
|
423
|
-
gsapCommitMutation,
|
|
424
|
-
previewIframeRef,
|
|
425
|
-
makeFetchFallback,
|
|
426
|
-
trackGsapInteractionFailure,
|
|
427
|
-
],
|
|
428
|
-
);
|
|
232
|
+
// ── Wiring: selection sync, GSAP cache, preview sync, selection handlers ──
|
|
429
233
|
|
|
430
234
|
const {
|
|
235
|
+
onClickToSource,
|
|
236
|
+
selectedGsapAnimations,
|
|
237
|
+
gsapMultipleTimelines,
|
|
238
|
+
gsapUnsupportedTimelinePattern,
|
|
239
|
+
trackGsapInteractionFailure,
|
|
240
|
+
makeFetchFallback,
|
|
431
241
|
handleGsapUpdateProperty,
|
|
432
242
|
handleGsapUpdateMeta,
|
|
433
243
|
handleGsapDeleteAnimation,
|
|
@@ -444,8 +254,26 @@ export function useDomEditSession({
|
|
|
444
254
|
handleGsapConvertToKeyframes,
|
|
445
255
|
handleGsapRemoveAllKeyframes,
|
|
446
256
|
handleResetSelectedElementKeyframes,
|
|
447
|
-
} =
|
|
257
|
+
} = useDomEditWiring({
|
|
258
|
+
projectId,
|
|
259
|
+
activeCompPath,
|
|
448
260
|
domEditSelection,
|
|
261
|
+
domEditSelectionRef,
|
|
262
|
+
previewIframeRef,
|
|
263
|
+
previewIframe,
|
|
264
|
+
captionEditMode,
|
|
265
|
+
refreshKey,
|
|
266
|
+
gsapCacheVersion,
|
|
267
|
+
bumpGsapCache,
|
|
268
|
+
showToast,
|
|
269
|
+
refreshPreviewDocumentVersion,
|
|
270
|
+
syncPreviewHistoryHotkey,
|
|
271
|
+
applyStudioManualEditsToPreviewRef,
|
|
272
|
+
applyDomSelection,
|
|
273
|
+
buildDomSelectionFromTarget,
|
|
274
|
+
openSourceForSelection,
|
|
275
|
+
selectSidebarTab,
|
|
276
|
+
getSidebarTab,
|
|
449
277
|
updateGsapProperty,
|
|
450
278
|
updateGsapMeta,
|
|
451
279
|
deleteGsapAnimation,
|
|
@@ -462,47 +290,54 @@ export function useDomEditSession({
|
|
|
462
290
|
convertToKeyframes,
|
|
463
291
|
removeAllKeyframes,
|
|
464
292
|
handleDomManualEditsReset,
|
|
465
|
-
selectedGsapAnimations,
|
|
466
293
|
});
|
|
467
294
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
295
|
+
// ── Preview interaction ──
|
|
296
|
+
|
|
297
|
+
const {
|
|
298
|
+
handlePreviewCanvasMouseDown,
|
|
299
|
+
handlePreviewCanvasPointerMove,
|
|
300
|
+
handlePreviewCanvasPointerLeave,
|
|
301
|
+
handleBlockedDomMove,
|
|
302
|
+
handleDomManualDragStart,
|
|
303
|
+
} = usePreviewInteraction({
|
|
304
|
+
captionEditMode,
|
|
305
|
+
compositionLoading,
|
|
473
306
|
previewIframeRef,
|
|
474
|
-
|
|
307
|
+
showToast,
|
|
308
|
+
applyDomSelection,
|
|
309
|
+
resolveDomSelectionFromPreviewPoint,
|
|
310
|
+
resolveAllDomSelectionsFromPreviewPoint,
|
|
311
|
+
updateDomEditHoverSelection,
|
|
312
|
+
onClickToSource,
|
|
475
313
|
});
|
|
476
314
|
|
|
477
|
-
|
|
478
|
-
(animId: string, config: Parameters<typeof setArcPath>[2]) => {
|
|
479
|
-
if (!domEditSelection) return;
|
|
480
|
-
setArcPath(domEditSelection, animId, config);
|
|
481
|
-
},
|
|
482
|
-
[domEditSelection, setArcPath],
|
|
483
|
-
);
|
|
315
|
+
// ── GSAP-aware geometry intercepts + animated property commit ──
|
|
484
316
|
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
previewIframe,
|
|
495
|
-
activeCompPath,
|
|
496
|
-
captionEditMode,
|
|
497
|
-
domEditSelectionRef,
|
|
317
|
+
const {
|
|
318
|
+
handleGsapAwarePathOffsetCommit,
|
|
319
|
+
handleGsapAwareBoxSizeCommit,
|
|
320
|
+
handleGsapAwareRotationCommit,
|
|
321
|
+
commitAnimatedProperty,
|
|
322
|
+
handleSetArcPath,
|
|
323
|
+
handleUpdateArcSegment,
|
|
324
|
+
commitMutation,
|
|
325
|
+
} = useGsapAwareEditing({
|
|
498
326
|
domEditSelection,
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
327
|
+
selectedGsapAnimations,
|
|
328
|
+
gsapCommitMutation,
|
|
329
|
+
previewIframeRef,
|
|
330
|
+
showToast,
|
|
331
|
+
bumpGsapCache,
|
|
332
|
+
makeFetchFallback,
|
|
333
|
+
trackGsapInteractionFailure,
|
|
334
|
+
handleDomPathOffsetCommit,
|
|
335
|
+
handleDomBoxSizeCommit,
|
|
336
|
+
handleDomRotationCommit,
|
|
337
|
+
addGsapAnimation,
|
|
338
|
+
convertToKeyframes,
|
|
339
|
+
setArcPath,
|
|
340
|
+
updateArcSegment,
|
|
506
341
|
});
|
|
507
342
|
|
|
508
343
|
return {
|
|
@@ -574,12 +409,6 @@ export function useDomEditSession({
|
|
|
574
409
|
handleUpdateArcSegment,
|
|
575
410
|
invalidateGsapCache: bumpGsapCache,
|
|
576
411
|
previewIframeRef,
|
|
577
|
-
commitMutation
|
|
578
|
-
mutation: Record<string, unknown>,
|
|
579
|
-
options: { label: string; softReload?: boolean },
|
|
580
|
-
) => {
|
|
581
|
-
if (!domEditSelection) return;
|
|
582
|
-
await gsapCommitMutation(domEditSelection, mutation, options);
|
|
583
|
-
},
|
|
412
|
+
commitMutation,
|
|
584
413
|
};
|
|
585
414
|
}
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
type DomEditSelection,
|
|
23
23
|
} from "../components/editor/domEditing";
|
|
24
24
|
import type { ImportedFontAsset } from "../components/editor/fontAssets";
|
|
25
|
-
import type { PersistDomEditOperations } from "./
|
|
25
|
+
import type { PersistDomEditOperations } from "./domEditCommitTypes";
|
|
26
26
|
|
|
27
27
|
// ── Types ──
|
|
28
28
|
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiring layer for DOM edit sessions: click-to-source navigation,
|
|
3
|
+
* DOM selection to timeline sync, GSAP cache invalidation on refresh,
|
|
4
|
+
* GSAP cache population, animation resolution for the selected element,
|
|
5
|
+
* and preview sync side-effects.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from useDomEditSession to isolate orchestration wiring from
|
|
8
|
+
* the GSAP-aware geometry intercept logic.
|
|
9
|
+
*/
|
|
10
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
11
|
+
import type { DomEditSelection } from "../components/editor/domEditingTypes";
|
|
12
|
+
import { STUDIO_GSAP_PANEL_ENABLED } from "../components/editor/manualEditingAvailability";
|
|
13
|
+
import { usePlayerStore } from "../player";
|
|
14
|
+
import { useDomEditPreviewSync } from "./useDomEditPreviewSync";
|
|
15
|
+
import { useGsapAnimationsForElement, usePopulateKeyframeCacheForFile } from "./useGsapTweenCache";
|
|
16
|
+
import { useGsapAnimationFetchFallback } from "./useGsapAnimationFetchFallback";
|
|
17
|
+
import { useGsapInteractionFailureTelemetry } from "./useGsapInteractionFailureTelemetry";
|
|
18
|
+
import { useGsapSelectionHandlers } from "./useGsapSelectionHandlers";
|
|
19
|
+
import type { PatchTarget } from "../utils/sourcePatcher";
|
|
20
|
+
import type { SidebarTab } from "../components/sidebar/LeftSidebar";
|
|
21
|
+
|
|
22
|
+
export interface UseDomEditWiringParams {
|
|
23
|
+
projectId: string | null;
|
|
24
|
+
activeCompPath: string | null;
|
|
25
|
+
domEditSelection: DomEditSelection | null;
|
|
26
|
+
domEditSelectionRef: React.MutableRefObject<DomEditSelection | null>;
|
|
27
|
+
previewIframeRef: React.RefObject<HTMLIFrameElement | null>;
|
|
28
|
+
previewIframe: HTMLIFrameElement | null;
|
|
29
|
+
captionEditMode: boolean;
|
|
30
|
+
refreshKey: number;
|
|
31
|
+
gsapCacheVersion: number;
|
|
32
|
+
bumpGsapCache: () => void;
|
|
33
|
+
showToast: (message: string, tone?: "error" | "info") => void;
|
|
34
|
+
refreshPreviewDocumentVersion: () => void;
|
|
35
|
+
syncPreviewHistoryHotkey: (iframe: HTMLIFrameElement | null) => void;
|
|
36
|
+
applyStudioManualEditsToPreviewRef: React.MutableRefObject<
|
|
37
|
+
(iframe: HTMLIFrameElement) => Promise<void>
|
|
38
|
+
>;
|
|
39
|
+
applyDomSelection: (
|
|
40
|
+
selection: DomEditSelection | null,
|
|
41
|
+
options?: { revealPanel?: boolean; preserveGroup?: boolean },
|
|
42
|
+
) => void;
|
|
43
|
+
buildDomSelectionFromTarget: (element: HTMLElement) => Promise<DomEditSelection | null>;
|
|
44
|
+
openSourceForSelection?: (sourceFile: string, target: PatchTarget) => void;
|
|
45
|
+
selectSidebarTab?: (tab: SidebarTab) => void;
|
|
46
|
+
getSidebarTab?: () => SidebarTab;
|
|
47
|
+
// GSAP script commit ops (from useGsapScriptCommits)
|
|
48
|
+
updateGsapProperty: (
|
|
49
|
+
sel: DomEditSelection,
|
|
50
|
+
animId: string,
|
|
51
|
+
prop: string,
|
|
52
|
+
value: number | string,
|
|
53
|
+
) => void;
|
|
54
|
+
updateGsapMeta: (
|
|
55
|
+
sel: DomEditSelection,
|
|
56
|
+
animId: string,
|
|
57
|
+
updates: { duration?: number; ease?: string; position?: number },
|
|
58
|
+
) => void;
|
|
59
|
+
deleteGsapAnimation: (sel: DomEditSelection, animId: string) => void;
|
|
60
|
+
deleteAllForSelector: (sel: DomEditSelection, targetSelector: string) => void;
|
|
61
|
+
addGsapAnimation: (
|
|
62
|
+
sel: DomEditSelection,
|
|
63
|
+
method: "to" | "from" | "set" | "fromTo",
|
|
64
|
+
time: number,
|
|
65
|
+
) => Promise<void>;
|
|
66
|
+
addGsapProperty: (sel: DomEditSelection, animId: string, prop: string) => void;
|
|
67
|
+
removeGsapProperty: (sel: DomEditSelection, animId: string, prop: string) => void;
|
|
68
|
+
updateGsapFromProperty: (
|
|
69
|
+
sel: DomEditSelection,
|
|
70
|
+
animId: string,
|
|
71
|
+
prop: string,
|
|
72
|
+
value: number | string,
|
|
73
|
+
) => void;
|
|
74
|
+
addGsapFromProperty: (sel: DomEditSelection, animId: string, prop: string) => void;
|
|
75
|
+
removeGsapFromProperty: (sel: DomEditSelection, animId: string, prop: string) => void;
|
|
76
|
+
addKeyframe: (
|
|
77
|
+
sel: DomEditSelection,
|
|
78
|
+
animId: string,
|
|
79
|
+
percentage: number,
|
|
80
|
+
property: string,
|
|
81
|
+
value: number | string,
|
|
82
|
+
) => void;
|
|
83
|
+
addKeyframeBatch: (
|
|
84
|
+
sel: DomEditSelection,
|
|
85
|
+
animId: string,
|
|
86
|
+
percentage: number,
|
|
87
|
+
properties: Record<string, number | string>,
|
|
88
|
+
) => Promise<void>;
|
|
89
|
+
removeKeyframe: (sel: DomEditSelection, animId: string, percentage: number) => void;
|
|
90
|
+
convertToKeyframes: (
|
|
91
|
+
sel: DomEditSelection,
|
|
92
|
+
animId: string,
|
|
93
|
+
resolvedFromValues?: Record<string, number | string>,
|
|
94
|
+
) => Promise<void>;
|
|
95
|
+
removeAllKeyframes: (sel: DomEditSelection, animId: string) => void;
|
|
96
|
+
handleDomManualEditsReset: (sel: DomEditSelection) => void;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// fallow-ignore-next-line complexity
|
|
100
|
+
export function useDomEditWiring({
|
|
101
|
+
projectId,
|
|
102
|
+
activeCompPath,
|
|
103
|
+
domEditSelection,
|
|
104
|
+
domEditSelectionRef,
|
|
105
|
+
previewIframeRef,
|
|
106
|
+
previewIframe,
|
|
107
|
+
captionEditMode,
|
|
108
|
+
refreshKey,
|
|
109
|
+
gsapCacheVersion,
|
|
110
|
+
bumpGsapCache,
|
|
111
|
+
showToast,
|
|
112
|
+
refreshPreviewDocumentVersion,
|
|
113
|
+
syncPreviewHistoryHotkey,
|
|
114
|
+
applyStudioManualEditsToPreviewRef,
|
|
115
|
+
applyDomSelection,
|
|
116
|
+
buildDomSelectionFromTarget,
|
|
117
|
+
openSourceForSelection,
|
|
118
|
+
selectSidebarTab,
|
|
119
|
+
getSidebarTab,
|
|
120
|
+
updateGsapProperty,
|
|
121
|
+
updateGsapMeta,
|
|
122
|
+
deleteGsapAnimation,
|
|
123
|
+
deleteAllForSelector,
|
|
124
|
+
addGsapAnimation,
|
|
125
|
+
addGsapProperty,
|
|
126
|
+
removeGsapProperty,
|
|
127
|
+
updateGsapFromProperty,
|
|
128
|
+
addGsapFromProperty,
|
|
129
|
+
removeGsapFromProperty,
|
|
130
|
+
addKeyframe,
|
|
131
|
+
addKeyframeBatch,
|
|
132
|
+
removeKeyframe,
|
|
133
|
+
convertToKeyframes,
|
|
134
|
+
removeAllKeyframes,
|
|
135
|
+
handleDomManualEditsReset,
|
|
136
|
+
}: UseDomEditWiringParams) {
|
|
137
|
+
// ── Click-to-source navigation ──
|
|
138
|
+
|
|
139
|
+
const onClickToSource = useCallback(
|
|
140
|
+
(selection: DomEditSelection) => {
|
|
141
|
+
if (!openSourceForSelection || !selectSidebarTab) return;
|
|
142
|
+
if (!selection.sourceFile) return;
|
|
143
|
+
selectSidebarTab("code");
|
|
144
|
+
openSourceForSelection(selection.sourceFile, {
|
|
145
|
+
id: selection.id,
|
|
146
|
+
selector: selection.selector,
|
|
147
|
+
selectorIndex: selection.selectorIndex,
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
[openSourceForSelection, selectSidebarTab],
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// ── DOM selection -> timeline element sync ──
|
|
154
|
+
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (!domEditSelection?.id) return;
|
|
157
|
+
const { selectedElementId, elements, setSelectedElementId } = usePlayerStore.getState();
|
|
158
|
+
const matchKey = elements.find(
|
|
159
|
+
(el) => el.domId === domEditSelection.id || el.id === domEditSelection.id,
|
|
160
|
+
);
|
|
161
|
+
const key = matchKey ? (matchKey.key ?? matchKey.id) : null;
|
|
162
|
+
if (key && key !== selectedElementId) setSelectedElementId(key);
|
|
163
|
+
}, [domEditSelection?.id]);
|
|
164
|
+
|
|
165
|
+
// ── GSAP cache sync ──
|
|
166
|
+
|
|
167
|
+
// Bump GSAP cache when refreshKey changes (code-tab edits trigger iframe
|
|
168
|
+
// reload via refreshKey but don't go through commitMutation, so the cache
|
|
169
|
+
// would otherwise retain stale keyframe entries).
|
|
170
|
+
const prevRefreshKeyRef = useRef(refreshKey);
|
|
171
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (refreshKey !== prevRefreshKeyRef.current) {
|
|
174
|
+
prevRefreshKeyRef.current = refreshKey;
|
|
175
|
+
bumpGsapCache();
|
|
176
|
+
}
|
|
177
|
+
}, [refreshKey, bumpGsapCache]);
|
|
178
|
+
|
|
179
|
+
const gsapSourceFile = domEditSelection?.sourceFile || activeCompPath || "index.html";
|
|
180
|
+
|
|
181
|
+
usePopulateKeyframeCacheForFile(
|
|
182
|
+
STUDIO_GSAP_PANEL_ENABLED ? (projectId ?? null) : null,
|
|
183
|
+
gsapSourceFile,
|
|
184
|
+
gsapCacheVersion,
|
|
185
|
+
previewIframeRef,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
const {
|
|
189
|
+
animations: selectedGsapAnimations,
|
|
190
|
+
multipleTimelines: gsapMultipleTimelines,
|
|
191
|
+
unsupportedTimelinePattern: gsapUnsupportedTimelinePattern,
|
|
192
|
+
} = useGsapAnimationsForElement(
|
|
193
|
+
STUDIO_GSAP_PANEL_ENABLED ? (projectId ?? null) : null,
|
|
194
|
+
gsapSourceFile,
|
|
195
|
+
domEditSelection
|
|
196
|
+
? { id: domEditSelection.id ?? null, selector: domEditSelection.selector ?? null }
|
|
197
|
+
: null,
|
|
198
|
+
gsapCacheVersion,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// ── Telemetry & fallback ──
|
|
202
|
+
|
|
203
|
+
const trackGsapInteractionFailure = useGsapInteractionFailureTelemetry(activeCompPath, showToast);
|
|
204
|
+
const makeFetchFallback = useGsapAnimationFetchFallback(projectId, gsapSourceFile);
|
|
205
|
+
|
|
206
|
+
// ── GSAP selection handlers ──
|
|
207
|
+
|
|
208
|
+
const gsapSelectionHandlers = useGsapSelectionHandlers({
|
|
209
|
+
domEditSelection,
|
|
210
|
+
updateGsapProperty,
|
|
211
|
+
updateGsapMeta,
|
|
212
|
+
deleteGsapAnimation,
|
|
213
|
+
deleteAllForSelector,
|
|
214
|
+
addGsapAnimation,
|
|
215
|
+
addGsapProperty,
|
|
216
|
+
removeGsapProperty,
|
|
217
|
+
updateGsapFromProperty,
|
|
218
|
+
addGsapFromProperty,
|
|
219
|
+
removeGsapFromProperty,
|
|
220
|
+
addKeyframe,
|
|
221
|
+
addKeyframeBatch,
|
|
222
|
+
removeKeyframe,
|
|
223
|
+
convertToKeyframes,
|
|
224
|
+
removeAllKeyframes,
|
|
225
|
+
handleDomManualEditsReset,
|
|
226
|
+
selectedGsapAnimations,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// ── Preview sync side-effects ──
|
|
230
|
+
|
|
231
|
+
useDomEditPreviewSync({
|
|
232
|
+
previewIframe,
|
|
233
|
+
activeCompPath,
|
|
234
|
+
captionEditMode,
|
|
235
|
+
domEditSelectionRef,
|
|
236
|
+
domEditSelection,
|
|
237
|
+
applyDomSelection,
|
|
238
|
+
buildDomSelectionFromTarget,
|
|
239
|
+
refreshPreviewDocumentVersion,
|
|
240
|
+
syncPreviewHistoryHotkey,
|
|
241
|
+
applyStudioManualEditsToPreviewRef,
|
|
242
|
+
openSourceForSelection,
|
|
243
|
+
getSidebarTab,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
onClickToSource,
|
|
248
|
+
selectedGsapAnimations,
|
|
249
|
+
gsapMultipleTimelines,
|
|
250
|
+
gsapUnsupportedTimelinePattern,
|
|
251
|
+
trackGsapInteractionFailure,
|
|
252
|
+
makeFetchFallback,
|
|
253
|
+
...gsapSelectionHandlers,
|
|
254
|
+
};
|
|
255
|
+
}
|