@hyperframes/studio 0.8.115 → 0.8.117
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-C-9N8nk0.js → hyperframes-player-5fcgSstJ.js} +1 -1
- package/dist/assets/{index-CAvZ0AA-.js → index-C5qKdpgl.js} +205 -203
- package/dist/assets/{index-BoUM6Xqb.js → index-XJSawwYe.js} +1 -1
- package/dist/assets/{index-CVuorIMv.js → index-tg2LXUeu.js} +1 -1
- package/dist/{chunk-TXZMMOMA.js → chunk-3F47EFCT.js} +100 -2
- package/dist/chunk-3F47EFCT.js.map +1 -0
- package/dist/{domEditingLayers-D7VYFJJD.js → domEditingLayers-OL7GLSS4.js} +2 -2
- package/dist/index.d.ts +132 -41
- package/dist/index.html +1 -1
- package/dist/index.js +1184 -1084
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/captions/hooks/useCaptionSync.ts +10 -6
- package/src/components/ProjectUnreachableBanner.tsx +3 -3
- package/src/components/editor/DomEditOverlay.tsx +1 -0
- package/src/components/editor/domEditOverlayGestures.ts +1 -0
- package/src/components/editor/domEditOverlayStartGesture.ts +2 -2
- package/src/components/editor/domGroupMoveBy.test.tsx +252 -0
- package/src/components/editor/domGroupMoveBy.ts +95 -0
- package/src/components/editor/dragUndoPaint.test.ts +5 -5
- package/src/components/editor/gestureHoldsPreviewReload.test.tsx +33 -0
- package/src/components/editor/manualEditingAvailability.ts +8 -0
- package/src/components/editor/manualOffsetDrag.ts +8 -9
- package/src/components/editor/motionPathSelection.test.ts +6 -31
- package/src/components/editor/plainTranslate.ts +5 -3
- package/src/components/editor/probeSourceElement.ts +2 -1
- package/src/components/editor/propertyPanelFont.tsx +3 -2
- package/src/components/editor/propertyPanelHelpers.ts +2 -2
- package/src/components/editor/propertyPanelTransformCommit.ts +2 -2
- package/src/components/editor/rotationDraft.ts +2 -1
- package/src/components/editor/useColorGradingController.ts +2 -1
- package/src/components/editor/useDomEditOverlayGestures.ts +2 -2
- package/src/components/editor/useFxCarveNodes.ts +2 -1
- package/src/components/editor/useFxLevelling.ts +2 -1
- package/src/components/feedback/projectProvenance.ts +2 -1
- package/src/components/nle/NLEContext.test.ts +1 -0
- package/src/components/nle/NLEContext.tsx +2 -1
- package/src/components/nle/NLEPreview.tsx +12 -10
- package/src/components/nle/PreviewPoster.tsx +2 -1
- package/src/components/renders/useFfmpegStatus.ts +2 -1
- package/src/components/renders/useRenderQueue.ts +5 -4
- package/src/components/sidebar/AssetsTab.tsx +2 -1
- package/src/components/sidebar/GlobalAssetsView.tsx +2 -1
- package/src/contexts/DomEditContext.tsx +4 -0
- package/src/hooks/domSelectionTestHarness.ts +23 -0
- package/src/hooks/domStyleCommit.propertyNames.test.ts +73 -0
- package/src/hooks/domStyleCommit.ts +8 -1
- package/src/hooks/draggedGsapPosition.ts +10 -0
- package/src/hooks/gsapDragCommit.test.ts +75 -164
- package/src/hooks/gsapDragCommit.ts +2 -105
- package/src/hooks/gsapDragPositionCommit.ts +30 -393
- package/src/hooks/gsapEditOutcome.ts +22 -2
- package/src/hooks/gsapGroupPreflight.ts +34 -0
- package/src/hooks/gsapMutationClient.ts +4 -3
- package/src/hooks/gsapParsedTween.test-helpers.ts +75 -0
- package/src/hooks/gsapParsedTween.test.ts +62 -0
- package/src/hooks/gsapParsedTween.ts +243 -0
- package/src/hooks/gsapPositionDetection.test.ts +78 -0
- package/src/hooks/gsapPositionDetection.ts +52 -33
- package/src/hooks/gsapResizeDropPoint.test.ts +4 -3
- package/src/hooks/gsapResizeElementSize.test.tsx +1 -0
- package/src/hooks/gsapResizeGeometrySweep.test.ts +13 -25
- package/src/hooks/gsapResizeIntercept.test.ts +63 -11
- package/src/hooks/gsapResizeIntercept.ts +79 -169
- package/src/hooks/gsapResizeSweep.test.ts +16 -33
- package/src/hooks/gsapRuntimeBridge.sharedTween.test.ts +6 -8
- package/src/hooks/gsapRuntimeBridge.test.ts +41 -6
- package/src/hooks/gsapRuntimeBridge.ts +105 -131
- package/src/hooks/gsapRuntimeKeyframes.ts +16 -3
- package/src/hooks/gsapScriptCommitHelpers.ts +2 -1
- package/src/hooks/gsapScriptCommitTypes.ts +9 -1
- package/src/hooks/gsapShared.ts +7 -0
- package/src/hooks/gsapShared.writeTarget.test.ts +26 -58
- package/src/hooks/gsapValueAtPlayhead.browser.test.ts +83 -0
- package/src/hooks/gsapValueAtPlayhead.realGsap.test.ts +179 -0
- package/src/hooks/gsapValueAtPlayhead.test.ts +552 -0
- package/src/hooks/gsapValueAtPlayhead.ts +292 -0
- package/src/hooks/gsapWholePropertyOffsetCommit.test.ts +43 -0
- package/src/hooks/gsapWholePropertyOffsetCommit.ts +2 -3
- package/src/hooks/keyframeCacheAstLoad.ts +2 -1
- package/src/hooks/newTweenTarget.test.ts +41 -76
- package/src/hooks/plainRotation.ts +5 -1
- package/src/hooks/timelineTimingSync.test.ts +1 -0
- package/src/hooks/timelineTimingSync.ts +2 -1
- package/src/hooks/useAskAgentModal.ts +2 -1
- package/src/hooks/useBlockCatalog.ts +2 -1
- package/src/hooks/useCaptionDetection.ts +2 -1
- package/src/hooks/useCompositionContentLoader.ts +2 -1
- package/src/hooks/useDomEditCommitsHelpers.ts +3 -2
- package/src/hooks/useDomEditPersist.test.tsx +335 -0
- package/src/hooks/useDomEditSession.test.tsx +30 -1
- package/src/hooks/useDomEditSession.ts +10 -0
- package/src/hooks/useDomEditTextCommits.test.tsx +23 -0
- package/src/hooks/useDomEditTextCommits.ts +8 -2
- package/src/hooks/useDomGeometryCommit.test.tsx +5 -1
- package/src/hooks/useDomGeometryCommit.ts +7 -9
- package/src/hooks/useEditHistoryActions.paint.test.tsx +82 -20
- package/src/hooks/useEditHistoryActions.test.tsx +15 -0
- package/src/hooks/useEditHistoryActions.ts +16 -5
- package/src/hooks/useElementLifecycleOps.ts +2 -1
- package/src/hooks/useEnableKeyframes.test.ts +32 -1
- package/src/hooks/useEnableKeyframes.ts +2 -1
- package/src/hooks/useExternalFileChangeCoordinator.test.tsx +19 -0
- package/src/hooks/useExternalFileChangeCoordinator.ts +4 -2
- package/src/hooks/useFileManager.projectOwnership.test.tsx +6 -1
- package/src/hooks/useFileManager.ts +14 -10
- package/src/hooks/useFileTree.ts +3 -2
- package/src/hooks/useFrameCapture.ts +5 -1
- package/src/hooks/useFreezeFrame.test.ts +39 -3
- package/src/hooks/useFreezeFrame.ts +7 -3
- package/src/hooks/useGroupCommits.ts +2 -1
- package/src/hooks/useGsapAwareEditing.groupPlan.test.tsx +160 -0
- package/src/hooks/useGsapAwareEditing.test.tsx +42 -29
- package/src/hooks/useGsapAwareEditing.testHelpers.tsx +42 -0
- package/src/hooks/useGsapAwareEditing.ts +17 -8
- package/src/hooks/useGsapAwareGroupMove.ts +18 -18
- package/src/hooks/useGsapInteractionFailureTelemetry.ts +10 -4
- package/src/hooks/useGsapScriptCommits.test.tsx +72 -2
- package/src/hooks/useGsapScriptCommits.ts +21 -17
- package/src/hooks/useLintModal.ts +2 -1
- package/src/hooks/usePersistentEditHistory.test.ts +302 -3
- package/src/hooks/usePersistentEditHistory.ts +50 -17
- package/src/hooks/usePreviewPersistence.history.test.tsx +14 -3
- package/src/hooks/usePreviewPersistence.ts +13 -11
- package/src/hooks/useProjectFileWriter.ts +20 -19
- package/src/hooks/useRemoveBackground.ts +2 -1
- package/src/hooks/useSdkSession.ts +2 -1
- package/src/hooks/useServerConnection.ts +4 -3
- package/src/hooks/useStudioSelectionPublisher.ts +2 -1
- package/src/hooks/useThumbnailStill.test.ts +100 -0
- package/src/hooks/useThumbnailStill.ts +37 -0
- package/src/hooks/useTimelineDeleteOps.ts +2 -1
- package/src/index.ts +4 -0
- package/src/player/components/AudioWaveform.tsx +3 -2
- package/src/player/components/CompositionThumbnail.tsx +2 -1
- package/src/player/components/clipMenuNormalize.ts +2 -1
- package/src/player/components/clipPeakMap.ts +2 -1
- package/src/player/hooks/useTimelinePlayer.ts +5 -1
- package/src/player/lib/thumbnailScheduler.test.ts +16 -0
- package/src/player/lib/thumbnailScheduler.ts +10 -2
- package/src/player/sceneSwap.ts +2 -1
- package/src/telemetry/client.ts +2 -1
- package/src/utils/blockInstaller.ts +2 -1
- package/src/utils/elementGsap.test.ts +9 -0
- package/src/utils/elementGsap.ts +7 -0
- package/src/utils/gsapSoftReload.test.ts +39 -0
- package/src/utils/gsapSoftReload.ts +9 -14
- package/src/utils/gsapUndoRestore.browser.test.ts +94 -41
- package/src/utils/gsapUndoRestore.ts +3 -6
- package/src/utils/previewPrefetch.ts +2 -1
- package/src/utils/razorSplitTransaction.ts +12 -8
- package/src/utils/softReloadTargets.ts +5 -6
- package/src/utils/studioApiFetch.test.ts +75 -0
- package/src/utils/studioApiFetch.ts +13 -0
- package/src/utils/studioFileHistory.ts +5 -1
- package/src/utils/studioHelpers.ts +7 -1
- package/src/utils/studioPendingEdits.test.ts +23 -6
- package/src/utils/studioPendingEdits.ts +32 -8
- package/src/utils/studioTelemetry.ts +2 -1
- package/src/utils/timelineCompositionInsert.ts +3 -2
- package/src/webmcp/StudioAgentTools.tsx +2 -1
- package/dist/chunk-TXZMMOMA.js.map +0 -1
- /package/dist/{domEditingLayers-D7VYFJJD.js.map → domEditingLayers-OL7GLSS4.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -71,6 +71,10 @@ import {
|
|
|
71
71
|
} from "./chunk-5VSXRGIX.js";
|
|
72
72
|
import {
|
|
73
73
|
DOM_EDIT_LAYER_OBSERVER_INIT,
|
|
74
|
+
STUDIO_FLAT_INSPECTOR_ENABLED,
|
|
75
|
+
STUDIO_SDK_CUTOVER_ENABLED,
|
|
76
|
+
STUDIO_SDK_CUTOVER_FAMILIES,
|
|
77
|
+
STUDIO_SDK_RESOLVER_SHADOW_ENABLED,
|
|
74
78
|
buildDefaultDomEditTextField,
|
|
75
79
|
buildDomEditPatchTarget,
|
|
76
80
|
buildDomEditRichTextPatchOperation,
|
|
@@ -88,13 +92,16 @@ import {
|
|
|
88
92
|
getDomEditTargetKey,
|
|
89
93
|
getDomLayerPatchTarget,
|
|
90
94
|
isElementComputedVisible,
|
|
95
|
+
isSdkFamilyEnabled,
|
|
91
96
|
isTextEditableSelection,
|
|
92
97
|
liveLayerElement,
|
|
98
|
+
manualEditingAvailability_exports,
|
|
93
99
|
readHfId,
|
|
94
100
|
resolveAllVisualDomEditTargets,
|
|
95
101
|
resolveDomEditSelection,
|
|
96
|
-
serializeDomEditTextFields
|
|
97
|
-
|
|
102
|
+
serializeDomEditTextFields,
|
|
103
|
+
studioApiFetch
|
|
104
|
+
} from "./chunk-3F47EFCT.js";
|
|
98
105
|
import {
|
|
99
106
|
LAYER_REVEAL_PRIOR_POSITION_ATTR,
|
|
100
107
|
LAYER_REVEAL_PRIOR_Z_ATTR,
|
|
@@ -155,9 +162,7 @@ import {
|
|
|
155
162
|
toHexColor,
|
|
156
163
|
wrapTimeline
|
|
157
164
|
} from "./chunk-5EKWJZLM.js";
|
|
158
|
-
import
|
|
159
|
-
__export
|
|
160
|
-
} from "./chunk-PZ5AY32C.js";
|
|
165
|
+
import "./chunk-PZ5AY32C.js";
|
|
161
166
|
|
|
162
167
|
// src/components/EditorShell.tsx
|
|
163
168
|
import { memo as memo33, useCallback as useCallback74 } from "react";
|
|
@@ -545,7 +550,7 @@ async function flushEvents() {
|
|
|
545
550
|
const controller = new AbortController();
|
|
546
551
|
const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);
|
|
547
552
|
try {
|
|
548
|
-
await
|
|
553
|
+
await studioApiFetch(`${POSTHOG_HOST}/batch/`, {
|
|
549
554
|
method: "POST",
|
|
550
555
|
headers: { "Content-Type": "application/json" },
|
|
551
556
|
body: JSON.stringify({ api_key: POSTHOG_API_KEY2, batch }),
|
|
@@ -4218,8 +4223,8 @@ function useAbandonEditsOnReadOnly(readOnly, abandon) {
|
|
|
4218
4223
|
if (readOnly) abandonNow();
|
|
4219
4224
|
}, [readOnly]);
|
|
4220
4225
|
}
|
|
4221
|
-
function reportPressOnTravel(start,
|
|
4222
|
-
if (!
|
|
4226
|
+
function reportPressOnTravel(start, refuse2, threshold = 4) {
|
|
4227
|
+
if (!refuse2) return;
|
|
4223
4228
|
const stop2 = () => {
|
|
4224
4229
|
window.removeEventListener("pointermove", onMove, true);
|
|
4225
4230
|
window.removeEventListener("pointerup", stop2, true);
|
|
@@ -4229,7 +4234,7 @@ function reportPressOnTravel(start, refuse, threshold = 4) {
|
|
|
4229
4234
|
if (Math.hypot(event.clientX - start.clientX, event.clientY - start.clientY) < threshold)
|
|
4230
4235
|
return;
|
|
4231
4236
|
stop2();
|
|
4232
|
-
|
|
4237
|
+
refuse2();
|
|
4233
4238
|
};
|
|
4234
4239
|
window.addEventListener("pointermove", onMove, true);
|
|
4235
4240
|
window.addEventListener("pointerup", stop2, true);
|
|
@@ -4359,6 +4364,10 @@ var PROPERTY_DEFAULTS = {
|
|
|
4359
4364
|
width: 100,
|
|
4360
4365
|
height: 100
|
|
4361
4366
|
};
|
|
4367
|
+
function keyframeEases(anim) {
|
|
4368
|
+
if (!anim.keyframes) return { easeEach: anim.ease };
|
|
4369
|
+
return { ease: anim.ease, easeEach: anim.keyframes.easeEach };
|
|
4370
|
+
}
|
|
4362
4371
|
function isInstantHold(animation) {
|
|
4363
4372
|
return animation.method === "set" || (animation.method === "to" || animation.method === "fromTo") && resolveTweenDuration(animation) === 0;
|
|
4364
4373
|
}
|
|
@@ -6736,7 +6745,7 @@ function flush() {
|
|
|
6736
6745
|
}
|
|
6737
6746
|
function send(url, payload) {
|
|
6738
6747
|
try {
|
|
6739
|
-
void
|
|
6748
|
+
void studioApiFetch(url, {
|
|
6740
6749
|
method: "POST",
|
|
6741
6750
|
headers: { "Content-Type": "application/json" },
|
|
6742
6751
|
body: payload,
|
|
@@ -11634,6 +11643,7 @@ function asElement(target) {
|
|
|
11634
11643
|
// src/utils/studioPendingEdits.ts
|
|
11635
11644
|
var STUDIO_FLUSH_PENDING_EDITS_EVENT = "hf-studio-flush-pending-edits";
|
|
11636
11645
|
var pendingEdits = /* @__PURE__ */ new Map();
|
|
11646
|
+
var NOT_SAVED = () => Promise.resolve(false);
|
|
11637
11647
|
var adopting = false;
|
|
11638
11648
|
function waitForPostBlurEffects() {
|
|
11639
11649
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -11677,7 +11687,7 @@ function trackStudioPendingEdit(result) {
|
|
|
11677
11687
|
if (!result) return void 0;
|
|
11678
11688
|
const promise = Promise.resolve(result);
|
|
11679
11689
|
if (adopting) return promise;
|
|
11680
|
-
pendingEdits.set(promise, { revert: null });
|
|
11690
|
+
pendingEdits.set(promise, { revert: null, landed: NOT_SAVED });
|
|
11681
11691
|
promise.then(
|
|
11682
11692
|
() => pendingEdits.delete(promise),
|
|
11683
11693
|
() => pendingEdits.delete(promise)
|
|
@@ -11696,6 +11706,8 @@ function beginStudioPendingEdit(revert) {
|
|
|
11696
11706
|
const promise = trackStudioPendingEdit(new Promise((resolve) => settle = resolve));
|
|
11697
11707
|
const entry2 = pendingEdits.get(promise);
|
|
11698
11708
|
entry2.revert = revert;
|
|
11709
|
+
let landed = Promise.resolve(false);
|
|
11710
|
+
entry2.landed = () => landed;
|
|
11699
11711
|
return {
|
|
11700
11712
|
settle,
|
|
11701
11713
|
reverted: () => entry2.revert === null && revert !== null,
|
|
@@ -11703,7 +11715,12 @@ function beginStudioPendingEdit(revert) {
|
|
|
11703
11715
|
adopt(start) {
|
|
11704
11716
|
adopting = true;
|
|
11705
11717
|
try {
|
|
11706
|
-
|
|
11718
|
+
const committed = start();
|
|
11719
|
+
landed = Promise.resolve(committed).then(
|
|
11720
|
+
() => true,
|
|
11721
|
+
() => false
|
|
11722
|
+
);
|
|
11723
|
+
return committed;
|
|
11707
11724
|
} catch (error) {
|
|
11708
11725
|
settle();
|
|
11709
11726
|
throw error;
|
|
@@ -11713,14 +11730,19 @@ function beginStudioPendingEdit(revert) {
|
|
|
11713
11730
|
}
|
|
11714
11731
|
};
|
|
11715
11732
|
}
|
|
11716
|
-
function
|
|
11733
|
+
function paintBackNewestStudioPendingEdit() {
|
|
11717
11734
|
const newest = [...pendingEdits.values()].at(-1);
|
|
11718
11735
|
const revert = newest?.revert;
|
|
11719
11736
|
if (!newest || !revert) return null;
|
|
11720
11737
|
newest.revert = null;
|
|
11721
|
-
return revert();
|
|
11738
|
+
return { showAgain: revert(), landed: newest.landed };
|
|
11722
11739
|
}
|
|
11723
|
-
|
|
11740
|
+
function revertNewestStudioPendingEdit() {
|
|
11741
|
+
return paintBackNewestStudioPendingEdit()?.showAgain ?? null;
|
|
11742
|
+
}
|
|
11743
|
+
async function flushStudioPendingEdits({
|
|
11744
|
+
onlyCurrent = false
|
|
11745
|
+
} = {}) {
|
|
11724
11746
|
const active = focusedField();
|
|
11725
11747
|
if (active) {
|
|
11726
11748
|
active.blur();
|
|
@@ -11731,10 +11753,12 @@ async function flushStudioPendingEdits() {
|
|
|
11731
11753
|
window.dispatchEvent(
|
|
11732
11754
|
new CustomEvent(STUDIO_FLUSH_PENDING_EDITS_EVENT, { detail })
|
|
11733
11755
|
);
|
|
11756
|
+
const current = onlyCurrent ? new Set(pendingEdits.keys()) : null;
|
|
11757
|
+
const waiting = () => [...pendingEdits.keys()].filter((edit) => !current || current.has(edit));
|
|
11734
11758
|
let conflict;
|
|
11735
11759
|
let firstFailure;
|
|
11736
|
-
while (detail.promises.length > 0 ||
|
|
11737
|
-
const promises = [...detail.promises, ...
|
|
11760
|
+
while (detail.promises.length > 0 || waiting().length > 0) {
|
|
11761
|
+
const promises = [...detail.promises, ...waiting()];
|
|
11738
11762
|
detail.promises = [];
|
|
11739
11763
|
const batchFailures = inspectDrainFailures(await Promise.allSettled(promises));
|
|
11740
11764
|
conflict ??= batchFailures.conflict;
|
|
@@ -11774,6 +11798,7 @@ var SAVES_AN_EDIT = {
|
|
|
11774
11798
|
handleDomAttributeBatchCommit: true,
|
|
11775
11799
|
handleDomPathOffsetCommit: true,
|
|
11776
11800
|
handleDomGroupPathOffsetCommit: true,
|
|
11801
|
+
handleDomGroupMoveBy: true,
|
|
11777
11802
|
handleDomZIndexReorderCommit: true,
|
|
11778
11803
|
handleDomBoxSizeCommit: true,
|
|
11779
11804
|
handleDomRotationCommit: true,
|
|
@@ -11894,6 +11919,7 @@ function DomEditProvider({
|
|
|
11894
11919
|
handleDomAttributeBatchCommit,
|
|
11895
11920
|
handleDomPathOffsetCommit,
|
|
11896
11921
|
handleDomGroupPathOffsetCommit,
|
|
11922
|
+
handleDomGroupMoveBy,
|
|
11897
11923
|
handleDomZIndexReorderCommit,
|
|
11898
11924
|
handleDomBoxSizeCommit,
|
|
11899
11925
|
handleDomRotationCommit,
|
|
@@ -11983,6 +12009,7 @@ function DomEditProvider({
|
|
|
11983
12009
|
handleDomAttributeBatchCommit,
|
|
11984
12010
|
handleDomPathOffsetCommit,
|
|
11985
12011
|
handleDomGroupPathOffsetCommit,
|
|
12012
|
+
handleDomGroupMoveBy,
|
|
11986
12013
|
handleDomZIndexReorderCommit,
|
|
11987
12014
|
handleDomBoxSizeCommit,
|
|
11988
12015
|
handleDomRotationCommit,
|
|
@@ -12059,6 +12086,7 @@ function DomEditProvider({
|
|
|
12059
12086
|
handleDomAttributeBatchCommit,
|
|
12060
12087
|
handleDomPathOffsetCommit,
|
|
12061
12088
|
handleDomGroupPathOffsetCommit,
|
|
12089
|
+
handleDomGroupMoveBy,
|
|
12062
12090
|
handleDomZIndexReorderCommit,
|
|
12063
12091
|
handleDomBoxSizeCommit,
|
|
12064
12092
|
handleDomRotationCommit,
|
|
@@ -14543,8 +14571,8 @@ function TimelineProvider({
|
|
|
14543
14571
|
useLayoutEffect6(() => {
|
|
14544
14572
|
onReadOnlyPressRef.current = onReadOnlyPress;
|
|
14545
14573
|
});
|
|
14546
|
-
const
|
|
14547
|
-
const readOnlyPress = readOnly ?
|
|
14574
|
+
const refuse2 = useCallback24(() => onReadOnlyPressRef.current?.(), []);
|
|
14575
|
+
const readOnlyPress = readOnly ? refuse2 : null;
|
|
14548
14576
|
return /* @__PURE__ */ jsx24(TimelineReadOnlyContext.Provider, { value: readOnlyPress, children: /* @__PURE__ */ jsx24(TimelineEditProvider, { value: readOnly ? NO_EDITS : editContext ?? props, children: /* @__PURE__ */ jsx24(TimelineProviderState, { ...props, children }) }) });
|
|
14549
14577
|
}
|
|
14550
14578
|
function TimelineProviderState({ children, ...props }) {
|
|
@@ -15591,6 +15619,15 @@ var ThumbnailScheduler = class {
|
|
|
15591
15619
|
entry2.controller = controller;
|
|
15592
15620
|
this.activeByBucket[bucket]++;
|
|
15593
15621
|
this.activeByKind[entry2.request.kind]++;
|
|
15622
|
+
let slotFree = false;
|
|
15623
|
+
const freeSlot = () => {
|
|
15624
|
+
if (slotFree) return;
|
|
15625
|
+
slotFree = true;
|
|
15626
|
+
this.activeByBucket[bucket]--;
|
|
15627
|
+
this.activeByKind[entry2.request.kind]--;
|
|
15628
|
+
this.pump();
|
|
15629
|
+
};
|
|
15630
|
+
controller.signal.addEventListener("abort", freeSlot, { once: true });
|
|
15594
15631
|
this.notify(entry2);
|
|
15595
15632
|
const pending2 = this.loadWithTimeout(entry2, controller);
|
|
15596
15633
|
void pending2.then((result) => this.acceptResult(entry2, controller, result)).catch((reason) => {
|
|
@@ -15608,8 +15645,7 @@ var ThumbnailScheduler = class {
|
|
|
15608
15645
|
this.evictFailures();
|
|
15609
15646
|
}).finally(() => {
|
|
15610
15647
|
if (entry2.controller === controller) entry2.controller = null;
|
|
15611
|
-
|
|
15612
|
-
this.activeByKind[entry2.request.kind]--;
|
|
15648
|
+
freeSlot();
|
|
15613
15649
|
this.pump();
|
|
15614
15650
|
});
|
|
15615
15651
|
}
|
|
@@ -15906,7 +15942,7 @@ async function loadWaveform(audioUrl, waveformUrl, signal) {
|
|
|
15906
15942
|
return waveformUrl ? await fetchWaveformPeaks(waveformUrl, signal) : await decodeWaveformPeaks(audioUrl, signal);
|
|
15907
15943
|
}
|
|
15908
15944
|
async function fetchWaveformPeaks(url, signal) {
|
|
15909
|
-
const response = await
|
|
15945
|
+
const response = await studioApiFetch(url, { signal });
|
|
15910
15946
|
if (!response.ok) throw new Error(`Waveform request failed (${response.status})`);
|
|
15911
15947
|
const data = await response.json();
|
|
15912
15948
|
if (typeof data !== "object" || data === null || !("peaks" in data) || !Array.isArray(data.peaks) || !data.peaks.every((peak) => typeof peak === "number")) {
|
|
@@ -15915,7 +15951,7 @@ async function fetchWaveformPeaks(url, signal) {
|
|
|
15915
15951
|
return data.peaks;
|
|
15916
15952
|
}
|
|
15917
15953
|
async function decodeWaveformPeaks(url, signal) {
|
|
15918
|
-
const response = await
|
|
15954
|
+
const response = await studioApiFetch(url, { signal });
|
|
15919
15955
|
if (!response.ok) throw new Error(`Audio request failed (${response.status})`);
|
|
15920
15956
|
const buffer = await response.arrayBuffer();
|
|
15921
15957
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
@@ -16722,7 +16758,9 @@ function serializeStudioFileMutations(writer, targetPaths, task) {
|
|
|
16722
16758
|
|
|
16723
16759
|
// src/utils/studioFileHistory.ts
|
|
16724
16760
|
async function readProjectFileContent(pid, path) {
|
|
16725
|
-
const response = await
|
|
16761
|
+
const response = await studioApiFetch(
|
|
16762
|
+
buildProjectApiPath(pid, `/files/${encodeURIComponent(path)}`)
|
|
16763
|
+
);
|
|
16726
16764
|
if (!response.ok) {
|
|
16727
16765
|
throw await createStudioSaveHttpError(response, `Failed to read ${path}`);
|
|
16728
16766
|
}
|
|
@@ -16903,6 +16941,28 @@ function applyAuthoredInlineOpacity(style, authored) {
|
|
|
16903
16941
|
else style.setProperty("opacity", authored);
|
|
16904
16942
|
}
|
|
16905
16943
|
|
|
16944
|
+
// src/utils/elementGsap.ts
|
|
16945
|
+
function gsapOf(element) {
|
|
16946
|
+
return element.ownerDocument.defaultView?.gsap;
|
|
16947
|
+
}
|
|
16948
|
+
function setElementGsapPosition(element, x, y) {
|
|
16949
|
+
const gsap3 = gsapOf(element);
|
|
16950
|
+
if (!gsap3?.set) return false;
|
|
16951
|
+
gsap3.set(element, { x, y });
|
|
16952
|
+
return true;
|
|
16953
|
+
}
|
|
16954
|
+
function setElementGsapScale(element, x, y) {
|
|
16955
|
+
const gsap3 = gsapOf(element);
|
|
16956
|
+
if (!gsap3?.set) return false;
|
|
16957
|
+
gsap3.set(element, { scaleX: x, scaleY: y });
|
|
16958
|
+
return true;
|
|
16959
|
+
}
|
|
16960
|
+
function elementTargets(tween) {
|
|
16961
|
+
return (tween.targets?.() ?? []).filter(
|
|
16962
|
+
(t2) => Boolean(t2?.style && t2.nodeType)
|
|
16963
|
+
);
|
|
16964
|
+
}
|
|
16965
|
+
|
|
16906
16966
|
// src/utils/softReloadTargets.ts
|
|
16907
16967
|
var NESTED_VARS = /* @__PURE__ */ new Set(["css", "startAt", "keyframes"]);
|
|
16908
16968
|
function tweenedProps(vars) {
|
|
@@ -16923,14 +16983,14 @@ function authoringFile(el) {
|
|
|
16923
16983
|
}
|
|
16924
16984
|
function addTweenTargets(tween, targets) {
|
|
16925
16985
|
const props = tweenedProps(tween.vars);
|
|
16926
|
-
for (const el of tween
|
|
16986
|
+
for (const el of elementTargets(tween)) {
|
|
16927
16987
|
const seen = targets.get(el) ?? /* @__PURE__ */ new Set();
|
|
16928
16988
|
for (const prop of props) seen.add(prop);
|
|
16929
16989
|
targets.set(el, seen);
|
|
16930
16990
|
}
|
|
16931
16991
|
}
|
|
16932
|
-
function collectResetTargets(win, doc, targetKeys
|
|
16933
|
-
const targets =
|
|
16992
|
+
function collectResetTargets(win, doc, targetKeys) {
|
|
16993
|
+
const targets = /* @__PURE__ */ new Map();
|
|
16934
16994
|
const timelines = win.__timelines ?? {};
|
|
16935
16995
|
const others = new Set(
|
|
16936
16996
|
Object.entries(timelines).flatMap(([key2, tl]) => targetKeys.includes(key2) ? [] : [tl])
|
|
@@ -16949,7 +17009,7 @@ function tweenTargetsIn(timelines) {
|
|
|
16949
17009
|
for (const tl of timelines) {
|
|
16950
17010
|
try {
|
|
16951
17011
|
for (const tween of tl?.getChildren?.(true) ?? []) {
|
|
16952
|
-
for (const el of tween
|
|
17012
|
+
for (const el of elementTargets(tween)) els.add(el);
|
|
16953
17013
|
}
|
|
16954
17014
|
} catch {
|
|
16955
17015
|
}
|
|
@@ -17063,7 +17123,8 @@ function runSuppressed(win, reload) {
|
|
|
17063
17123
|
if (win.__hfSuppressSceneMutations) win.__hfSuppressSceneMutations(reload);
|
|
17064
17124
|
else reload();
|
|
17065
17125
|
return true;
|
|
17066
|
-
} catch {
|
|
17126
|
+
} catch (error) {
|
|
17127
|
+
console.error("[Studio] GSAP soft reload threw; falling back to a full reload", error);
|
|
17067
17128
|
return false;
|
|
17068
17129
|
}
|
|
17069
17130
|
}
|
|
@@ -17090,7 +17151,7 @@ function timelineKeys(scriptText) {
|
|
|
17090
17151
|
return [...scriptText.matchAll(/__timelines\s*\[\s*["'`]([^"'`]+)["'`]\s*\]/g)].map((m) => m[1]).filter((key2) => key2 !== "__proxied");
|
|
17091
17152
|
}
|
|
17092
17153
|
function applySoftReload(iframe, scriptText, options = {}) {
|
|
17093
|
-
const { onAsyncFailure, currentTimeOverride, authoredHtml
|
|
17154
|
+
const { onAsyncFailure, currentTimeOverride, authoredHtml } = options;
|
|
17094
17155
|
if (!iframe || !scriptText) return "cannot-soft-reload";
|
|
17095
17156
|
const win = iframe.contentWindow;
|
|
17096
17157
|
const doc = iframe.contentDocument;
|
|
@@ -17120,7 +17181,7 @@ function applySoftReload(iframe, scriptText, options = {}) {
|
|
|
17120
17181
|
const readAuthoredOpacity = (el) => findAuthoredStyle(el)?.opacity ?? readStampedAuthoredOpacity(el);
|
|
17121
17182
|
const doReload = () => {
|
|
17122
17183
|
const timelines = win.__timelines;
|
|
17123
|
-
const targets = collectResetTargets(win, doc, targetKeys
|
|
17184
|
+
const targets = collectResetTargets(win, doc, targetKeys);
|
|
17124
17185
|
if (timelines) {
|
|
17125
17186
|
for (const key2 of targetKeys) {
|
|
17126
17187
|
const tl = timelines[key2];
|
|
@@ -17135,15 +17196,10 @@ function applySoftReload(iframe, scriptText, options = {}) {
|
|
|
17135
17196
|
}
|
|
17136
17197
|
const allTargets = [...targets.keys()];
|
|
17137
17198
|
if (allTargets.length > 0 && win.gsap?.set) {
|
|
17138
|
-
const saved2 =
|
|
17139
|
-
|
|
17140
|
-
|
|
17141
|
-
|
|
17142
|
-
}
|
|
17143
|
-
try {
|
|
17144
|
-
win.gsap.set(allTargets, { clearProps: "all" });
|
|
17145
|
-
} catch {
|
|
17146
|
-
}
|
|
17199
|
+
const saved2 = allTargets.map(
|
|
17200
|
+
(el) => [el, el.style.cssText]
|
|
17201
|
+
);
|
|
17202
|
+
win.gsap.set(allTargets, { clearProps: "all" });
|
|
17147
17203
|
for (const [el, css2] of saved2) {
|
|
17148
17204
|
const s = el.style;
|
|
17149
17205
|
s.cssText = css2;
|
|
@@ -17289,7 +17345,7 @@ var GsapPreviewConvergenceError = class extends Error {
|
|
|
17289
17345
|
var GsapOwnershipProtocolError = class extends GsapPreviewConvergenceError {
|
|
17290
17346
|
};
|
|
17291
17347
|
async function requireGsapOwnershipProtocol(projectId) {
|
|
17292
|
-
const response = await
|
|
17348
|
+
const response = await studioApiFetch(
|
|
17293
17349
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutation-capabilities`
|
|
17294
17350
|
);
|
|
17295
17351
|
if (!response.ok) {
|
|
@@ -17300,18 +17356,18 @@ async function requireGsapOwnershipProtocol(projectId) {
|
|
|
17300
17356
|
throw new GsapOwnershipProtocolError("Invalid GSAP mutation capability response");
|
|
17301
17357
|
}
|
|
17302
17358
|
}
|
|
17303
|
-
async function rollbackOwnedMutation(projectId, targetPath, expected,
|
|
17359
|
+
async function rollbackOwnedMutation(projectId, targetPath, expected, restore2) {
|
|
17304
17360
|
if (targetPath.includes("\0") || targetPath.includes("..")) {
|
|
17305
17361
|
throw new Error(`Unsafe path: ${targetPath}`);
|
|
17306
17362
|
}
|
|
17307
|
-
const response = await
|
|
17363
|
+
const response = await studioApiFetch(
|
|
17308
17364
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutation-rollback/${encodeURIComponent(targetPath)}`,
|
|
17309
17365
|
{
|
|
17310
17366
|
method: "POST",
|
|
17311
17367
|
// Deliberately unclaimed: a rollback runs because a mutation did not
|
|
17312
17368
|
// converge, so let the restored file reload the preview.
|
|
17313
17369
|
headers: { "Content-Type": "application/json" },
|
|
17314
|
-
body: JSON.stringify({ expected, restore })
|
|
17370
|
+
body: JSON.stringify({ expected, restore: restore2 })
|
|
17315
17371
|
}
|
|
17316
17372
|
);
|
|
17317
17373
|
if (!response.ok) {
|
|
@@ -17343,7 +17399,7 @@ function readMutationError(value, fallback) {
|
|
|
17343
17399
|
async function postGsapMutation(projectId, filePath, mutation, fallback) {
|
|
17344
17400
|
let response;
|
|
17345
17401
|
try {
|
|
17346
|
-
response = await
|
|
17402
|
+
response = await studioApiFetch(
|
|
17347
17403
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutations/${encodeURIComponent(filePath)}`,
|
|
17348
17404
|
{
|
|
17349
17405
|
method: "POST",
|
|
@@ -17370,7 +17426,7 @@ async function readFileContent(projectId, targetPath) {
|
|
|
17370
17426
|
if (targetPath.includes("\0") || targetPath.includes("..")) {
|
|
17371
17427
|
throw new Error(`Unsafe path: ${targetPath}`);
|
|
17372
17428
|
}
|
|
17373
|
-
const response = await
|
|
17429
|
+
const response = await studioApiFetch(
|
|
17374
17430
|
`/api/projects/${encodeURIComponent(projectId)}/files/${encodeURIComponent(targetPath)}`
|
|
17375
17431
|
);
|
|
17376
17432
|
if (!response.ok) {
|
|
@@ -20480,6 +20536,10 @@ function normalizeDomEditStyleValue(property, value) {
|
|
|
20480
20536
|
function isImageBackgroundValue(value) {
|
|
20481
20537
|
return /^url\(/i.test(value.trim());
|
|
20482
20538
|
}
|
|
20539
|
+
function cssPropertyName(property) {
|
|
20540
|
+
if (property.startsWith("--")) return property;
|
|
20541
|
+
return property.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`).replace(/^ms-/, "-ms-");
|
|
20542
|
+
}
|
|
20483
20543
|
function isManualGeometryStyleProperty(property) {
|
|
20484
20544
|
return property === "left" || property === "top" || property === "width" || property === "height";
|
|
20485
20545
|
}
|
|
@@ -20626,7 +20686,7 @@ function isPlainRecord(value) {
|
|
|
20626
20686
|
}
|
|
20627
20687
|
async function resolveAssetHasAudio(projectId, assetPath) {
|
|
20628
20688
|
try {
|
|
20629
|
-
const response = await
|
|
20689
|
+
const response = await studioApiFetch(mediaMetadataUrl(projectId, assetPath));
|
|
20630
20690
|
if (!response.ok) return null;
|
|
20631
20691
|
const data = await response.json();
|
|
20632
20692
|
const metadata = isPlainRecord(data) ? data.metadata : void 0;
|
|
@@ -22356,7 +22416,7 @@ function TimelineTrackRow({
|
|
|
22356
22416
|
),
|
|
22357
22417
|
propertyRows.map((row) => {
|
|
22358
22418
|
const group = row.propertyGroup;
|
|
22359
|
-
const
|
|
22419
|
+
const keyframeCount2 = row.items.filter((item) => item.kind === "keyframe").length;
|
|
22360
22420
|
const easeCount = row.items.filter((item) => item.kind === "ease").length;
|
|
22361
22421
|
return (
|
|
22362
22422
|
// ponytail: aria-owns maps this hidden logical row onto the two visible
|
|
@@ -22387,7 +22447,7 @@ function TimelineTrackRow({
|
|
|
22387
22447
|
"aria-colindex": 2,
|
|
22388
22448
|
"aria-owns": timelineLogicalRowCellId(lanesId, row.id, "content"),
|
|
22389
22449
|
children: [
|
|
22390
|
-
|
|
22450
|
+
keyframeCount2,
|
|
22391
22451
|
" keyframes, ",
|
|
22392
22452
|
easeCount,
|
|
22393
22453
|
" ease controls"
|
|
@@ -24125,7 +24185,7 @@ var peakMapRequests = /* @__PURE__ */ new Map();
|
|
|
24125
24185
|
function loadPeakMap(url) {
|
|
24126
24186
|
const pending2 = peakMapRequests.get(url);
|
|
24127
24187
|
if (pending2) return pending2;
|
|
24128
|
-
const request =
|
|
24188
|
+
const request = studioApiFetch(url).then((res) => res.ok ? res.json() : null).then((body) => isPeakMap(body) ? body : null).catch(() => null);
|
|
24129
24189
|
peakMapRequests.set(url, request);
|
|
24130
24190
|
return request;
|
|
24131
24191
|
}
|
|
@@ -24200,7 +24260,7 @@ function isNormalizePlan(value) {
|
|
|
24200
24260
|
(key2) => typeof Reflect.get(value, key2) === "number"
|
|
24201
24261
|
);
|
|
24202
24262
|
}
|
|
24203
|
-
async function requestNormalizePlan(projectId, el, fetchImpl =
|
|
24263
|
+
async function requestNormalizePlan(projectId, el, fetchImpl = studioApiFetch) {
|
|
24204
24264
|
const origin2 = window.location.origin;
|
|
24205
24265
|
const relative = resolvePreviewRelative(
|
|
24206
24266
|
resolveMediaPreviewUrl(el.src ?? "", projectId, origin2),
|
|
@@ -25257,7 +25317,7 @@ async function measureCarve(doc, voices, strength, bed) {
|
|
|
25257
25317
|
const Ctor = window.OfflineAudioContext ?? window.webkitOfflineAudioContext;
|
|
25258
25318
|
if (!Ctor) return null;
|
|
25259
25319
|
const decode = async (relative, clock) => {
|
|
25260
|
-
const res = await
|
|
25320
|
+
const res = await studioApiFetch(new URL(relative, doc.baseURI).href);
|
|
25261
25321
|
const buffer = await new Ctor(1, 1, DECODE_SAMPLE_RATE).decodeAudioData(
|
|
25262
25322
|
await res.arrayBuffer()
|
|
25263
25323
|
);
|
|
@@ -27147,7 +27207,7 @@ function compositionThumbnailRequest(url, projectId, { sessionEpoch = 0, priorit
|
|
|
27147
27207
|
};
|
|
27148
27208
|
}
|
|
27149
27209
|
async function loadCompositionImage(url, signal) {
|
|
27150
|
-
const response = await
|
|
27210
|
+
const response = await studioApiFetch(url, { signal });
|
|
27151
27211
|
if (!response.ok) throw new Error(`Composition thumbnail failed (${response.status})`);
|
|
27152
27212
|
const blob = await response.blob();
|
|
27153
27213
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
@@ -29369,6 +29429,14 @@ var BOX_CHANNELS = [
|
|
|
29369
29429
|
function gsapWritesBox(el) {
|
|
29370
29430
|
return gsapWritesPosition(el) || gsapWritesChannels(el, BOX_CHANNELS);
|
|
29371
29431
|
}
|
|
29432
|
+
var GSAP_OWNS = {
|
|
29433
|
+
move: gsapWritesPosition,
|
|
29434
|
+
resize: gsapWritesBox,
|
|
29435
|
+
rotate: gsapWritesRotation
|
|
29436
|
+
};
|
|
29437
|
+
function editsPlainCss(el, gesture) {
|
|
29438
|
+
return !GSAP_OWNS[gesture](el);
|
|
29439
|
+
}
|
|
29372
29440
|
function elementHasNonHoldTween(el, channels) {
|
|
29373
29441
|
const win = el.ownerDocument.defaultView;
|
|
29374
29442
|
return hasNonHoldTween(win?.__timelines ?? null, el, channels);
|
|
@@ -29921,7 +29989,7 @@ function sceneSwapFor(iframe) {
|
|
|
29921
29989
|
);
|
|
29922
29990
|
cancel?.addEventListener("abort", () => deadline.abort(cancel.reason), { once: true });
|
|
29923
29991
|
const swapping = (async () => {
|
|
29924
|
-
const response = await
|
|
29992
|
+
const response = await studioApiFetch(url, { signal: deadline.signal });
|
|
29925
29993
|
if (!response.ok) throw new Error(`preview request failed with ${response.status}`);
|
|
29926
29994
|
const html2 = await response.text();
|
|
29927
29995
|
if (!isCurrent()) throw new Error("superseded by a newer reload");
|
|
@@ -31048,7 +31116,9 @@ function useTimelinePlayer({
|
|
|
31048
31116
|
const slot = previewGeneration();
|
|
31049
31117
|
const isCurrent = () => gen === refreshGenRef.current && slot === previewGeneration();
|
|
31050
31118
|
const swap = sceneSwapFor(iframe);
|
|
31051
|
-
|
|
31119
|
+
const swapWouldReplaceGestureNode = !!iframe.contentDocument && isStudioManualEditGestureLiveIn(iframe.contentDocument);
|
|
31120
|
+
if (!swap || isRefreshingRef.current || swapWouldReplaceGestureNode)
|
|
31121
|
+
return reloadWholeFilm(url.toString());
|
|
31052
31122
|
swap(url.toString(), isCurrent, cancel.signal).catch((error) => {
|
|
31053
31123
|
if (!isCurrent()) return;
|
|
31054
31124
|
logReload("scene-swap-refused", { reason: String(error) });
|
|
@@ -31363,7 +31433,7 @@ function NLEProvider({
|
|
|
31363
31433
|
setCompIdToSrc(emptyMap);
|
|
31364
31434
|
setCompositionSourceMap(emptyMap);
|
|
31365
31435
|
onCompIdToSrcChangeRef.current?.(emptyMap);
|
|
31366
|
-
|
|
31436
|
+
studioApiFetch(buildProjectApiPath(projectId, `/files/index.html`), {
|
|
31367
31437
|
signal: controller.signal
|
|
31368
31438
|
}).then((r) => {
|
|
31369
31439
|
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
@@ -31899,7 +31969,7 @@ function renderPosterForNextOpen(projectId) {
|
|
|
31899
31969
|
priority: "overscan",
|
|
31900
31970
|
discardWhenReleased: true,
|
|
31901
31971
|
load: async (signal) => {
|
|
31902
|
-
await (await
|
|
31972
|
+
await (await studioApiFetch(url, { signal })).arrayBuffer();
|
|
31903
31973
|
return { value: { kind: "image", url, aspect: 16 / 9 }, weight: 0 };
|
|
31904
31974
|
}
|
|
31905
31975
|
};
|
|
@@ -35897,7 +35967,7 @@ function OffCanvasIndicators({
|
|
|
35897
35967
|
const selectOffCanvas = async () => {
|
|
35898
35968
|
const el = elements.current.get(r.key);
|
|
35899
35969
|
if (!el) return;
|
|
35900
|
-
const { resolveDomEditSelection: resolveDomEditSelection2 } = await import("./domEditingLayers-
|
|
35970
|
+
const { resolveDomEditSelection: resolveDomEditSelection2 } = await import("./domEditingLayers-OL7GLSS4.js");
|
|
35901
35971
|
const acp = activeCompositionPathRef.current ?? "index.html";
|
|
35902
35972
|
const sel = await resolveDomEditSelection2(el, {
|
|
35903
35973
|
activeCompositionPath: acp,
|
|
@@ -36563,6 +36633,13 @@ function computeDraggedGsapPosition(element, studioOffset, fallbackBase) {
|
|
|
36563
36633
|
baseGsapY
|
|
36564
36634
|
};
|
|
36565
36635
|
}
|
|
36636
|
+
function restoreDragOffset(element) {
|
|
36637
|
+
const origin2 = (axis) => Number.parseFloat(element.getAttribute(`data-hf-drag-initial-offset-${axis}`) ?? "") || 0;
|
|
36638
|
+
element.style.setProperty("--hf-studio-offset-x", `${origin2("x")}px`);
|
|
36639
|
+
element.style.setProperty("--hf-studio-offset-y", `${origin2("y")}px`);
|
|
36640
|
+
element.removeAttribute("data-hf-drag-initial-offset-x");
|
|
36641
|
+
element.removeAttribute("data-hf-drag-initial-offset-y");
|
|
36642
|
+
}
|
|
36566
36643
|
|
|
36567
36644
|
// src/components/editor/plainTranslate.ts
|
|
36568
36645
|
var TOKEN = /\s*(?:(\d*\.?\d+(?:e[-+]?\d+)?)(px|%)?|([a-z]+)\(|([-+*/(),]))/giy;
|
|
@@ -36665,11 +36742,11 @@ function writeTranslatePx(el, p) {
|
|
|
36665
36742
|
}
|
|
36666
36743
|
el.style.setProperty("translate", formatTranslatePx(p));
|
|
36667
36744
|
}
|
|
36668
|
-
function readMoveOffset(el, plainTranslate =
|
|
36745
|
+
function readMoveOffset(el, plainTranslate = editsPlainCss(el, "move")) {
|
|
36669
36746
|
return plainTranslate ? readTranslatePx(el) : readStudioPathOffset(el);
|
|
36670
36747
|
}
|
|
36671
36748
|
function readShownRotation(el) {
|
|
36672
|
-
return
|
|
36749
|
+
return editsPlainCss(el, "rotate") ? { angle: roundTo3(readCssRotation(el)) } : readStudioRotation(el);
|
|
36673
36750
|
}
|
|
36674
36751
|
|
|
36675
36752
|
// src/components/editor/manualOffsetDrag.ts
|
|
@@ -36723,11 +36800,11 @@ function getRectCenter(element) {
|
|
|
36723
36800
|
if (!finiteRect(rect) || rect.width <= 0 && rect.height <= 0) {
|
|
36724
36801
|
return null;
|
|
36725
36802
|
}
|
|
36726
|
-
|
|
36727
|
-
|
|
36728
|
-
|
|
36729
|
-
|
|
36730
|
-
let win =
|
|
36803
|
+
const point = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
36804
|
+
return toTopScreenPoint(element.ownerDocument, point);
|
|
36805
|
+
}
|
|
36806
|
+
function toTopScreenPoint(doc, point) {
|
|
36807
|
+
let win = doc.defaultView;
|
|
36731
36808
|
while (win) {
|
|
36732
36809
|
const frameElement = getFrameElement(win);
|
|
36733
36810
|
if (!frameElement) break;
|
|
@@ -36833,8 +36910,7 @@ function resolveManualOffsetForPointerDelta(input) {
|
|
|
36833
36910
|
};
|
|
36834
36911
|
}
|
|
36835
36912
|
function createManualOffsetDragMember(input) {
|
|
36836
|
-
const
|
|
36837
|
-
const plainTranslate = !!input.gesture && !gsapOwns(input.element);
|
|
36913
|
+
const plainTranslate = !!input.gesture && editsPlainCss(input.element, input.gesture === "resize" ? "resize" : "move");
|
|
36838
36914
|
const initialOffset = plainTranslate ? readTranslatePx(input.element) : readAppliedStudioPathOffset(input.element);
|
|
36839
36915
|
if (!finitePoint(initialOffset))
|
|
36840
36916
|
return { ok: false, reason: UNREADABLE_TRANSLATE, selection: input.selection };
|
|
@@ -37835,7 +37911,7 @@ function startGesture(kind, e, opts, options) {
|
|
|
37835
37911
|
}
|
|
37836
37912
|
}
|
|
37837
37913
|
try {
|
|
37838
|
-
const plain = !!pathOffsetMember?.plainTranslate ||
|
|
37914
|
+
const plain = !!pathOffsetMember?.plainTranslate || editsPlainCss(sel.element, "rotate");
|
|
37839
37915
|
const plainRotation = plain && kind === "rotate" ? readCssRotationTarget(sel.element) : null;
|
|
37840
37916
|
const rotation = { angle: readRotationBase(sel.element, plain) };
|
|
37841
37917
|
const overlayBounds = overlayEl?.getBoundingClientRect();
|
|
@@ -38368,7 +38444,7 @@ function createDomEditOverlayGestureHandlers(opts) {
|
|
|
38368
38444
|
return;
|
|
38369
38445
|
}
|
|
38370
38446
|
applyRotationDraft(sel.element, finalRotation.angle, g.plainRotation);
|
|
38371
|
-
const commit =
|
|
38447
|
+
const commit = { ...finalRotation, plain: g.plainRotation };
|
|
38372
38448
|
void Promise.resolve(opts.onRotationCommitRef.current(sel, commit)).then((result) => trackPreviewEditResult("rotate", "drag", result)).catch((error) => {
|
|
38373
38449
|
logGestureCommitFailure("rotate commit failed", error);
|
|
38374
38450
|
if (g.manualEditDragToken && isStudioManualEditGestureCurrent(sel.element, g.manualEditDragToken))
|
|
@@ -38427,14 +38503,14 @@ function createDomEditOverlayGestureHandlers(opts) {
|
|
|
38427
38503
|
hasMember: !!member,
|
|
38428
38504
|
inlineStyle: sel.element.getAttribute("style")
|
|
38429
38505
|
});
|
|
38430
|
-
const
|
|
38506
|
+
const restore2 = () => {
|
|
38431
38507
|
if (!g.manualEditDragToken || !isStudioManualEditGestureCurrent(sel.element, g.manualEditDragToken))
|
|
38432
38508
|
return;
|
|
38433
38509
|
restoreStudioBoxSize(sel.element, g.initialBoxSize);
|
|
38434
38510
|
if (finalOffset) restoreStudioPathOffset(sel.element, g.initialPathOffset);
|
|
38435
38511
|
};
|
|
38436
38512
|
void Promise.resolve(
|
|
38437
|
-
opts.onBoxSizeCommitRef.current(sel, finalSize, finalOffset ?? void 0,
|
|
38513
|
+
opts.onBoxSizeCommitRef.current(sel, finalSize, finalOffset ?? void 0, restore2, member)
|
|
38438
38514
|
).then((result) => trackPreviewEditResult("resize", "drag", result)).catch((error) => {
|
|
38439
38515
|
logGestureCommitFailure("resize commit failed", error);
|
|
38440
38516
|
}).finally(() => {
|
|
@@ -41580,77 +41656,6 @@ function isDomEditPersistOutcome(value) {
|
|
|
41580
41656
|
return typeof Reflect.get(value, "sourceFile") === "string" && typeof Reflect.get(value, "version") === "string" && typeof Reflect.get(value, "changed") === "boolean";
|
|
41581
41657
|
}
|
|
41582
41658
|
|
|
41583
|
-
// src/components/editor/manualEditingAvailability.ts
|
|
41584
|
-
var manualEditingAvailability_exports = {};
|
|
41585
|
-
__export(manualEditingAvailability_exports, {
|
|
41586
|
-
STUDIO_FLAT_INSPECTOR_ENABLED: () => STUDIO_FLAT_INSPECTOR_ENABLED,
|
|
41587
|
-
STUDIO_SDK_CUTOVER_ENABLED: () => STUDIO_SDK_CUTOVER_ENABLED,
|
|
41588
|
-
STUDIO_SDK_CUTOVER_FAMILIES: () => STUDIO_SDK_CUTOVER_FAMILIES,
|
|
41589
|
-
STUDIO_SDK_RESOLVER_SHADOW_ENABLED: () => STUDIO_SDK_RESOLVER_SHADOW_ENABLED,
|
|
41590
|
-
resolveStudioBooleanEnvFlag: () => resolveStudioBooleanEnvFlag
|
|
41591
|
-
});
|
|
41592
|
-
|
|
41593
|
-
// src/utils/sdkCutoverPolicy.ts
|
|
41594
|
-
var STUDIO_SDK_OPERATION_FAMILIES = [
|
|
41595
|
-
"dom",
|
|
41596
|
-
"timing",
|
|
41597
|
-
"gsap-animation",
|
|
41598
|
-
"gsap-keyframe",
|
|
41599
|
-
"lifecycle"
|
|
41600
|
-
];
|
|
41601
|
-
function resolveEnabledSdkFamilies(env2, masterEnabled) {
|
|
41602
|
-
if (!masterEnabled) return /* @__PURE__ */ new Set();
|
|
41603
|
-
const raw = env2["VITE_STUDIO_SDK_CUTOVER_FAMILIES"];
|
|
41604
|
-
if (typeof raw !== "string" || raw.trim() === "") return new Set(STUDIO_SDK_OPERATION_FAMILIES);
|
|
41605
|
-
const requested = raw.split(",").map((family) => family.trim()).filter(Boolean);
|
|
41606
|
-
const known = new Set(STUDIO_SDK_OPERATION_FAMILIES);
|
|
41607
|
-
const unknown = requested.filter((family) => !known.has(family));
|
|
41608
|
-
if (unknown.length > 0) {
|
|
41609
|
-
throw new Error(`Unknown Studio SDK cutover families: ${unknown.join(", ")}`);
|
|
41610
|
-
}
|
|
41611
|
-
return new Set(requested);
|
|
41612
|
-
}
|
|
41613
|
-
function isSdkFamilyEnabled(masterEnabled, configured, family) {
|
|
41614
|
-
return masterEnabled && (configured?.has(family) ?? true);
|
|
41615
|
-
}
|
|
41616
|
-
|
|
41617
|
-
// src/components/editor/manualEditingAvailability.ts
|
|
41618
|
-
var TRUTHY_ENV_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on", "enabled"]);
|
|
41619
|
-
var FALSY_ENV_VALUES = /* @__PURE__ */ new Set(["0", "false", "no", "off", "disabled"]);
|
|
41620
|
-
function resolveStudioBooleanEnvFlag(env2, names, fallback) {
|
|
41621
|
-
for (const name of names) {
|
|
41622
|
-
const value = env2[name];
|
|
41623
|
-
if (typeof value === "boolean") return value;
|
|
41624
|
-
if (typeof value !== "string") continue;
|
|
41625
|
-
const normalized = value.trim().toLowerCase();
|
|
41626
|
-
if (!normalized) continue;
|
|
41627
|
-
if (TRUTHY_ENV_VALUES.has(normalized)) return true;
|
|
41628
|
-
if (FALSY_ENV_VALUES.has(normalized)) return false;
|
|
41629
|
-
}
|
|
41630
|
-
return fallback;
|
|
41631
|
-
}
|
|
41632
|
-
var runtimeEnv = typeof window !== "undefined" ? window.__HF_STUDIO_ENV__ ?? {} : {};
|
|
41633
|
-
var env = { ...import.meta.env ?? {}, ...runtimeEnv };
|
|
41634
|
-
var STUDIO_SDK_CUTOVER_ENABLED = resolveStudioBooleanEnvFlag(
|
|
41635
|
-
env,
|
|
41636
|
-
["VITE_STUDIO_SDK_CUTOVER_ENABLED"],
|
|
41637
|
-
true
|
|
41638
|
-
);
|
|
41639
|
-
var STUDIO_SDK_CUTOVER_FAMILIES = resolveEnabledSdkFamilies(
|
|
41640
|
-
env,
|
|
41641
|
-
STUDIO_SDK_CUTOVER_ENABLED
|
|
41642
|
-
);
|
|
41643
|
-
var STUDIO_SDK_RESOLVER_SHADOW_ENABLED = resolveStudioBooleanEnvFlag(
|
|
41644
|
-
env,
|
|
41645
|
-
["VITE_STUDIO_SDK_RESOLVER_SHADOW_ENABLED"],
|
|
41646
|
-
true
|
|
41647
|
-
);
|
|
41648
|
-
var STUDIO_FLAT_INSPECTOR_ENABLED = resolveStudioBooleanEnvFlag(
|
|
41649
|
-
env,
|
|
41650
|
-
["VITE_STUDIO_ENABLE_FLAT_INSPECTOR", "VITE_STUDIO_FLAT_INSPECTOR_ENABLED"],
|
|
41651
|
-
true
|
|
41652
|
-
);
|
|
41653
|
-
|
|
41654
41659
|
// src/utils/sdkOpMapping.ts
|
|
41655
41660
|
function patchOpsToSdkEditOps(hfId, ops) {
|
|
41656
41661
|
const result = [];
|
|
@@ -42772,7 +42777,7 @@ function useElementLifecycleOps({
|
|
|
42772
42777
|
writeFile: writeProjectFile,
|
|
42773
42778
|
recordEdit: editHistory.recordEdit,
|
|
42774
42779
|
rewrite: async (originalContent) => {
|
|
42775
|
-
const removeResponse = await
|
|
42780
|
+
const removeResponse = await studioApiFetch(
|
|
42776
42781
|
buildProjectApiPath(
|
|
42777
42782
|
pid,
|
|
42778
42783
|
`/file-mutations/remove-elements/${encodeURIComponent(targetPath)}`
|
|
@@ -44716,7 +44721,9 @@ import { editabilityForProvenance } from "@hyperframes/core/gsap-parser";
|
|
|
44716
44721
|
var GSAP_EDIT_BLOCK_COPY = {
|
|
44717
44722
|
"no-selector": "This layer needs a stable selector before Studio can save the edit.",
|
|
44718
44723
|
"unroll-required": "This motion comes from a helper or loop. Choose Unroll to edit it explicitly.",
|
|
44719
|
-
"source-uneditable": "This animation is computed at runtime. Edit the animation in the Code tab."
|
|
44724
|
+
"source-uneditable": "This animation is computed at runtime. Edit the animation in the Code tab.",
|
|
44725
|
+
"keyframes-uneditable": "Studio can't add this edit to the animation's keyframes. Edit this animation in the Code tab.",
|
|
44726
|
+
"mixed-files": "These layers are animated in different files. Move each file's layers separately."
|
|
44720
44727
|
};
|
|
44721
44728
|
var GsapEditBlockedError = class extends Error {
|
|
44722
44729
|
constructor(reason, detail) {
|
|
@@ -44943,62 +44950,6 @@ async function commitStaticGsapSize(selection, size, selector, existingSet, call
|
|
|
44943
44950
|
{ label: "Resize layer", softReload: true }
|
|
44944
44951
|
);
|
|
44945
44952
|
}
|
|
44946
|
-
function positiveOr(n, fallback) {
|
|
44947
|
-
return Number.isFinite(n) && n > 0 ? roundToLayoutPx(n) : fallback;
|
|
44948
|
-
}
|
|
44949
|
-
function resolvePriorSize(sizeSet, el, fallbackW, fallbackH) {
|
|
44950
|
-
if (sizeSet) {
|
|
44951
|
-
return {
|
|
44952
|
-
width: positiveOr(Number(sizeSet.properties.width), fallbackW),
|
|
44953
|
-
height: positiveOr(Number(sizeSet.properties.height), fallbackH)
|
|
44954
|
-
};
|
|
44955
|
-
}
|
|
44956
|
-
const ow = Number.parseFloat(el?.getAttribute(STUDIO_ORIGINAL_WIDTH_ATTR) ?? "");
|
|
44957
|
-
const oh = Number.parseFloat(el?.getAttribute(STUDIO_ORIGINAL_HEIGHT_ATTR) ?? "");
|
|
44958
|
-
return { width: positiveOr(ow, fallbackW), height: positiveOr(oh, fallbackH) };
|
|
44959
|
-
}
|
|
44960
|
-
async function commitKeyframedSizeFromResize(selection, size, selector, sizeSet, animatedTween, callbacks) {
|
|
44961
|
-
const ts = resolveTweenStart(animatedTween) ?? 0;
|
|
44962
|
-
const td = resolveTweenDuration(animatedTween);
|
|
44963
|
-
if (!(td > 0)) return false;
|
|
44964
|
-
const newW = roundToLayoutPx(size.width);
|
|
44965
|
-
const newH = roundToLayoutPx(size.height);
|
|
44966
|
-
const prior = resolvePriorSize(sizeSet, selection.element, newW, newH);
|
|
44967
|
-
const ct = usePlayerStore.getState().currentTime;
|
|
44968
|
-
const pct = Math.max(0, Math.min(100, Math.round((ct - ts) / td * 1e3) / 10));
|
|
44969
|
-
const pcts = new Set(
|
|
44970
|
-
animatedTween.keyframes?.keyframes.map((k) => k.percentage) ?? [0, 100]
|
|
44971
|
-
);
|
|
44972
|
-
pcts.add(0);
|
|
44973
|
-
pcts.add(100);
|
|
44974
|
-
pcts.add(pct);
|
|
44975
|
-
const keyframes = Array.from(pcts).sort((a, b) => a - b).map((p) => ({
|
|
44976
|
-
percentage: p,
|
|
44977
|
-
properties: Math.abs(p - pct) < 0.05 ? { width: newW, height: newH } : { ...prior }
|
|
44978
|
-
}));
|
|
44979
|
-
const addLabel = `Resize (size keyframe ${pct.toFixed(0)}%)`;
|
|
44980
|
-
const target = newTweenTarget(selection);
|
|
44981
|
-
if (!target) return false;
|
|
44982
|
-
await callbacks.commitMutation(
|
|
44983
|
-
selection,
|
|
44984
|
-
{
|
|
44985
|
-
type: "add-with-keyframes",
|
|
44986
|
-
targetSelector: target,
|
|
44987
|
-
position: roundTo3(ts),
|
|
44988
|
-
duration: roundTo3(td),
|
|
44989
|
-
keyframes
|
|
44990
|
-
},
|
|
44991
|
-
sizeSet ? { label: addLabel, skipReload: true } : { label: addLabel, softReload: true }
|
|
44992
|
-
);
|
|
44993
|
-
if (sizeSet) {
|
|
44994
|
-
await callbacks.commitMutation(
|
|
44995
|
-
selection,
|
|
44996
|
-
{ type: "delete", animationId: sizeSet.id },
|
|
44997
|
-
{ label: "Resize layer", softReload: true }
|
|
44998
|
-
);
|
|
44999
|
-
}
|
|
45000
|
-
return true;
|
|
45001
|
-
}
|
|
45002
44953
|
async function commitWholePathOffset(selection, anim, studioOffset, gsapPos, iframe, selector, callbacks) {
|
|
45003
44954
|
const el = selection.element;
|
|
45004
44955
|
const { newX, newY, baseGsapX, baseGsapY } = computeDraggedGsapPosition(
|
|
@@ -45023,7 +44974,6 @@ async function commitWholePathOffset(selection, anim, studioOffset, gsapPos, ifr
|
|
|
45023
44974
|
}
|
|
45024
44975
|
const ts = resolveTweenStart(effectiveAnim);
|
|
45025
44976
|
const td = resolveTweenDuration(effectiveAnim);
|
|
45026
|
-
const ease = effectiveAnim.keyframes?.easeEach ?? effectiveAnim.ease;
|
|
45027
44977
|
let kfs = effectiveAnim.keyframes?.keyframes ?? [];
|
|
45028
44978
|
if (kfs.length === 0) {
|
|
45029
44979
|
const fromProps = effectiveAnim.fromProperties ?? {};
|
|
@@ -45055,7 +45005,7 @@ async function commitWholePathOffset(selection, anim, studioOffset, gsapPos, ifr
|
|
|
45055
45005
|
position: roundTo3(ts ?? 0),
|
|
45056
45006
|
duration: roundTo3(td || 1),
|
|
45057
45007
|
keyframes: shifted,
|
|
45058
|
-
|
|
45008
|
+
...keyframeEases(effectiveAnim)
|
|
45059
45009
|
},
|
|
45060
45010
|
{ label: "Move animation path", softReload: true, beforeReload: restoreOffset }
|
|
45061
45011
|
);
|
|
@@ -45070,7 +45020,6 @@ async function commitWholePropertyOffset(selection, anim, newValues, currentPct,
|
|
|
45070
45020
|
}
|
|
45071
45021
|
const ts = resolveTweenStart(effectiveAnim);
|
|
45072
45022
|
const td = resolveTweenDuration(effectiveAnim);
|
|
45073
|
-
const ease = effectiveAnim.keyframes?.easeEach ?? effectiveAnim.ease;
|
|
45074
45023
|
const keys = Object.keys(newValues);
|
|
45075
45024
|
const at = (props, key2) => typeof props[key2] === "number" ? props[key2] : PROPERTY_DEFAULTS[key2] ?? 0;
|
|
45076
45025
|
const kfs = effectiveAnim.keyframes?.keyframes ?? synthesizeFlatTweenKeyframes(effectiveAnim)?.keyframes;
|
|
@@ -45103,7 +45052,7 @@ async function commitWholePropertyOffset(selection, anim, newValues, currentPct,
|
|
|
45103
45052
|
position: roundTo3(ts ?? 0),
|
|
45104
45053
|
duration: roundTo3(td || 1),
|
|
45105
45054
|
keyframes: shifted,
|
|
45106
|
-
|
|
45055
|
+
...keyframeEases(effectiveAnim)
|
|
45107
45056
|
},
|
|
45108
45057
|
{ label, softReload: true }
|
|
45109
45058
|
);
|
|
@@ -46261,7 +46210,7 @@ import { useCallback as useCallback68, useMemo as useMemo45 } from "react";
|
|
|
46261
46210
|
import { useEffect as useEffect62, useMemo as useMemo44, useRef as useRef78, useState as useState55, useCallback as useCallback67 } from "react";
|
|
46262
46211
|
|
|
46263
46212
|
// src/hooks/gsapRuntimeBridge.ts
|
|
46264
|
-
import { isXYPositionWrite } from "@hyperframes/parsers/gsap-constants";
|
|
46213
|
+
import { isXYPositionWrite, PROPERTY_GROUPS } from "@hyperframes/parsers/gsap-constants";
|
|
46265
46214
|
|
|
46266
46215
|
// src/hooks/gsapRuntimeReaders.ts
|
|
46267
46216
|
import { classifyPropertyGroup as classifyPropertyGroup3 } from "@hyperframes/core/gsap-parser";
|
|
@@ -46367,183 +46316,382 @@ function readAllAnimatedProperties(iframe, selector, anim, group) {
|
|
|
46367
46316
|
return result;
|
|
46368
46317
|
}
|
|
46369
46318
|
|
|
46370
|
-
// src/hooks/
|
|
46371
|
-
|
|
46372
|
-
function
|
|
46373
|
-
|
|
46374
|
-
|
|
46375
|
-
|
|
46376
|
-
|
|
46377
|
-
|
|
46378
|
-
|
|
46379
|
-
{ percentage, properties }
|
|
46380
|
-
].sort((a, b) => a.percentage - b.percentage);
|
|
46319
|
+
// src/hooks/gsapParsedTween.ts
|
|
46320
|
+
var PARSED_NAME = { scale: "scaleX", rotate: "rotation" };
|
|
46321
|
+
function findPropTween(pt, prop) {
|
|
46322
|
+
for (let node = pt; node; node = node._next) {
|
|
46323
|
+
if (node.p === prop && typeof node.s === "number" && typeof node.c === "number") return node;
|
|
46324
|
+
const nested = findPropTween(node.d?._pt, prop);
|
|
46325
|
+
if (nested) return nested;
|
|
46326
|
+
}
|
|
46327
|
+
return null;
|
|
46381
46328
|
}
|
|
46382
|
-
|
|
46383
|
-
const
|
|
46384
|
-
|
|
46385
|
-
const
|
|
46386
|
-
|
|
46387
|
-
const existingKfs = anim.keyframes?.keyframes ?? [];
|
|
46388
|
-
const remappedKfs = [];
|
|
46389
|
-
for (const kf of existingKfs) {
|
|
46390
|
-
const absTime = tweenStart + kf.percentage / 100 * tweenDuration;
|
|
46391
|
-
const newPct = Math.round((absTime - newStart) / newDuration * 1e3) / 10;
|
|
46392
|
-
const props = { ...kf.properties };
|
|
46393
|
-
for (const k of Object.keys(properties)) {
|
|
46394
|
-
if (!(k in props) && backfillDefaults?.[k] != null) props[k] = backfillDefaults[k];
|
|
46395
|
-
}
|
|
46396
|
-
remappedKfs.push({ percentage: newPct, properties: props });
|
|
46397
|
-
}
|
|
46398
|
-
const targetPct = Math.round((targetTime - newStart) / newDuration * 1e3) / 10;
|
|
46399
|
-
remappedKfs.push({ percentage: targetPct, properties });
|
|
46400
|
-
remappedKfs.sort((a, b) => a.percentage - b.percentage);
|
|
46401
|
-
await callbacks.commitMutation(
|
|
46402
|
-
selection,
|
|
46403
|
-
{
|
|
46404
|
-
type: "replace-with-keyframes",
|
|
46405
|
-
animationId: anim.id,
|
|
46406
|
-
targetSelector: anim.targetSelector,
|
|
46407
|
-
position: roundTo3(newStart),
|
|
46408
|
-
duration: roundTo3(newDuration),
|
|
46409
|
-
keyframes: remappedKfs
|
|
46410
|
-
},
|
|
46411
|
-
{
|
|
46412
|
-
label: `Move layer (extended keyframe)`,
|
|
46413
|
-
keyframeAction: "add",
|
|
46414
|
-
softReload: true,
|
|
46415
|
-
beforeReload
|
|
46416
|
-
}
|
|
46417
|
-
);
|
|
46329
|
+
function endsIn(tween, prop) {
|
|
46330
|
+
const pt = findPropTween(tween._pt, PARSED_NAME[prop] ?? prop);
|
|
46331
|
+
if (!pt) return null;
|
|
46332
|
+
const pair = [pt.s, pt.s + pt.c];
|
|
46333
|
+
return tween._from ? [pair[1], pair[0]] : pair;
|
|
46418
46334
|
}
|
|
46419
|
-
|
|
46420
|
-
const
|
|
46421
|
-
const
|
|
46422
|
-
|
|
46423
|
-
|
|
46424
|
-
|
|
46425
|
-
|
|
46426
|
-
|
|
46427
|
-
|
|
46428
|
-
|
|
46429
|
-
|
|
46430
|
-
|
|
46431
|
-
|
|
46432
|
-
|
|
46433
|
-
|
|
46434
|
-
|
|
46435
|
-
|
|
46436
|
-
|
|
46335
|
+
function findParsedTween(iframe, element, anim) {
|
|
46336
|
+
const win = iframe?.contentWindow;
|
|
46337
|
+
const start = resolveTweenStart(anim);
|
|
46338
|
+
if (!win?.__timelines || start == null) return null;
|
|
46339
|
+
const steps = anim.keyframes?.keyframes.map((kf) => kf.properties) ?? [anim.properties];
|
|
46340
|
+
const props = [...new Set(steps.flatMap((step) => Object.keys(step)))];
|
|
46341
|
+
const keyframed = Boolean(anim.keyframes);
|
|
46342
|
+
for (const timeline of Object.values(win.__timelines)) {
|
|
46343
|
+
for (const tween of timeline?.getChildren?.(true) ?? []) {
|
|
46344
|
+
const targets = tween.targets?.() ?? [];
|
|
46345
|
+
if (!targets.includes(element) && !targets.some((t2) => element.id && t2.id === element.id))
|
|
46346
|
+
continue;
|
|
46347
|
+
if (!startsAt(tween, start)) continue;
|
|
46348
|
+
const vars = tween.vars ?? {};
|
|
46349
|
+
const carries = keyframed ? "keyframes" in vars : props.some((p) => p in vars);
|
|
46350
|
+
if (!carries || !((tween.duration?.() ?? 0) > 0)) continue;
|
|
46351
|
+
parseUnplayed(win, element, tween, props);
|
|
46352
|
+
return tween;
|
|
46353
|
+
}
|
|
46437
46354
|
}
|
|
46355
|
+
return null;
|
|
46438
46356
|
}
|
|
46439
|
-
function
|
|
46440
|
-
|
|
46441
|
-
|
|
46442
|
-
|
|
46443
|
-
|
|
46444
|
-
|
|
46357
|
+
function startsAt(tween, start) {
|
|
46358
|
+
const delay2 = Number(tween.vars?.delay) || 0;
|
|
46359
|
+
return Math.abs((tween.startTime?.() ?? Number.NaN) - delay2 - start) <= 1e-3;
|
|
46360
|
+
}
|
|
46361
|
+
function withLiveTiming(anim, tween) {
|
|
46362
|
+
const start = tween?.startTime?.();
|
|
46363
|
+
return {
|
|
46364
|
+
...anim,
|
|
46365
|
+
...Number.isFinite(start) && { resolvedStart: start },
|
|
46366
|
+
...anim.duration == null && tween && { duration: tween.duration?.() }
|
|
46367
|
+
};
|
|
46368
|
+
}
|
|
46369
|
+
function parseUnplayed(win, element, tween, props) {
|
|
46370
|
+
const seek = !isParsed(tween) && seekable(win, tween);
|
|
46371
|
+
if (!seek) return;
|
|
46372
|
+
const { parent, gsap: gsap3 } = seek;
|
|
46373
|
+
const before = layerStates(tween, element);
|
|
46374
|
+
const now2 = parent.time();
|
|
46375
|
+
const start = tween.startTime?.() ?? 0;
|
|
46376
|
+
gsap3.set(element, { clearProps: props.join(",") });
|
|
46377
|
+
for (const p of props) gsap3.getProperty(element, p);
|
|
46445
46378
|
try {
|
|
46446
|
-
|
|
46447
|
-
|
|
46448
|
-
|
|
46379
|
+
parent.seek(start, true);
|
|
46380
|
+
parent.seek(start + (tween.duration?.() ?? 0), true);
|
|
46381
|
+
} finally {
|
|
46382
|
+
parent.seek(now2, true);
|
|
46383
|
+
restore(before);
|
|
46449
46384
|
}
|
|
46450
|
-
|
|
46451
|
-
|
|
46452
|
-
const
|
|
46453
|
-
|
|
46385
|
+
}
|
|
46386
|
+
function isParsed(tween) {
|
|
46387
|
+
const parts = tween.timeline?.getChildren?.() ?? [];
|
|
46388
|
+
return Boolean(tween._initted) && parts.every((part) => part._initted);
|
|
46389
|
+
}
|
|
46390
|
+
function seekable(win, tween) {
|
|
46391
|
+
const { parent } = tween;
|
|
46392
|
+
const gsap3 = win.gsap;
|
|
46393
|
+
if (!parent?.seek || !parent.time || !gsap3?.getProperty || !gsap3.set) return null;
|
|
46454
46394
|
return {
|
|
46455
|
-
|
|
46456
|
-
|
|
46457
|
-
|
|
46458
|
-
|
|
46395
|
+
parent: { seek: parent.seek.bind(parent), time: parent.time.bind(parent) },
|
|
46396
|
+
gsap: { getProperty: gsap3.getProperty.bind(gsap3), set: gsap3.set.bind(gsap3) }
|
|
46397
|
+
};
|
|
46398
|
+
}
|
|
46399
|
+
function layerStates(tween, element) {
|
|
46400
|
+
const layers = /* @__PURE__ */ new Set([element]);
|
|
46401
|
+
for (const child of tween.parent?.getChildren?.(true, true, false) ?? [])
|
|
46402
|
+
for (const target of elementTargets(child)) layers.add(target);
|
|
46403
|
+
return [...layers].map((layer) => ({
|
|
46404
|
+
layer,
|
|
46405
|
+
attributes: new Map([...layer.attributes].map((a) => [a.name, a.value])),
|
|
46406
|
+
cache: layer._gsap && { ...layer._gsap }
|
|
46407
|
+
}));
|
|
46459
46408
|
}
|
|
46460
|
-
|
|
46461
|
-
const
|
|
46462
|
-
|
|
46463
|
-
|
|
46464
|
-
|
|
46465
|
-
|
|
46466
|
-
|
|
46467
|
-
const
|
|
46468
|
-
|
|
46469
|
-
|
|
46470
|
-
|
|
46471
|
-
|
|
46472
|
-
|
|
46473
|
-
|
|
46474
|
-
|
|
46475
|
-
|
|
46476
|
-
|
|
46477
|
-
|
|
46409
|
+
function restore(states) {
|
|
46410
|
+
for (const { layer, attributes, cache: cache2 } of states) {
|
|
46411
|
+
restoreAttributes(layer, attributes);
|
|
46412
|
+
if (cache2 && layer._gsap) restoreCache(layer._gsap, cache2);
|
|
46413
|
+
}
|
|
46414
|
+
}
|
|
46415
|
+
function restoreAttributes(layer, attributes) {
|
|
46416
|
+
for (const { name } of [...layer.attributes])
|
|
46417
|
+
if (!attributes.has(name)) layer.removeAttribute(name);
|
|
46418
|
+
for (const [name, value] of attributes)
|
|
46419
|
+
if (layer.getAttribute(name) !== value) layer.setAttribute(name, value);
|
|
46420
|
+
}
|
|
46421
|
+
function restoreCache(live, cache2) {
|
|
46422
|
+
for (const key2 of Object.keys(live)) if (!(key2 in cache2)) delete live[key2];
|
|
46423
|
+
Object.assign(live, cache2);
|
|
46424
|
+
}
|
|
46425
|
+
function parsedImplicitEndValue(tween) {
|
|
46426
|
+
return (prop, end) => {
|
|
46427
|
+
if (!tween) return null;
|
|
46428
|
+
for (const part of partsFrom(tween, end)) {
|
|
46429
|
+
const pair = endsIn(part, prop);
|
|
46430
|
+
if (pair) return end === "start" ? pair[0] : pair[1];
|
|
46431
|
+
if (prop in (part.vars ?? {})) return null;
|
|
46478
46432
|
}
|
|
46479
|
-
|
|
46480
|
-
|
|
46481
|
-
|
|
46482
|
-
|
|
46483
|
-
|
|
46484
|
-
|
|
46485
|
-
|
|
46486
|
-
|
|
46487
|
-
|
|
46488
|
-
|
|
46489
|
-
|
|
46490
|
-
|
|
46433
|
+
return null;
|
|
46434
|
+
};
|
|
46435
|
+
}
|
|
46436
|
+
function partsFrom(tween, end) {
|
|
46437
|
+
const parts = tween.timeline?.getChildren?.() ?? [];
|
|
46438
|
+
if (parts.length === 0) return [tween];
|
|
46439
|
+
return end === "start" ? parts : [...parts].reverse();
|
|
46440
|
+
}
|
|
46441
|
+
function parsedTweenEase(iframe, tween) {
|
|
46442
|
+
if (!tween) return null;
|
|
46443
|
+
const win = iframe?.contentWindow;
|
|
46444
|
+
const ease = tween.vars?.ease ?? tween.parent?.vars?.defaults?.ease ?? win?.gsap?.defaults?.().ease;
|
|
46445
|
+
if (typeof ease === "string") return ease;
|
|
46446
|
+
const named = (name) => win?.gsap?.parseEase?.(name) === ease;
|
|
46447
|
+
return typeof ease === "function" ? BUILT_IN_EASES.find(named) ?? null : null;
|
|
46448
|
+
}
|
|
46449
|
+
var BUILT_IN_EASES = [
|
|
46450
|
+
"none",
|
|
46451
|
+
...[
|
|
46452
|
+
"power1",
|
|
46453
|
+
"power2",
|
|
46454
|
+
"power3",
|
|
46455
|
+
"power4",
|
|
46456
|
+
"sine",
|
|
46457
|
+
"expo",
|
|
46458
|
+
"circ",
|
|
46459
|
+
"back",
|
|
46460
|
+
"elastic",
|
|
46461
|
+
"bounce"
|
|
46462
|
+
].flatMap((e) => [`${e}.in`, `${e}.out`, `${e}.inOut`])
|
|
46463
|
+
];
|
|
46464
|
+
function withExactStepTimes(anim, tween) {
|
|
46465
|
+
const data = anim.keyframes;
|
|
46466
|
+
const parts = tween?.timeline?.getChildren?.() ?? [];
|
|
46467
|
+
const total = tween?.duration?.() ?? 0;
|
|
46468
|
+
if (data?.format !== "object-array" || parts.length !== data.keyframes.length || !(total > 0))
|
|
46469
|
+
return anim;
|
|
46470
|
+
const ends = parts.map((part) => ((part.startTime?.() ?? 0) + (part.duration?.() ?? 0)) / total);
|
|
46471
|
+
if (ends.some((end) => !Number.isFinite(end))) return anim;
|
|
46472
|
+
const keyframes = data.keyframes.map((kf, i) => ({
|
|
46473
|
+
...kf,
|
|
46474
|
+
percentage: Math.round(ends[i] * 1e5) / 1e3
|
|
46475
|
+
}));
|
|
46476
|
+
return { ...anim, keyframes: { ...data, keyframes } };
|
|
46477
|
+
}
|
|
46478
|
+
|
|
46479
|
+
// src/hooks/gsapTweenReach.ts
|
|
46480
|
+
function tweenReach(animation, el) {
|
|
46481
|
+
const doc = el?.ownerDocument;
|
|
46482
|
+
if (!el?.isConnected || !doc || animation.hasUnresolvedSelector) return "unknown";
|
|
46483
|
+
let hits;
|
|
46484
|
+
try {
|
|
46485
|
+
hits = [...doc.querySelectorAll(animation.targetSelector)];
|
|
46486
|
+
} catch {
|
|
46487
|
+
return "unknown";
|
|
46488
|
+
}
|
|
46489
|
+
if (!hits.includes(el)) return "elsewhere";
|
|
46490
|
+
return hits.length === 1 ? "own" : "shared";
|
|
46491
|
+
}
|
|
46492
|
+
function tweensForThisElement(selection, animations) {
|
|
46493
|
+
return animations.filter((a) => {
|
|
46494
|
+
const reach = tweenReach(a, selection.element);
|
|
46495
|
+
return reach === "own" || reach === "unknown";
|
|
46496
|
+
});
|
|
46497
|
+
}
|
|
46498
|
+
|
|
46499
|
+
// src/hooks/gsapValueAtPlayhead.ts
|
|
46500
|
+
var PERCENTAGE_SEGMENT_EASE = "power1.inOut";
|
|
46501
|
+
var ARRAY_STEP_EASE = "none";
|
|
46502
|
+
var roundPct2 = (pct) => Math.round(pct * 1e3) / 1e3;
|
|
46503
|
+
var isLinear = (ease) => !ease || ease === "none" || ease === "linear";
|
|
46504
|
+
function refuse(reason) {
|
|
46505
|
+
return { ok: false, reason };
|
|
46506
|
+
}
|
|
46507
|
+
function normalize(edit) {
|
|
46508
|
+
const { anim } = edit;
|
|
46509
|
+
const data = anim.keyframes;
|
|
46510
|
+
if (data) {
|
|
46511
|
+
if (!isLinear(anim.ease) || !isLinear(data.ease)) return { reason: "eased-keyframes" };
|
|
46512
|
+
if (data.format === "simple-array") return { reason: "simple-array-keyframes" };
|
|
46513
|
+
const arrayStep = data.format === "object-array";
|
|
46514
|
+
return {
|
|
46515
|
+
keyframes: data.keyframes.map((kf) => ({
|
|
46516
|
+
...kf,
|
|
46517
|
+
properties: { ...kf.properties },
|
|
46518
|
+
...arrayStep ? { ease: kf.ease ?? ARRAY_STEP_EASE } : {}
|
|
46519
|
+
})),
|
|
46520
|
+
...data.easeEach && !arrayStep ? { easeEach: data.easeEach } : {}
|
|
46521
|
+
};
|
|
46522
|
+
}
|
|
46523
|
+
const easeEach = anim.ease ?? edit.parsedEase;
|
|
46524
|
+
if (!easeEach) return { reason: "unknown-ease" };
|
|
46525
|
+
const to = { percentage: 100, properties: { ...anim.properties } };
|
|
46526
|
+
if (anim.method === "to") return { keyframes: [to], easeEach };
|
|
46527
|
+
if (anim.method === "fromTo") {
|
|
46528
|
+
return { keyframes: [{ percentage: 0, properties: { ...anim.fromProperties } }, to], easeEach };
|
|
46529
|
+
}
|
|
46530
|
+
if (anim.method === "from") {
|
|
46531
|
+
const end = {};
|
|
46532
|
+
for (const prop of Object.keys(anim.properties)) {
|
|
46533
|
+
const value = edit.implicitEndValue(prop, "end");
|
|
46534
|
+
if (value == null) return { reason: "implicit-end-unknown" };
|
|
46535
|
+
end[prop] = value;
|
|
46491
46536
|
}
|
|
46537
|
+
return {
|
|
46538
|
+
keyframes: [
|
|
46539
|
+
{ percentage: 0, properties: { ...anim.properties } },
|
|
46540
|
+
{ percentage: 100, properties: end }
|
|
46541
|
+
],
|
|
46542
|
+
easeEach
|
|
46543
|
+
};
|
|
46492
46544
|
}
|
|
46493
|
-
|
|
46494
|
-
|
|
46495
|
-
|
|
46496
|
-
|
|
46497
|
-
|
|
46498
|
-
|
|
46499
|
-
|
|
46500
|
-
|
|
46501
|
-
|
|
46502
|
-
|
|
46503
|
-
);
|
|
46504
|
-
|
|
46505
|
-
const converted = fresh.find((a) => a.id === anim.id && a.keyframes) ?? fresh.find((a) => a.targetSelector === anim.targetSelector && a.keyframes) ?? anim;
|
|
46506
|
-
const convertedStart = resolveTweenStart(converted) ?? ts;
|
|
46507
|
-
const convertedDur = resolveTweenDuration(converted) || td;
|
|
46508
|
-
await extendTweenAndAddKeyframe(
|
|
46509
|
-
selection,
|
|
46510
|
-
converted,
|
|
46511
|
-
properties,
|
|
46512
|
-
ct,
|
|
46513
|
-
convertedStart,
|
|
46514
|
-
convertedDur,
|
|
46515
|
-
callbacks,
|
|
46516
|
-
beforeReload
|
|
46517
|
-
);
|
|
46518
|
-
return;
|
|
46545
|
+
return { reason: "not-a-tween" };
|
|
46546
|
+
}
|
|
46547
|
+
function valueAt(keyframes, prop, side) {
|
|
46548
|
+
const carrying = keyframes.filter((kf2) => kf2.properties[prop] != null);
|
|
46549
|
+
const kf = side === "first" ? carrying[0] : carrying.at(-1);
|
|
46550
|
+
return kf ? { percentage: kf.percentage, value: kf.properties[prop] } : null;
|
|
46551
|
+
}
|
|
46552
|
+
function upsert(keyframes, percentage, properties, ease) {
|
|
46553
|
+
const hit = keyframes.find((kf) => Math.abs(kf.percentage - percentage) <= KEYFRAME_PCT_MATCH);
|
|
46554
|
+
if (hit) {
|
|
46555
|
+
Object.assign(hit.properties, properties);
|
|
46556
|
+
return hit;
|
|
46519
46557
|
}
|
|
46520
|
-
const
|
|
46521
|
-
|
|
46522
|
-
|
|
46523
|
-
|
|
46524
|
-
|
|
46525
|
-
|
|
46526
|
-
|
|
46527
|
-
|
|
46528
|
-
|
|
46529
|
-
|
|
46530
|
-
|
|
46531
|
-
|
|
46532
|
-
|
|
46533
|
-
|
|
46534
|
-
|
|
46535
|
-
|
|
46536
|
-
|
|
46558
|
+
const added = { percentage, properties, ...ease ? { ease } : {} };
|
|
46559
|
+
keyframes.push(added);
|
|
46560
|
+
return added;
|
|
46561
|
+
}
|
|
46562
|
+
function heldEnds(norm, end, implicit, backfilled) {
|
|
46563
|
+
const props = new Set(norm.keyframes.flatMap((kf) => Object.keys(kf.properties)));
|
|
46564
|
+
const held = {};
|
|
46565
|
+
for (const prop of props) {
|
|
46566
|
+
const known = valueAt(norm.keyframes, prop, end === "start" ? "first" : "last");
|
|
46567
|
+
const value = known && (end === "end" || known.percentage <= 0) ? known.value : backfilled[prop] ?? implicit(prop, "start");
|
|
46568
|
+
if (value == null) return null;
|
|
46569
|
+
held[prop] = value;
|
|
46570
|
+
}
|
|
46571
|
+
return held;
|
|
46572
|
+
}
|
|
46573
|
+
function planValueAtPlayhead(edit) {
|
|
46574
|
+
const { anim, values } = edit;
|
|
46575
|
+
const start = resolveTweenStart(anim);
|
|
46576
|
+
const duration = resolveTweenDuration(anim);
|
|
46577
|
+
if (start == null || !(duration > 0)) return refuse("no-timing");
|
|
46578
|
+
const norm = normalize(edit);
|
|
46579
|
+
if ("reason" in norm) return refuse(norm.reason);
|
|
46580
|
+
const keyframes = norm.keyframes;
|
|
46581
|
+
const authored = keyframes.length;
|
|
46582
|
+
if ("scaleX" in values || "scaleY" in values) {
|
|
46583
|
+
for (const kf of keyframes) {
|
|
46584
|
+
const { scale, ...rest } = kf.properties;
|
|
46585
|
+
if (scale != null) kf.properties = { scaleX: scale, scaleY: scale, ...rest };
|
|
46586
|
+
}
|
|
46587
|
+
}
|
|
46588
|
+
const backfilled = {};
|
|
46589
|
+
for (const [prop, value] of Object.entries(edit.backfill ?? {})) {
|
|
46590
|
+
if (!(prop in values) || keyframes.some((kf) => kf.properties[prop] != null)) continue;
|
|
46591
|
+
if (edit.holdFromStart && !keyframes.some((kf) => kf.percentage <= 0))
|
|
46592
|
+
keyframes.unshift({ percentage: 0, properties: {} });
|
|
46593
|
+
for (const kf of keyframes) kf.properties[prop] = value;
|
|
46594
|
+
backfilled[prop] = value;
|
|
46595
|
+
}
|
|
46596
|
+
if (Object.keys(values).some((prop) => !keyframes.some((kf) => kf.properties[prop] != null)))
|
|
46597
|
+
return refuse("implicit-end-unknown");
|
|
46598
|
+
let position = start;
|
|
46599
|
+
let span = duration;
|
|
46600
|
+
const pct = "percentage" in edit.at ? edit.at.percentage : roundPct2((edit.at.time - start) / duration * 100);
|
|
46601
|
+
if (pct >= -KEYFRAME_PCT_MATCH && pct <= 100 + KEYFRAME_PCT_MATCH) {
|
|
46602
|
+
const at = Math.min(100, Math.max(0, pct));
|
|
46603
|
+
const next = [...keyframes].sort((a, b) => a.percentage - b.percentage).find((kf) => kf.percentage > at + KEYFRAME_PCT_MATCH);
|
|
46604
|
+
upsert(keyframes, at, { ...values }, next?.ease);
|
|
46605
|
+
} else {
|
|
46606
|
+
const time = edit.at.time;
|
|
46607
|
+
const before = time < start;
|
|
46608
|
+
const held = heldEnds(norm, before ? "start" : "end", edit.implicitEndValue, backfilled);
|
|
46609
|
+
if (!held) return refuse("implicit-end-unknown");
|
|
46610
|
+
position = before ? time : start;
|
|
46611
|
+
span = before ? start + duration - time : time - start;
|
|
46612
|
+
for (const kf of keyframes) {
|
|
46613
|
+
kf.percentage = roundPct2(
|
|
46614
|
+
(start + kf.percentage / 100 * duration - position) / span * 100
|
|
46615
|
+
);
|
|
46616
|
+
}
|
|
46617
|
+
const oldEdge = roundPct2(((before ? start : start + duration) - position) / span * 100);
|
|
46618
|
+
const linear = isLinear(norm.easeEach ?? PERCENTAGE_SEGMENT_EASE) ? void 0 : "none";
|
|
46619
|
+
const edge = upsert(keyframes, oldEdge, held, linear);
|
|
46620
|
+
if (before && linear) edge.ease ??= linear;
|
|
46621
|
+
upsert(keyframes, before ? 0 : 100, { ...held, ...values }, before ? void 0 : linear);
|
|
46622
|
+
}
|
|
46623
|
+
keyframes.sort((a, b) => a.percentage - b.percentage);
|
|
46624
|
+
return {
|
|
46625
|
+
ok: true,
|
|
46626
|
+
mutation: {
|
|
46627
|
+
type: "replace-with-keyframes",
|
|
46537
46628
|
animationId: anim.id,
|
|
46538
|
-
|
|
46539
|
-
|
|
46540
|
-
|
|
46541
|
-
|
|
46542
|
-
|
|
46629
|
+
targetSelector: anim.targetSelector,
|
|
46630
|
+
position: roundTo3(position),
|
|
46631
|
+
duration: roundTo3(span),
|
|
46632
|
+
keyframes,
|
|
46633
|
+
...norm.easeEach ? { easeEach: norm.easeEach } : {}
|
|
46634
|
+
},
|
|
46635
|
+
added: keyframes.length > authored
|
|
46636
|
+
};
|
|
46637
|
+
}
|
|
46638
|
+
function planValueEdit(selection, anim, values, iframe, { backfill, holdFromStart } = {}) {
|
|
46639
|
+
if (tweenReach(anim, selection.element) === "shared") return refuse("shared-tween");
|
|
46640
|
+
const { activeKeyframePct, currentTime } = usePlayerStore.getState();
|
|
46641
|
+
const tween = findParsedTween(iframe, selection.element, anim);
|
|
46642
|
+
const timed = withExactStepTimes(anim, tween);
|
|
46643
|
+
return planValueAtPlayhead({
|
|
46644
|
+
anim: withLiveTiming(timed, tween),
|
|
46645
|
+
at: activeKeyframePct != null ? { percentage: activeKeyframePct } : { time: currentTime },
|
|
46646
|
+
values,
|
|
46647
|
+
backfill,
|
|
46648
|
+
holdFromStart,
|
|
46649
|
+
implicitEndValue: parsedImplicitEndValue(tween),
|
|
46650
|
+
parsedEase: parsedTweenEase(iframe, tween)
|
|
46651
|
+
});
|
|
46652
|
+
}
|
|
46653
|
+
async function commitValueAtPlayhead(selection, anim, values, iframe, callbacks, options) {
|
|
46654
|
+
await materializeIfDynamic(anim, iframe, callbacks.commitMutation, selection);
|
|
46655
|
+
const { activeKeyframePct, setActiveKeyframePct } = usePlayerStore.getState();
|
|
46656
|
+
const plan = planValueEdit(selection, anim, values, iframe, options);
|
|
46657
|
+
if (!plan.ok) return { status: "blocked", reason: "keyframes-uneditable", detail: plan.reason };
|
|
46658
|
+
await callbacks.commitMutation(selection, plan.mutation, {
|
|
46659
|
+
label: options.label,
|
|
46660
|
+
softReload: true,
|
|
46661
|
+
beforeReload: options.beforeReload,
|
|
46662
|
+
...plan.added && { keyframeAction: "add" }
|
|
46663
|
+
});
|
|
46664
|
+
if (activeKeyframePct != null) {
|
|
46665
|
+
setActiveKeyframePct(null);
|
|
46666
|
+
parkPlayheadOnKeyframe(anim, activeKeyframePct);
|
|
46667
|
+
}
|
|
46668
|
+
return { status: "persisted" };
|
|
46669
|
+
}
|
|
46670
|
+
|
|
46671
|
+
// src/hooks/gsapDragPositionCommit.ts
|
|
46672
|
+
function buildTemporalArcKeyframes(anim, percentage, properties) {
|
|
46673
|
+
return [
|
|
46674
|
+
...(anim.keyframes?.keyframes ?? []).filter((keyframe) => Math.abs(keyframe.percentage - percentage) > KEYFRAME_PCT_MATCH).map((keyframe) => ({
|
|
46675
|
+
percentage: keyframe.percentage,
|
|
46676
|
+
properties: { ...keyframe.properties },
|
|
46677
|
+
...keyframe.ease ? { ease: keyframe.ease } : {}
|
|
46678
|
+
})),
|
|
46679
|
+
{ percentage, properties }
|
|
46680
|
+
].sort((a, b) => a.percentage - b.percentage);
|
|
46681
|
+
}
|
|
46682
|
+
function gsapPositionFromDragOutcome(selection, anim, studioOffset, gsapPos, iframe) {
|
|
46683
|
+
if (anim.arcPath?.enabled) return { status: "persisted" };
|
|
46684
|
+
const { newX, newY, baseGsapX, baseGsapY } = computeDraggedGsapPosition(
|
|
46685
|
+
selection.element,
|
|
46686
|
+
studioOffset,
|
|
46687
|
+
gsapPos
|
|
46543
46688
|
);
|
|
46544
|
-
|
|
46689
|
+
const plan = planValueEdit(selection, anim, { x: newX, y: newY }, iframe, {
|
|
46690
|
+
backfill: { x: baseGsapX, y: baseGsapY }
|
|
46691
|
+
});
|
|
46692
|
+
return plan.ok ? { status: "persisted" } : { status: "blocked", reason: "keyframes-uneditable", detail: plan.reason };
|
|
46545
46693
|
}
|
|
46546
|
-
async function commitGsapPositionFromDrag(selection, anim, studioOffset, gsapPos, iframe,
|
|
46694
|
+
async function commitGsapPositionFromDrag(selection, anim, studioOffset, gsapPos, iframe, callbacks) {
|
|
46547
46695
|
const el = selection.element;
|
|
46548
46696
|
const { newX, newY, baseGsapX, baseGsapY } = computeDraggedGsapPosition(
|
|
46549
46697
|
el,
|
|
@@ -46558,8 +46706,6 @@ async function commitGsapPositionFromDrag(selection, anim, studioOffset, gsapPos
|
|
|
46558
46706
|
el.removeAttribute("data-hf-drag-initial-offset-x");
|
|
46559
46707
|
el.removeAttribute("data-hf-drag-initial-offset-y");
|
|
46560
46708
|
};
|
|
46561
|
-
const backfillDefaults = { x: baseGsapX, y: baseGsapY };
|
|
46562
|
-
const ct = usePlayerStore.getState().currentTime;
|
|
46563
46709
|
if (anim.arcPath?.enabled) {
|
|
46564
46710
|
const { activeKeyframePct, setActiveKeyframePct } = usePlayerStore.getState();
|
|
46565
46711
|
const pct = activeKeyframePct ?? computeCurrentPercentage(selection, anim);
|
|
@@ -46581,11 +46727,12 @@ async function commitGsapPositionFromDrag(selection, anim, studioOffset, gsapPos
|
|
|
46581
46727
|
);
|
|
46582
46728
|
setActiveKeyframePct(null);
|
|
46583
46729
|
parkPlayheadOnKeyframe(anim, pct);
|
|
46584
|
-
return;
|
|
46730
|
+
return { status: "persisted" };
|
|
46585
46731
|
}
|
|
46586
46732
|
const tweenStart = resolveTweenStart(anim);
|
|
46587
46733
|
const tweenDuration = resolveEditableTweenDuration(anim, selection);
|
|
46588
|
-
if (tweenStart === null || tweenDuration <= 0 || keyframes.length < 2)
|
|
46734
|
+
if (tweenStart === null || tweenDuration <= 0 || keyframes.length < 2)
|
|
46735
|
+
return { status: "persisted" };
|
|
46589
46736
|
const temporalKeyframes = buildTemporalArcKeyframes(anim, pct, { x: newX, y: newY });
|
|
46590
46737
|
await callbacks.commitMutation(
|
|
46591
46738
|
selection,
|
|
@@ -46605,171 +46752,12 @@ async function commitGsapPositionFromDrag(selection, anim, studioOffset, gsapPos
|
|
|
46605
46752
|
beforeReload: restoreOffset
|
|
46606
46753
|
}
|
|
46607
46754
|
);
|
|
46608
|
-
return;
|
|
46609
|
-
}
|
|
46610
|
-
if (anim.keyframes) {
|
|
46611
|
-
const newId = await materializeIfDynamic(anim, iframe, callbacks.commitMutation, selection);
|
|
46612
|
-
const effectiveAnim = newId ? { ...anim, id: newId } : anim;
|
|
46613
|
-
const dragProps = { x: newX, y: newY };
|
|
46614
|
-
const ts = resolveTweenStart(effectiveAnim);
|
|
46615
|
-
const td = resolveEditableTweenDuration(effectiveAnim, selection);
|
|
46616
|
-
const outsideRange = ts !== null && td > 0 && (ct < ts - 0.01 || ct > ts + td + 0.01);
|
|
46617
|
-
const hasSelectedKeyframe = usePlayerStore.getState().activeKeyframePct != null;
|
|
46618
|
-
if (outsideRange && !hasSelectedKeyframe) {
|
|
46619
|
-
await extendTweenAndAddKeyframe(
|
|
46620
|
-
selection,
|
|
46621
|
-
effectiveAnim,
|
|
46622
|
-
dragProps,
|
|
46623
|
-
ct,
|
|
46624
|
-
ts,
|
|
46625
|
-
td,
|
|
46626
|
-
callbacks,
|
|
46627
|
-
restoreOffset,
|
|
46628
|
-
backfillDefaults
|
|
46629
|
-
);
|
|
46630
|
-
} else {
|
|
46631
|
-
await commitKeyframedPosition(
|
|
46632
|
-
selection,
|
|
46633
|
-
effectiveAnim,
|
|
46634
|
-
dragProps,
|
|
46635
|
-
callbacks,
|
|
46636
|
-
restoreOffset,
|
|
46637
|
-
backfillDefaults
|
|
46638
|
-
);
|
|
46639
|
-
}
|
|
46640
|
-
} else if (anim.method === "from" || anim.method === "fromTo") {
|
|
46641
|
-
const ct2 = usePlayerStore.getState().currentTime;
|
|
46642
|
-
const ts = resolveTweenStart(anim);
|
|
46643
|
-
const td = resolveEditableTweenDuration(anim, selection);
|
|
46644
|
-
const hasSelectedKeyframe = usePlayerStore.getState().activeKeyframePct != null;
|
|
46645
|
-
const outsideRange = !hasSelectedKeyframe && ts !== null && td > 0 && (ct2 < ts - 0.01 || ct2 > ts + td + 0.01);
|
|
46646
|
-
const dragProps = { x: newX, y: newY };
|
|
46647
|
-
if (outsideRange && ts !== null) {
|
|
46648
|
-
await callbacks.commitMutation(
|
|
46649
|
-
selection,
|
|
46650
|
-
{ type: "split-into-property-groups", animationId: anim.id },
|
|
46651
|
-
{ label: "Split from() for drag", skipReload: true }
|
|
46652
|
-
);
|
|
46653
|
-
const allAnims = callbacks.fetchAnimations ? await callbacks.fetchAnimations() : [];
|
|
46654
|
-
const existingPosAnim = allAnims.find(
|
|
46655
|
-
(a) => a.propertyGroup === "position" && a.targetSelector === anim.targetSelector
|
|
46656
|
-
);
|
|
46657
|
-
if (existingPosAnim?.keyframes) {
|
|
46658
|
-
const posTs = resolveTweenStart(existingPosAnim);
|
|
46659
|
-
const posTd = resolveEditableTweenDuration(existingPosAnim, selection);
|
|
46660
|
-
if (posTs !== null) {
|
|
46661
|
-
await extendTweenAndAddKeyframe(
|
|
46662
|
-
selection,
|
|
46663
|
-
existingPosAnim,
|
|
46664
|
-
{ x: newX, y: newY },
|
|
46665
|
-
ct2,
|
|
46666
|
-
posTs,
|
|
46667
|
-
posTd,
|
|
46668
|
-
callbacks,
|
|
46669
|
-
restoreOffset,
|
|
46670
|
-
backfillDefaults
|
|
46671
|
-
);
|
|
46672
|
-
return;
|
|
46673
|
-
}
|
|
46674
|
-
}
|
|
46675
|
-
const newStart = Math.min(ct2, ts);
|
|
46676
|
-
const newEnd = Math.max(ct2, ts + td);
|
|
46677
|
-
const newDuration = Math.max(0.01, newEnd - newStart);
|
|
46678
|
-
const dragBefore = ct2 < ts;
|
|
46679
|
-
const origStartPct = Math.round((ts - newStart) / newDuration * 1e3) / 10;
|
|
46680
|
-
const origEndPct = Math.round((ts + td - newStart) / newDuration * 1e3) / 10;
|
|
46681
|
-
const keyframes = [];
|
|
46682
|
-
if (dragBefore) {
|
|
46683
|
-
keyframes.push({ percentage: 0, properties: { x: newX, y: newY } });
|
|
46684
|
-
if (origStartPct > 0.5 && origStartPct < 99.5) {
|
|
46685
|
-
keyframes.push({ percentage: origStartPct, properties: { x: 0, y: 0 } });
|
|
46686
|
-
}
|
|
46687
|
-
keyframes.push({ percentage: 100, properties: { x: 0, y: 0 } });
|
|
46688
|
-
} else {
|
|
46689
|
-
keyframes.push({ percentage: 0, properties: { x: 0, y: 0 } });
|
|
46690
|
-
if (origEndPct > 0.5 && origEndPct < 99.5) {
|
|
46691
|
-
keyframes.push({ percentage: origEndPct, properties: { x: 0, y: 0 } });
|
|
46692
|
-
}
|
|
46693
|
-
keyframes.push({ percentage: 100, properties: { x: newX, y: newY } });
|
|
46694
|
-
}
|
|
46695
|
-
keyframes.sort((a, b) => a.percentage - b.percentage);
|
|
46696
|
-
const baseKf = {
|
|
46697
|
-
targetSelector: anim.targetSelector,
|
|
46698
|
-
position: roundTo3(newStart),
|
|
46699
|
-
duration: roundTo3(newDuration),
|
|
46700
|
-
keyframes
|
|
46701
|
-
};
|
|
46702
|
-
await callbacks.commitMutation(
|
|
46703
|
-
selection,
|
|
46704
|
-
existingPosAnim ? { type: "replace-with-keyframes", animationId: existingPosAnim.id, ...baseKf } : { type: "add-with-keyframes", ...baseKf },
|
|
46705
|
-
{
|
|
46706
|
-
label: "Move layer (from extended)",
|
|
46707
|
-
keyframeAction: "add",
|
|
46708
|
-
softReload: true,
|
|
46709
|
-
beforeReload: restoreOffset
|
|
46710
|
-
}
|
|
46711
|
-
);
|
|
46712
|
-
} else {
|
|
46713
|
-
const coalesceKey = `gsap:convert-drag:${anim.id}`;
|
|
46714
|
-
await callbacks.commitMutation(
|
|
46715
|
-
selection,
|
|
46716
|
-
{
|
|
46717
|
-
type: "convert-to-keyframes",
|
|
46718
|
-
animationId: anim.id
|
|
46719
|
-
},
|
|
46720
|
-
{ label: "Convert from() for drag", skipReload: true, coalesceKey }
|
|
46721
|
-
);
|
|
46722
|
-
const { activeKeyframePct, setActiveKeyframePct } = usePlayerStore.getState();
|
|
46723
|
-
const pct = activeKeyframePct ?? computeCurrentPercentage(selection, anim);
|
|
46724
|
-
if (activeKeyframePct != null) setActiveKeyframePct(null);
|
|
46725
|
-
await callbacks.commitMutation(
|
|
46726
|
-
selection,
|
|
46727
|
-
{
|
|
46728
|
-
type: "add-keyframe",
|
|
46729
|
-
animationId: anim.id,
|
|
46730
|
-
percentage: pct,
|
|
46731
|
-
properties: dragProps,
|
|
46732
|
-
...backfillDefaults ? { backfillDefaults } : {}
|
|
46733
|
-
},
|
|
46734
|
-
{
|
|
46735
|
-
label: `Move layer (keyframe ${pct}%)`,
|
|
46736
|
-
softReload: true,
|
|
46737
|
-
beforeReload: restoreOffset,
|
|
46738
|
-
coalesceKey
|
|
46739
|
-
}
|
|
46740
|
-
);
|
|
46741
|
-
}
|
|
46742
|
-
} else {
|
|
46743
|
-
await commitFlatViaKeyframes(
|
|
46744
|
-
selection,
|
|
46745
|
-
anim,
|
|
46746
|
-
{ x: newX, y: newY },
|
|
46747
|
-
callbacks,
|
|
46748
|
-
restoreOffset,
|
|
46749
|
-
iframe,
|
|
46750
|
-
selector,
|
|
46751
|
-
backfillDefaults
|
|
46752
|
-
);
|
|
46753
|
-
}
|
|
46754
|
-
}
|
|
46755
|
-
|
|
46756
|
-
// src/hooks/gsapTweenReach.ts
|
|
46757
|
-
function tweenReach(animation, el) {
|
|
46758
|
-
const doc = el?.ownerDocument;
|
|
46759
|
-
if (!el?.isConnected || !doc || animation.hasUnresolvedSelector) return "unknown";
|
|
46760
|
-
let hits;
|
|
46761
|
-
try {
|
|
46762
|
-
hits = [...doc.querySelectorAll(animation.targetSelector)];
|
|
46763
|
-
} catch {
|
|
46764
|
-
return "unknown";
|
|
46755
|
+
return { status: "persisted" };
|
|
46765
46756
|
}
|
|
46766
|
-
|
|
46767
|
-
|
|
46768
|
-
}
|
|
46769
|
-
|
|
46770
|
-
return animations.filter((a) => {
|
|
46771
|
-
const reach = tweenReach(a, selection.element);
|
|
46772
|
-
return reach === "own" || reach === "unknown";
|
|
46757
|
+
return commitValueAtPlayhead(selection, anim, { x: newX, y: newY }, iframe, callbacks, {
|
|
46758
|
+
label: "Move layer",
|
|
46759
|
+
backfill: { x: baseGsapX, y: baseGsapY },
|
|
46760
|
+
beforeReload: restoreOffset
|
|
46773
46761
|
});
|
|
46774
46762
|
}
|
|
46775
46763
|
|
|
@@ -46795,40 +46783,59 @@ function animHasPosition(anim) {
|
|
|
46795
46783
|
function findGsapPositionAnimation(animations, selector) {
|
|
46796
46784
|
if (animations.length === 0) return null;
|
|
46797
46785
|
const currentTime = usePlayerStore.getState().currentTime;
|
|
46798
|
-
const scored = animations.filter(
|
|
46786
|
+
const scored = animations.filter(animHasPosition).map((a) => {
|
|
46799
46787
|
let score = 0;
|
|
46800
|
-
if (animHasPosition(a)) score += 10;
|
|
46801
46788
|
if (a.keyframes) score += 5;
|
|
46802
46789
|
if (selector && a.targetSelector === selector) score += 8;
|
|
46803
46790
|
else if (a.targetSelector.includes(",")) score -= 5;
|
|
46804
|
-
|
|
46805
|
-
const dur = a.duration ?? 0;
|
|
46806
|
-
if (currentTime >= pos - 0.05 && currentTime <= pos + dur + 0.05) score += 50;
|
|
46807
|
-
else
|
|
46808
|
-
score -= Math.round(
|
|
46809
|
-
Math.min(Math.abs(currentTime - pos), Math.abs(currentTime - pos - dur)) * 5
|
|
46810
|
-
);
|
|
46811
|
-
return { anim: a, score };
|
|
46791
|
+
return { anim: a, score: score + playheadProximity(a, currentTime) };
|
|
46812
46792
|
});
|
|
46813
46793
|
scored.sort((a, b) => b.score - a.score);
|
|
46814
|
-
return scored[0]?.anim ??
|
|
46794
|
+
return scored[0]?.anim ?? null;
|
|
46795
|
+
}
|
|
46796
|
+
function playheadProximity(a, currentTime) {
|
|
46797
|
+
const pos = a.resolvedStart ?? (typeof a.position === "number" ? a.position : 0);
|
|
46798
|
+
const dur = a.duration ?? 0;
|
|
46799
|
+
if (currentTime >= pos - 0.05 && currentTime <= pos + dur + 0.05) return 50;
|
|
46800
|
+
return -Math.round(Math.min(Math.abs(currentTime - pos), Math.abs(currentTime - pos - dur)) * 5);
|
|
46801
|
+
}
|
|
46802
|
+
function statesValueAt(anim, time) {
|
|
46803
|
+
const duration = resolveTweenDuration(anim);
|
|
46804
|
+
if (!(duration > 0)) return false;
|
|
46805
|
+
const pct = (time - (resolveTweenStart(anim) ?? 0)) / duration * 100;
|
|
46806
|
+
const near3 = (keyframe) => Math.abs(keyframe - pct) <= KEYFRAME_PCT_MATCH;
|
|
46807
|
+
if (anim.keyframes) return anim.keyframes.keyframes.some((kf) => near3(kf.percentage));
|
|
46808
|
+
if (anim.method === "from") return near3(0);
|
|
46809
|
+
return near3(100) || anim.method === "fromTo" && near3(0);
|
|
46815
46810
|
}
|
|
46816
46811
|
function pickClosestToPlayhead(anims) {
|
|
46817
46812
|
if (anims.length <= 1) return anims[0] ?? null;
|
|
46818
46813
|
const ct = usePlayerStore.getState().currentTime;
|
|
46819
46814
|
return anims.reduce((best, a) => {
|
|
46820
|
-
const
|
|
46821
|
-
const
|
|
46822
|
-
const dist = ct >= s && ct <= e ? 0 : Math.min(Math.abs(ct - s), Math.abs(ct - e));
|
|
46823
|
-
const bestS = resolveTweenStart(best) ?? 0;
|
|
46824
|
-
const bestE = bestS + resolveTweenDuration(best);
|
|
46825
|
-
const bestDist = ct >= bestS && ct <= bestE ? 0 : Math.min(Math.abs(ct - bestS), Math.abs(ct - bestE));
|
|
46815
|
+
const dist = playheadDistance(a, ct);
|
|
46816
|
+
const bestDist = playheadDistance(best, ct);
|
|
46826
46817
|
if (dist < bestDist) return a;
|
|
46827
|
-
|
|
46828
|
-
|
|
46818
|
+
const stated = dist === 0 && bestDist === 0 ? statedValueWinner(a, best, ct) : null;
|
|
46819
|
+
if (stated) return stated;
|
|
46820
|
+
if (dist === bestDist && keyframeCount(a) > keyframeCount(best)) return a;
|
|
46829
46821
|
return best;
|
|
46830
46822
|
});
|
|
46831
46823
|
}
|
|
46824
|
+
function playheadDistance(anim, time) {
|
|
46825
|
+
const s = resolveTweenStart(anim) ?? 0;
|
|
46826
|
+
const e = s + resolveTweenDuration(anim);
|
|
46827
|
+
return time >= s && time <= e ? 0 : Math.min(Math.abs(time - s), Math.abs(time - e));
|
|
46828
|
+
}
|
|
46829
|
+
function statedValueWinner(a, b, time) {
|
|
46830
|
+
const [mine, theirs] = [statesValueAt(a, time), statesValueAt(b, time)];
|
|
46831
|
+
if (mine !== theirs) return mine ? a : b;
|
|
46832
|
+
const [s, bS] = [resolveTweenStart(a) ?? 0, resolveTweenStart(b) ?? 0];
|
|
46833
|
+
if (mine && s !== bS) return s > bS ? a : b;
|
|
46834
|
+
return null;
|
|
46835
|
+
}
|
|
46836
|
+
function keyframeCount(anim) {
|
|
46837
|
+
return anim.keyframes?.keyframes.length ?? 0;
|
|
46838
|
+
}
|
|
46832
46839
|
|
|
46833
46840
|
// src/hooks/gsapElementMatch.ts
|
|
46834
46841
|
function getAnimationsForElement(animations, target, element) {
|
|
@@ -46858,40 +46865,19 @@ function getAnimationsForElement(animations, target, element) {
|
|
|
46858
46865
|
// src/hooks/gsapRuntimeBridge.ts
|
|
46859
46866
|
var POSITION_CHANNEL_SET = new Set(POSITION_CHANNELS);
|
|
46860
46867
|
var ROTATION_CHANNEL_SET = new Set(ROTATION_CHANNELS);
|
|
46861
|
-
async function resolveGroupTween(group, animations,
|
|
46862
|
-
const
|
|
46863
|
-
|
|
46864
|
-
|
|
46865
|
-
|
|
46866
|
-
|
|
46867
|
-
|
|
46868
|
-
|
|
46869
|
-
|
|
46870
|
-
|
|
46871
|
-
|
|
46872
|
-
|
|
46873
|
-
|
|
46874
|
-
const freshGroupAnim = fresh.find((a) => a.propertyGroup === group);
|
|
46875
|
-
if (freshGroupAnim) return { anim: freshGroupAnim, animations: fresh };
|
|
46876
|
-
}
|
|
46877
|
-
}
|
|
46878
|
-
if (!legacyMixed && fetchFallbackAnimations) {
|
|
46879
|
-
const fresh = await fetchFallbackAnimations();
|
|
46880
|
-
const freshGroupAnim = fresh.find((a) => a.propertyGroup === group);
|
|
46881
|
-
if (freshGroupAnim) return { anim: freshGroupAnim, animations: fresh };
|
|
46882
|
-
const freshLegacy = fresh.find((a) => !a.propertyGroup);
|
|
46883
|
-
if (freshLegacy) {
|
|
46884
|
-
await commitMutation(
|
|
46885
|
-
selection,
|
|
46886
|
-
{ type: "split-into-property-groups", animationId: freshLegacy.id },
|
|
46887
|
-
{ label: "Split mixed tween into property groups", skipReload: true }
|
|
46888
|
-
);
|
|
46889
|
-
const reFetched = await fetchFallbackAnimations();
|
|
46890
|
-
const reFetchedGroup = reFetched.find((a) => a.propertyGroup === group);
|
|
46891
|
-
if (reFetchedGroup) return { anim: reFetchedGroup, animations: reFetched };
|
|
46892
|
-
}
|
|
46893
|
-
}
|
|
46894
|
-
return null;
|
|
46868
|
+
async function resolveGroupTween(group, animations, _selection, _commitMutation, fetchFallbackAnimations) {
|
|
46869
|
+
const inGroup = (list) => {
|
|
46870
|
+
const tagged = list.filter((a) => a.propertyGroup === group);
|
|
46871
|
+
const props = new Set(PROPERTY_GROUPS[group]);
|
|
46872
|
+
const mixed = list.filter((a) => !a.propertyGroup && animationWritesAnyProperty(a, props));
|
|
46873
|
+
return pickClosestToPlayhead(tagged.length > 0 ? tagged : mixed);
|
|
46874
|
+
};
|
|
46875
|
+
const anim = inGroup(animations);
|
|
46876
|
+
if (anim) return { anim, animations };
|
|
46877
|
+
if (!fetchFallbackAnimations) return null;
|
|
46878
|
+
const fresh = await fetchFallbackAnimations();
|
|
46879
|
+
const freshAnim = inGroup(fresh);
|
|
46880
|
+
return freshAnim ? { anim: freshAnim, animations: fresh } : null;
|
|
46895
46881
|
}
|
|
46896
46882
|
var writesPosition = (a) => animationWritesAnyProperty(a, POSITION_CHANNEL_SET);
|
|
46897
46883
|
function positionedOnlyBySharedTween(selection, animations) {
|
|
@@ -46939,6 +46925,36 @@ function oneUndoStep(commit) {
|
|
|
46939
46925
|
const coalesceKey = `gsap:drag:${++dragGestureCounter}`;
|
|
46940
46926
|
return (selection, mutation, options) => commit(selection, mutation, { ...options, coalesceKey, coalesceMs: Number.POSITIVE_INFINITY });
|
|
46941
46927
|
}
|
|
46928
|
+
var isPositionWriteOf = (selector) => (a) => a.targetSelector === selector && isXYPositionWrite(a);
|
|
46929
|
+
function positionWriteKeeper(animations, selector) {
|
|
46930
|
+
const dupes = animations.filter(isPositionWriteOf(selector));
|
|
46931
|
+
if (dupes.length < 2 || dupes.filter((a) => !isInstantHold(a)).length > 1) return null;
|
|
46932
|
+
return dupes.find((a) => a.keyframes) ?? dupes.find((a) => (a.duration ?? 0) > 0) ?? dupes[0];
|
|
46933
|
+
}
|
|
46934
|
+
function dragRoute(posAnim, iframe, selector, altKey) {
|
|
46935
|
+
const hasNonHold = hasNonHoldTweenForElement(iframe, selector, void 0, POSITION_CHANNELS);
|
|
46936
|
+
const hasKeyframedPosTween = !!posAnim?.keyframes && resolveTweenDuration(posAnim) > 0;
|
|
46937
|
+
if (!hasNonHold && !hasKeyframedPosTween) return "static";
|
|
46938
|
+
return altKey || !usePlayerStore.getState().autoKeyframeEnabled ? "whole-path" : "at-playhead";
|
|
46939
|
+
}
|
|
46940
|
+
async function planDrag(selection, offset, allAnimations, iframe, options) {
|
|
46941
|
+
const selector = selectorFromSelection(selection);
|
|
46942
|
+
if (!selector) return { status: "blocked", reason: "no-selector" };
|
|
46943
|
+
const own = tweensForThisElement(selection, allAnimations);
|
|
46944
|
+
const keeper = positionWriteKeeper(own, selector);
|
|
46945
|
+
const animations = keeper ? own.filter((a) => a === keeper || !isPositionWriteOf(selector)(a)) : own;
|
|
46946
|
+
const resolved2 = await resolveGroupTween("position", animations, selection, async () => {
|
|
46947
|
+
});
|
|
46948
|
+
const posAnim = resolved2?.anim ?? findGsapPositionAnimation(animations, selector);
|
|
46949
|
+
const route = dragRoute(posAnim, iframe, selector, options.altKey);
|
|
46950
|
+
if (route === "static") return { status: "persisted" };
|
|
46951
|
+
if (!posAnim) {
|
|
46952
|
+
return { status: "blocked", reason: "source-uneditable", detail: "no-position-tween" };
|
|
46953
|
+
}
|
|
46954
|
+
if (route === "whole-path") return { status: "persisted" };
|
|
46955
|
+
const gsapPos = readGsapPositionFromIframe(iframe, selector) ?? { x: 0, y: 0 };
|
|
46956
|
+
return gsapPositionFromDragOutcome(selection, posAnim, offset, gsapPos, iframe);
|
|
46957
|
+
}
|
|
46942
46958
|
async function tryGsapDragIntercept(selection, offset, allAnimations, iframe, gestureCommit, fetchAllAnimations, options) {
|
|
46943
46959
|
if (!options?.preflightPassed) {
|
|
46944
46960
|
const preflight = await preflightGsapDragIntercept(
|
|
@@ -46948,7 +46964,10 @@ async function tryGsapDragIntercept(selection, offset, allAnimations, iframe, ge
|
|
|
46948
46964
|
fetchAllAnimations,
|
|
46949
46965
|
options?.group
|
|
46950
46966
|
);
|
|
46951
|
-
if (preflight.status !== "persisted"
|
|
46967
|
+
if (preflight.status !== "persisted") return preflight;
|
|
46968
|
+
if (options?.preflightOnly) {
|
|
46969
|
+
return options.group ? planDrag(selection, offset, allAnimations, iframe, options) : preflight;
|
|
46970
|
+
}
|
|
46952
46971
|
}
|
|
46953
46972
|
const animations = tweensForThisElement(selection, allAnimations);
|
|
46954
46973
|
const fetchFallbackAnimations = fetchAllAnimations && (async () => tweensForThisElement(selection, await fetchAllAnimations()));
|
|
@@ -46956,12 +46975,10 @@ async function tryGsapDragIntercept(selection, offset, allAnimations, iframe, ge
|
|
|
46956
46975
|
if (!selector) return { status: "blocked", reason: "no-selector" };
|
|
46957
46976
|
const commitMutation = oneUndoStep(gestureCommit);
|
|
46958
46977
|
let workingAnimations = animations;
|
|
46959
|
-
|
|
46960
|
-
if (animations.filter(isPosWrite).length > 1 && fetchFallbackAnimations) {
|
|
46978
|
+
if (animations.filter(isPositionWriteOf(selector)).length > 1 && fetchFallbackAnimations) {
|
|
46961
46979
|
const fresh = await fetchFallbackAnimations();
|
|
46962
|
-
const
|
|
46963
|
-
if (
|
|
46964
|
-
const keeper = dupes.find((a) => a.keyframes) ?? dupes.find((a) => (a.duration ?? 0) > 0) ?? dupes[0];
|
|
46980
|
+
const keeper = positionWriteKeeper(fresh, selector);
|
|
46981
|
+
if (keeper) {
|
|
46965
46982
|
await commitMutation(
|
|
46966
46983
|
selection,
|
|
46967
46984
|
{
|
|
@@ -46994,9 +47011,8 @@ async function tryGsapDragIntercept(selection, offset, allAnimations, iframe, ge
|
|
|
46994
47011
|
}
|
|
46995
47012
|
}
|
|
46996
47013
|
const gsapPos = readGsapPositionFromIframe(iframe, selector) ?? { x: 0, y: 0 };
|
|
46997
|
-
const
|
|
46998
|
-
|
|
46999
|
-
if (!hasNonHold && !hasKeyframedPosTween) {
|
|
47014
|
+
const route = dragRoute(posAnim, iframe, selector, options?.altKey);
|
|
47015
|
+
if (route === "static") {
|
|
47000
47016
|
const existingSet = posAnim && isInstantHold(posAnim) && posAnim.targetSelector === selector ? posAnim : findExistingPositionWrite(resolvedAnimations, selector, selection.element);
|
|
47001
47017
|
await commitStaticGsapPosition(selection, offset, gsapPos, selector, existingSet, {
|
|
47002
47018
|
commitMutation,
|
|
@@ -47009,19 +47025,20 @@ async function tryGsapDragIntercept(selection, offset, allAnimations, iframe, ge
|
|
|
47009
47025
|
}
|
|
47010
47026
|
if (fetchFallbackAnimations) {
|
|
47011
47027
|
const fresh = await fetchFallbackAnimations();
|
|
47012
|
-
const freshMatch = fresh.find(
|
|
47013
|
-
|
|
47028
|
+
const freshMatch = fresh.find((a) => a.id === posAnim.id) ?? pickClosestToPlayhead(
|
|
47029
|
+
fresh.filter(
|
|
47030
|
+
(a) => a.targetSelector === posAnim.targetSelector && a.propertyGroup === posAnim.propertyGroup && isXYPositionWrite(a) === isXYPositionWrite(posAnim)
|
|
47031
|
+
)
|
|
47014
47032
|
);
|
|
47015
47033
|
if (freshMatch && freshMatch.id !== posAnim.id) {
|
|
47016
47034
|
posAnim = freshMatch;
|
|
47017
47035
|
}
|
|
47018
47036
|
}
|
|
47019
47037
|
const cbs = { commitMutation, fetchAnimations: fetchFallbackAnimations };
|
|
47020
|
-
|
|
47021
|
-
if (options?.altKey || !autoKeyframeEnabled) {
|
|
47038
|
+
if (route === "whole-path") {
|
|
47022
47039
|
await commitWholePathOffset(selection, posAnim, offset, gsapPos, iframe, selector, cbs);
|
|
47023
47040
|
} else {
|
|
47024
|
-
|
|
47041
|
+
return commitGsapPositionFromDrag(selection, posAnim, offset, gsapPos, iframe, cbs);
|
|
47025
47042
|
}
|
|
47026
47043
|
return { status: "persisted" };
|
|
47027
47044
|
}
|
|
@@ -47065,7 +47082,9 @@ async function tryGsapRotationIntercept(selection, angle, animations, iframe, co
|
|
|
47065
47082
|
const resolvedAnimations = resolved2?.animations ?? workingAnimations;
|
|
47066
47083
|
let anim = resolved2?.anim && animationWritesAnyProperty(resolved2.anim, ROTATION_CHANNEL_SET) ? resolved2.anim : null;
|
|
47067
47084
|
if (!anim) {
|
|
47068
|
-
anim =
|
|
47085
|
+
anim = pickClosestToPlayhead(
|
|
47086
|
+
workingAnimations.filter((a) => animationWritesAnyProperty(a, ROTATION_CHANNEL_SET))
|
|
47087
|
+
);
|
|
47069
47088
|
}
|
|
47070
47089
|
const newRotation = roundTo3(angle);
|
|
47071
47090
|
if (!anim || isInstantHold(anim)) {
|
|
@@ -47089,35 +47108,14 @@ async function tryGsapRotationIntercept(selection, angle, animations, iframe, co
|
|
|
47089
47108
|
);
|
|
47090
47109
|
return { status: "persisted" };
|
|
47091
47110
|
}
|
|
47092
|
-
|
|
47093
|
-
const newId = await materializeIfDynamic(anim, iframe, commitMutation, selection);
|
|
47094
|
-
if (newId) anim = { ...anim, id: newId };
|
|
47095
|
-
} else if (!anim.keyframes) {
|
|
47096
|
-
const resolvedFromValues = selector ? readAllAnimatedProperties(iframe, selector, anim, "rotation") : void 0;
|
|
47097
|
-
await commitMutation(
|
|
47098
|
-
selection,
|
|
47099
|
-
{ type: "convert-to-keyframes", animationId: anim.id, resolvedFromValues },
|
|
47100
|
-
{ label: "Convert to keyframes for rotation", skipReload: true }
|
|
47101
|
-
);
|
|
47102
|
-
}
|
|
47103
|
-
const runtimeProps = readAllAnimatedProperties(iframe, selector, anim, "rotation");
|
|
47104
|
-
const backfillDefaults = { ...runtimeProps };
|
|
47105
|
-
if (!("rotation" in runtimeProps)) {
|
|
47106
|
-
backfillDefaults.rotation = readGsapProperty(iframe, selector, "rotation") ?? 0;
|
|
47107
|
-
}
|
|
47108
|
-
const properties = { ...runtimeProps, rotation: newRotation };
|
|
47109
|
-
await commitMutation(
|
|
47111
|
+
return commitValueAtPlayhead(
|
|
47110
47112
|
selection,
|
|
47111
|
-
|
|
47112
|
-
|
|
47113
|
-
|
|
47114
|
-
|
|
47115
|
-
|
|
47116
|
-
backfillDefaults
|
|
47117
|
-
},
|
|
47118
|
-
{ label: `Rotate (keyframe ${pct}%)`, softReload: true }
|
|
47113
|
+
anim,
|
|
47114
|
+
{ rotation: newRotation },
|
|
47115
|
+
iframe,
|
|
47116
|
+
{ commitMutation, fetchAnimations: fetchFallbackAnimations },
|
|
47117
|
+
{ label: "Rotate", backfill: { rotation: newRotation }, holdFromStart: true }
|
|
47119
47118
|
);
|
|
47120
|
-
return { status: "persisted" };
|
|
47121
47119
|
}
|
|
47122
47120
|
|
|
47123
47121
|
// src/hooks/keyframeCacheAstLoad.ts
|
|
@@ -47141,7 +47139,7 @@ function fetchParsedAnimations(projectId, sourceFile, options = {}) {
|
|
|
47141
47139
|
async function requestParsedAnimations(projectId, sourceFile) {
|
|
47142
47140
|
if (!isPreviewBooted(projectId) && !await whenPreviewBooted(projectId)) return null;
|
|
47143
47141
|
try {
|
|
47144
|
-
const res = await
|
|
47142
|
+
const res = await studioApiFetch(
|
|
47145
47143
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-animations/${encodeURIComponent(sourceFile)}`,
|
|
47146
47144
|
// Always revalidate; an unchanged file answers 304. No per-call timestamp
|
|
47147
47145
|
// (a deterministic-render no-no).
|
|
@@ -47433,8 +47431,8 @@ var MIN_TWEEN_DURATION = 0.01;
|
|
|
47433
47431
|
var round33 = (n) => Math.round(n * 1e3) / 1e3;
|
|
47434
47432
|
var clamp5 = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
|
|
47435
47433
|
function resolveFlatTweenBoundaryRetime(opts) {
|
|
47436
|
-
const { keyframeCount, draggedTweenPct, tweenStart, tweenEnd, dropAbsTime } = opts;
|
|
47437
|
-
if (
|
|
47434
|
+
const { keyframeCount: keyframeCount2, draggedTweenPct, tweenStart, tweenEnd, dropAbsTime } = opts;
|
|
47435
|
+
if (keyframeCount2 > 0) return null;
|
|
47438
47436
|
if (draggedTweenPct !== 0 && draggedTweenPct !== 100) return { kind: "noop" };
|
|
47439
47437
|
const draggedTime = draggedTweenPct === 0 ? tweenStart : tweenEnd;
|
|
47440
47438
|
if (Math.abs(dropAbsTime - draggedTime) <= EPSILON_TIME) return { kind: "noop" };
|
|
@@ -49368,7 +49366,7 @@ function StudioAgentTools() {
|
|
|
49368
49366
|
// fetches the URL itself.
|
|
49369
49367
|
probeFrame: async (url) => {
|
|
49370
49368
|
try {
|
|
49371
|
-
const response = await
|
|
49369
|
+
const response = await studioApiFetch(url, { method: "HEAD" });
|
|
49372
49370
|
return { ok: response.ok, status: response.status };
|
|
49373
49371
|
} catch {
|
|
49374
49372
|
return { ok: false, status: 0 };
|
|
@@ -50779,6 +50777,23 @@ function useRenderClipContent({
|
|
|
50779
50777
|
);
|
|
50780
50778
|
}
|
|
50781
50779
|
|
|
50780
|
+
// src/hooks/useThumbnailStill.ts
|
|
50781
|
+
import { useMemo as useMemo49 } from "react";
|
|
50782
|
+
var LOADING = Object.freeze({ status: "loading" });
|
|
50783
|
+
var FAILED = Object.freeze({ status: "failed" });
|
|
50784
|
+
function useThumbnailStill(url, projectId, { sessionEpoch = 0, priority = "visible" } = {}) {
|
|
50785
|
+
const request = useMemo49(
|
|
50786
|
+
() => url ? compositionThumbnailRequest(url, projectId, { sessionEpoch, priority }) : null,
|
|
50787
|
+
[priority, projectId, sessionEpoch, url]
|
|
50788
|
+
);
|
|
50789
|
+
const snapshot3 = useThumbnailLease(request);
|
|
50790
|
+
const ready = snapshot3.status === "ready" && snapshot3.value.kind === "image" ? snapshot3.value.url : null;
|
|
50791
|
+
return useMemo49(
|
|
50792
|
+
() => ready ? { status: "ready", url: ready } : snapshot3.status === "error" ? FAILED : LOADING,
|
|
50793
|
+
[ready, snapshot3.status]
|
|
50794
|
+
);
|
|
50795
|
+
}
|
|
50796
|
+
|
|
50782
50797
|
// src/components/editor/SourceEditor.tsx
|
|
50783
50798
|
import { useRef as useRef82, useCallback as useCallback76, useEffect as useEffect68, memo as memo35 } from "react";
|
|
50784
50799
|
import {
|
|
@@ -51002,13 +51017,13 @@ var SourceEditor = memo35(function SourceEditor2({
|
|
|
51002
51017
|
});
|
|
51003
51018
|
|
|
51004
51019
|
// src/components/editor/PropertyPanel.tsx
|
|
51005
|
-
import { memo as memo39, useMemo as
|
|
51020
|
+
import { memo as memo39, useMemo as useMemo65, useRef as useRef113, useState as useState101 } from "react";
|
|
51006
51021
|
|
|
51007
51022
|
// src/components/editor/InspectorHeaderActions.tsx
|
|
51008
51023
|
import { Eye as Eye3, EyeSlash as EyeSlash2 } from "@phosphor-icons/react";
|
|
51009
51024
|
|
|
51010
51025
|
// src/contexts/DesignPanelInputContext.tsx
|
|
51011
|
-
import { useCallback as useCallback77, useContext as useContext12, useMemo as
|
|
51026
|
+
import { useCallback as useCallback77, useContext as useContext12, useMemo as useMemo50 } from "react";
|
|
51012
51027
|
|
|
51013
51028
|
// src/utils/designInputTracking.ts
|
|
51014
51029
|
var COALESCE_WINDOW_MS = 600;
|
|
@@ -51051,7 +51066,7 @@ function DesignPanelInputProvider({
|
|
|
51051
51066
|
children
|
|
51052
51067
|
}) {
|
|
51053
51068
|
const parent = useContext12(DesignPanelInputContext);
|
|
51054
|
-
const value =
|
|
51069
|
+
const value = useMemo50(
|
|
51055
51070
|
() => ({ ui: ui ?? parent.ui, section: section ?? parent.section }),
|
|
51056
51071
|
[ui, section, parent.ui, parent.section]
|
|
51057
51072
|
);
|
|
@@ -51587,7 +51602,7 @@ function readGsapRuntimeValuesForPanel(gsapAnimId, gsapAnimations, element, prev
|
|
|
51587
51602
|
const el = findPreviewNode(iframe.contentDocument, element);
|
|
51588
51603
|
if (!el) return null;
|
|
51589
51604
|
const propKeys = collectPanelPropKeys(gsapAnimations);
|
|
51590
|
-
const readsTransform =
|
|
51605
|
+
const readsTransform = !editsPlainCss(el, "move");
|
|
51591
51606
|
const result = {};
|
|
51592
51607
|
for (const prop of propKeys) {
|
|
51593
51608
|
if (!readsTransform && GSAP_TRANSFORM_KEYS.has(prop)) continue;
|
|
@@ -52267,7 +52282,7 @@ function createTransformCommitHandlers({
|
|
|
52267
52282
|
"This element's position can't be edited here yet \u2014 it is driven by its animation"
|
|
52268
52283
|
))
|
|
52269
52284
|
return;
|
|
52270
|
-
const plainTranslate =
|
|
52285
|
+
const plainTranslate = editsPlainCss(element.element, "move");
|
|
52271
52286
|
const current = readMoveOffset(element.element, plainTranslate);
|
|
52272
52287
|
if (!Number.isFinite(current.x) || !Number.isFinite(current.y))
|
|
52273
52288
|
return void showToast?.(UNREADABLE_TRANSLATE);
|
|
@@ -52716,7 +52731,7 @@ function MediaSection({
|
|
|
52716
52731
|
import { isHfColorGradingActive as isHfColorGradingActive2 } from "@hyperframes/core/color-grading";
|
|
52717
52732
|
|
|
52718
52733
|
// src/components/editor/propertyPanelColorGradingControls.tsx
|
|
52719
|
-
import { useMemo as
|
|
52734
|
+
import { useMemo as useMemo51, useRef as useRef87, useState as useState63 } from "react";
|
|
52720
52735
|
import {
|
|
52721
52736
|
HF_COLOR_GRADING_PRESETS,
|
|
52722
52737
|
normalizeHfColorGrading as normalizeHfColorGrading2
|
|
@@ -53160,7 +53175,7 @@ function ColorGradingControls({
|
|
|
53160
53175
|
const [detailSettings, setDetailSettings] = useState63(null);
|
|
53161
53176
|
const [lutImporting, setLutImporting] = useState63(false);
|
|
53162
53177
|
const [lutImportError, setLutImportError] = useState63(null);
|
|
53163
|
-
const lutAssets =
|
|
53178
|
+
const lutAssets = useMemo51(
|
|
53164
53179
|
() => assets.filter((asset) => LUT_EXT.test(asset)).sort((a, b) => a.localeCompare(b)),
|
|
53165
53180
|
[assets]
|
|
53166
53181
|
);
|
|
@@ -53440,7 +53455,7 @@ function ColorGradingControls({
|
|
|
53440
53455
|
}
|
|
53441
53456
|
|
|
53442
53457
|
// src/components/editor/useColorGradingController.ts
|
|
53443
|
-
import { useCallback as useCallback82, useEffect as useEffect75, useMemo as
|
|
53458
|
+
import { useCallback as useCallback82, useEffect as useEffect75, useMemo as useMemo52, useRef as useRef89, useState as useState65 } from "react";
|
|
53444
53459
|
import {
|
|
53445
53460
|
HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR3,
|
|
53446
53461
|
hasHfColorGradingAuthoredValues,
|
|
@@ -53803,7 +53818,7 @@ function useColorGradingController({
|
|
|
53803
53818
|
state: "pending",
|
|
53804
53819
|
message: "Waiting for runtime"
|
|
53805
53820
|
}));
|
|
53806
|
-
const selectedAssetPath =
|
|
53821
|
+
const selectedAssetPath = useMemo52(
|
|
53807
53822
|
() => selectedMediaAssetPath(element, projectId),
|
|
53808
53823
|
[element, projectId]
|
|
53809
53824
|
);
|
|
@@ -53850,7 +53865,7 @@ function useColorGradingController({
|
|
|
53850
53865
|
trackStudioPendingEdit(onSetAttributeLive(COLOR_GRADING_DATA_KEY, value));
|
|
53851
53866
|
};
|
|
53852
53867
|
}, [identityKey]);
|
|
53853
|
-
const target =
|
|
53868
|
+
const target = useMemo52(
|
|
53854
53869
|
() => ({
|
|
53855
53870
|
id: element.id ?? null,
|
|
53856
53871
|
hfId: element.hfId ?? null,
|
|
@@ -53871,7 +53886,7 @@ function useColorGradingController({
|
|
|
53871
53886
|
return;
|
|
53872
53887
|
}
|
|
53873
53888
|
const controller = new AbortController();
|
|
53874
|
-
|
|
53889
|
+
studioApiFetch(cacheKey, { signal: controller.signal }).then(async (response) => {
|
|
53875
53890
|
if (!response.ok) return { ok: false };
|
|
53876
53891
|
const data = await response.json();
|
|
53877
53892
|
return { ok: true, metadata: data?.metadata ?? null };
|
|
@@ -54830,7 +54845,7 @@ function ColorField({
|
|
|
54830
54845
|
}
|
|
54831
54846
|
|
|
54832
54847
|
// src/components/editor/propertyPanelFont.tsx
|
|
54833
|
-
import { useEffect as useEffect77, useMemo as
|
|
54848
|
+
import { useEffect as useEffect77, useMemo as useMemo53, useRef as useRef91, useState as useState67, useSyncExternalStore as useSyncExternalStore8 } from "react";
|
|
54834
54849
|
|
|
54835
54850
|
// src/components/editor/fontAssets.ts
|
|
54836
54851
|
var FONT_EXT_RE = /\.(eot|otf|ttc|ttf|woff2?)$/i;
|
|
@@ -54945,7 +54960,7 @@ var fontLists = {
|
|
|
54945
54960
|
var fontListsRequest = null;
|
|
54946
54961
|
var fontListListeners = /* @__PURE__ */ new Set();
|
|
54947
54962
|
async function fetchFontList(url) {
|
|
54948
|
-
const data = await (await
|
|
54963
|
+
const data = await (await studioApiFetch(url)).json();
|
|
54949
54964
|
if (!Array.isArray(data.fonts)) throw new Error(`${url} returned no font list`);
|
|
54950
54965
|
return data.fonts;
|
|
54951
54966
|
}
|
|
@@ -55076,13 +55091,13 @@ function FontFamilyField({
|
|
|
55076
55091
|
setImportingFonts(false);
|
|
55077
55092
|
}
|
|
55078
55093
|
};
|
|
55079
|
-
const projectFontAssets =
|
|
55094
|
+
const projectFontAssets = useMemo53(
|
|
55080
55095
|
() => uniqueFontOptions(
|
|
55081
55096
|
importedFonts.map((f) => ({ family: f.family, source: "Imported" }))
|
|
55082
55097
|
),
|
|
55083
55098
|
[importedFonts]
|
|
55084
55099
|
);
|
|
55085
|
-
const options =
|
|
55100
|
+
const options = useMemo53(() => {
|
|
55086
55101
|
if (!open) return [];
|
|
55087
55102
|
const documentFonts = collectDocumentFontFamilies();
|
|
55088
55103
|
return sortFontOptions(
|
|
@@ -55098,7 +55113,7 @@ function FontFamilyField({
|
|
|
55098
55113
|
])
|
|
55099
55114
|
);
|
|
55100
55115
|
}, [open, currentFamily, lists, localFonts, projectFontAssets]);
|
|
55101
|
-
const filteredOptions =
|
|
55116
|
+
const filteredOptions = useMemo53(() => {
|
|
55102
55117
|
const matches = options.filter((o) => fontMatchesQuery(o.family, query));
|
|
55103
55118
|
if (query.trim()) return matches.slice(0, 200);
|
|
55104
55119
|
const bySource = /* @__PURE__ */ new Map();
|
|
@@ -55128,7 +55143,7 @@ function FontFamilyField({
|
|
|
55128
55143
|
};
|
|
55129
55144
|
const importSystemFont = async (family) => {
|
|
55130
55145
|
if (!onImportFonts) return null;
|
|
55131
|
-
const response = await
|
|
55146
|
+
const response = await studioApiFetch(`/api/fonts/file?family=${encodeURIComponent(family)}`);
|
|
55132
55147
|
if (!response.ok) return null;
|
|
55133
55148
|
const blob = await response.blob();
|
|
55134
55149
|
const ext2 = response.headers.get("Content-Disposition")?.match(/\.(\w+)"?$/)?.[1] ?? "ttf";
|
|
@@ -55334,7 +55349,7 @@ function FontFamilyField({
|
|
|
55334
55349
|
import { useEffect as useEffect79 } from "react";
|
|
55335
55350
|
|
|
55336
55351
|
// src/components/panels/VariablesBindElement.tsx
|
|
55337
|
-
import { useMemo as
|
|
55352
|
+
import { useMemo as useMemo54, useState as useState69 } from "react";
|
|
55338
55353
|
|
|
55339
55354
|
// src/components/panels/VariablesValueControls.tsx
|
|
55340
55355
|
import { useState as useState68 } from "react";
|
|
@@ -55616,7 +55631,7 @@ function VariablesBindElement({
|
|
|
55616
55631
|
}) {
|
|
55617
55632
|
const [activeKey, setActiveKey] = useState69(null);
|
|
55618
55633
|
const [idDraft, setIdDraft] = useState69("");
|
|
55619
|
-
const actions =
|
|
55634
|
+
const actions = useMemo54(() => buildBindActions(selection, sdkSession), [selection, sdkSession]);
|
|
55620
55635
|
if (actions.length === 0) return null;
|
|
55621
55636
|
const active = actions.find((a) => a.key === activeKey) ?? null;
|
|
55622
55637
|
const trimmedId = sanitizeId(idDraft);
|
|
@@ -55727,7 +55742,7 @@ function uniqueId(base2, existing) {
|
|
|
55727
55742
|
}
|
|
55728
55743
|
|
|
55729
55744
|
// src/contexts/VariablePromoteContext.tsx
|
|
55730
|
-
import { useContext as useContext13, useEffect as useEffect78, useMemo as
|
|
55745
|
+
import { useContext as useContext13, useEffect as useEffect78, useMemo as useMemo55, useState as useState70 } from "react";
|
|
55731
55746
|
import { jsx as jsx129 } from "react/jsx-runtime";
|
|
55732
55747
|
var VariablePromoteContext = createStableContext(
|
|
55733
55748
|
"VariablePromoteContext",
|
|
@@ -55750,15 +55765,15 @@ function VariablePromoteProvider({
|
|
|
55750
55765
|
if (!session) return;
|
|
55751
55766
|
return session.on("change", () => setRevision((r) => r + 1));
|
|
55752
55767
|
}, [session]);
|
|
55753
|
-
const actions =
|
|
55768
|
+
const actions = useMemo55(() => {
|
|
55754
55769
|
if (!session || !selection) return [];
|
|
55755
55770
|
return buildBindActions(selection, session);
|
|
55756
55771
|
}, [session, selection, revision]);
|
|
55757
|
-
const declarations =
|
|
55772
|
+
const declarations = useMemo55(() => {
|
|
55758
55773
|
if (!session) return [];
|
|
55759
55774
|
return session.getVariableDeclarations();
|
|
55760
55775
|
}, [session, revision]);
|
|
55761
|
-
const value =
|
|
55776
|
+
const value = useMemo55(
|
|
55762
55777
|
() => ({ session, selection, actions, declarations, persist: persist2, onPersistError }),
|
|
55763
55778
|
[session, selection, actions, declarations, persist2, onPersistError]
|
|
55764
55779
|
);
|
|
@@ -55767,7 +55782,7 @@ function VariablePromoteProvider({
|
|
|
55767
55782
|
function useVariablePromoteChannel(channel) {
|
|
55768
55783
|
const ctx3 = useContext13(VariablePromoteContext);
|
|
55769
55784
|
const key2 = channel.kind === "style" ? `style:${channel.prop}` : channel.kind;
|
|
55770
|
-
return
|
|
55785
|
+
return useMemo55(() => {
|
|
55771
55786
|
if (!ctx3 || !ctx3.session || !ctx3.selection?.hfId) return null;
|
|
55772
55787
|
const { session, selection, actions, declarations, persist: persist2, onPersistError } = ctx3;
|
|
55773
55788
|
const hfId = selection.hfId;
|
|
@@ -56191,7 +56206,7 @@ function insertGradientStop(model, position) {
|
|
|
56191
56206
|
}
|
|
56192
56207
|
|
|
56193
56208
|
// src/components/editor/propertyPanelFill.tsx
|
|
56194
|
-
import { useMemo as
|
|
56209
|
+
import { useMemo as useMemo56, useRef as useRef92, useState as useState71 } from "react";
|
|
56195
56210
|
|
|
56196
56211
|
// src/components/icons/ReverseGradientIcon.tsx
|
|
56197
56212
|
import { jsx as jsx131, jsxs as jsxs97 } from "react/jsx-runtime";
|
|
@@ -56266,8 +56281,8 @@ function ImageFillField({
|
|
|
56266
56281
|
const fileInputRef = useRef92(null);
|
|
56267
56282
|
const [uploading, setUploading] = useState71(false);
|
|
56268
56283
|
const [uploadError, setUploadError] = useState71(null);
|
|
56269
|
-
const imageAssets =
|
|
56270
|
-
const selectedAsset =
|
|
56284
|
+
const imageAssets = useMemo56(() => assets.filter((a) => IMAGE_EXT.test(a)), [assets]);
|
|
56285
|
+
const selectedAsset = useMemo56(
|
|
56271
56286
|
() => resolveSelectedAsset(value, sourceFile, imageAssets),
|
|
56272
56287
|
[imageAssets, sourceFile, value]
|
|
56273
56288
|
);
|
|
@@ -57734,7 +57749,7 @@ import { useCallback as useCallback87, useState as useState76 } from "react";
|
|
|
57734
57749
|
import { useShallow } from "zustand/react/shallow";
|
|
57735
57750
|
|
|
57736
57751
|
// src/components/editor/AnimationCard.tsx
|
|
57737
|
-
import { memo as memo37, useCallback as useCallback86, useEffect as useEffect82, useMemo as
|
|
57752
|
+
import { memo as memo37, useCallback as useCallback86, useEffect as useEffect82, useMemo as useMemo57, useRef as useRef95, useState as useState75 } from "react";
|
|
57738
57753
|
import { SUPPORTED_EASES, SUPPORTED_PROPS } from "@hyperframes/core/gsap-constants";
|
|
57739
57754
|
|
|
57740
57755
|
// src/components/editor/gsapAnimationHelpers.ts
|
|
@@ -58275,21 +58290,21 @@ var AnimationCard = memo37(function AnimationCard2({
|
|
|
58275
58290
|
segment?.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
58276
58291
|
pendingAutoScrollRef.current = false;
|
|
58277
58292
|
}, [expandedKfPct]);
|
|
58278
|
-
const usedProps =
|
|
58293
|
+
const usedProps = useMemo57(
|
|
58279
58294
|
() => new Set(Object.keys(animation.properties)),
|
|
58280
58295
|
[animation.properties]
|
|
58281
58296
|
);
|
|
58282
|
-
const availableProps =
|
|
58297
|
+
const availableProps = useMemo57(
|
|
58283
58298
|
() => SUPPORTED_PROPS.filter(
|
|
58284
58299
|
(p) => !usedProps.has(p) && (animation.method === "set" || !BOOLEAN_PROPS.has(p))
|
|
58285
58300
|
),
|
|
58286
58301
|
[usedProps, animation.method]
|
|
58287
58302
|
);
|
|
58288
|
-
const usedFromProps =
|
|
58303
|
+
const usedFromProps = useMemo57(
|
|
58289
58304
|
() => new Set(Object.keys(animation.fromProperties ?? {})),
|
|
58290
58305
|
[animation.fromProperties]
|
|
58291
58306
|
);
|
|
58292
|
-
const availableFromProps =
|
|
58307
|
+
const availableFromProps = useMemo57(
|
|
58293
58308
|
() => SUPPORTED_PROPS.filter((p) => !usedFromProps.has(p) && !BOOLEAN_PROPS.has(p)),
|
|
58294
58309
|
[usedFromProps]
|
|
58295
58310
|
);
|
|
@@ -58336,7 +58351,7 @@ var AnimationCard = memo37(function AnimationCard2({
|
|
|
58336
58351
|
const easeName = (animation.keyframes ? animation.keyframes.easeEach : void 0) ?? animation.ease ?? "none";
|
|
58337
58352
|
const easeLabel = easeName.startsWith("custom(") ? "Custom curve" : EASE_LABELS[easeName] ?? easeName;
|
|
58338
58353
|
const endTime = typeof animation.position === "number" ? animation.position + (animation.duration ?? 0) : animation.position;
|
|
58339
|
-
const summary =
|
|
58354
|
+
const summary = useMemo57(() => buildTweenSummary(animation), [animation]);
|
|
58340
58355
|
const setKeys = Object.keys(animation.properties);
|
|
58341
58356
|
if (animation.method === "set" && // `every` is vacuously true on an empty bag — require at least one key so a
|
|
58342
58357
|
// property-less set doesn't masquerade as a position row.
|
|
@@ -59484,7 +59499,7 @@ function PropertyPanel3dTransform({
|
|
|
59484
59499
|
}
|
|
59485
59500
|
|
|
59486
59501
|
// src/components/editor/PropertyPanelFlat.tsx
|
|
59487
|
-
import { useEffect as useEffect103, useMemo as
|
|
59502
|
+
import { useEffect as useEffect103, useMemo as useMemo64, useRef as useRef112, useState as useState100 } from "react";
|
|
59488
59503
|
import { useShallow as useShallow2 } from "zustand/react/shallow";
|
|
59489
59504
|
|
|
59490
59505
|
// src/components/editor/audioFxSummary.ts
|
|
@@ -61654,7 +61669,7 @@ function motionSectionLabel(args) {
|
|
|
61654
61669
|
}
|
|
61655
61670
|
|
|
61656
61671
|
// src/components/editor/propertyPanelAudioFxGroup.tsx
|
|
61657
|
-
import { useMemo as
|
|
61672
|
+
import { useMemo as useMemo60, useState as useState88 } from "react";
|
|
61658
61673
|
import {
|
|
61659
61674
|
HF_AUDIO_FX_ATTR as HF_AUDIO_FX_ATTR11,
|
|
61660
61675
|
HF_AUDIO_FX_DATA_KEY as HF_AUDIO_FX_DATA_KEY2,
|
|
@@ -61674,7 +61689,7 @@ import {
|
|
|
61674
61689
|
} from "@hyperframes/core/audio-automation";
|
|
61675
61690
|
|
|
61676
61691
|
// src/components/editor/propertyPanelFxSection.tsx
|
|
61677
|
-
import { useCallback as useCallback90, useEffect as useEffect90, useMemo as
|
|
61692
|
+
import { useCallback as useCallback90, useEffect as useEffect90, useMemo as useMemo59, useRef as useRef100, useState as useState86 } from "react";
|
|
61678
61693
|
|
|
61679
61694
|
// src/components/editor/audioFxRevealTarget.ts
|
|
61680
61695
|
import { parseAutomationTarget as parseAutomationTarget2 } from "@hyperframes/core/audio-automation";
|
|
@@ -62632,7 +62647,7 @@ function FxEqModule({
|
|
|
62632
62647
|
import { getAudioFxPreset as getAudioFxPreset4 } from "@hyperframes/core/audio-fx-presets";
|
|
62633
62648
|
|
|
62634
62649
|
// src/components/editor/propertyPanelFxNodeRow.tsx
|
|
62635
|
-
import { useMemo as
|
|
62650
|
+
import { useMemo as useMemo58, useState as useState85 } from "react";
|
|
62636
62651
|
import {
|
|
62637
62652
|
defaultAudioFxParams as defaultAudioFxParams4,
|
|
62638
62653
|
getAudioFxDef as getAudioFxDef4
|
|
@@ -63069,11 +63084,11 @@ function FxNodeRow({
|
|
|
63069
63084
|
}) {
|
|
63070
63085
|
const trackInput = useTrackDesignInput();
|
|
63071
63086
|
const registryDef = getAudioFxDef4(node.type);
|
|
63072
|
-
const def =
|
|
63087
|
+
const def = useMemo58(() => registryDef ? plainDef(registryDef) : null, [registryDef]);
|
|
63073
63088
|
const primary = registryDef ? primaryParamOf(registryDef) : null;
|
|
63074
63089
|
const profile = getAudioFxProfile2(node.type);
|
|
63075
|
-
const derived =
|
|
63076
|
-
const onlyPrimary =
|
|
63090
|
+
const derived = useMemo58(() => profileParam(node.type), [node.type]);
|
|
63091
|
+
const onlyPrimary = useMemo58(
|
|
63077
63092
|
() => def && primary ? { ...def, params: def.params.filter((p) => p.key === primary) } : def,
|
|
63078
63093
|
[def, primary]
|
|
63079
63094
|
);
|
|
@@ -63745,12 +63760,12 @@ function FxSection({
|
|
|
63745
63760
|
[chain.nodes, mutate, onRemoveNodeAutomation, trackKind]
|
|
63746
63761
|
);
|
|
63747
63762
|
const [carveOpen, setCarveOpen] = useState86(true);
|
|
63748
|
-
const carveNodes =
|
|
63749
|
-
const handBuilt =
|
|
63763
|
+
const carveNodes = useMemo59(() => chain.nodes.filter((n) => n.fromCarve), [chain.nodes]);
|
|
63764
|
+
const handBuilt = useMemo59(
|
|
63750
63765
|
() => chain.nodes.map((node, i) => ({ node, i })).filter(({ node }) => !node.fromCarve && !node.fromEq),
|
|
63751
63766
|
[chain.nodes]
|
|
63752
63767
|
);
|
|
63753
|
-
const runs =
|
|
63768
|
+
const runs = useMemo59(() => {
|
|
63754
63769
|
const out = [];
|
|
63755
63770
|
for (const item of handBuilt) {
|
|
63756
63771
|
const preset = item.node.fromPreset;
|
|
@@ -63761,8 +63776,8 @@ function FxSection({
|
|
|
63761
63776
|
return out;
|
|
63762
63777
|
}, [handBuilt]);
|
|
63763
63778
|
const [collapsedRuns, setCollapsedRuns] = useState86(/* @__PURE__ */ new Set());
|
|
63764
|
-
const eqIds =
|
|
63765
|
-
const positions =
|
|
63779
|
+
const eqIds = useMemo59(() => audioEqIds(chain), [chain]);
|
|
63780
|
+
const positions = useMemo59(() => {
|
|
63766
63781
|
const map = /* @__PURE__ */ new Map();
|
|
63767
63782
|
let at = (showCarve ? 1 : 0) + eqIds.length;
|
|
63768
63783
|
for (const { i } of handBuilt) map.set(i, ++at);
|
|
@@ -64192,7 +64207,7 @@ function useFxLevelling(element, chain, automation, onSetAttributeQuiet, onSetAt
|
|
|
64192
64207
|
if (cached5?.src === src) return cached5;
|
|
64193
64208
|
const Ctor = window.OfflineAudioContext ?? window.webkitOfflineAudioContext;
|
|
64194
64209
|
if (!Ctor) return null;
|
|
64195
|
-
const res = await
|
|
64210
|
+
const res = await studioApiFetch(new URL(src, doc.baseURI).href);
|
|
64196
64211
|
const buffer = await new Ctor(1, 1, DECODE_SAMPLE_RATE2).decodeAudioData(
|
|
64197
64212
|
await res.arrayBuffer()
|
|
64198
64213
|
);
|
|
@@ -64391,7 +64406,7 @@ function AudioFxGroup({
|
|
|
64391
64406
|
const removeNodeAutomation = (nodeId) => removeNodesAutomation([nodeId]);
|
|
64392
64407
|
const [analysing, setAnalysing] = useState88(false);
|
|
64393
64408
|
const storeElements = usePlayerStore((s) => s.elements);
|
|
64394
|
-
const signalPath =
|
|
64409
|
+
const signalPath = useMemo60(() => {
|
|
64395
64410
|
const doc = element.element?.ownerDocument;
|
|
64396
64411
|
return audioFxSignalPath(
|
|
64397
64412
|
element.tagName?.toLowerCase(),
|
|
@@ -65064,7 +65079,7 @@ function createGsapLivePreview(iframeRef) {
|
|
|
65064
65079
|
}
|
|
65065
65080
|
|
|
65066
65081
|
// src/components/editor/propertyPanelFlatColorGradingSection.tsx
|
|
65067
|
-
import { useEffect as useEffect100, useMemo as
|
|
65082
|
+
import { useEffect as useEffect100, useMemo as useMemo62, useRef as useRef111, useState as useState96 } from "react";
|
|
65068
65083
|
import {
|
|
65069
65084
|
HF_COLOR_GRADING_GRADE_PRESETS,
|
|
65070
65085
|
normalizeHfColorGrading as normalizeHfColorGrading6,
|
|
@@ -65099,7 +65114,7 @@ function presetPreviewHandlers({
|
|
|
65099
65114
|
import { useState as useState92 } from "react";
|
|
65100
65115
|
|
|
65101
65116
|
// src/components/editor/propertyPanelColorCurveGraph.tsx
|
|
65102
|
-
import { useMemo as
|
|
65117
|
+
import { useMemo as useMemo61, useRef as useRef104 } from "react";
|
|
65103
65118
|
import {
|
|
65104
65119
|
compileHfColorCurve,
|
|
65105
65120
|
compileHfHueCurve,
|
|
@@ -65287,8 +65302,8 @@ function CurveGraph({
|
|
|
65287
65302
|
onCancel
|
|
65288
65303
|
}) {
|
|
65289
65304
|
const pointerRef = useRef104(null);
|
|
65290
|
-
const samples =
|
|
65291
|
-
const path =
|
|
65305
|
+
const samples = useMemo61(() => samplesFor(points, tab), [points, tab]);
|
|
65306
|
+
const path = useMemo61(() => curvePath(samples, tab), [samples, tab]);
|
|
65292
65307
|
const selectRelativePoint = (offset) => {
|
|
65293
65308
|
if (points.length === 0) return;
|
|
65294
65309
|
const current = selectedIndex ?? (offset > 0 ? -1 : 0);
|
|
@@ -67078,14 +67093,14 @@ function FlatColorGradingSection({
|
|
|
67078
67093
|
const lutInputRef = useRef111(null);
|
|
67079
67094
|
const [lutOpen, setLutOpen] = useState96(false);
|
|
67080
67095
|
const [detailSettingsOpen, setDetailSettingsOpen] = useState96(null);
|
|
67081
|
-
const lutAssets =
|
|
67096
|
+
const lutAssets = useMemo62(
|
|
67082
67097
|
() => assets.filter((asset) => LUT_EXT.test(asset)).sort((a, b) => a.localeCompare(b)),
|
|
67083
67098
|
[assets]
|
|
67084
67099
|
);
|
|
67085
67100
|
const lut = grading.lut;
|
|
67086
67101
|
const selectedLutName = lut?.src ? lut.src.split("/").pop() ?? lut.src : null;
|
|
67087
|
-
const resolvedGrading =
|
|
67088
|
-
const secondaryInputGrading =
|
|
67102
|
+
const resolvedGrading = useMemo62(() => resolveColorGrading(grading), [grading]);
|
|
67103
|
+
const secondaryInputGrading = useMemo62(
|
|
67089
67104
|
() => resolveColorGrading({
|
|
67090
67105
|
intensity: 1,
|
|
67091
67106
|
adjust: resolvedGrading.adjust,
|
|
@@ -67103,7 +67118,7 @@ function FlatColorGradingSection({
|
|
|
67103
67118
|
]
|
|
67104
67119
|
);
|
|
67105
67120
|
const actions = createColorGradingActions(grading, onCommitColorGrading);
|
|
67106
|
-
const scopesRefreshKey =
|
|
67121
|
+
const scopesRefreshKey = useMemo62(() => serializeHfColorGrading2(grading), [grading]);
|
|
67107
67122
|
useEffect100(() => {
|
|
67108
67123
|
if (presetPreviews.status === "idle") onRequestPresetPreviews();
|
|
67109
67124
|
}, [onRequestPresetPreviews, presetPreviews.status]);
|
|
@@ -68233,7 +68248,7 @@ function FlatEffectsSection({
|
|
|
68233
68248
|
import { useState as useState99 } from "react";
|
|
68234
68249
|
|
|
68235
68250
|
// src/hooks/useBlockCatalog.ts
|
|
68236
|
-
import { useState as useState98, useEffect as useEffect102, useMemo as
|
|
68251
|
+
import { useState as useState98, useEffect as useEffect102, useMemo as useMemo63 } from "react";
|
|
68237
68252
|
|
|
68238
68253
|
// src/utils/blockCategories.ts
|
|
68239
68254
|
import {
|
|
@@ -68291,7 +68306,7 @@ function getCategoryColors(category) {
|
|
|
68291
68306
|
var catalogCache = null;
|
|
68292
68307
|
var catalogRequest = null;
|
|
68293
68308
|
function loadCatalog() {
|
|
68294
|
-
catalogRequest ??=
|
|
68309
|
+
catalogRequest ??= studioApiFetch("/api/registry/blocks").then((response) => {
|
|
68295
68310
|
if (!response.ok) throw new Error("Failed to load catalog");
|
|
68296
68311
|
return response.json();
|
|
68297
68312
|
}).then((items) => {
|
|
@@ -68332,7 +68347,7 @@ function useBlockCatalog() {
|
|
|
68332
68347
|
cancelled = true;
|
|
68333
68348
|
};
|
|
68334
68349
|
}, []);
|
|
68335
|
-
const filteredBlocks =
|
|
68350
|
+
const filteredBlocks = useMemo63(() => {
|
|
68336
68351
|
let result = blocks;
|
|
68337
68352
|
if (category) {
|
|
68338
68353
|
result = result.filter((b) => b.category === category);
|
|
@@ -68648,7 +68663,7 @@ function PropertyPanelFlat({
|
|
|
68648
68663
|
onSetAttributeQuiet ?? onSetAttributeLive,
|
|
68649
68664
|
linkedSpeed
|
|
68650
68665
|
);
|
|
68651
|
-
const audioGroupLabel =
|
|
68666
|
+
const audioGroupLabel = useMemo64(() => {
|
|
68652
68667
|
const doc = element.element?.ownerDocument;
|
|
68653
68668
|
const id = element.id;
|
|
68654
68669
|
if (!doc || !id) return void 0;
|
|
@@ -69214,12 +69229,12 @@ var PropertyPanel = memo39(function PropertyPanel2(props) {
|
|
|
69214
69229
|
const cacheEntry = usePlayerStore((s) => s.keyframeCache.get(cacheElementKey));
|
|
69215
69230
|
const iframeRef = previewIframeRef ?? { current: null };
|
|
69216
69231
|
const gsapAnimIdForMemo = element ? gsapAnimations?.find((a) => a.keyframes)?.id ?? gsapAnimations?.[0]?.id ?? null : null;
|
|
69217
|
-
const gsapRuntimeValues =
|
|
69232
|
+
const gsapRuntimeValues = useMemo65(
|
|
69218
69233
|
() => element ? readGsapRuntimeValuesForPanel(gsapAnimIdForMemo, gsapAnimations, element, iframeRef) : null,
|
|
69219
69234
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- iframeRef is stable; currentTime drives re-reads during playback
|
|
69220
69235
|
[gsapAnimIdForMemo, gsapAnimations, element, currentTime]
|
|
69221
69236
|
);
|
|
69222
|
-
const gsapBorderRadius =
|
|
69237
|
+
const gsapBorderRadius = useMemo65(
|
|
69223
69238
|
() => element ? readGsapBorderRadiusForPanel(gsapRuntimeValues, gsapAnimations, element, iframeRef) : null,
|
|
69224
69239
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
69225
69240
|
[gsapRuntimeValues, gsapAnimations, element, currentTime]
|
|
@@ -69596,11 +69611,11 @@ var PropertyPanel = memo39(function PropertyPanel2(props) {
|
|
|
69596
69611
|
});
|
|
69597
69612
|
|
|
69598
69613
|
// src/components/editor/FileTree.tsx
|
|
69599
|
-
import { memo as memo41, useState as useState103, useCallback as useCallback92, useMemo as
|
|
69614
|
+
import { memo as memo41, useState as useState103, useCallback as useCallback92, useMemo as useMemo67, useRef as useRef115 } from "react";
|
|
69600
69615
|
import { Plus as Plus2, FolderSimplePlus as FolderSimplePlus2 } from "@phosphor-icons/react";
|
|
69601
69616
|
|
|
69602
69617
|
// src/components/editor/FileTreeNodes.tsx
|
|
69603
|
-
import { memo as memo40, useState as useState102, useCallback as useCallback91, useMemo as
|
|
69618
|
+
import { memo as memo40, useState as useState102, useCallback as useCallback91, useMemo as useMemo66, useRef as useRef114, useEffect as useEffect104 } from "react";
|
|
69604
69619
|
import {
|
|
69605
69620
|
PencilSimple,
|
|
69606
69621
|
Copy,
|
|
@@ -70103,7 +70118,7 @@ var TreeFolder = memo40(function TreeFolder2({
|
|
|
70103
70118
|
}) {
|
|
70104
70119
|
const [isOpen, setIsOpen] = useState102(defaultOpen);
|
|
70105
70120
|
const toggle = useCallback91(() => setIsOpen((v) => !v), []);
|
|
70106
|
-
const children =
|
|
70121
|
+
const children = useMemo66(() => sortChildren(node.children), [node.children]);
|
|
70107
70122
|
const Chevron = isOpen ? ChevronDown : ChevronRight;
|
|
70108
70123
|
const isDragOver = dragOverFolder === node.fullPath;
|
|
70109
70124
|
const isRenaming = inlineInput?.mode === "rename" && inlineInput.originalPath === node.fullPath;
|
|
@@ -70234,8 +70249,8 @@ var FileTree = memo41(function FileTree2({
|
|
|
70234
70249
|
onImportFiles,
|
|
70235
70250
|
lintFindingsByFile
|
|
70236
70251
|
}) {
|
|
70237
|
-
const tree =
|
|
70238
|
-
const children =
|
|
70252
|
+
const tree = useMemo67(() => buildTree(files), [files]);
|
|
70253
|
+
const children = useMemo67(() => sortChildren(tree.children), [tree]);
|
|
70239
70254
|
const [contextMenu, setContextMenu] = useState103(null);
|
|
70240
70255
|
const [inlineInput, setInlineInput] = useState103(null);
|
|
70241
70256
|
const [deleteTarget, setDeleteTarget] = useState103(null);
|
|
@@ -70502,7 +70517,7 @@ var FileTree = memo41(function FileTree2({
|
|
|
70502
70517
|
});
|
|
70503
70518
|
|
|
70504
70519
|
// src/App.tsx
|
|
70505
|
-
import { useState as useState158, useCallback as useCallback194, useRef as useRef187, useMemo as
|
|
70520
|
+
import { useState as useState158, useCallback as useCallback194, useRef as useRef187, useMemo as useMemo89 } from "react";
|
|
70506
70521
|
|
|
70507
70522
|
// src/hooks/useStableHandlers.ts
|
|
70508
70523
|
import { useRef as useRef116 } from "react";
|
|
@@ -70534,9 +70549,9 @@ function useStableHandlers(value, scope) {
|
|
|
70534
70549
|
}
|
|
70535
70550
|
|
|
70536
70551
|
// src/hooks/useShellSlices.ts
|
|
70537
|
-
import { useMemo as
|
|
70552
|
+
import { useMemo as useMemo68 } from "react";
|
|
70538
70553
|
function useHistoryFlags({ canUndo, canRedo, undoLabel, redoLabel }) {
|
|
70539
|
-
return
|
|
70554
|
+
return useMemo68(
|
|
70540
70555
|
() => ({ canUndo, canRedo, undoLabel, redoLabel }),
|
|
70541
70556
|
[canUndo, canRedo, undoLabel, redoLabel]
|
|
70542
70557
|
);
|
|
@@ -70552,7 +70567,7 @@ function useToolbarSession(session) {
|
|
|
70552
70567
|
handleGsapConvertToKeyframes,
|
|
70553
70568
|
handleGsapRemoveKeyframe
|
|
70554
70569
|
} = session;
|
|
70555
|
-
return
|
|
70570
|
+
return useMemo68(
|
|
70556
70571
|
() => ({
|
|
70557
70572
|
domEditSelection,
|
|
70558
70573
|
selectedGsapAnimations,
|
|
@@ -70625,7 +70640,7 @@ function useRightPanelIntent() {
|
|
|
70625
70640
|
}
|
|
70626
70641
|
|
|
70627
70642
|
// src/components/renders/useRenderQueue.ts
|
|
70628
|
-
import { useState as useState105, useEffect as useEffect107, useCallback as useCallback95, useRef as useRef119, useMemo as
|
|
70643
|
+
import { useState as useState105, useEffect as useEffect107, useCallback as useCallback95, useRef as useRef119, useMemo as useMemo69 } from "react";
|
|
70629
70644
|
|
|
70630
70645
|
// src/components/renders/serverError.ts
|
|
70631
70646
|
async function readServerError(res) {
|
|
@@ -70665,7 +70680,7 @@ function parseStatus(body) {
|
|
|
70665
70680
|
}
|
|
70666
70681
|
async function probe() {
|
|
70667
70682
|
try {
|
|
70668
|
-
const res = await
|
|
70683
|
+
const res = await studioApiFetch("/api/environment/ffmpeg");
|
|
70669
70684
|
return res.ok ? parseStatus(await res.json()) : null;
|
|
70670
70685
|
} catch {
|
|
70671
70686
|
return null;
|
|
@@ -70889,7 +70904,7 @@ function useRenderQueue(projectId, activeCompPathRef) {
|
|
|
70889
70904
|
const loadRenders = useCallback95(async () => {
|
|
70890
70905
|
if (!projectId) return;
|
|
70891
70906
|
try {
|
|
70892
|
-
const res = await
|
|
70907
|
+
const res = await studioApiFetch(buildProjectApiPath(projectId, `/renders`));
|
|
70893
70908
|
if (!res.ok) {
|
|
70894
70909
|
setLoadError(`Couldn't load render history (server error ${res.status}).`);
|
|
70895
70910
|
return;
|
|
@@ -70976,7 +70991,7 @@ function useRenderQueue(projectId, activeCompPathRef) {
|
|
|
70976
70991
|
}
|
|
70977
70992
|
let res;
|
|
70978
70993
|
try {
|
|
70979
|
-
res = await
|
|
70994
|
+
res = await studioApiFetch(buildProjectApiPath(projectId, `/render`), {
|
|
70980
70995
|
method: "POST",
|
|
70981
70996
|
headers: { "Content-Type": "application/json" },
|
|
70982
70997
|
body: JSON.stringify(body)
|
|
@@ -71069,7 +71084,7 @@ function useRenderQueue(projectId, activeCompPathRef) {
|
|
|
71069
71084
|
)
|
|
71070
71085
|
);
|
|
71071
71086
|
try {
|
|
71072
|
-
const res = await
|
|
71087
|
+
const res = await studioApiFetch(`/api/render/${jobId}/cancel`, { method: "POST" });
|
|
71073
71088
|
if (!res.ok && res.status !== 404) {
|
|
71074
71089
|
setActionError("Couldn't cancel on the server \u2014 the render may still be running.");
|
|
71075
71090
|
return;
|
|
@@ -71091,7 +71106,7 @@ function useRenderQueue(projectId, activeCompPathRef) {
|
|
|
71091
71106
|
setActionError(null);
|
|
71092
71107
|
closeActiveEventSource(jobId);
|
|
71093
71108
|
try {
|
|
71094
|
-
const res = await
|
|
71109
|
+
const res = await studioApiFetch(`/api/render/${jobId}`, { method: "DELETE" });
|
|
71095
71110
|
if (!res.ok) {
|
|
71096
71111
|
setActionError("Couldn't delete the render \u2014 it's still on disk.");
|
|
71097
71112
|
return;
|
|
@@ -71148,7 +71163,7 @@ function useRenderQueue(projectId, activeCompPathRef) {
|
|
|
71148
71163
|
};
|
|
71149
71164
|
}, [projectId]);
|
|
71150
71165
|
const isRendering = jobs.some((j) => j.status === "rendering");
|
|
71151
|
-
return
|
|
71166
|
+
return useMemo69(
|
|
71152
71167
|
() => ({
|
|
71153
71168
|
jobs,
|
|
71154
71169
|
isRendering,
|
|
@@ -71574,7 +71589,7 @@ async function captureProjectProvenance(projectId, files, compositions) {
|
|
|
71574
71589
|
};
|
|
71575
71590
|
if (!scaffolded) return;
|
|
71576
71591
|
try {
|
|
71577
|
-
const res = await
|
|
71592
|
+
const res = await studioApiFetch(
|
|
71578
71593
|
buildProjectApiPath(projectId, `/files/${encodeURIComponent(CONFIG_FILE)}`)
|
|
71579
71594
|
);
|
|
71580
71595
|
if (!res.ok) return;
|
|
@@ -72195,7 +72210,7 @@ function ProjectUnreachableBanner({ projectId }) {
|
|
|
72195
72210
|
const [served, setServed] = useState110(null);
|
|
72196
72211
|
useEffect110(() => {
|
|
72197
72212
|
let cancelled = false;
|
|
72198
|
-
|
|
72213
|
+
studioApiFetch("/api/projects").then((res) => res.ok ? res.json() : { projects: [] }).then((data) => {
|
|
72199
72214
|
if (!cancelled) setServed(data.projects ?? []);
|
|
72200
72215
|
}).catch(() => {
|
|
72201
72216
|
if (!cancelled) setServed([]);
|
|
@@ -72305,11 +72320,14 @@ function useCaptionSync(projectId) {
|
|
|
72305
72320
|
}
|
|
72306
72321
|
const seqAtSave = editSeqRef.current;
|
|
72307
72322
|
const overrides = buildOverrides(state.model);
|
|
72308
|
-
|
|
72309
|
-
|
|
72310
|
-
|
|
72311
|
-
|
|
72312
|
-
|
|
72323
|
+
studioApiFetch(
|
|
72324
|
+
buildProjectApiPath(pid, `/files/${encodeURIComponent("caption-overrides.json")}`),
|
|
72325
|
+
{
|
|
72326
|
+
method: "PUT",
|
|
72327
|
+
headers: { "Content-Type": "text/plain", ...studioWriteHeaders() },
|
|
72328
|
+
body: JSON.stringify(overrides, null, 2)
|
|
72329
|
+
}
|
|
72330
|
+
).then((res) => {
|
|
72313
72331
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
72314
72332
|
if (editSeqRef.current === seqAtSave) pendingRef.current = false;
|
|
72315
72333
|
const s = useCaptionStore.getState();
|
|
@@ -72362,7 +72380,7 @@ function useCaptionSync(projectId) {
|
|
|
72362
72380
|
if (!pid) return;
|
|
72363
72381
|
let data;
|
|
72364
72382
|
try {
|
|
72365
|
-
const res = await
|
|
72383
|
+
const res = await studioApiFetch(
|
|
72366
72384
|
buildProjectApiPath(pid, `/files/${encodeURIComponent("caption-overrides.json")}`)
|
|
72367
72385
|
);
|
|
72368
72386
|
if (!res.ok) return;
|
|
@@ -72418,13 +72436,15 @@ function useCaptionSync(projectId) {
|
|
|
72418
72436
|
}
|
|
72419
72437
|
|
|
72420
72438
|
// src/hooks/usePersistentEditHistory.ts
|
|
72421
|
-
import { useCallback as useCallback99, useEffect as useEffect111, useMemo as
|
|
72439
|
+
import { useCallback as useCallback99, useEffect as useEffect111, useMemo as useMemo70, useRef as useRef126, useState as useState111 } from "react";
|
|
72422
72440
|
var EMPTY3 = { entries: [], back: null, forward: null };
|
|
72423
72441
|
var OWN_ENTRIES_KEPT = 100;
|
|
72424
72442
|
function createOwnHistory() {
|
|
72425
72443
|
const own = /* @__PURE__ */ new Map();
|
|
72426
72444
|
let next = null;
|
|
72427
72445
|
let changes = 0;
|
|
72446
|
+
let claimed = { count: 0, id: "" };
|
|
72447
|
+
const undoneIds = /* @__PURE__ */ new Set();
|
|
72428
72448
|
const remember = (id, files) => {
|
|
72429
72449
|
const known = own.get(id) ?? {};
|
|
72430
72450
|
for (const [path, { before, after }] of Object.entries(files)) {
|
|
@@ -72445,7 +72465,15 @@ function createOwnHistory() {
|
|
|
72445
72465
|
if (seen === changes) next = { undo: view.back, redo: view.forward };
|
|
72446
72466
|
},
|
|
72447
72467
|
changes: () => changes,
|
|
72468
|
+
claimCount: () => claimed.count,
|
|
72469
|
+
claimedAfter: (count) => claimed.count > count && claimed.id ? claimed.id : null,
|
|
72470
|
+
targetFor: (id, pressedAt) => !id || !undoneIds.has(id) ? id : claimed.count > pressedAt ? false : null,
|
|
72471
|
+
noteClaim: (id) => {
|
|
72472
|
+
claimed = { count: claimed.count + 1, id };
|
|
72473
|
+
},
|
|
72448
72474
|
stepped: (entry2) => {
|
|
72475
|
+
if (entry2.undoes) undoneIds.add(entry2.undoes);
|
|
72476
|
+
if (undoneIds.size > OWN_ENTRIES_KEPT) undoneIds.delete(undoneIds.values().next().value);
|
|
72449
72477
|
const undone = entry2.undoes ? own.get(entry2.undoes) : void 0;
|
|
72450
72478
|
if (!undone) return;
|
|
72451
72479
|
const swapped = Object.entries(undone).map(([path, f]) => [
|
|
@@ -72461,14 +72489,15 @@ function createOwnHistory() {
|
|
|
72461
72489
|
const paths = files ? Object.keys(files) : [];
|
|
72462
72490
|
if (!files || paths.length !== step.paths.length || !paths.every((p) => step.paths.includes(p)))
|
|
72463
72491
|
return null;
|
|
72464
|
-
const
|
|
72492
|
+
const restore2 = paths.map((path) => [
|
|
72465
72493
|
path,
|
|
72466
72494
|
{ previous: files[path].after, restored: files[path].before }
|
|
72467
72495
|
]);
|
|
72468
|
-
return { id: step.id, files: Object.fromEntries(
|
|
72496
|
+
return { id: step.id, files: Object.fromEntries(restore2) };
|
|
72469
72497
|
},
|
|
72470
72498
|
clear: () => {
|
|
72471
72499
|
own.clear();
|
|
72500
|
+
undoneIds.clear();
|
|
72472
72501
|
next = null;
|
|
72473
72502
|
changes += 1;
|
|
72474
72503
|
}
|
|
@@ -72478,8 +72507,14 @@ var DEFAULT_COALESCE_MS = 300;
|
|
|
72478
72507
|
function historyUrl(projectId, path = "") {
|
|
72479
72508
|
return `/api/projects/${encodeURIComponent(projectId)}/history${path}`;
|
|
72480
72509
|
}
|
|
72510
|
+
async function cantUndo(paths) {
|
|
72511
|
+
return { ok: false, reason: "content-mismatch", paths: paths ?? [] };
|
|
72512
|
+
}
|
|
72513
|
+
function unionPaths(...lists) {
|
|
72514
|
+
return [...new Set(lists.flatMap((list) => list ?? []))];
|
|
72515
|
+
}
|
|
72481
72516
|
async function post(url, body, headers = {}) {
|
|
72482
|
-
const response = await
|
|
72517
|
+
const response = await studioApiFetch(url, {
|
|
72483
72518
|
method: "POST",
|
|
72484
72519
|
headers: { "Content-Type": "application/json", ...headers },
|
|
72485
72520
|
body: JSON.stringify(body)
|
|
@@ -72538,9 +72573,10 @@ function usePersistentEditHistory({ projectId }) {
|
|
|
72538
72573
|
const refresh = useCallback99(async () => {
|
|
72539
72574
|
if (!projectId) return;
|
|
72540
72575
|
const seen = own.changes();
|
|
72541
|
-
const response = await
|
|
72542
|
-
const next = response?.ok ? await response.json() : EMPTY3;
|
|
72576
|
+
const response = await studioApiFetch(historyUrl(projectId)).catch(() => null);
|
|
72577
|
+
const next = response?.ok ? await response.json().catch(() => null) : EMPTY3;
|
|
72543
72578
|
if (projectIdRef.current !== projectId) return;
|
|
72579
|
+
if (!next) return console.error("The history's reply was unreadable.");
|
|
72544
72580
|
setView(next);
|
|
72545
72581
|
own.offered(seen, next);
|
|
72546
72582
|
}, [projectId, own]);
|
|
@@ -72552,9 +72588,9 @@ function usePersistentEditHistory({ projectId }) {
|
|
|
72552
72588
|
void refresh().finally(() => setLoaded(true));
|
|
72553
72589
|
}, [refresh, own]);
|
|
72554
72590
|
const recordEdit = useCallback99(
|
|
72555
|
-
async ({ label, coalesceKey, coalesceMs, files }) => {
|
|
72591
|
+
async ({ label, coalesceKey, coalesceMs, files, created = [] }) => {
|
|
72556
72592
|
if (!projectId) return;
|
|
72557
|
-
const paths = Object.keys(files);
|
|
72593
|
+
const paths = [...Object.keys(files), ...created];
|
|
72558
72594
|
own.overtake();
|
|
72559
72595
|
const reply = await post(historyUrl(projectId, "/claim"), {
|
|
72560
72596
|
label,
|
|
@@ -72563,7 +72599,10 @@ function usePersistentEditHistory({ projectId }) {
|
|
|
72563
72599
|
...coalesceKey && { coalesceKey, idleMs: coalesceMs ?? DEFAULT_COALESCE_MS }
|
|
72564
72600
|
});
|
|
72565
72601
|
const claimed = claimHeld(reply, label);
|
|
72566
|
-
if (claimed)
|
|
72602
|
+
if (claimed) {
|
|
72603
|
+
own.remember(claimed, files);
|
|
72604
|
+
own.noteClaim(claimed);
|
|
72605
|
+
}
|
|
72567
72606
|
heldClaimRef.current = claimed && coalesceKey ? { paths, at: Date.now() } : null;
|
|
72568
72607
|
await refresh();
|
|
72569
72608
|
},
|
|
@@ -72572,14 +72611,17 @@ function usePersistentEditHistory({ projectId }) {
|
|
|
72572
72611
|
const step = useCallback99(
|
|
72573
72612
|
async (direction, callbacks) => {
|
|
72574
72613
|
if (!projectId) return { ok: false, reason: "empty" };
|
|
72614
|
+
const candidate = direction === "undo" && callbacks.claimedAfter !== void 0 ? own.claimedAfter(callbacks.claimedAfter) : null;
|
|
72575
72615
|
const next = direction === "undo" ? view.back : view.forward;
|
|
72576
|
-
const
|
|
72616
|
+
const stepPaths = candidate ? Object.keys(own.afterOf(candidate)) : next?.paths;
|
|
72617
|
+
const paths = unionPaths(stepPaths, heldClaimRef.current?.paths);
|
|
72618
|
+
const pressedAt = own.claimCount();
|
|
72577
72619
|
own.overtake();
|
|
72578
|
-
const
|
|
72620
|
+
const attempt = async (target) => {
|
|
72579
72621
|
const previous = await readAll(paths, callbacks.readFile);
|
|
72580
72622
|
const posted = await post(
|
|
72581
|
-
historyUrl(projectId, "/step"),
|
|
72582
|
-
{ direction: direction === "undo" ? "back" : "forward" },
|
|
72623
|
+
historyUrl(projectId, target ? "/undo" : "/step"),
|
|
72624
|
+
target ? { entryId: target } : { direction: direction === "undo" ? "back" : "forward" },
|
|
72583
72625
|
studioWriteHeaders()
|
|
72584
72626
|
);
|
|
72585
72627
|
heldClaimRef.current = null;
|
|
@@ -72606,6 +72648,10 @@ function usePersistentEditHistory({ projectId }) {
|
|
|
72606
72648
|
)
|
|
72607
72649
|
};
|
|
72608
72650
|
};
|
|
72651
|
+
const run = () => {
|
|
72652
|
+
const target = own.targetFor(candidate, pressedAt);
|
|
72653
|
+
return target === false ? cantUndo(stepPaths) : attempt(target);
|
|
72654
|
+
};
|
|
72609
72655
|
return callbacks.serialize ? callbacks.serialize(paths, run) : run();
|
|
72610
72656
|
},
|
|
72611
72657
|
[projectId, view, refresh, own]
|
|
@@ -72616,7 +72662,7 @@ function usePersistentEditHistory({ projectId }) {
|
|
|
72616
72662
|
}, [own, refresh]);
|
|
72617
72663
|
const undo = useCallback99((callbacks) => step("undo", callbacks), [step]);
|
|
72618
72664
|
const redo = useCallback99((callbacks) => step("redo", callbacks), [step]);
|
|
72619
|
-
const state =
|
|
72665
|
+
const state = useMemo70(() => {
|
|
72620
72666
|
const backAt = Math.max(view.back?.endedAt ?? 0, heldClaimRef.current?.at ?? 0);
|
|
72621
72667
|
const redoAt = redoneAt(view);
|
|
72622
72668
|
return {
|
|
@@ -72637,6 +72683,7 @@ function usePersistentEditHistory({ projectId }) {
|
|
|
72637
72683
|
undo,
|
|
72638
72684
|
redo,
|
|
72639
72685
|
predict: own.predict,
|
|
72686
|
+
claims: own.claimCount,
|
|
72640
72687
|
noteOutsideChange
|
|
72641
72688
|
};
|
|
72642
72689
|
}
|
|
@@ -72701,7 +72748,7 @@ function usePanelLayout(initialState2) {
|
|
|
72701
72748
|
import { useState as useState113, useCallback as useCallback104, useRef as useRef130 } from "react";
|
|
72702
72749
|
|
|
72703
72750
|
// src/hooks/useFileTree.ts
|
|
72704
|
-
import { useState as useState112, useCallback as useCallback101, useEffect as useEffect113, useMemo as
|
|
72751
|
+
import { useState as useState112, useCallback as useCallback101, useEffect as useEffect113, useMemo as useMemo71, useRef as useRef128 } from "react";
|
|
72705
72752
|
var EMPTY_FILE_LIST = [];
|
|
72706
72753
|
function useFileTree({ projectId, projectIdRef }) {
|
|
72707
72754
|
const [fetched, setFetched] = useState112(null);
|
|
@@ -72714,7 +72761,7 @@ function useFileTree({ projectId, projectIdRef }) {
|
|
|
72714
72761
|
if (!projectId) return;
|
|
72715
72762
|
let cancelled = false;
|
|
72716
72763
|
setFetched({ projectId, loaded: false, fileTree: [], compositionPaths: [], projectDir: null });
|
|
72717
|
-
|
|
72764
|
+
studioApiFetch(buildProjectApiPath(projectId)).then((r) => {
|
|
72718
72765
|
if (!r.ok) throw new Error(`tree fetch failed: ${r.status}`);
|
|
72719
72766
|
return r.json();
|
|
72720
72767
|
}).then((data) => {
|
|
@@ -72747,7 +72794,7 @@ function useFileTree({ projectId, projectIdRef }) {
|
|
|
72747
72794
|
refreshAbortRef.current = controller;
|
|
72748
72795
|
let data;
|
|
72749
72796
|
try {
|
|
72750
|
-
const res = await
|
|
72797
|
+
const res = await studioApiFetch(buildProjectApiPath(pid), { signal: controller.signal });
|
|
72751
72798
|
data = await res.json();
|
|
72752
72799
|
} catch (err) {
|
|
72753
72800
|
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
@@ -72765,11 +72812,11 @@ function useFileTree({ projectId, projectIdRef }) {
|
|
|
72765
72812
|
);
|
|
72766
72813
|
}
|
|
72767
72814
|
}, [projectIdRef]);
|
|
72768
|
-
const assets =
|
|
72815
|
+
const assets = useMemo71(
|
|
72769
72816
|
() => fileTree.filter((f) => !f.endsWith(".html") && !f.endsWith(".md") && !f.endsWith(".json")),
|
|
72770
72817
|
[fileTree]
|
|
72771
72818
|
);
|
|
72772
|
-
const fontAssets =
|
|
72819
|
+
const fontAssets = useMemo71(
|
|
72773
72820
|
() => (projectId ? assets : []).filter((asset) => FONT_EXT.test(asset)).map((asset) => ({
|
|
72774
72821
|
family: fontFamilyFromAssetPath(asset),
|
|
72775
72822
|
path: asset,
|
|
@@ -72913,9 +72960,18 @@ function useEditorSave({
|
|
|
72913
72960
|
}
|
|
72914
72961
|
|
|
72915
72962
|
// src/hooks/useProjectFileWriter.ts
|
|
72916
|
-
import { useCallback as useCallback103, useMemo as
|
|
72963
|
+
import { useCallback as useCallback103, useMemo as useMemo72 } from "react";
|
|
72964
|
+
async function fetchProjectFile(projectId, path, query) {
|
|
72965
|
+
if (!projectId) throw new Error("No active project");
|
|
72966
|
+
const response = await studioApiFetch(
|
|
72967
|
+
`/api/projects/${encodeURIComponent(projectId)}/files/${encodeURIComponent(path)}${query}`
|
|
72968
|
+
);
|
|
72969
|
+
if (!response.ok) throw new Error(`Failed to read ${path}`);
|
|
72970
|
+
const data = await response.json();
|
|
72971
|
+
return { content: data.content, version: data.version ?? response.headers.get("etag") };
|
|
72972
|
+
}
|
|
72917
72973
|
function useProjectFileWriter({ projectId }) {
|
|
72918
|
-
const fileVersionScope =
|
|
72974
|
+
const fileVersionScope = useMemo72(
|
|
72919
72975
|
() => ({ projectId, versions: /* @__PURE__ */ new Map() }),
|
|
72920
72976
|
[projectId]
|
|
72921
72977
|
);
|
|
@@ -72928,28 +72984,18 @@ function useProjectFileWriter({ projectId }) {
|
|
|
72928
72984
|
);
|
|
72929
72985
|
const readProjectFile = useCallback103(
|
|
72930
72986
|
async (path) => {
|
|
72931
|
-
|
|
72932
|
-
|
|
72933
|
-
|
|
72934
|
-
|
|
72935
|
-
if (!response.ok) throw new Error(`Failed to read ${path}`);
|
|
72936
|
-
const data = await response.json();
|
|
72937
|
-
if (typeof data.content !== "string") throw new Error(`Missing file contents for ${path}`);
|
|
72938
|
-
fileVersions.set(path, data.version ?? response.headers.get("etag"));
|
|
72939
|
-
return data.content;
|
|
72987
|
+
const { content, version } = await fetchProjectFile(projectId, path, "");
|
|
72988
|
+
if (typeof content !== "string") throw new Error(`Missing file contents for ${path}`);
|
|
72989
|
+
fileVersions.set(path, version);
|
|
72990
|
+
return content;
|
|
72940
72991
|
},
|
|
72941
72992
|
[fileVersions, projectId]
|
|
72942
72993
|
);
|
|
72943
72994
|
const readOptionalProjectFile = useCallback103(
|
|
72944
72995
|
async (path) => {
|
|
72945
|
-
|
|
72946
|
-
|
|
72947
|
-
|
|
72948
|
-
);
|
|
72949
|
-
if (!response.ok) throw new Error(`Failed to read ${path}`);
|
|
72950
|
-
const data = await response.json();
|
|
72951
|
-
fileVersions.set(path, data.version ?? response.headers.get("etag"));
|
|
72952
|
-
return typeof data.content === "string" ? data.content : "";
|
|
72996
|
+
const { content, version } = await fetchProjectFile(projectId, path, "?optional=1");
|
|
72997
|
+
fileVersions.set(path, version);
|
|
72998
|
+
return typeof content === "string" ? content : "";
|
|
72953
72999
|
},
|
|
72954
73000
|
[fileVersions, projectId]
|
|
72955
73001
|
);
|
|
@@ -72959,7 +73005,7 @@ function useProjectFileWriter({ projectId }) {
|
|
|
72959
73005
|
const writeProjectId = projectId;
|
|
72960
73006
|
let expectedVersion = await studioExpectedFileVersion(fileVersions, path, expectedContent);
|
|
72961
73007
|
if (expectedVersion === void 0) {
|
|
72962
|
-
const preflight = await
|
|
73008
|
+
const preflight = await studioApiFetch(
|
|
72963
73009
|
`/api/projects/${encodeURIComponent(writeProjectId)}/files/${encodeURIComponent(path)}`
|
|
72964
73010
|
);
|
|
72965
73011
|
if (preflight.ok) {
|
|
@@ -72979,7 +73025,7 @@ function useProjectFileWriter({ projectId }) {
|
|
|
72979
73025
|
await retryStudioSave(async () => {
|
|
72980
73026
|
let response;
|
|
72981
73027
|
try {
|
|
72982
|
-
response = await
|
|
73028
|
+
response = await studioApiFetch(
|
|
72983
73029
|
`/api/projects/${encodeURIComponent(writeProjectId)}/files/${encodeURIComponent(path)}`,
|
|
72984
73030
|
{
|
|
72985
73031
|
method: "PUT",
|
|
@@ -73020,7 +73066,7 @@ function useProjectFileWriter({ projectId }) {
|
|
|
73020
73066
|
},
|
|
73021
73067
|
[fileVersions, projectId]
|
|
73022
73068
|
);
|
|
73023
|
-
return
|
|
73069
|
+
return useMemo72(
|
|
73024
73070
|
() => ({
|
|
73025
73071
|
fileVersions,
|
|
73026
73072
|
readProjectFile,
|
|
@@ -73124,7 +73170,7 @@ function useFileManager({
|
|
|
73124
73170
|
setEditingFile({ path, content: null });
|
|
73125
73171
|
return;
|
|
73126
73172
|
}
|
|
73127
|
-
|
|
73173
|
+
studioApiFetch(`/api/projects/${encodeURIComponent(pid)}/files/${encodeURIComponent(path)}`).then((r) => {
|
|
73128
73174
|
if (!r.ok) throw new Error(`Failed to load ${path} (${r.status})`);
|
|
73129
73175
|
return r.json();
|
|
73130
73176
|
}).then((data) => {
|
|
@@ -73152,9 +73198,12 @@ function useFileManager({
|
|
|
73152
73198
|
const requestId = ++revealRequestIdRef.current;
|
|
73153
73199
|
const controller = new AbortController();
|
|
73154
73200
|
revealAbortRef.current = controller;
|
|
73155
|
-
|
|
73156
|
-
|
|
73157
|
-
|
|
73201
|
+
studioApiFetch(
|
|
73202
|
+
`/api/projects/${encodeURIComponent(pid)}/files/${encodeURIComponent(sourceFile)}`,
|
|
73203
|
+
{
|
|
73204
|
+
signal: controller.signal
|
|
73205
|
+
}
|
|
73206
|
+
).then((r) => r.json()).then((data) => {
|
|
73158
73207
|
if (requestId !== revealRequestIdRef.current) return;
|
|
73159
73208
|
if (data.content != null) {
|
|
73160
73209
|
fileVersions.set(sourceFile, data.version ?? null);
|
|
@@ -73178,7 +73227,7 @@ function useFileManager({
|
|
|
73178
73227
|
}
|
|
73179
73228
|
const qs = dir ? `?dir=${encodeURIComponent(dir)}` : "";
|
|
73180
73229
|
try {
|
|
73181
|
-
const res = await
|
|
73230
|
+
const res = await studioApiFetch(`/api/projects/${encodeURIComponent(pid)}/upload${qs}`, {
|
|
73182
73231
|
method: "POST",
|
|
73183
73232
|
body: formData
|
|
73184
73233
|
});
|
|
@@ -73218,7 +73267,7 @@ function useFileManager({
|
|
|
73218
73267
|
if (path.endsWith(".html")) {
|
|
73219
73268
|
content = '<!DOCTYPE html>\n<html>\n<head>\n <meta charset="UTF-8">\n</head>\n<body>\n\n</body>\n</html>\n';
|
|
73220
73269
|
}
|
|
73221
|
-
const res = await
|
|
73270
|
+
const res = await studioApiFetch(
|
|
73222
73271
|
`/api/projects/${encodeURIComponent(pid)}/files/${encodeURIComponent(path)}`,
|
|
73223
73272
|
{
|
|
73224
73273
|
method: "POST",
|
|
@@ -73241,7 +73290,7 @@ function useFileManager({
|
|
|
73241
73290
|
async (path) => {
|
|
73242
73291
|
const pid = projectIdRef.current;
|
|
73243
73292
|
if (!pid) return;
|
|
73244
|
-
const res = await
|
|
73293
|
+
const res = await studioApiFetch(
|
|
73245
73294
|
`/api/projects/${encodeURIComponent(pid)}/files/${encodeURIComponent(path + "/.gitkeep")}`,
|
|
73246
73295
|
{
|
|
73247
73296
|
method: "POST",
|
|
@@ -73263,7 +73312,7 @@ function useFileManager({
|
|
|
73263
73312
|
async (path) => {
|
|
73264
73313
|
const pid = projectIdRef.current;
|
|
73265
73314
|
if (!pid) return;
|
|
73266
|
-
const res = await
|
|
73315
|
+
const res = await studioApiFetch(
|
|
73267
73316
|
`/api/projects/${encodeURIComponent(pid)}/files/${encodeURIComponent(path)}`,
|
|
73268
73317
|
{
|
|
73269
73318
|
method: "DELETE"
|
|
@@ -73284,7 +73333,7 @@ function useFileManager({
|
|
|
73284
73333
|
async (oldPath, newPath) => {
|
|
73285
73334
|
const pid = projectIdRef.current;
|
|
73286
73335
|
if (!pid) return;
|
|
73287
|
-
const res = await
|
|
73336
|
+
const res = await studioApiFetch(
|
|
73288
73337
|
`/api/projects/${encodeURIComponent(pid)}/files/${encodeURIComponent(oldPath)}`,
|
|
73289
73338
|
{
|
|
73290
73339
|
method: "PATCH",
|
|
@@ -73310,7 +73359,7 @@ function useFileManager({
|
|
|
73310
73359
|
async (path) => {
|
|
73311
73360
|
const pid = projectIdRef.current;
|
|
73312
73361
|
if (!pid) return;
|
|
73313
|
-
const res = await
|
|
73362
|
+
const res = await studioApiFetch(`/api/projects/${encodeURIComponent(pid)}/duplicate-file`, {
|
|
73314
73363
|
method: "POST",
|
|
73315
73364
|
headers: { "Content-Type": "application/json" },
|
|
73316
73365
|
body: JSON.stringify({ path })
|
|
@@ -73619,18 +73668,17 @@ function applyUndoRestoreToPreview(iframe, activeCompPath, files, currentTime, r
|
|
|
73619
73668
|
const active = files[activeDocPath];
|
|
73620
73669
|
const restoredScript = active ? extractGsapScriptText(active.restored) : null;
|
|
73621
73670
|
const previousScript = active ? extractGsapScriptText(active.previous) : null;
|
|
73622
|
-
const reparse = plan.scripted
|
|
73623
|
-
if (reparse
|
|
73671
|
+
const reparse = plan.scripted && plan.targets.some(({ live }) => "_gsap" in live);
|
|
73672
|
+
if (reparse && !restoredScript) {
|
|
73624
73673
|
reloadPreview();
|
|
73625
73674
|
return "full";
|
|
73626
73675
|
}
|
|
73627
|
-
if (restoredScript && (restoredScript !== previousScript || reparse
|
|
73676
|
+
if (restoredScript && (restoredScript !== previousScript || reparse)) {
|
|
73628
73677
|
syncStaleEditMarks(doc, active);
|
|
73629
73678
|
const result = applySoftReload(iframe, restoredScript, {
|
|
73630
73679
|
onAsyncFailure: reloadPreview,
|
|
73631
73680
|
currentTimeOverride: currentTime,
|
|
73632
73681
|
authoredHtml: active.restored,
|
|
73633
|
-
reparse,
|
|
73634
73682
|
nestedFiles
|
|
73635
73683
|
});
|
|
73636
73684
|
if (result === "cannot-soft-reload") {
|
|
@@ -73799,8 +73847,9 @@ function usePreviewPersistence({
|
|
|
73799
73847
|
if (result.status !== "clean") throw result.error;
|
|
73800
73848
|
}, [drainPendingDomEditSaves]);
|
|
73801
73849
|
const settlePendingEdits = useCallback105(async () => {
|
|
73802
|
-
|
|
73803
|
-
await
|
|
73850
|
+
const queued = domEditSaveQueueRef.current?.waitForIdle();
|
|
73851
|
+
await flushStudioPendingEdits({ onlyCurrent: true });
|
|
73852
|
+
await queued;
|
|
73804
73853
|
}, []);
|
|
73805
73854
|
const resetDomEditSaveQueueBreaker = useCallback105(() => {
|
|
73806
73855
|
domEditSaveQueueRef.current?.reset();
|
|
@@ -73825,20 +73874,20 @@ function usePreviewPersistence({
|
|
|
73825
73874
|
);
|
|
73826
73875
|
applyStudioManualEditsToPreviewRef.current = applyStudioManualEditsToPreview;
|
|
73827
73876
|
const syncHistoryPreviewAfterApply = useCallback105(
|
|
73828
|
-
async (
|
|
73877
|
+
async (restore2) => {
|
|
73829
73878
|
const projectId = usePlayerStore.getState().timelineProjectId;
|
|
73830
73879
|
const nestedFiles = await settleNestedReads(
|
|
73831
|
-
|
|
73880
|
+
readUndoNestedFiles(
|
|
73832
73881
|
previewIframeRef.current,
|
|
73833
73882
|
activeCompPathRef.current,
|
|
73834
|
-
|
|
73835
|
-
(path) => readProjectFileContent(projectId, path)
|
|
73836
|
-
)
|
|
73883
|
+
restore2.files,
|
|
73884
|
+
(path) => projectId ? readProjectFileContent(projectId, path) : Promise.reject(new Error("No project is open to read nested files from."))
|
|
73885
|
+
)
|
|
73837
73886
|
);
|
|
73838
73887
|
const strategy = applyUndoRestoreToPreview(
|
|
73839
73888
|
previewIframeRef.current,
|
|
73840
73889
|
activeCompPathRef.current,
|
|
73841
|
-
|
|
73890
|
+
restore2.files,
|
|
73842
73891
|
usePlayerStore.getState().currentTime,
|
|
73843
73892
|
reloadPreview,
|
|
73844
73893
|
nestedFiles
|
|
@@ -73952,7 +74001,7 @@ function buildAtomicCutIntents(elements, splitTime, activeCompPath) {
|
|
|
73952
74001
|
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
73953
74002
|
}
|
|
73954
74003
|
async function readFileVersion(projectId, path) {
|
|
73955
|
-
const response = await
|
|
74004
|
+
const response = await studioApiFetch(
|
|
73956
74005
|
buildProjectApiPath(projectId, `/files/${encodeURIComponent(path)}`)
|
|
73957
74006
|
);
|
|
73958
74007
|
if (!response.ok) throw new Error(`Failed to read ${path} before cut (${response.status})`);
|
|
@@ -73971,14 +74020,17 @@ async function requestAtomicCut(projectId, intents) {
|
|
|
73971
74020
|
}
|
|
73972
74021
|
const transactionToken = `cut:${crypto.randomUUID()}`;
|
|
73973
74022
|
markStudioWriteToken(transactionToken);
|
|
73974
|
-
const response = await
|
|
73975
|
-
|
|
73976
|
-
|
|
73977
|
-
|
|
73978
|
-
|
|
73979
|
-
|
|
73980
|
-
|
|
73981
|
-
|
|
74023
|
+
const response = await studioApiFetch(
|
|
74024
|
+
buildProjectApiPath(projectId, "/file-mutations/split-batch"),
|
|
74025
|
+
{
|
|
74026
|
+
method: "POST",
|
|
74027
|
+
headers: {
|
|
74028
|
+
"Content-Type": "application/json",
|
|
74029
|
+
"X-Hyperframes-Write-Token": transactionToken
|
|
74030
|
+
},
|
|
74031
|
+
body: JSON.stringify({ files, transactionToken })
|
|
74032
|
+
}
|
|
74033
|
+
);
|
|
73982
74034
|
const body = await response.json().catch(() => null);
|
|
73983
74035
|
if (!response.ok || body?.ok !== true || !Array.isArray(body.files)) {
|
|
73984
74036
|
const prefix = response.status === 409 ? "Cut conflict" : "Cut failed";
|
|
@@ -74039,14 +74091,14 @@ import { useCallback as useCallback107, useRef as useRef133 } from "react";
|
|
|
74039
74091
|
function isFreezeFrameResponse(value) {
|
|
74040
74092
|
if (typeof value !== "object" || value === null) return false;
|
|
74041
74093
|
const body = value;
|
|
74042
|
-
return typeof body.before === "string" && typeof body.after === "string" && typeof body.version === "string";
|
|
74094
|
+
return typeof body.before === "string" && typeof body.after === "string" && typeof body.version === "string" && typeof body.stillPath === "string";
|
|
74043
74095
|
}
|
|
74044
74096
|
function errorOf(value) {
|
|
74045
74097
|
if (typeof value !== "object" || value === null || !("error" in value)) return null;
|
|
74046
74098
|
return typeof value.error === "string" ? value.error : null;
|
|
74047
74099
|
}
|
|
74048
74100
|
async function readVersion(projectId, path) {
|
|
74049
|
-
const response = await
|
|
74101
|
+
const response = await studioApiFetch(
|
|
74050
74102
|
buildProjectApiPath(projectId, `/files/${encodeURIComponent(path)}`)
|
|
74051
74103
|
);
|
|
74052
74104
|
const body = await response.json().catch(() => null);
|
|
@@ -74060,7 +74112,7 @@ async function requestFreezeFrame(input) {
|
|
|
74060
74112
|
const expectedVersion = await readVersion(input.projectId, input.path);
|
|
74061
74113
|
const transactionToken = `freeze:${crypto.randomUUID()}`;
|
|
74062
74114
|
markStudioWriteToken(transactionToken);
|
|
74063
|
-
const response = await
|
|
74115
|
+
const response = await studioApiFetch(
|
|
74064
74116
|
buildProjectApiPath(input.projectId, "/file-mutations/freeze-frame"),
|
|
74065
74117
|
{
|
|
74066
74118
|
method: "POST",
|
|
@@ -74100,7 +74152,8 @@ function useFreezeFrame(options) {
|
|
|
74100
74152
|
const result = await requestFreezeFrame({ projectId, path, element, playhead });
|
|
74101
74153
|
await opts.recordEdit({
|
|
74102
74154
|
label: "Freeze frame",
|
|
74103
|
-
files: { [path]: { before: result.before, after: result.after } }
|
|
74155
|
+
files: { [path]: { before: result.before, after: result.after } },
|
|
74156
|
+
created: [result.stillPath]
|
|
74104
74157
|
});
|
|
74105
74158
|
opts.observeProjectFileVersion?.(path, result.version);
|
|
74106
74159
|
});
|
|
@@ -74315,7 +74368,7 @@ function resolveDropTrack(input) {
|
|
|
74315
74368
|
|
|
74316
74369
|
// src/utils/timelineCompositionInsert.ts
|
|
74317
74370
|
async function insertTimelineComposition(input) {
|
|
74318
|
-
const current = await
|
|
74371
|
+
const current = await studioApiFetch(
|
|
74319
74372
|
buildProjectApiPath(input.projectId, `/files/${encodeURIComponent(input.targetPath)}`)
|
|
74320
74373
|
);
|
|
74321
74374
|
if (!current.ok) {
|
|
@@ -74323,7 +74376,7 @@ async function insertTimelineComposition(input) {
|
|
|
74323
74376
|
}
|
|
74324
74377
|
const snapshot3 = await current.json();
|
|
74325
74378
|
if (typeof snapshot3.version !== "string") throw new Error("Missing composition file version");
|
|
74326
|
-
const response = await
|
|
74379
|
+
const response = await studioApiFetch(
|
|
74327
74380
|
buildProjectApiPath(
|
|
74328
74381
|
input.projectId,
|
|
74329
74382
|
`/file-mutations/insert-composition/${encodeURIComponent(input.targetPath)}`
|
|
@@ -74656,10 +74709,10 @@ function useTimelineEditRefusal(canEdit, showToast) {
|
|
|
74656
74709
|
);
|
|
74657
74710
|
}
|
|
74658
74711
|
function useTimelineEditGate(canEdit, showToast) {
|
|
74659
|
-
const
|
|
74712
|
+
const refuse2 = useTimelineEditRefusal(canEdit, showToast);
|
|
74660
74713
|
return useCallback110(
|
|
74661
|
-
(targets) =>
|
|
74662
|
-
[
|
|
74714
|
+
(targets) => refuse2(targets) === null,
|
|
74715
|
+
[refuse2]
|
|
74663
74716
|
);
|
|
74664
74717
|
}
|
|
74665
74718
|
|
|
@@ -75261,7 +75314,7 @@ function useTimelineDeleteOps({
|
|
|
75261
75314
|
}
|
|
75262
75315
|
return patchTarget;
|
|
75263
75316
|
});
|
|
75264
|
-
const removeResponse = await
|
|
75317
|
+
const removeResponse = await studioApiFetch(
|
|
75265
75318
|
buildProjectApiPath(
|
|
75266
75319
|
pid,
|
|
75267
75320
|
`/file-mutations/remove-elements/${encodeURIComponent(targetPath)}`
|
|
@@ -75892,11 +75945,11 @@ function assertAudioGroupEditable(checkEditable, elements) {
|
|
|
75892
75945
|
throw new Error("Timeline edit blocked");
|
|
75893
75946
|
}
|
|
75894
75947
|
}
|
|
75895
|
-
function patchLiveAudioGroupState(iframe, elements, groupId, activeCompPath,
|
|
75948
|
+
function patchLiveAudioGroupState(iframe, elements, groupId, activeCompPath, restore2) {
|
|
75896
75949
|
for (const element of elements) {
|
|
75897
75950
|
const target = findTimelineElementInIframe(iframe, element, activeCompPath);
|
|
75898
75951
|
if (!target) continue;
|
|
75899
|
-
const next =
|
|
75952
|
+
const next = restore2 ? restore2.get(element) ?? null : groupId;
|
|
75900
75953
|
if (next) target.setAttribute(HF_AUDIO_GROUP_ATTR5, next);
|
|
75901
75954
|
else target.removeAttribute(HF_AUDIO_GROUP_ATTR5);
|
|
75902
75955
|
}
|
|
@@ -76896,9 +76949,9 @@ function useTimelineEditing({
|
|
|
76896
76949
|
handleTimelineGroupResize: track2(
|
|
76897
76950
|
guard((changes) => changes.map((c) => c.element), groupEditing.handleTimelineGroupResize)
|
|
76898
76951
|
),
|
|
76899
|
-
restoreLiveLanes: (
|
|
76900
|
-
setElementFxAttribute.restoreLive(
|
|
76901
|
-
setAudioGroupAttribute2.restoreLive(
|
|
76952
|
+
restoreLiveLanes: (restore2) => {
|
|
76953
|
+
setElementFxAttribute.restoreLive(restore2);
|
|
76954
|
+
setAudioGroupAttribute2.restoreLive(restore2);
|
|
76902
76955
|
}
|
|
76903
76956
|
};
|
|
76904
76957
|
return useStableHandlers(editing, projectId);
|
|
@@ -76958,7 +77011,7 @@ function useAskAgentModal({
|
|
|
76958
77011
|
if (!pid) return;
|
|
76959
77012
|
const targetPath = selection.sourceFile || activeCompPath || "index.html";
|
|
76960
77013
|
try {
|
|
76961
|
-
const response = await
|
|
77014
|
+
const response = await studioApiFetch(
|
|
76962
77015
|
buildProjectApiPath(pid, `/files/${encodeURIComponent(targetPath)}`)
|
|
76963
77016
|
);
|
|
76964
77017
|
if (!response.ok) return;
|
|
@@ -78014,7 +78067,7 @@ async function assignGsapTargetAutoIdIfNeeded({
|
|
|
78014
78067
|
showToast?.("Couldn't assign element id because the patch contains invalid values", "error");
|
|
78015
78068
|
return false;
|
|
78016
78069
|
}
|
|
78017
|
-
const res = await
|
|
78070
|
+
const res = await studioApiFetch(
|
|
78018
78071
|
`/api/projects/${encodeURIComponent(projectId)}/file-mutations/patch-element/${encodeURIComponent(targetPath)}`,
|
|
78019
78072
|
{
|
|
78020
78073
|
method: "POST",
|
|
@@ -78080,7 +78133,7 @@ function isAtomicElementPatchFile(value) {
|
|
|
78080
78133
|
async function patchElementBatches(projectId, batches) {
|
|
78081
78134
|
const body = JSON.stringify({ batches });
|
|
78082
78135
|
try {
|
|
78083
|
-
const response = await
|
|
78136
|
+
const response = await studioApiFetch(
|
|
78084
78137
|
`/api/projects/${encodeURIComponent(projectId)}/file-mutations/patch-element-batches`,
|
|
78085
78138
|
{
|
|
78086
78139
|
method: "POST",
|
|
@@ -78120,7 +78173,7 @@ function batchesAreInlineStyleOnly(batches) {
|
|
|
78120
78173
|
);
|
|
78121
78174
|
}
|
|
78122
78175
|
async function postPatchElement(projectId, targetPath, body, showToast) {
|
|
78123
|
-
const response = await
|
|
78176
|
+
const response = await studioApiFetch(
|
|
78124
78177
|
buildProjectApiPath(
|
|
78125
78178
|
projectId,
|
|
78126
78179
|
`/file-mutations/patch-element/${encodeURIComponent(targetPath)}`
|
|
@@ -78418,7 +78471,13 @@ var IMAGE_BACKGROUND_FIT = [
|
|
|
78418
78471
|
["background-repeat", "no-repeat"],
|
|
78419
78472
|
["background-size", "contain"]
|
|
78420
78473
|
];
|
|
78421
|
-
async function commitDomStyles(context, selection,
|
|
78474
|
+
async function commitDomStyles(context, selection, stylesInEitherCase) {
|
|
78475
|
+
const styles = Object.fromEntries(
|
|
78476
|
+
Object.entries(stylesInEitherCase).map(([property, value]) => [
|
|
78477
|
+
cssPropertyName(property),
|
|
78478
|
+
value
|
|
78479
|
+
])
|
|
78480
|
+
);
|
|
78422
78481
|
const entries = Object.entries(styles);
|
|
78423
78482
|
if (entries.length === 0) return domEditCommitDeclined("no-selection");
|
|
78424
78483
|
if (entries.some(([property]) => isManualGeometryStyleProperty(property)))
|
|
@@ -78855,7 +78914,7 @@ function useDomEditTextCommits({
|
|
|
78855
78914
|
persistDomEditOperations
|
|
78856
78915
|
});
|
|
78857
78916
|
const handleDomStyleCommitForSelection = useCallback130(
|
|
78858
|
-
(selection,
|
|
78917
|
+
(selection, propertyOrStylesAsOnePatch, value = "") => commitDomStyles(
|
|
78859
78918
|
{
|
|
78860
78919
|
activeCompPath,
|
|
78861
78920
|
previewIframeRef,
|
|
@@ -78866,7 +78925,7 @@ function useDomEditTextCommits({
|
|
|
78866
78925
|
resync: refreshDomEditSelectionFromPreview
|
|
78867
78926
|
},
|
|
78868
78927
|
selection,
|
|
78869
|
-
{ [
|
|
78928
|
+
typeof propertyOrStylesAsOnePatch === "string" ? { [propertyOrStylesAsOnePatch]: value } : propertyOrStylesAsOnePatch
|
|
78870
78929
|
),
|
|
78871
78930
|
[
|
|
78872
78931
|
activeCompPath,
|
|
@@ -78953,24 +79012,24 @@ function useDomEditTextCommits({
|
|
|
78953
79012
|
if (element.isConnected && element.innerHTML === html2) element.innerHTML = previousHtml;
|
|
78954
79013
|
};
|
|
78955
79014
|
if (latestReadOnlyPreviewRef.current) return putBack();
|
|
78956
|
-
const
|
|
79015
|
+
const refuse2 = (reason) => {
|
|
78957
79016
|
console.error("[Studio] text edit not saved:", reason, element);
|
|
78958
79017
|
showToast(`Couldn't save the text edit: ${reason}`, "error");
|
|
78959
79018
|
putBack();
|
|
78960
79019
|
};
|
|
78961
79020
|
const doc = previewIframeRef.current?.contentDocument;
|
|
78962
79021
|
if (!doc || !element.isConnected || element.ownerDocument !== doc) {
|
|
78963
|
-
return
|
|
79022
|
+
return refuse2("the text's element is gone from the preview");
|
|
78964
79023
|
}
|
|
78965
79024
|
const selection = await buildDomSelectionFromTarget(element, {
|
|
78966
79025
|
exactTarget: true,
|
|
78967
79026
|
skipSourceProbe: true
|
|
78968
79027
|
});
|
|
78969
79028
|
if (selection?.element !== element) {
|
|
78970
|
-
return
|
|
79029
|
+
return refuse2("this text was not found in the composition's source");
|
|
78971
79030
|
}
|
|
78972
79031
|
if (!canCommitInlineTextSelection(selection, element)) {
|
|
78973
|
-
return
|
|
79032
|
+
return refuse2("this text can't be edited in place");
|
|
78974
79033
|
}
|
|
78975
79034
|
const isLatestTextCommit = bumpDomEditCommitMapVersion(
|
|
78976
79035
|
domTextCommitVersionRef.current,
|
|
@@ -79229,18 +79288,18 @@ var ELEMENT_OFFSET_REFUSED = {
|
|
|
79229
79288
|
anchored: "This layer is anchored from its right or bottom edge. Move it in the Code tab.",
|
|
79230
79289
|
percent: "This layer's position is set in percent. Move it in the Code tab."
|
|
79231
79290
|
};
|
|
79232
|
-
function
|
|
79291
|
+
function gsapOf2(el) {
|
|
79233
79292
|
return el.ownerDocument.defaultView?.gsap;
|
|
79234
79293
|
}
|
|
79235
79294
|
function settleGsapDraftAtGestureStart(el) {
|
|
79236
|
-
const gsap3 =
|
|
79295
|
+
const gsap3 = gsapOf2(el);
|
|
79237
79296
|
const x = Number.parseFloat(el.getAttribute("data-hf-drag-gsap-base-x") ?? "");
|
|
79238
79297
|
const y = Number.parseFloat(el.getAttribute("data-hf-drag-gsap-base-y") ?? "");
|
|
79239
79298
|
if (gsap3 && Number.isFinite(x) && Number.isFinite(y)) gsap3.set(el, { x, y });
|
|
79240
79299
|
}
|
|
79241
79300
|
function refuseGsapTakeover(el, showToast) {
|
|
79242
79301
|
if (!gsapHoldsTranslate(el)) return;
|
|
79243
|
-
|
|
79302
|
+
gsapOf2(el)?.set(el, { x: 0, y: 0, xPercent: 0, yPercent: 0 });
|
|
79244
79303
|
showToast(GSAP_TOOK_OVER, "error");
|
|
79245
79304
|
throw markStudioSaveErrorAlreadyToasted(new Error(GSAP_TOOK_OVER));
|
|
79246
79305
|
}
|
|
@@ -79299,7 +79358,11 @@ function savePlainRotation({ commitPositionPatchToHtml, readOnlyPreview }, selec
|
|
|
79299
79358
|
const before = captureStudioRotation(element);
|
|
79300
79359
|
const patches = element.hasAttribute(STUDIO_ROTATION_ATTR) ? buildClearRotationPatches(element) : [];
|
|
79301
79360
|
if (patches.length) clearStudioRotation(element);
|
|
79302
|
-
const drawn = applyCssRotation(
|
|
79361
|
+
const drawn = applyCssRotation(
|
|
79362
|
+
element,
|
|
79363
|
+
next.angle,
|
|
79364
|
+
patches.length ? void 0 : next.plain ?? void 0
|
|
79365
|
+
);
|
|
79303
79366
|
patches.push(...drawn);
|
|
79304
79367
|
const turn = drawn.at(-1);
|
|
79305
79368
|
return commitPositionPatchToHtml(selection, patches, {
|
|
@@ -79331,7 +79394,7 @@ function useDomGeometryCommits({
|
|
|
79331
79394
|
[commitPositionPatchToHtml, readOnlyPreview, showToast]
|
|
79332
79395
|
);
|
|
79333
79396
|
const handleDomBoxSizeCommit = useCallback131(
|
|
79334
|
-
(selection, next, offset,
|
|
79397
|
+
(selection, next, offset, restore2, undoKey) => {
|
|
79335
79398
|
if (readOnlyPreview) return Promise.resolve(void 0);
|
|
79336
79399
|
const element = selection.element;
|
|
79337
79400
|
const beforeSize = captureStudioBoxSize(element);
|
|
@@ -79349,7 +79412,7 @@ function useDomGeometryCommits({
|
|
|
79349
79412
|
restoreStudioBoxSize(element, beforeSize);
|
|
79350
79413
|
if (offset) restoreStudioPathOffset(element, beforeOffset);
|
|
79351
79414
|
crop?.revert();
|
|
79352
|
-
|
|
79415
|
+
restore2?.();
|
|
79353
79416
|
throw error;
|
|
79354
79417
|
});
|
|
79355
79418
|
},
|
|
@@ -79672,7 +79735,7 @@ async function commitStructuralMutation(pid, targetPath, route, body, label, dep
|
|
|
79672
79735
|
writeFile: deps.writeProjectFile,
|
|
79673
79736
|
recordEdit: deps.editHistory.recordEdit,
|
|
79674
79737
|
rewrite: async (originalContent) => {
|
|
79675
|
-
const mutateResponse = await
|
|
79738
|
+
const mutateResponse = await studioApiFetch(
|
|
79676
79739
|
buildProjectApiPath(pid, `/file-mutations/${route}/${encodeURIComponent(targetPath)}`),
|
|
79677
79740
|
{
|
|
79678
79741
|
method: "POST",
|
|
@@ -79749,7 +79812,7 @@ function useGroupCommits(params) {
|
|
|
79749
79812
|
}
|
|
79750
79813
|
|
|
79751
79814
|
// src/hooks/useGsapScriptCommits.ts
|
|
79752
|
-
import { useCallback as useCallback139, useMemo as
|
|
79815
|
+
import { useCallback as useCallback139, useMemo as useMemo73, useRef as useRef148 } from "react";
|
|
79753
79816
|
import { findUnsafeMutationValues } from "@hyperframes/core/studio-api/finite-mutation";
|
|
79754
79817
|
|
|
79755
79818
|
// src/hooks/gsapRuntimePatch.ts
|
|
@@ -79943,6 +80006,11 @@ function createKeyedSerializer() {
|
|
|
79943
80006
|
};
|
|
79944
80007
|
}
|
|
79945
80008
|
|
|
80009
|
+
// src/hooks/gsapScriptCommitTypes.ts
|
|
80010
|
+
function gsapWriteFile(selection, activeCompPath) {
|
|
80011
|
+
return selection.sourceFile || activeCompPath || "index.html";
|
|
80012
|
+
}
|
|
80013
|
+
|
|
79946
80014
|
// src/hooks/useGsapAnimationOps.ts
|
|
79947
80015
|
import { useCallback as useCallback134 } from "react";
|
|
79948
80016
|
function useGsapAnimationOps({
|
|
@@ -80862,7 +80930,7 @@ function useSafeGsapCommitMutation(commitMutation, trackGsapSaveFailure, showToa
|
|
|
80862
80930
|
|
|
80863
80931
|
// src/hooks/useGsapScriptCommits.ts
|
|
80864
80932
|
async function mutateGsapScript(projectId, sourceFile, mutation) {
|
|
80865
|
-
const res = await
|
|
80933
|
+
const res = await studioApiFetch(
|
|
80866
80934
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutations/${encodeURIComponent(sourceFile)}`,
|
|
80867
80935
|
{
|
|
80868
80936
|
method: "POST",
|
|
@@ -80876,7 +80944,7 @@ async function mutateGsapScript(projectId, sourceFile, mutation) {
|
|
|
80876
80944
|
return result;
|
|
80877
80945
|
}
|
|
80878
80946
|
async function mutateGsapScriptBatch(projectId, sourceFile, mutations) {
|
|
80879
|
-
const res = await
|
|
80947
|
+
const res = await studioApiFetch(
|
|
80880
80948
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutations-batch/${encodeURIComponent(sourceFile)}`,
|
|
80881
80949
|
{
|
|
80882
80950
|
method: "POST",
|
|
@@ -80981,7 +81049,8 @@ function softReloadOrEscalate(iframe, scriptText, reloadPreview, origin2, author
|
|
|
80981
81049
|
}
|
|
80982
81050
|
function applyPreviewSync(iframe, result, options, reloadPreview, nestedFiles) {
|
|
80983
81051
|
const patches = instantPatchesFor(options);
|
|
80984
|
-
|
|
81052
|
+
const writtenWithoutPatch = patches.length === 0;
|
|
81053
|
+
let needsFallback = options.previewFallbackLatch?.pending === true || writtenWithoutPatch;
|
|
80985
81054
|
if (patches.length > 0) {
|
|
80986
81055
|
const deferSeek = options.deferPreviewSync === true;
|
|
80987
81056
|
const missed = patches.find(
|
|
@@ -81101,11 +81170,14 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
81101
81170
|
if (!result) return;
|
|
81102
81171
|
trackKeyframeCommit(mutations, result, options, calls.map((call) => call.options));
|
|
81103
81172
|
options.onResult?.(result);
|
|
81104
|
-
const instantPatches = calls.
|
|
81173
|
+
const instantPatches = calls.flatMap(
|
|
81174
|
+
({ options: callOptions }) => callOptions.instantPatch ? [callOptions.instantPatch] : []
|
|
81175
|
+
);
|
|
81105
81176
|
const { instantPatch: _instantPatch, ...batchOptions } = options;
|
|
81106
|
-
|
|
81177
|
+
const allPatched = instantPatches.length === calls.length;
|
|
81178
|
+
await finalizeSuccessfulMutation(pid, compositionPath, last.selection, last.mutation, targetPath, result, allPatched ? { ...batchOptions, instantPatches } : batchOptions);
|
|
81107
81179
|
}, [showToast, finalizeSuccessfulMutation]);
|
|
81108
|
-
const commitMutation =
|
|
81180
|
+
const commitMutation = useMemo73(() => {
|
|
81109
81181
|
const serializeFile = (file, task) => {
|
|
81110
81182
|
if (writeProjectFile) {
|
|
81111
81183
|
return serializeStudioFileMutation(writeProjectFile, file, task);
|
|
@@ -81122,7 +81194,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
81122
81194
|
};
|
|
81123
81195
|
const commit = trackedStudioEdit((selection, mutation, options) => {
|
|
81124
81196
|
if (!activeProjectId) return Promise.resolve();
|
|
81125
|
-
const file = selection
|
|
81197
|
+
const file = gsapWriteFile(selection, activeCompPath);
|
|
81126
81198
|
return serializeCommit(
|
|
81127
81199
|
file,
|
|
81128
81200
|
options.serializeKey,
|
|
@@ -81131,7 +81203,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
81131
81203
|
});
|
|
81132
81204
|
commit.batch = trackedStudioEdit((calls, options) => {
|
|
81133
81205
|
if (!activeProjectId) return Promise.resolve();
|
|
81134
|
-
const file = calls[0]
|
|
81206
|
+
const file = calls[0] ? gsapWriteFile(calls[0].selection, activeCompPath) : "index.html";
|
|
81135
81207
|
return serializeCommit(
|
|
81136
81208
|
file,
|
|
81137
81209
|
options.serializeKey,
|
|
@@ -81170,7 +81242,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
81170
81242
|
},
|
|
81171
81243
|
[activeProjectId]
|
|
81172
81244
|
);
|
|
81173
|
-
const sdkDeps =
|
|
81245
|
+
const sdkDeps = useMemo73(
|
|
81174
81246
|
() => writeProjectFile ? {
|
|
81175
81247
|
editHistory: { recordEdit: editHistory.recordEdit },
|
|
81176
81248
|
writeProjectFile,
|
|
@@ -81363,13 +81435,13 @@ function useGsapAnimationFetchFallback(projectId) {
|
|
|
81363
81435
|
|
|
81364
81436
|
// src/hooks/useGsapInteractionFailureTelemetry.ts
|
|
81365
81437
|
import { useCallback as useCallback141 } from "react";
|
|
81366
|
-
function failureToast(error) {
|
|
81367
|
-
if (wasAlreadyToasted(error)) return null;
|
|
81438
|
+
function failureToast(error, toast) {
|
|
81439
|
+
if (!toast || wasAlreadyToasted(error)) return null;
|
|
81368
81440
|
return isGsapEditBlockedError(error) ? error.message : "Failed to save animated edit.";
|
|
81369
81441
|
}
|
|
81370
81442
|
function useGsapInteractionFailureTelemetry(activeCompPath, showToast) {
|
|
81371
81443
|
return useCallback141(
|
|
81372
|
-
(error, selection, mutationType, label) => {
|
|
81444
|
+
(error, selection, mutationType, label, toast) => {
|
|
81373
81445
|
const report = isGsapEditBlockedError(error) ? trackStudioEditBlocked : trackStudioSaveFailure;
|
|
81374
81446
|
report({
|
|
81375
81447
|
source: "gsap_commit",
|
|
@@ -81381,7 +81453,7 @@ function useGsapInteractionFailureTelemetry(activeCompPath, showToast) {
|
|
|
81381
81453
|
targetSelector: selection?.selector,
|
|
81382
81454
|
targetSourceFile: selection?.sourceFile
|
|
81383
81455
|
});
|
|
81384
|
-
const message = failureToast(error);
|
|
81456
|
+
const message = failureToast(error, toast !== false);
|
|
81385
81457
|
if (message) showToast(message, "error");
|
|
81386
81458
|
},
|
|
81387
81459
|
[activeCompPath, showToast]
|
|
@@ -81835,17 +81907,29 @@ function useDomEditWiring(params) {
|
|
|
81835
81907
|
|
|
81836
81908
|
// src/hooks/useGsapAwareGroupMove.ts
|
|
81837
81909
|
import { useCallback as useCallback144 } from "react";
|
|
81838
|
-
|
|
81839
|
-
|
|
81910
|
+
|
|
81911
|
+
// src/hooks/gsapGroupPreflight.ts
|
|
81912
|
+
function firstPreflightFailure(results, updates, savedOnElement, activeCompPath) {
|
|
81840
81913
|
for (const [index, result] of results.entries()) {
|
|
81841
81914
|
if (result.status !== "rejected") continue;
|
|
81842
81915
|
const selection = updates[index]?.selection;
|
|
81843
81916
|
if (selection) return { error: result.reason, selection };
|
|
81844
81917
|
}
|
|
81845
|
-
return
|
|
81918
|
+
return secondFile(updates, savedOnElement, activeCompPath);
|
|
81919
|
+
}
|
|
81920
|
+
function secondFile(updates, savedOnElement, activeCompPath) {
|
|
81921
|
+
const scripted = updates.filter(({ selection }) => !savedOnElement.has(selection));
|
|
81922
|
+
const fileOf = (s) => gsapWriteFile(s, activeCompPath);
|
|
81923
|
+
const file = scripted[0] && fileOf(scripted[0].selection);
|
|
81924
|
+
const other = scripted.find(({ selection }) => fileOf(selection) !== file);
|
|
81925
|
+
return other ? { error: new GsapEditBlockedError("mixed-files"), selection: other.selection } : null;
|
|
81846
81926
|
}
|
|
81927
|
+
|
|
81928
|
+
// src/hooks/useGsapAwareGroupMove.ts
|
|
81929
|
+
var groupDragCommitCounter = 0;
|
|
81847
81930
|
function useGsapAwareGroupMove({
|
|
81848
81931
|
gsapCommitMutation,
|
|
81932
|
+
activeCompPath,
|
|
81849
81933
|
previewIframeRef,
|
|
81850
81934
|
makeFetchFallback,
|
|
81851
81935
|
trackGsapInteractionFailure,
|
|
@@ -81853,15 +81937,16 @@ function useGsapAwareGroupMove({
|
|
|
81853
81937
|
showToast
|
|
81854
81938
|
}) {
|
|
81855
81939
|
const handleGsapAwareGroupPathOffsetCommit = useCallback144(
|
|
81856
|
-
async (updates) => {
|
|
81940
|
+
async (updates, options = {}) => {
|
|
81857
81941
|
const writes = observeGsapGesture(gsapCommitMutation);
|
|
81858
81942
|
const writer = writes.commit;
|
|
81859
81943
|
if (!writer) return { ok: true, changed: false };
|
|
81944
|
+
const toastRefusal = options.refusalToast !== false;
|
|
81860
81945
|
const coalesceKey = `group-drag:${++groupDragCommitCounter}`;
|
|
81861
81946
|
let renderOnCommit = false;
|
|
81862
81947
|
const previewFallbackLatch = { pending: false };
|
|
81863
|
-
const withGroupOptions = (
|
|
81864
|
-
...
|
|
81948
|
+
const withGroupOptions = (options2) => ({
|
|
81949
|
+
...options2,
|
|
81865
81950
|
coalesceKey,
|
|
81866
81951
|
coalesceMs: Number.POSITIVE_INFINITY,
|
|
81867
81952
|
deferPreviewSync: !renderOnCommit,
|
|
@@ -81882,23 +81967,24 @@ function useGsapAwareGroupMove({
|
|
|
81882
81967
|
label: "Move animated layer (group)"
|
|
81883
81968
|
});
|
|
81884
81969
|
};
|
|
81885
|
-
const coalescedCommit = (selection, mutation,
|
|
81886
|
-
queued.push({ selection, mutation, options: withGroupOptions(
|
|
81970
|
+
const coalescedCommit = (selection, mutation, options2) => {
|
|
81971
|
+
queued.push({ selection, mutation, options: withGroupOptions(options2) });
|
|
81887
81972
|
return Promise.resolve();
|
|
81888
81973
|
};
|
|
81889
81974
|
const preflightAnimations = /* @__PURE__ */ new Map();
|
|
81890
81975
|
const offsetMembers = /* @__PURE__ */ new Map();
|
|
81891
81976
|
const preflightResults = await Promise.allSettled(
|
|
81892
|
-
updates.map(async ({ selection, plainTranslate }) => {
|
|
81893
|
-
if (plainTranslate ??
|
|
81894
|
-
refuseGsapTakeover(selection.element, showToast)
|
|
81977
|
+
updates.map(async ({ selection, next, plainTranslate }) => {
|
|
81978
|
+
if (plainTranslate ?? editsPlainCss(selection.element, "move")) {
|
|
81979
|
+
refuseGsapTakeover(selection.element, toastRefusal ? showToast : () => {
|
|
81980
|
+
});
|
|
81895
81981
|
return void offsetMembers.set(selection, true);
|
|
81896
81982
|
}
|
|
81897
81983
|
const animations = await makeFetchFallback(selection, { failOnFetchError: true })();
|
|
81898
81984
|
preflightAnimations.set(selection, animations);
|
|
81899
81985
|
const outcome = await tryGsapDragIntercept(
|
|
81900
81986
|
selection,
|
|
81901
|
-
|
|
81987
|
+
next,
|
|
81902
81988
|
animations,
|
|
81903
81989
|
previewIframeRef.current,
|
|
81904
81990
|
coalescedCommit,
|
|
@@ -81909,13 +81995,19 @@ function useGsapAwareGroupMove({
|
|
|
81909
81995
|
assertGsapEditPersisted(outcome);
|
|
81910
81996
|
})
|
|
81911
81997
|
);
|
|
81912
|
-
const preflightFailure = firstPreflightFailure(
|
|
81998
|
+
const preflightFailure = firstPreflightFailure(
|
|
81999
|
+
preflightResults,
|
|
82000
|
+
updates,
|
|
82001
|
+
offsetMembers,
|
|
82002
|
+
activeCompPath
|
|
82003
|
+
);
|
|
81913
82004
|
if (preflightFailure) {
|
|
81914
82005
|
trackGsapInteractionFailure(
|
|
81915
82006
|
preflightFailure.error,
|
|
81916
82007
|
preflightFailure.selection,
|
|
81917
82008
|
"drag",
|
|
81918
|
-
"Move animated layer (group)"
|
|
82009
|
+
"Move animated layer (group)",
|
|
82010
|
+
toastRefusal
|
|
81919
82011
|
);
|
|
81920
82012
|
throw preflightFailure.error;
|
|
81921
82013
|
}
|
|
@@ -81967,6 +82059,7 @@ function useGsapAwareGroupMove({
|
|
|
81967
82059
|
},
|
|
81968
82060
|
[
|
|
81969
82061
|
gsapCommitMutation,
|
|
82062
|
+
activeCompPath,
|
|
81970
82063
|
previewIframeRef,
|
|
81971
82064
|
makeFetchFallback,
|
|
81972
82065
|
trackGsapInteractionFailure,
|
|
@@ -81980,23 +82073,6 @@ function useGsapAwareGroupMove({
|
|
|
81980
82073
|
// src/hooks/useGsapAwareEditing.ts
|
|
81981
82074
|
import { useCallback as useCallback146 } from "react";
|
|
81982
82075
|
|
|
81983
|
-
// src/utils/elementGsap.ts
|
|
81984
|
-
function gsapOf2(element) {
|
|
81985
|
-
return element.ownerDocument.defaultView?.gsap;
|
|
81986
|
-
}
|
|
81987
|
-
function setElementGsapPosition(element, x, y) {
|
|
81988
|
-
const gsap3 = gsapOf2(element);
|
|
81989
|
-
if (!gsap3?.set) return false;
|
|
81990
|
-
gsap3.set(element, { x, y });
|
|
81991
|
-
return true;
|
|
81992
|
-
}
|
|
81993
|
-
function setElementGsapScale(element, x, y) {
|
|
81994
|
-
const gsap3 = gsapOf2(element);
|
|
81995
|
-
if (!gsap3?.set) return false;
|
|
81996
|
-
gsap3.set(element, { scaleX: x, scaleY: y });
|
|
81997
|
-
return true;
|
|
81998
|
-
}
|
|
81999
|
-
|
|
82000
82076
|
// src/hooks/gsapResizePreflight.ts
|
|
82001
82077
|
function resizeRoute(animations, fetchedAnimations) {
|
|
82002
82078
|
const allKnownAnimations = [...animations, ...fetchedAnimations];
|
|
@@ -82028,16 +82104,8 @@ function preflightGsapResizeIntercept(selection, animations, iframe, fetchedAnim
|
|
|
82028
82104
|
}
|
|
82029
82105
|
|
|
82030
82106
|
// src/hooks/gsapResizeIntercept.ts
|
|
82031
|
-
var IDENTITY_ONE_PROPS = /* @__PURE__ */ new Set(["opacity", "autoAlpha", "scale", "scaleX", "scaleY"]);
|
|
82032
82107
|
var SIZE_PROPS = /* @__PURE__ */ new Set(["width", "height"]);
|
|
82033
|
-
|
|
82034
|
-
const id = {};
|
|
82035
|
-
for (const [k, v] of Object.entries(source)) {
|
|
82036
|
-
if (typeof v === "number") id[k] = IDENTITY_ONE_PROPS.has(k) ? 1 : 0;
|
|
82037
|
-
else id[k] = v;
|
|
82038
|
-
}
|
|
82039
|
-
return id;
|
|
82040
|
-
}
|
|
82108
|
+
var POSITION_XY = /* @__PURE__ */ new Set(["x", "y"]);
|
|
82041
82109
|
function originalBoxSize(el, measuredAttr, inlineProperty) {
|
|
82042
82110
|
const measured = Number.parseFloat(el?.getAttribute(measuredAttr) ?? "");
|
|
82043
82111
|
if (Number.isFinite(measured) && measured > 0) return measured;
|
|
@@ -82046,6 +82114,36 @@ function originalBoxSize(el, measuredAttr, inlineProperty) {
|
|
|
82046
82114
|
);
|
|
82047
82115
|
return Number.isFinite(inline) && inline > 0 ? inline : null;
|
|
82048
82116
|
}
|
|
82117
|
+
function preGestureBoxSize(el) {
|
|
82118
|
+
const width = originalBoxSize(el, STUDIO_ORIGINAL_BOX_WIDTH_ATTR, "width");
|
|
82119
|
+
const height = originalBoxSize(el, STUDIO_ORIGINAL_BOX_HEIGHT_ATTR, "height");
|
|
82120
|
+
return { ...width != null && { width }, ...height != null && { height } };
|
|
82121
|
+
}
|
|
82122
|
+
async function commitSizeAtPlayhead(selection, anim, size, iframe, dragOffset, callbacks) {
|
|
82123
|
+
const selector = selectorFromSelection(selection);
|
|
82124
|
+
const moves = !!dragOffset && (dragOffset.x !== 0 || dragOffset.y !== 0) && !!selector;
|
|
82125
|
+
const anchor2 = moves && animationWritesAnyProperty(anim, POSITION_XY) ? computeDraggedGsapPosition(
|
|
82126
|
+
selection.element,
|
|
82127
|
+
dragOffset,
|
|
82128
|
+
readGsapPositionFromIframe(iframe, selector) ?? { x: 0, y: 0 }
|
|
82129
|
+
) : null;
|
|
82130
|
+
const written = await commitValueAtPlayhead(
|
|
82131
|
+
selection,
|
|
82132
|
+
anim,
|
|
82133
|
+
anchor2 ? { ...size, x: anchor2.newX, y: anchor2.newY } : size,
|
|
82134
|
+
iframe,
|
|
82135
|
+
callbacks,
|
|
82136
|
+
{
|
|
82137
|
+
label: "Resize",
|
|
82138
|
+
backfill: {
|
|
82139
|
+
...preGestureBoxSize(selection.element),
|
|
82140
|
+
...anchor2 && { x: anchor2.baseGsapX, y: anchor2.baseGsapY }
|
|
82141
|
+
},
|
|
82142
|
+
...anchor2 && { beforeReload: () => restoreDragOffset(selection.element) }
|
|
82143
|
+
}
|
|
82144
|
+
);
|
|
82145
|
+
return written.status === "persisted" && anchor2 ? { ...written, ownsDragOffset: true } : written;
|
|
82146
|
+
}
|
|
82049
82147
|
function tweenUsesScaleLonghands(anim) {
|
|
82050
82148
|
const isLonghand = (name) => name === "scaleX" || name === "scaleY";
|
|
82051
82149
|
const inKeyframes = (anim?.keyframes?.keyframes ?? []).some(
|
|
@@ -82053,7 +82151,7 @@ function tweenUsesScaleLonghands(anim) {
|
|
|
82053
82151
|
);
|
|
82054
82152
|
return inKeyframes || Object.keys(anim?.properties ?? {}).some(isLonghand);
|
|
82055
82153
|
}
|
|
82056
|
-
async function tryGsapResizeIntercept(selection, size, animations, iframe, commitMutation, fetchFallbackAnimations) {
|
|
82154
|
+
async function tryGsapResizeIntercept(selection, size, animations, iframe, commitMutation, fetchFallbackAnimations, dragOffset) {
|
|
82057
82155
|
const fetchedAnimations = fetchFallbackAnimations ? await fetchFallbackAnimations() : [];
|
|
82058
82156
|
const outcome = preflightGsapResizeIntercept(selection, animations, iframe, fetchedAnimations);
|
|
82059
82157
|
if (outcome.status === "blocked") return outcome;
|
|
@@ -82069,7 +82167,7 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
82069
82167
|
commitMutation,
|
|
82070
82168
|
postSplitFetch
|
|
82071
82169
|
);
|
|
82072
|
-
|
|
82170
|
+
const anim = resolved2?.anim && animationWritesAnyProperty(resolved2.anim, resizeProperties) ? resolved2.anim : null;
|
|
82073
82171
|
logResize("intercept-enter", {
|
|
82074
82172
|
hasScaleGroup: resizeGroup === "scale",
|
|
82075
82173
|
resizeGroup,
|
|
@@ -82093,15 +82191,11 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
82093
82191
|
);
|
|
82094
82192
|
if (animatedTween) {
|
|
82095
82193
|
logResize("intercept-route", { route: "keyframed-size", tweenId: animatedTween.id });
|
|
82096
|
-
const
|
|
82097
|
-
|
|
82098
|
-
|
|
82099
|
-
|
|
82100
|
-
|
|
82101
|
-
animatedTween,
|
|
82102
|
-
{ commitMutation, fetchAnimations: fetchFallbackAnimations }
|
|
82103
|
-
);
|
|
82104
|
-
if (handled) return { status: "persisted" };
|
|
82194
|
+
const sized = { width: roundToLayoutPx(size.width), height: roundToLayoutPx(size.height) };
|
|
82195
|
+
return commitSizeAtPlayhead(selection, animatedTween, sized, iframe, dragOffset, {
|
|
82196
|
+
commitMutation,
|
|
82197
|
+
fetchAnimations: fetchFallbackAnimations
|
|
82198
|
+
});
|
|
82105
82199
|
}
|
|
82106
82200
|
}
|
|
82107
82201
|
logResize("intercept-route", { route: "static-size-set", hadSizeSet: !!sizeSet, resizeGroup });
|
|
@@ -82117,10 +82211,9 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
82117
82211
|
}
|
|
82118
82212
|
const { activeKeyframePct, setActiveKeyframePct } = usePlayerStore.getState();
|
|
82119
82213
|
const pct = activeKeyframePct ?? computeCurrentPercentage(selection, anim);
|
|
82120
|
-
if (activeKeyframePct != null) setActiveKeyframePct(null);
|
|
82121
82214
|
const selector = selectorFromSelection(selection);
|
|
82122
|
-
const runtimeProps = selector ? readAllAnimatedProperties(iframe, selector, anim, resizeGroup) : {};
|
|
82123
82215
|
let resizeProps;
|
|
82216
|
+
let resizeBackfill;
|
|
82124
82217
|
let scaleDraftEl = null;
|
|
82125
82218
|
let scaleDraftDropPoint = null;
|
|
82126
82219
|
let committedScale = null;
|
|
@@ -82140,6 +82233,7 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
82140
82233
|
nonUniformScale = uniformDrift > 0.5;
|
|
82141
82234
|
useScaleLonghands = nonUniformScale || tweenUsesScaleLonghands(anim);
|
|
82142
82235
|
resizeProps = useScaleLonghands ? { scaleX: newScaleX, scaleY: newScaleY } : { scale: newScaleX };
|
|
82236
|
+
resizeBackfill = { scaleX: liveScaleX, scaleY: liveScaleY };
|
|
82143
82237
|
logResize("intercept-route", {
|
|
82144
82238
|
route: "scale-tween",
|
|
82145
82239
|
cssW,
|
|
@@ -82161,6 +82255,7 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
82161
82255
|
width: roundToLayoutPx(size.width),
|
|
82162
82256
|
height: roundToLayoutPx(size.height)
|
|
82163
82257
|
};
|
|
82258
|
+
resizeBackfill = preGestureBoxSize(selection.element);
|
|
82164
82259
|
}
|
|
82165
82260
|
const finalizeScaleResizeCommit = async () => {
|
|
82166
82261
|
if (!scaleDraftEl) return false;
|
|
@@ -82206,10 +82301,12 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
82206
82301
|
);
|
|
82207
82302
|
if (positionTween) {
|
|
82208
82303
|
logResize("scale-finalize", { route: "position-keyframe", tweenId: positionTween.id });
|
|
82209
|
-
|
|
82210
|
-
|
|
82211
|
-
|
|
82212
|
-
|
|
82304
|
+
assertGsapEditPersisted(
|
|
82305
|
+
await commitGsapPositionFromDrag(selection, positionTween, delta, base2, iframe, {
|
|
82306
|
+
commitMutation,
|
|
82307
|
+
fetchAnimations: fetchFallbackAnimations
|
|
82308
|
+
})
|
|
82309
|
+
);
|
|
82213
82310
|
return true;
|
|
82214
82311
|
}
|
|
82215
82312
|
const existingSet = findExistingPositionWrite(currentAnimations, selector, selection.element);
|
|
@@ -82232,102 +82329,14 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
82232
82329
|
);
|
|
82233
82330
|
return { status: "persisted", ownsDragOffset: await finalizeScaleResizeCommit() };
|
|
82234
82331
|
}
|
|
82235
|
-
const
|
|
82236
|
-
|
|
82237
|
-
|
|
82238
|
-
const
|
|
82239
|
-
|
|
82240
|
-
|
|
82241
|
-
|
|
82242
|
-
|
|
82243
|
-
} else if (!anim.keyframes) {
|
|
82244
|
-
const resolvedFromValues = selector ? readAllAnimatedProperties(iframe, selector, anim, resizeGroup) : void 0;
|
|
82245
|
-
await commitMutation(
|
|
82246
|
-
selection,
|
|
82247
|
-
{ type: "convert-to-keyframes", animationId: anim.id, resolvedFromValues },
|
|
82248
|
-
{ label: "Convert to keyframes for resize" }
|
|
82249
|
-
);
|
|
82250
|
-
if (fetchFallbackAnimations) {
|
|
82251
|
-
const fresh = await fetchFallbackAnimations();
|
|
82252
|
-
const refreshed = fresh.find(
|
|
82253
|
-
(a) => a.targetSelector === anim.targetSelector && a.keyframes
|
|
82254
|
-
);
|
|
82255
|
-
if (refreshed) anim = refreshed;
|
|
82256
|
-
}
|
|
82257
|
-
}
|
|
82258
|
-
}
|
|
82259
|
-
if ((outsideRange || useScaleLonghands) && ts !== null) {
|
|
82260
|
-
const kfs = anim.keyframes?.keyframes ?? (() => {
|
|
82261
|
-
const fromProps = anim.method === "from" || anim.method === "fromTo" ? { ...anim.properties } : synthesizeIdentityProps(anim.properties);
|
|
82262
|
-
const toProps = anim.method === "from" ? synthesizeIdentityProps(anim.properties) : { ...anim.properties };
|
|
82263
|
-
return [
|
|
82264
|
-
{ percentage: 0, properties: fromProps },
|
|
82265
|
-
{ percentage: 100, properties: toProps }
|
|
82266
|
-
];
|
|
82267
|
-
})();
|
|
82268
|
-
const newStart = Math.min(ct, ts);
|
|
82269
|
-
const newEnd = Math.max(ct, ts + td);
|
|
82270
|
-
const newDuration = Math.max(0.01, newEnd - newStart);
|
|
82271
|
-
const existingKfs = kfs;
|
|
82272
|
-
const remapped = [];
|
|
82273
|
-
for (const kf of existingKfs) {
|
|
82274
|
-
const absTime = ts + kf.percentage / 100 * td;
|
|
82275
|
-
const newPct = Math.round((absTime - newStart) / newDuration * 1e3) / 10;
|
|
82276
|
-
const props = { ...kf.properties };
|
|
82277
|
-
if (nonUniformScale && "scale" in props) {
|
|
82278
|
-
const uniform = props.scale;
|
|
82279
|
-
if (typeof uniform === "number") {
|
|
82280
|
-
props.scaleX = uniform;
|
|
82281
|
-
props.scaleY = uniform;
|
|
82282
|
-
}
|
|
82283
|
-
delete props.scale;
|
|
82284
|
-
}
|
|
82285
|
-
for (const k of Object.keys(resizeProps)) {
|
|
82286
|
-
if (k in props) continue;
|
|
82287
|
-
if (k === "width" || k === "height") continue;
|
|
82288
|
-
props[k] = IDENTITY_ONE_PROPS.has(k) ? 1 : 0;
|
|
82289
|
-
}
|
|
82290
|
-
remapped.push({ percentage: newPct, properties: props });
|
|
82291
|
-
}
|
|
82292
|
-
const targetPct = Math.round((ct - newStart) / newDuration * 1e3) / 10;
|
|
82293
|
-
const collidingKf = remapped.find((kf) => Math.abs(kf.percentage - targetPct) < 0.05);
|
|
82294
|
-
if (collidingKf) Object.assign(collidingKf.properties, resizeProps);
|
|
82295
|
-
else remapped.push({ percentage: targetPct, properties: resizeProps });
|
|
82296
|
-
remapped.sort((a, b) => a.percentage - b.percentage);
|
|
82297
|
-
await commitMutation(
|
|
82298
|
-
selection,
|
|
82299
|
-
{
|
|
82300
|
-
type: "replace-with-keyframes",
|
|
82301
|
-
animationId: anim.id,
|
|
82302
|
-
targetSelector: anim.targetSelector,
|
|
82303
|
-
position: roundTo3(newStart),
|
|
82304
|
-
duration: roundTo3(newDuration),
|
|
82305
|
-
keyframes: remapped
|
|
82306
|
-
},
|
|
82307
|
-
{
|
|
82308
|
-
label: outsideRange ? `Resize (extended to ${ct.toFixed(2)}s)` : `Resize (keyframe ${Math.round((ct - newStart) / newDuration * 1e3) / 10}%)`,
|
|
82309
|
-
softReload: true,
|
|
82310
|
-
...!collidingKf ? { keyframeAction: "add" } : {}
|
|
82311
|
-
}
|
|
82312
|
-
);
|
|
82313
|
-
return { status: "persisted", ownsDragOffset: await finalizeScaleResizeCommit() };
|
|
82314
|
-
}
|
|
82315
|
-
const backfillDefaults = {};
|
|
82316
|
-
for (const k of Object.keys(runtimeProps)) {
|
|
82317
|
-
if (SIZE_PROPS.has(k)) continue;
|
|
82318
|
-
backfillDefaults[k] = IDENTITY_ONE_PROPS.has(k) ? 1 : 0;
|
|
82319
|
-
}
|
|
82320
|
-
await commitMutation(
|
|
82321
|
-
selection,
|
|
82322
|
-
{
|
|
82323
|
-
type: "add-keyframe",
|
|
82324
|
-
animationId: anim.id,
|
|
82325
|
-
percentage: pct,
|
|
82326
|
-
properties: resizeProps,
|
|
82327
|
-
backfillDefaults
|
|
82328
|
-
},
|
|
82329
|
-
{ label: `Resize (keyframe ${pct}%)`, softReload: true }
|
|
82330
|
-
);
|
|
82332
|
+
const callbacks = { commitMutation, fetchAnimations: fetchFallbackAnimations };
|
|
82333
|
+
if (resizeGroup === "size")
|
|
82334
|
+
return commitSizeAtPlayhead(selection, anim, resizeProps, iframe, dragOffset, callbacks);
|
|
82335
|
+
const written = await commitValueAtPlayhead(selection, anim, resizeProps, iframe, callbacks, {
|
|
82336
|
+
label: "Resize",
|
|
82337
|
+
backfill: resizeBackfill
|
|
82338
|
+
});
|
|
82339
|
+
if (written.status !== "persisted") return written;
|
|
82331
82340
|
return { status: "persisted", ownsDragOffset: await finalizeScaleResizeCommit() };
|
|
82332
82341
|
}
|
|
82333
82342
|
|
|
@@ -82690,6 +82699,7 @@ function useGsapAwareEditing({
|
|
|
82690
82699
|
domEditSelection,
|
|
82691
82700
|
selectedGsapAnimations,
|
|
82692
82701
|
gsapCommitMutation,
|
|
82702
|
+
activeCompPath,
|
|
82693
82703
|
previewIframeRef,
|
|
82694
82704
|
showToast,
|
|
82695
82705
|
bumpGsapCache,
|
|
@@ -82714,7 +82724,7 @@ function useGsapAwareEditing({
|
|
|
82714
82724
|
const handleGsapAwarePathOffsetCommit = useCallback146(
|
|
82715
82725
|
async (selection, next, modifiers) => {
|
|
82716
82726
|
const writes = observeGsapGesture(gsapCommitMutation);
|
|
82717
|
-
if (modifiers?.plainTranslate ??
|
|
82727
|
+
if (modifiers?.plainTranslate ?? editsPlainCss(selection.element, "move")) {
|
|
82718
82728
|
const result = await stageElementPositionOffset(selection, next, true).save();
|
|
82719
82729
|
return writes.finish(result?.changed === true);
|
|
82720
82730
|
}
|
|
@@ -82753,6 +82763,7 @@ function useGsapAwareEditing({
|
|
|
82753
82763
|
);
|
|
82754
82764
|
const handleGsapAwareGroupPathOffsetCommit = useGsapAwareGroupMove({
|
|
82755
82765
|
gsapCommitMutation,
|
|
82766
|
+
activeCompPath,
|
|
82756
82767
|
previewIframeRef,
|
|
82757
82768
|
makeFetchFallback,
|
|
82758
82769
|
trackGsapInteractionFailure,
|
|
@@ -82760,10 +82771,10 @@ function useGsapAwareEditing({
|
|
|
82760
82771
|
showToast
|
|
82761
82772
|
});
|
|
82762
82773
|
const handleGsapAwareBoxSizeCommit = useCallback146(
|
|
82763
|
-
async (selection, next, offset,
|
|
82774
|
+
async (selection, next, offset, restore2 = () => void 0, route) => {
|
|
82764
82775
|
const writes = observeGsapGesture(gsapCommitMutation);
|
|
82765
|
-
if (
|
|
82766
|
-
const result = await handleDomBoxSizeCommit(selection, next, offset,
|
|
82776
|
+
if (route?.plainTranslate ?? editsPlainCss(selection.element, "resize")) {
|
|
82777
|
+
const result = await handleDomBoxSizeCommit(selection, next, offset, restore2);
|
|
82767
82778
|
return writes.finish(result?.changed === true);
|
|
82768
82779
|
}
|
|
82769
82780
|
let targetAnimations;
|
|
@@ -82771,7 +82782,7 @@ function useGsapAwareEditing({
|
|
|
82771
82782
|
const ownedAnimations = getGsapAnimationsForSelection(selection);
|
|
82772
82783
|
targetAnimations = Array.isArray(ownedAnimations) ? ownedAnimations : await ownedAnimations;
|
|
82773
82784
|
} catch (error) {
|
|
82774
|
-
|
|
82785
|
+
restore2();
|
|
82775
82786
|
trackGsapInteractionFailure(error, selection, "resize", "Resize animated layer");
|
|
82776
82787
|
throw error;
|
|
82777
82788
|
}
|
|
@@ -82804,8 +82815,13 @@ function useGsapAwareEditing({
|
|
|
82804
82815
|
makeFetchFallback(selection)
|
|
82805
82816
|
);
|
|
82806
82817
|
await saveMove(dragOutcome, async () => {
|
|
82807
|
-
const
|
|
82808
|
-
anchorMove = stageElementPositionOffset(
|
|
82818
|
+
const plainAfterSettle = editsPlainCss(selection.element, "move");
|
|
82819
|
+
anchorMove = stageElementPositionOffset(
|
|
82820
|
+
selection,
|
|
82821
|
+
offset,
|
|
82822
|
+
plainAfterSettle,
|
|
82823
|
+
coalesceKey
|
|
82824
|
+
);
|
|
82809
82825
|
});
|
|
82810
82826
|
}
|
|
82811
82827
|
};
|
|
@@ -82832,7 +82848,8 @@ function useGsapAwareEditing({
|
|
|
82832
82848
|
targetAnimations,
|
|
82833
82849
|
previewIframeRef.current,
|
|
82834
82850
|
commitMutation2,
|
|
82835
|
-
makeFetchFallback(selection)
|
|
82851
|
+
makeFetchFallback(selection),
|
|
82852
|
+
offset
|
|
82836
82853
|
);
|
|
82837
82854
|
assertGsapEditPersisted(outcome);
|
|
82838
82855
|
if (outcome.status === "element-size") {
|
|
@@ -82876,7 +82893,7 @@ function useGsapAwareEditing({
|
|
|
82876
82893
|
},
|
|
82877
82894
|
restore: () => {
|
|
82878
82895
|
anchorMove?.rollback();
|
|
82879
|
-
|
|
82896
|
+
restore2();
|
|
82880
82897
|
},
|
|
82881
82898
|
skipPixelAssert: hasLivePositionTween
|
|
82882
82899
|
});
|
|
@@ -82896,7 +82913,7 @@ function useGsapAwareEditing({
|
|
|
82896
82913
|
const handleGsapAwareRotationCommit = useCallback146(
|
|
82897
82914
|
async (selection, next) => {
|
|
82898
82915
|
const writes = observeGsapGesture(gsapCommitMutation);
|
|
82899
|
-
if (next.plain
|
|
82916
|
+
if (next.plain === void 0 ? editsPlainCss(selection.element, "rotate") : next.plain) {
|
|
82900
82917
|
const result = await handleDomRotationCommit(selection, next);
|
|
82901
82918
|
return writes.finish(result?.changed === true);
|
|
82902
82919
|
}
|
|
@@ -83089,7 +83106,7 @@ function reportSelectionPublishError(error) {
|
|
|
83089
83106
|
console.warn("[Studio] Failed to update agent selection context", error);
|
|
83090
83107
|
}
|
|
83091
83108
|
function putSelection(projectId, selection, signal) {
|
|
83092
|
-
return
|
|
83109
|
+
return studioApiFetch(`/api/projects/${encodeURIComponent(projectId)}/selection`, {
|
|
83093
83110
|
method: "PUT",
|
|
83094
83111
|
headers: { "Content-Type": "application/json" },
|
|
83095
83112
|
body: JSON.stringify({ selection }),
|
|
@@ -83205,7 +83222,7 @@ function useKeyframeEaseCommits({
|
|
|
83205
83222
|
}
|
|
83206
83223
|
|
|
83207
83224
|
// src/hooks/useCommitPreflightCapabilities.ts
|
|
83208
|
-
import { useEffect as useEffect122, useMemo as
|
|
83225
|
+
import { useEffect as useEffect122, useMemo as useMemo74, useRef as useRef153, useState as useState119 } from "react";
|
|
83209
83226
|
function runCommitPreflights(selection, fileAnimations, iframe, group) {
|
|
83210
83227
|
const target = { id: selection.id ?? null, selector: selection.selector ?? null };
|
|
83211
83228
|
const animations = getAnimationsForElement(fileAnimations, target, selection.element);
|
|
@@ -83274,7 +83291,7 @@ function useCommitPreflightCapabilities({
|
|
|
83274
83291
|
});
|
|
83275
83292
|
}
|
|
83276
83293
|
}, [enabled, projectId, selection, groupSelections, version]);
|
|
83277
|
-
return
|
|
83294
|
+
return useMemo74(() => {
|
|
83278
83295
|
void parseTick;
|
|
83279
83296
|
if (!enabled || !projectId) return { selection, groupSelections };
|
|
83280
83297
|
const group = groupSelections.length > 1;
|
|
@@ -83322,6 +83339,70 @@ function useLivePreviewIframe2(host) {
|
|
|
83322
83339
|
return live;
|
|
83323
83340
|
}
|
|
83324
83341
|
|
|
83342
|
+
// src/components/editor/domGroupMoveBy.ts
|
|
83343
|
+
function toScreen(doc, delta) {
|
|
83344
|
+
const origin2 = toTopScreenPoint(doc, { x: 0, y: 0 });
|
|
83345
|
+
const moved = toTopScreenPoint(doc, delta);
|
|
83346
|
+
if (!origin2 || !moved) throw new Error("The preview has no measurable size.");
|
|
83347
|
+
return { x: moved.x - origin2.x, y: moved.y - origin2.y };
|
|
83348
|
+
}
|
|
83349
|
+
function memberFor(selection, scale) {
|
|
83350
|
+
const { element } = selection;
|
|
83351
|
+
const box = element.getBoundingClientRect();
|
|
83352
|
+
const result = createManualOffsetDragMember({
|
|
83353
|
+
key: selection.id ?? selection.selector ?? selection.label,
|
|
83354
|
+
selection,
|
|
83355
|
+
element,
|
|
83356
|
+
rect: {
|
|
83357
|
+
left: box.left,
|
|
83358
|
+
top: box.top,
|
|
83359
|
+
width: box.width,
|
|
83360
|
+
height: box.height,
|
|
83361
|
+
editScaleX: scale.x,
|
|
83362
|
+
editScaleY: scale.y
|
|
83363
|
+
},
|
|
83364
|
+
gesture: "drag"
|
|
83365
|
+
});
|
|
83366
|
+
if (!result.ok) throw new Error(result.reason);
|
|
83367
|
+
return result.member;
|
|
83368
|
+
}
|
|
83369
|
+
async function moveDomGroupBy(moves, commitGroup) {
|
|
83370
|
+
const moving = moves.filter(({ delta }) => delta.x !== 0 || delta.y !== 0);
|
|
83371
|
+
if (moving.length === 0) return;
|
|
83372
|
+
const selections = moving.map(({ selection }) => selection);
|
|
83373
|
+
if (!canCanvasNudgeTargets(selections)) {
|
|
83374
|
+
const blocked = selections.find((s) => !s.capabilities.canApplyManualOffset);
|
|
83375
|
+
throw new Error(blocked?.capabilities.reasonIfDisabled ?? `${blocked?.label} can't be moved.`);
|
|
83376
|
+
}
|
|
83377
|
+
const screens = moving.map(
|
|
83378
|
+
({ selection, delta }) => toScreen(selection.element.ownerDocument, delta)
|
|
83379
|
+
);
|
|
83380
|
+
const members = [];
|
|
83381
|
+
try {
|
|
83382
|
+
for (const selection of selections)
|
|
83383
|
+
members.push(memberFor(selection, toScreen(selection.element.ownerDocument, { x: 1, y: 1 })));
|
|
83384
|
+
} catch (error) {
|
|
83385
|
+
restoreManualOffsetDragMembers(members);
|
|
83386
|
+
throw error;
|
|
83387
|
+
}
|
|
83388
|
+
const updates = members.map((member, index) => ({
|
|
83389
|
+
selection: member.selection,
|
|
83390
|
+
next: applyManualOffsetDragCommit(member, screens[index].x, screens[index].y),
|
|
83391
|
+
plainTranslate: member.plainTranslate
|
|
83392
|
+
}));
|
|
83393
|
+
try {
|
|
83394
|
+
await countStudioManualEditSave(members[0].element, () => commitGroup(updates));
|
|
83395
|
+
} catch (error) {
|
|
83396
|
+
for (const member of members) {
|
|
83397
|
+
if (isStudioManualEditGestureCurrent(member.element, member.gestureToken))
|
|
83398
|
+
restoreStudioPathOffset(member.element, member.initialPathOffset);
|
|
83399
|
+
}
|
|
83400
|
+
throw error;
|
|
83401
|
+
} finally {
|
|
83402
|
+
endManualOffsetDragMembers(members);
|
|
83403
|
+
}
|
|
83404
|
+
}
|
|
83405
|
+
|
|
83325
83406
|
// src/hooks/useDomEditSession.ts
|
|
83326
83407
|
function useDomEditSession({
|
|
83327
83408
|
projectId,
|
|
@@ -83674,6 +83755,7 @@ function useDomEditSession({
|
|
|
83674
83755
|
domEditSelection,
|
|
83675
83756
|
selectedGsapAnimations,
|
|
83676
83757
|
gsapCommitMutation,
|
|
83758
|
+
activeCompPath,
|
|
83677
83759
|
previewIframeRef,
|
|
83678
83760
|
showToast,
|
|
83679
83761
|
bumpGsapCache,
|
|
@@ -83688,6 +83770,13 @@ function useDomEditSession({
|
|
|
83688
83770
|
setArcPath,
|
|
83689
83771
|
updateArcSegment
|
|
83690
83772
|
});
|
|
83773
|
+
const handleDomGroupMoveBy = useCallback148(
|
|
83774
|
+
(moves) => moveDomGroupBy(
|
|
83775
|
+
moves,
|
|
83776
|
+
(updates) => handleGsapAwareGroupPathOffsetCommit(updates, { refusalToast: false })
|
|
83777
|
+
),
|
|
83778
|
+
[handleGsapAwareGroupPathOffsetCommit]
|
|
83779
|
+
);
|
|
83691
83780
|
const { handleUpdateSegmentEase, handleUpdateKeyframeEase, handleSetAllKeyframeEases } = useKeyframeEaseCommits({ gsapCommitMutation, domEditSelectionRef });
|
|
83692
83781
|
const committable = useCommitPreflightCapabilities({
|
|
83693
83782
|
projectId,
|
|
@@ -83725,6 +83814,7 @@ function useDomEditSession({
|
|
|
83725
83814
|
handleDomAttributeBatchCommit,
|
|
83726
83815
|
handleDomPathOffsetCommit: handleGsapAwarePathOffsetCommit,
|
|
83727
83816
|
handleDomGroupPathOffsetCommit: handleGsapAwareGroupPathOffsetCommit,
|
|
83817
|
+
handleDomGroupMoveBy,
|
|
83728
83818
|
handleDomZIndexReorderCommit,
|
|
83729
83819
|
handleDomBoxSizeCommit: handleGsapAwareBoxSizeCommit,
|
|
83730
83820
|
handleDomRotationCommit: handleGsapAwareRotationCommit,
|
|
@@ -83873,7 +83963,7 @@ async function readProjectFileOptional(projectId, path) {
|
|
|
83873
83963
|
let res;
|
|
83874
83964
|
const fetchStarted = performance.now();
|
|
83875
83965
|
try {
|
|
83876
|
-
res = await
|
|
83966
|
+
res = await studioApiFetch(
|
|
83877
83967
|
`/api/projects/${encodeURIComponent(projectId)}/files/${encodeURIComponent(path)}?optional=1`
|
|
83878
83968
|
);
|
|
83879
83969
|
} catch {
|
|
@@ -84458,8 +84548,10 @@ function useExternalFileChangeCoordinator({
|
|
|
84458
84548
|
},
|
|
84459
84549
|
[projectId, pendingTimelineEditPathRef, startDrainLoop, onAcceptedPersistedFileChange]
|
|
84460
84550
|
);
|
|
84551
|
+
const processChangeRef = useRef156(processChange);
|
|
84552
|
+
processChangeRef.current = processChange;
|
|
84461
84553
|
useEffect127(() => {
|
|
84462
|
-
const handler = (delivery) =>
|
|
84554
|
+
const handler = (delivery) => processChangeRef.current(decodeFileChange(delivery));
|
|
84463
84555
|
const adapter = testHotAdapter();
|
|
84464
84556
|
if (adapter) {
|
|
84465
84557
|
adapter.on("hf:file-change", handler);
|
|
@@ -84470,7 +84562,7 @@ function useExternalFileChangeCoordinator({
|
|
|
84470
84562
|
return () => import.meta.hot?.off?.("hf:file-change", handler);
|
|
84471
84563
|
}
|
|
84472
84564
|
return sseFileChangeChannel(handler);
|
|
84473
|
-
}, [
|
|
84565
|
+
}, []);
|
|
84474
84566
|
const retry = useCallback150(async () => {
|
|
84475
84567
|
const current = blockedRef.current;
|
|
84476
84568
|
if (!current || current.status === "conflict" || current.recovered) return;
|
|
@@ -84617,7 +84709,7 @@ function useStudioExternalFileChanges({
|
|
|
84617
84709
|
}
|
|
84618
84710
|
|
|
84619
84711
|
// src/hooks/useBlockHandlers.ts
|
|
84620
|
-
import { useCallback as useCallback152, useMemo as
|
|
84712
|
+
import { useCallback as useCallback152, useMemo as useMemo75, useRef as useRef157, useState as useState123 } from "react";
|
|
84621
84713
|
|
|
84622
84714
|
// src/utils/blockInstaller.ts
|
|
84623
84715
|
function getMaxZIndexFromIframe(iframe) {
|
|
@@ -84646,7 +84738,7 @@ async function installRegistryItem({
|
|
|
84646
84738
|
blockName,
|
|
84647
84739
|
showToast
|
|
84648
84740
|
}) {
|
|
84649
|
-
const response = await
|
|
84741
|
+
const response = await studioApiFetch(buildProjectApiPath(projectId, `/registry/install`), {
|
|
84650
84742
|
method: "POST",
|
|
84651
84743
|
headers: { "Content-Type": "application/json" },
|
|
84652
84744
|
body: JSON.stringify({ blockName })
|
|
@@ -84811,7 +84903,7 @@ function useBlockHandlers({
|
|
|
84811
84903
|
setRightPanelTab
|
|
84812
84904
|
}) {
|
|
84813
84905
|
const [activeBlockParams, setActiveBlockParams] = useState123(null);
|
|
84814
|
-
const blockCtx =
|
|
84906
|
+
const blockCtx = useMemo75(
|
|
84815
84907
|
() => ({
|
|
84816
84908
|
activeCompPath: blockCtxDeps.activeCompPath,
|
|
84817
84909
|
timelineElements: blockCtxDeps.timelineElements,
|
|
@@ -85178,7 +85270,7 @@ function dispatchPlainKey(event, key2, cb) {
|
|
|
85178
85270
|
}
|
|
85179
85271
|
|
|
85180
85272
|
// src/hooks/useEditHistoryActions.ts
|
|
85181
|
-
import { useCallback as useCallback153, useMemo as
|
|
85273
|
+
import { useCallback as useCallback153, useMemo as useMemo76 } from "react";
|
|
85182
85274
|
function useEditHistoryActions({
|
|
85183
85275
|
editHistory,
|
|
85184
85276
|
readOptionalProjectFile,
|
|
@@ -85205,20 +85297,25 @@ function useEditHistoryActions({
|
|
|
85205
85297
|
const noun = direction === "undo" ? "Undo" : "Redo";
|
|
85206
85298
|
const predicted = editHistory.predict?.(direction) ?? null;
|
|
85207
85299
|
const predictedShown = predicted ? showHistoryRestoreNow?.(predicted.files) ?? null : null;
|
|
85208
|
-
const pendingEditShown = !predictedShown && direction === "undo" ?
|
|
85209
|
-
const putBack = predictedShown ?? pendingEditShown;
|
|
85300
|
+
const pendingEditShown = !predictedShown && direction === "undo" ? paintBackNewestStudioPendingEdit() : null;
|
|
85301
|
+
const putBack = predictedShown ?? pendingEditShown?.showAgain;
|
|
85302
|
+
const claimedAfter = direction === "undo" && hasStudioPendingEdits() ? editHistory.claims?.() : void 0;
|
|
85210
85303
|
let result = { ok: false, reason: "failed" };
|
|
85211
85304
|
let serverSteppedShown = false;
|
|
85305
|
+
let revertIsTheUndo = false;
|
|
85212
85306
|
try {
|
|
85213
85307
|
await waitForPendingDomEditSaves();
|
|
85308
|
+
revertIsTheUndo = Boolean(pendingEditShown && !await pendingEditShown.landed());
|
|
85309
|
+
if (revertIsTheUndo) return;
|
|
85214
85310
|
result = await editHistory[direction]({
|
|
85215
85311
|
readFile: readHistoryFile,
|
|
85216
|
-
serialize: serializeHistoryFiles
|
|
85312
|
+
serialize: serializeHistoryFiles,
|
|
85313
|
+
claimedAfter
|
|
85217
85314
|
});
|
|
85218
85315
|
const stepped = Boolean(result.ok && result.label);
|
|
85219
85316
|
serverSteppedShown = predictedShown ? stepped && result.undoes === predicted?.id : stepped && Boolean(pendingEditShown);
|
|
85220
85317
|
} finally {
|
|
85221
|
-
if (putBack && !serverSteppedShown) putBack();
|
|
85318
|
+
if (putBack && !serverSteppedShown && !revertIsTheUndo) putBack();
|
|
85222
85319
|
}
|
|
85223
85320
|
if (!result.ok && result.reason === "content-mismatch") {
|
|
85224
85321
|
showToast(
|
|
@@ -85233,12 +85330,12 @@ function useEditHistoryActions({
|
|
|
85233
85330
|
}
|
|
85234
85331
|
if (result.ok && result.label) {
|
|
85235
85332
|
const files = serverSteppedShown && predictedShown ? fromShown(result.files, predicted.files) : result.files;
|
|
85236
|
-
const
|
|
85237
|
-
onAfterUndoRedo?.(
|
|
85333
|
+
const restore2 = { paths: result.paths, files };
|
|
85334
|
+
onAfterUndoRedo?.(restore2);
|
|
85238
85335
|
if (activeCompPath && result.paths?.includes(activeCompPath)) {
|
|
85239
85336
|
forceReloadSdkSession?.();
|
|
85240
85337
|
}
|
|
85241
|
-
await syncHistoryPreviewAfterApply(
|
|
85338
|
+
await syncHistoryPreviewAfterApply(restore2);
|
|
85242
85339
|
showToast(result.label, "info");
|
|
85243
85340
|
}
|
|
85244
85341
|
},
|
|
@@ -85257,7 +85354,7 @@ function useEditHistoryActions({
|
|
|
85257
85354
|
);
|
|
85258
85355
|
const undo = useCallback153(() => apply("undo"), [apply]);
|
|
85259
85356
|
const redo = useCallback153(() => apply("redo"), [apply]);
|
|
85260
|
-
return
|
|
85357
|
+
return useMemo76(() => ({ undo, redo }), [undo, redo]);
|
|
85261
85358
|
}
|
|
85262
85359
|
function fromShown(files, shown2) {
|
|
85263
85360
|
if (!files) return files;
|
|
@@ -86252,7 +86349,7 @@ function useCaptionDetection({
|
|
|
86252
86349
|
if (!captionSrcPath) return;
|
|
86253
86350
|
activating = true;
|
|
86254
86351
|
const srcPath = captionSrcPath;
|
|
86255
|
-
|
|
86352
|
+
studioApiFetch(buildProjectApiPath(projectId, `/files/${encodeURIComponent(srcPath)}`)).then((r) => r.json()).then((data) => {
|
|
86256
86353
|
if (!data.content || !doc || !win || useCaptionStore.getState().isEditMode) return;
|
|
86257
86354
|
const root = doc.querySelector("[data-composition-id]");
|
|
86258
86355
|
const w = parseInt(root?.getAttribute("data-width") ?? "1920", 10);
|
|
@@ -86365,7 +86462,7 @@ function useConsoleErrorCapture(previewIframe) {
|
|
|
86365
86462
|
}
|
|
86366
86463
|
|
|
86367
86464
|
// src/hooks/useFrameCapture.ts
|
|
86368
|
-
import { useMemo as
|
|
86465
|
+
import { useMemo as useMemo77, useState as useState125, useCallback as useCallback157, useRef as useRef162 } from "react";
|
|
86369
86466
|
function useFrameCapture({
|
|
86370
86467
|
projectId,
|
|
86371
86468
|
activeCompPath,
|
|
@@ -86417,7 +86514,10 @@ function useFrameCapture({
|
|
|
86417
86514
|
const controller = new AbortController();
|
|
86418
86515
|
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
86419
86516
|
try {
|
|
86420
|
-
const response = await
|
|
86517
|
+
const response = await studioApiFetch(href, {
|
|
86518
|
+
cache: "no-store",
|
|
86519
|
+
signal: controller.signal
|
|
86520
|
+
});
|
|
86421
86521
|
clearTimeout(timeout);
|
|
86422
86522
|
if (!response.ok) {
|
|
86423
86523
|
let msg = `Capture failed (${response.status})`;
|
|
@@ -86453,7 +86553,7 @@ function useFrameCapture({
|
|
|
86453
86553
|
},
|
|
86454
86554
|
[activeCompPath, playheadTime, projectId, showToast, waitForPendingDomEditSaves]
|
|
86455
86555
|
);
|
|
86456
|
-
const captureFrameHref =
|
|
86556
|
+
const captureFrameHref = useMemo77(
|
|
86457
86557
|
() => projectId ? buildFrameCaptureUrl({
|
|
86458
86558
|
projectId,
|
|
86459
86559
|
compositionPath: activeCompPath,
|
|
@@ -86473,7 +86573,7 @@ function useFrameCapture({
|
|
|
86473
86573
|
}
|
|
86474
86574
|
|
|
86475
86575
|
// src/hooks/useLintModal.ts
|
|
86476
|
-
import { useState as useState126, useCallback as useCallback158, useEffect as useEffect131, useRef as useRef163, useMemo as
|
|
86576
|
+
import { useState as useState126, useCallback as useCallback158, useEffect as useEffect131, useRef as useRef163, useMemo as useMemo78 } from "react";
|
|
86477
86577
|
function parseFinding(f) {
|
|
86478
86578
|
return {
|
|
86479
86579
|
severity: f.severity === "error" ? "error" : "warning",
|
|
@@ -86484,7 +86584,7 @@ function parseFinding(f) {
|
|
|
86484
86584
|
};
|
|
86485
86585
|
}
|
|
86486
86586
|
async function fetchLintFindings(projectId) {
|
|
86487
|
-
const res = await
|
|
86587
|
+
const res = await studioApiFetch(buildProjectApiPath(projectId, `/lint`));
|
|
86488
86588
|
const data = await res.json();
|
|
86489
86589
|
return (data.findings ?? []).map(parseFinding);
|
|
86490
86590
|
}
|
|
@@ -86546,8 +86646,8 @@ function useLintModal(projectId, refreshKey) {
|
|
|
86546
86646
|
},
|
|
86547
86647
|
[backgroundFindings]
|
|
86548
86648
|
);
|
|
86549
|
-
const findingsByElement =
|
|
86550
|
-
const findingsByFile =
|
|
86649
|
+
const findingsByElement = useMemo78(() => groupFindings((f) => f.elementId), [groupFindings]);
|
|
86650
|
+
const findingsByFile = useMemo78(() => groupFindings((f) => f.file), [groupFindings]);
|
|
86551
86651
|
const badgeFindings = lintModal ?? backgroundFindings;
|
|
86552
86652
|
const lintFindingCount = badgeFindings.length;
|
|
86553
86653
|
const hasLintError = badgeFindings.some((f) => f.severity === "error");
|
|
@@ -87012,16 +87112,16 @@ function useStudioUrlState({
|
|
|
87012
87112
|
}
|
|
87013
87113
|
|
|
87014
87114
|
// src/hooks/useEffectiveTimelineDuration.ts
|
|
87015
|
-
import { useMemo as
|
|
87115
|
+
import { useMemo as useMemo79 } from "react";
|
|
87016
87116
|
function useEffectiveTimelineDuration(timelineDuration2, timelineElements) {
|
|
87017
|
-
return
|
|
87117
|
+
return useMemo79(
|
|
87018
87118
|
() => getEffectiveTimelineDuration(timelineDuration2, timelineElements),
|
|
87019
87119
|
[timelineDuration2, timelineElements]
|
|
87020
87120
|
);
|
|
87021
87121
|
}
|
|
87022
87122
|
|
|
87023
87123
|
// src/hooks/useStudioContextValue.ts
|
|
87024
|
-
import { useCallback as useCallback161, useMemo as
|
|
87124
|
+
import { useCallback as useCallback161, useMemo as useMemo80 } from "react";
|
|
87025
87125
|
function buildStudioContextValue(input) {
|
|
87026
87126
|
return {
|
|
87027
87127
|
projectId: input.projectId,
|
|
@@ -87049,7 +87149,7 @@ function buildStudioContextValue(input) {
|
|
|
87049
87149
|
};
|
|
87050
87150
|
}
|
|
87051
87151
|
function useInspectorState(rightPanel, isPlaying, domEditSelection, isGestureRecording) {
|
|
87052
|
-
return
|
|
87152
|
+
return useMemo80(() => {
|
|
87053
87153
|
const inspectorPanelActive = rightPanel === "layers" || rightPanel === "design";
|
|
87054
87154
|
return {
|
|
87055
87155
|
inspectorPanelActive,
|
|
@@ -87095,7 +87195,7 @@ function useGlobalFileDrop(handleTimelineFileDrop) {
|
|
|
87095
87195
|
import { memo as memo44, useContext as useContext15 } from "react";
|
|
87096
87196
|
|
|
87097
87197
|
// src/contexts/PanelLayoutContext.tsx
|
|
87098
|
-
import { useContext as useContext14, useMemo as
|
|
87198
|
+
import { useContext as useContext14, useMemo as useMemo81 } from "react";
|
|
87099
87199
|
import { jsx as jsx199 } from "react/jsx-runtime";
|
|
87100
87200
|
var PanelLayoutContext = createStableContext("PanelLayoutContext", null);
|
|
87101
87201
|
function usePanelLayoutContext() {
|
|
@@ -87107,7 +87207,7 @@ function PanelLayoutProvider({
|
|
|
87107
87207
|
value: { rightCollapsed, setRightCollapsed, rightPanelTab, setRightPanelTab },
|
|
87108
87208
|
children
|
|
87109
87209
|
}) {
|
|
87110
|
-
const stable =
|
|
87210
|
+
const stable = useMemo81(
|
|
87111
87211
|
() => ({ rightCollapsed, setRightCollapsed, rightPanelTab, setRightPanelTab }),
|
|
87112
87212
|
[rightCollapsed, setRightCollapsed, rightPanelTab, setRightPanelTab]
|
|
87113
87213
|
);
|
|
@@ -88179,7 +88279,7 @@ function useGestureCommit({
|
|
|
88179
88279
|
import { useLayoutEffect as useLayoutEffect13, useRef as useRef168 } from "react";
|
|
88180
88280
|
|
|
88181
88281
|
// src/components/editor/GestureTrailOverlay.tsx
|
|
88182
|
-
import { memo as memo45, useMemo as
|
|
88282
|
+
import { memo as memo45, useMemo as useMemo82 } from "react";
|
|
88183
88283
|
import { Fragment as Fragment57, jsx as jsx203, jsxs as jsxs162 } from "react/jsx-runtime";
|
|
88184
88284
|
var GestureTrailOverlay = memo45(function GestureTrailOverlay2({
|
|
88185
88285
|
samples,
|
|
@@ -88191,7 +88291,7 @@ var GestureTrailOverlay = memo45(function GestureTrailOverlay2({
|
|
|
88191
88291
|
mode,
|
|
88192
88292
|
accentColor = "#3CE6AC"
|
|
88193
88293
|
}) {
|
|
88194
|
-
const trailPoints =
|
|
88294
|
+
const trailPoints = useMemo82(() => {
|
|
88195
88295
|
if (!canvasRect) return "";
|
|
88196
88296
|
if (trail2 && trail2.length > 1) {
|
|
88197
88297
|
return trail2.map((p) => `${p.x - canvasRect.left},${p.y - canvasRect.top}`).join(" ");
|
|
@@ -88199,7 +88299,7 @@ var GestureTrailOverlay = memo45(function GestureTrailOverlay2({
|
|
|
88199
88299
|
if (samples.length === 0) return "";
|
|
88200
88300
|
return samples.filter((s) => s.properties.x != null && s.properties.y != null).map((s) => `${s.properties.x},${s.properties.y}`).join(" ");
|
|
88201
88301
|
}, [samples, trail2, sampleCount, canvasRect?.left, canvasRect?.top]);
|
|
88202
|
-
const simplifiedPath =
|
|
88302
|
+
const simplifiedPath = useMemo82(() => {
|
|
88203
88303
|
if (!simplifiedPoints || simplifiedPoints.size === 0) return "";
|
|
88204
88304
|
const pts = [];
|
|
88205
88305
|
for (const [pct, props] of simplifiedPoints) {
|
|
@@ -88211,7 +88311,7 @@ var GestureTrailOverlay = memo45(function GestureTrailOverlay2({
|
|
|
88211
88311
|
if (pts.length === 0) return "";
|
|
88212
88312
|
return pts.map((p) => `${p.x},${p.y}`).join(" ");
|
|
88213
88313
|
}, [simplifiedPoints]);
|
|
88214
|
-
const diamondPositions =
|
|
88314
|
+
const diamondPositions = useMemo82(() => {
|
|
88215
88315
|
if (!simplifiedPoints || simplifiedPoints.size === 0) return [];
|
|
88216
88316
|
const pts = [];
|
|
88217
88317
|
for (const [pct, props] of simplifiedPoints) {
|
|
@@ -88319,7 +88419,7 @@ function useGestureTrailOverlay({
|
|
|
88319
88419
|
}
|
|
88320
88420
|
|
|
88321
88421
|
// src/hooks/useStudioSidePanels.tsx
|
|
88322
|
-
import { useMemo as
|
|
88422
|
+
import { useMemo as useMemo88 } from "react";
|
|
88323
88423
|
|
|
88324
88424
|
// src/components/StudioLeftPanels.tsx
|
|
88325
88425
|
import { memo as memo49, useCallback as useCallback171 } from "react";
|
|
@@ -88441,7 +88541,7 @@ function MediaPreview({ projectId, filePath }) {
|
|
|
88441
88541
|
}
|
|
88442
88542
|
|
|
88443
88543
|
// src/components/sidebar/AssetsTab.tsx
|
|
88444
|
-
import { memo as memo46, useState as useState137, useCallback as useCallback167, useRef as useRef172, useMemo as
|
|
88544
|
+
import { memo as memo46, useState as useState137, useCallback as useCallback167, useRef as useRef172, useMemo as useMemo84, useEffect as useEffect140 } from "react";
|
|
88445
88545
|
|
|
88446
88546
|
// src/components/ui/SearchInput.tsx
|
|
88447
88547
|
import { jsx as jsx206, jsxs as jsxs164 } from "react/jsx-runtime";
|
|
@@ -89053,7 +89153,7 @@ function AudioRow({
|
|
|
89053
89153
|
}
|
|
89054
89154
|
|
|
89055
89155
|
// src/components/sidebar/GlobalAssetsView.tsx
|
|
89056
|
-
import { useEffect as useEffect137, useMemo as
|
|
89156
|
+
import { useEffect as useEffect137, useMemo as useMemo83, useState as useState134 } from "react";
|
|
89057
89157
|
import { jsx as jsx209, jsxs as jsxs167 } from "react/jsx-runtime";
|
|
89058
89158
|
function globalAssetRows(records, query = "") {
|
|
89059
89159
|
const q = query.trim().toLowerCase();
|
|
@@ -89071,7 +89171,7 @@ function GlobalAssetsView({ searchQuery }) {
|
|
|
89071
89171
|
const [records, setRecords] = useState134(null);
|
|
89072
89172
|
useEffect137(() => {
|
|
89073
89173
|
let cancelled = false;
|
|
89074
|
-
|
|
89174
|
+
studioApiFetch("/api/assets/global").then((r) => r.ok ? r.json() : { assets: [] }).then((d) => {
|
|
89075
89175
|
if (!cancelled) setRecords(Array.isArray(d.assets) ? d.assets : []);
|
|
89076
89176
|
}).catch(() => {
|
|
89077
89177
|
if (!cancelled) setRecords([]);
|
|
@@ -89080,7 +89180,7 @@ function GlobalAssetsView({ searchQuery }) {
|
|
|
89080
89180
|
cancelled = true;
|
|
89081
89181
|
};
|
|
89082
89182
|
}, []);
|
|
89083
|
-
const rows =
|
|
89183
|
+
const rows = useMemo83(() => globalAssetRows(records ?? [], searchQuery), [records, searchQuery]);
|
|
89084
89184
|
if (records === null) {
|
|
89085
89185
|
return /* @__PURE__ */ jsx209("p", { className: "px-4 py-3 text-[11px] text-panel-text-5", children: "Loading global assets\u2026" });
|
|
89086
89186
|
}
|
|
@@ -89592,7 +89692,7 @@ var AssetsTab = memo46(function AssetsTab2({
|
|
|
89592
89692
|
useEffect140(() => {
|
|
89593
89693
|
if (manifest404Ref.current.has(projectId)) return;
|
|
89594
89694
|
let cancelled = false;
|
|
89595
|
-
|
|
89695
|
+
studioApiFetch(buildProjectApiPath(projectId, `/preview/.media/manifest.jsonl`)).then((r) => {
|
|
89596
89696
|
if (!r.ok) {
|
|
89597
89697
|
manifest404Ref.current.add(projectId);
|
|
89598
89698
|
return "";
|
|
@@ -89642,12 +89742,12 @@ var AssetsTab = memo46(function AssetsTab2({
|
|
|
89642
89742
|
setTimeout(() => setCopyFeedback(null), copied ? 1500 : 3e3);
|
|
89643
89743
|
}, []);
|
|
89644
89744
|
const elements = usePlayerStore((s) => s.elements);
|
|
89645
|
-
const usedPaths =
|
|
89646
|
-
const allMediaAssets =
|
|
89745
|
+
const usedPaths = useMemo84(() => deriveUsedPaths(elements), [elements]);
|
|
89746
|
+
const allMediaAssets = useMemo84(
|
|
89647
89747
|
() => assets.filter((a) => MEDIA_EXT.test(a) || FONT_EXT.test(a)),
|
|
89648
89748
|
[assets]
|
|
89649
89749
|
);
|
|
89650
|
-
const mediaAssets =
|
|
89750
|
+
const mediaAssets = useMemo84(() => {
|
|
89651
89751
|
const all = filterByUsage(allMediaAssets, usedPaths, usageFilter);
|
|
89652
89752
|
if (!searchQuery) return all;
|
|
89653
89753
|
const q = searchQuery.toLowerCase();
|
|
@@ -89657,7 +89757,7 @@ var AssetsTab = memo46(function AssetsTab2({
|
|
|
89657
89757
|
return rec?.description?.toLowerCase().includes(q);
|
|
89658
89758
|
});
|
|
89659
89759
|
}, [allMediaAssets, searchQuery, manifest, usageFilter, usedPaths]);
|
|
89660
|
-
const categorized =
|
|
89760
|
+
const categorized = useMemo84(() => {
|
|
89661
89761
|
const groups = { audio: [], images: [], video: [], fonts: [] };
|
|
89662
89762
|
for (const a of mediaAssets) {
|
|
89663
89763
|
const cat = getCategory(a);
|
|
@@ -89672,12 +89772,12 @@ var AssetsTab = memo46(function AssetsTab2({
|
|
|
89672
89772
|
}
|
|
89673
89773
|
return groups;
|
|
89674
89774
|
}, [mediaAssets, usedPaths]);
|
|
89675
|
-
const counts =
|
|
89775
|
+
const counts = useMemo84(() => {
|
|
89676
89776
|
const c = { all: mediaAssets.length };
|
|
89677
89777
|
for (const cat of FILTER_ORDER) c[cat] = categorized[cat].length;
|
|
89678
89778
|
return c;
|
|
89679
89779
|
}, [mediaAssets, categorized]);
|
|
89680
|
-
const usageCounts =
|
|
89780
|
+
const usageCounts = useMemo84(
|
|
89681
89781
|
() => countUsage(
|
|
89682
89782
|
assets.filter((a) => MEDIA_EXT.test(a) || FONT_EXT.test(a)),
|
|
89683
89783
|
usedPaths
|
|
@@ -90337,7 +90437,7 @@ function BlockCard({
|
|
|
90337
90437
|
}
|
|
90338
90438
|
|
|
90339
90439
|
// src/components/sidebar/CompositionsTab.tsx
|
|
90340
|
-
import { memo as memo48, useCallback as useCallback170, useEffect as useEffect143, useLayoutEffect as useLayoutEffect15, useMemo as
|
|
90440
|
+
import { memo as memo48, useCallback as useCallback170, useEffect as useEffect143, useLayoutEffect as useLayoutEffect15, useMemo as useMemo85, useRef as useRef175, useState as useState140 } from "react";
|
|
90341
90441
|
import { jsx as jsx215, jsxs as jsxs172 } from "react/jsx-runtime";
|
|
90342
90442
|
var DEFAULT_PREVIEW_STAGE = { width: 1920, height: 1080 };
|
|
90343
90443
|
var CARD_W = 80;
|
|
@@ -90479,7 +90579,7 @@ function CompCard({
|
|
|
90479
90579
|
const thumbnailUrl2 = compositionCardThumbnailUrl(projectId, comp, contentRevision);
|
|
90480
90580
|
const [thumbnailBox, nearViewport] = useNearViewport();
|
|
90481
90581
|
const timelineSessionEpoch = usePlayerStore((state) => state.timelineSessionEpoch);
|
|
90482
|
-
const thumbnailRequest =
|
|
90582
|
+
const thumbnailRequest = useMemo85(
|
|
90483
90583
|
() => previewBooted && nearViewport ? compositionThumbnailRequest(thumbnailUrl2, projectId, {
|
|
90484
90584
|
sessionEpoch: timelineSessionEpoch,
|
|
90485
90585
|
rich: true
|
|
@@ -94102,7 +94202,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
94102
94202
|
}
|
|
94103
94203
|
|
|
94104
94204
|
// src/components/panels/VariablesPanel.tsx
|
|
94105
|
-
import { memo as memo58, useCallback as useCallback183, useEffect as useEffect151, useMemo as
|
|
94205
|
+
import { memo as memo58, useCallback as useCallback183, useEffect as useEffect151, useMemo as useMemo86, useState as useState154 } from "react";
|
|
94106
94206
|
|
|
94107
94207
|
// src/hooks/useVariablesPersist.ts
|
|
94108
94208
|
import { useCallback as useCallback180 } from "react";
|
|
@@ -94798,22 +94898,22 @@ var VariablesPanel = memo58(function VariablesPanel2({
|
|
|
94798
94898
|
reloadPreview,
|
|
94799
94899
|
publishSdkSession
|
|
94800
94900
|
});
|
|
94801
|
-
const declarations =
|
|
94901
|
+
const declarations = useMemo86(
|
|
94802
94902
|
() => sdkSession?.getVariableDeclarations() ?? [],
|
|
94803
94903
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94804
94904
|
[sdkSession, refreshKey, revision]
|
|
94805
94905
|
);
|
|
94806
|
-
const usage =
|
|
94906
|
+
const usage = useMemo86(
|
|
94807
94907
|
() => sdkSession?.getVariableUsage() ?? null,
|
|
94808
94908
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94809
94909
|
[sdkSession, refreshKey, revision]
|
|
94810
94910
|
);
|
|
94811
|
-
const issues =
|
|
94911
|
+
const issues = useMemo86(
|
|
94812
94912
|
() => previewValues && sdkSession ? sdkSession.validateVariableValues(previewValues) : [],
|
|
94813
94913
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94814
94914
|
[sdkSession, previewValues, refreshKey, revision]
|
|
94815
94915
|
);
|
|
94816
|
-
const effectiveValues =
|
|
94916
|
+
const effectiveValues = useMemo86(
|
|
94817
94917
|
() => sdkSession?.getVariableValues(previewValues ?? void 0) ?? {},
|
|
94818
94918
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94819
94919
|
[sdkSession, previewValues, refreshKey, revision]
|
|
@@ -95128,15 +95228,15 @@ function useSlideshowPersist({
|
|
|
95128
95228
|
}
|
|
95129
95229
|
|
|
95130
95230
|
// src/hooks/useSlideshowTabState.ts
|
|
95131
|
-
import { useMemo as
|
|
95231
|
+
import { useMemo as useMemo87 } from "react";
|
|
95132
95232
|
import { SLIDESHOW_ISLAND_TYPE as SLIDESHOW_ISLAND_TYPE2, slideshowIslandRegex as slideshowIslandRegex2 } from "@hyperframes/core/slideshow";
|
|
95133
95233
|
function useSlideshowTabState(params) {
|
|
95134
95234
|
const { editingFileContent, previewIframeRef, refreshKey, slideshowVisible } = params;
|
|
95135
|
-
const isSlideshowComposition =
|
|
95235
|
+
const isSlideshowComposition = useMemo87(() => {
|
|
95136
95236
|
if (!editingFileContent || !editingFileContent.includes(SLIDESHOW_ISLAND_TYPE2)) return false;
|
|
95137
95237
|
return slideshowIslandRegex2("i").test(editingFileContent);
|
|
95138
95238
|
}, [editingFileContent]);
|
|
95139
|
-
const slideshowScenes =
|
|
95239
|
+
const slideshowScenes = useMemo87(() => {
|
|
95140
95240
|
try {
|
|
95141
95241
|
const win = previewIframeRef.current?.contentWindow;
|
|
95142
95242
|
return (win?.__clipManifest?.scenes ?? []).map((s) => ({
|
|
@@ -95313,7 +95413,7 @@ function useRemoveBackground(projectId, refreshFileTree, showToast) {
|
|
|
95313
95413
|
return useCallback186(
|
|
95314
95414
|
// fallow-ignore-next-line complexity
|
|
95315
95415
|
async (inputPath, options) => {
|
|
95316
|
-
const response = await
|
|
95416
|
+
const response = await studioApiFetch(
|
|
95317
95417
|
`/api/projects/${encodeURIComponent(projectId)}/media/remove-background`,
|
|
95318
95418
|
{
|
|
95319
95419
|
method: "POST",
|
|
@@ -95751,7 +95851,7 @@ import { Fragment as Fragment66, jsx as jsx236, jsxs as jsxs189 } from "react/js
|
|
|
95751
95851
|
function useStudioSidePanels(left, right, projectId) {
|
|
95752
95852
|
const stableLeft = useStableHandlers(left, projectId);
|
|
95753
95853
|
const stableRight = useStableHandlers(right, projectId);
|
|
95754
|
-
return
|
|
95854
|
+
return useMemo88(
|
|
95755
95855
|
() => /* @__PURE__ */ jsxs189(Fragment66, { children: [
|
|
95756
95856
|
/* @__PURE__ */ jsx236(StudioLeftPanels, { ...stableLeft }),
|
|
95757
95857
|
/* @__PURE__ */ jsx236(StudioRightPanels, { ...stableRight })
|
|
@@ -95890,7 +95990,7 @@ async function extendKeyframedTweenToPlayhead(session, sel, anim, currentTime, d
|
|
|
95890
95990
|
position: extended.position,
|
|
95891
95991
|
duration: extended.duration,
|
|
95892
95992
|
keyframes: extended.keyframes,
|
|
95893
|
-
|
|
95993
|
+
...keyframeEases(anim)
|
|
95894
95994
|
},
|
|
95895
95995
|
{
|
|
95896
95996
|
label: "Add keyframe",
|
|
@@ -96770,7 +96870,7 @@ function StudioSplash({ waiting }) {
|
|
|
96770
96870
|
import { useEffect as useEffect155, useState as useState156 } from "react";
|
|
96771
96871
|
async function resolveHashProject(id) {
|
|
96772
96872
|
try {
|
|
96773
|
-
const res = await
|
|
96873
|
+
const res = await studioApiFetch(`/api/projects/${encodeURIComponent(id)}`);
|
|
96774
96874
|
if (res.ok) return { outcome: "ok", status: res.status };
|
|
96775
96875
|
return { outcome: res.status === 404 ? "missing" : "unknown", status: res.status };
|
|
96776
96876
|
} catch {
|
|
@@ -96782,7 +96882,7 @@ function replaceProjectHash(projectId) {
|
|
|
96782
96882
|
}
|
|
96783
96883
|
async function firstProjectId() {
|
|
96784
96884
|
try {
|
|
96785
|
-
const res = await
|
|
96885
|
+
const res = await studioApiFetch("/api/projects");
|
|
96786
96886
|
const data = await res.json();
|
|
96787
96887
|
return data.projects?.[0]?.id ?? null;
|
|
96788
96888
|
} catch {
|
|
@@ -96809,7 +96909,7 @@ function useServerConnection() {
|
|
|
96809
96909
|
retryTimer = window.setTimeout(tryConnect, 2e3);
|
|
96810
96910
|
}
|
|
96811
96911
|
function tryConnect() {
|
|
96812
|
-
|
|
96912
|
+
studioApiFetch("/api/projects").then((r) => r.json()).then(async (data) => {
|
|
96813
96913
|
if (cancelled) return;
|
|
96814
96914
|
if (hashProjectId) {
|
|
96815
96915
|
const verdict = await resolveHashProject(hashProjectId);
|
|
@@ -96953,7 +97053,7 @@ function useCompositionContentLoader({
|
|
|
96953
97053
|
if (!projectId) return;
|
|
96954
97054
|
setActiveCompPath(comp.endsWith(".html") ? comp : null);
|
|
96955
97055
|
setEditingFile({ path: comp, content: null });
|
|
96956
|
-
|
|
97056
|
+
studioApiFetch(buildProjectApiPath(projectId, `/files/${encodeURIComponent(comp)}`)).then(async (r) => {
|
|
96957
97057
|
if (!r.ok) throw new Error(`Failed to load ${comp} (${r.status})`);
|
|
96958
97058
|
return r.json();
|
|
96959
97059
|
}).then((data) => {
|
|
@@ -97074,7 +97174,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
|
|
|
97074
97174
|
setRefreshKey
|
|
97075
97175
|
});
|
|
97076
97176
|
const fileManager = useStableHandlers(fileManagerResult, projectId);
|
|
97077
|
-
const masterCompPath =
|
|
97177
|
+
const masterCompPath = useMemo89(
|
|
97078
97178
|
() => resolveMasterCompositionPath(fileManager.compositions),
|
|
97079
97179
|
[fileManager.compositions]
|
|
97080
97180
|
);
|
|
@@ -97178,7 +97278,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
|
|
|
97178
97278
|
} = useStableHandlers(blockHandlersResult, projectId);
|
|
97179
97279
|
const dismissBlockParams = useCallback194(() => setActiveBlockParams(null), [setActiveBlockParams]);
|
|
97180
97280
|
const setRightPanelTab = useDismissingTabSetter(panelLayout.setRightPanelTab, dismissBlockParams);
|
|
97181
|
-
const layout =
|
|
97281
|
+
const layout = useMemo89(
|
|
97182
97282
|
() => ({ ...panelLayout, setRightPanelTab }),
|
|
97183
97283
|
[panelLayout, setRightPanelTab]
|
|
97184
97284
|
);
|
|
@@ -97227,9 +97327,9 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
|
|
|
97227
97327
|
handleDuplicate,
|
|
97228
97328
|
onResetKeyframes: () => resetKeyframesRef.current(),
|
|
97229
97329
|
onDeleteSelectedKeyframes: () => deleteSelectedKeyframesRef.current(),
|
|
97230
|
-
onAfterUndoRedo: (
|
|
97330
|
+
onAfterUndoRedo: (restore2) => {
|
|
97231
97331
|
invalidateGsapCacheRef.current();
|
|
97232
|
-
timelineEditing.restoreLiveLanes(
|
|
97332
|
+
timelineEditing.restoreLiveLanes(restore2);
|
|
97233
97333
|
},
|
|
97234
97334
|
onGroupSelection: () => domEditSessionRef.current.handleGroupSelection(),
|
|
97235
97335
|
onUngroupSelection: () => domEditSessionRef.current.handleUngroupSelection(),
|
|
@@ -97403,7 +97503,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
|
|
|
97403
97503
|
handlePreviewIframeRef,
|
|
97404
97504
|
refreshPreviewDocumentVersion
|
|
97405
97505
|
});
|
|
97406
|
-
const timelineToolbar =
|
|
97506
|
+
const timelineToolbar = useMemo89(
|
|
97407
97507
|
() => /* @__PURE__ */ jsx244(
|
|
97408
97508
|
TimelineToolbar,
|
|
97409
97509
|
{
|
|
@@ -97912,7 +98012,7 @@ function findElementBlock(html2, elementId2) {
|
|
|
97912
98012
|
}
|
|
97913
98013
|
|
|
97914
98014
|
// src/hooks/useDomStyleCommit.ts
|
|
97915
|
-
import { useCallback as useCallback196, useMemo as
|
|
98015
|
+
import { useCallback as useCallback196, useMemo as useMemo90, useRef as useRef189, useState as useState160 } from "react";
|
|
97916
98016
|
var noop = () => {
|
|
97917
98017
|
};
|
|
97918
98018
|
function useDomStyleCommit({
|
|
@@ -97928,7 +98028,7 @@ function useDomStyleCommit({
|
|
|
97928
98028
|
const [queue2] = useState160(createDomEditSaveQueue);
|
|
97929
98029
|
useMountEffect(() => () => queue2.destroy());
|
|
97930
98030
|
const versions = useRef189(/* @__PURE__ */ new Map()).current;
|
|
97931
|
-
const editHistory =
|
|
98031
|
+
const editHistory = useMemo90(() => ({ recordEdit }), [recordEdit]);
|
|
97932
98032
|
const persistDomEditOperations = useDomEditPersist({
|
|
97933
98033
|
activeCompPath,
|
|
97934
98034
|
previewIframeRef: iframeRef,
|
|
@@ -97960,7 +98060,7 @@ function useDomStyleCommit({
|
|
|
97960
98060
|
}
|
|
97961
98061
|
|
|
97962
98062
|
// src/hooks/useDomGeometryCommit.ts
|
|
97963
|
-
import { useCallback as useCallback197, useMemo as
|
|
98063
|
+
import { useCallback as useCallback197, useMemo as useMemo91, useRef as useRef190, useState as useState161 } from "react";
|
|
97964
98064
|
var noop2 = () => {
|
|
97965
98065
|
};
|
|
97966
98066
|
var NO_SELECTED_ANIMATIONS = [];
|
|
@@ -97976,18 +98076,14 @@ function useDomGeometryCommit({
|
|
|
97976
98076
|
const projectIdRef = useRef190(projectId);
|
|
97977
98077
|
projectIdRef.current = projectId;
|
|
97978
98078
|
const pending2 = useRef190(/* @__PURE__ */ new Set()).current;
|
|
97979
|
-
const editHistory =
|
|
97980
|
-
const reload = useCallback197(
|
|
97981
|
-
() => reloadPreview ? reloadPreview() : iframeRef.current?.contentWindow?.location.reload(),
|
|
97982
|
-
[reloadPreview, iframeRef]
|
|
97983
|
-
);
|
|
98079
|
+
const editHistory = useMemo91(() => ({ recordEdit }), [recordEdit]);
|
|
97984
98080
|
const { bump: bumpGsapCache } = useGsapCacheVersion();
|
|
97985
98081
|
const gsap3 = useGsapScriptCommits({
|
|
97986
98082
|
projectIdRef,
|
|
97987
98083
|
activeCompPath,
|
|
97988
98084
|
previewIframeRef: iframeRef,
|
|
97989
98085
|
editHistory,
|
|
97990
|
-
reloadPreview
|
|
98086
|
+
reloadPreview,
|
|
97991
98087
|
onCacheInvalidate: bumpGsapCache,
|
|
97992
98088
|
showToast,
|
|
97993
98089
|
writeProjectFile
|
|
@@ -98033,6 +98129,7 @@ function useDomGeometryCommit({
|
|
|
98033
98129
|
domEditSelection: null,
|
|
98034
98130
|
selectedGsapAnimations: NO_SELECTED_ANIMATIONS,
|
|
98035
98131
|
gsapCommitMutation: gsap3.commitMutation,
|
|
98132
|
+
activeCompPath,
|
|
98036
98133
|
previewIframeRef: iframeRef,
|
|
98037
98134
|
showToast,
|
|
98038
98135
|
bumpGsapCache,
|
|
@@ -98048,10 +98145,10 @@ function useDomGeometryCommit({
|
|
|
98048
98145
|
updateArcSegment: gsap3.updateArcSegment
|
|
98049
98146
|
});
|
|
98050
98147
|
const saved2 = useCallback197(
|
|
98051
|
-
async (commit,
|
|
98148
|
+
async (commit, restore2 = noop2) => {
|
|
98052
98149
|
const refusal2 = !projectIdRef.current ? "No project is open" : !isPreviewBooted(projectIdRef.current) ? "The preview for this project has not loaded yet" : null;
|
|
98053
98150
|
if (refusal2) {
|
|
98054
|
-
|
|
98151
|
+
restore2();
|
|
98055
98152
|
showToast(refusal2, "error");
|
|
98056
98153
|
throw new Error(refusal2);
|
|
98057
98154
|
}
|
|
@@ -98065,11 +98162,11 @@ function useDomGeometryCommit({
|
|
|
98065
98162
|
},
|
|
98066
98163
|
[pending2, showToast]
|
|
98067
98164
|
);
|
|
98068
|
-
return
|
|
98165
|
+
return useMemo91(
|
|
98069
98166
|
() => ({
|
|
98070
98167
|
commitPathOffset: (selection, next, modifiers) => saved2(() => handleGsapAwarePathOffsetCommit(selection, next, modifiers)),
|
|
98071
98168
|
commitGroupPathOffset: (updates) => saved2(() => handleGsapAwareGroupPathOffsetCommit(updates)),
|
|
98072
|
-
commitBoxSize: (selection, next, offset,
|
|
98169
|
+
commitBoxSize: (selection, next, offset, restore2, route) => saved2(() => handleGsapAwareBoxSizeCommit(selection, next, offset, restore2, route), restore2),
|
|
98073
98170
|
commitRotation: (selection, next) => saved2(() => handleGsapAwareRotationCommit(selection, next)),
|
|
98074
98171
|
waitForPendingSaves: () => Promise.allSettled([...pending2]).then(noop2)
|
|
98075
98172
|
}),
|
|
@@ -98093,6 +98190,7 @@ export {
|
|
|
98093
98190
|
AudioMeterStrip,
|
|
98094
98191
|
AudioWaveform,
|
|
98095
98192
|
Button,
|
|
98193
|
+
ColorField,
|
|
98096
98194
|
CompositionBreadcrumb,
|
|
98097
98195
|
CompositionThumbnail,
|
|
98098
98196
|
ConnectedDomEditOverlay,
|
|
@@ -98104,6 +98202,7 @@ export {
|
|
|
98104
98202
|
EditorShell,
|
|
98105
98203
|
ExternalFileConflictBanner,
|
|
98106
98204
|
FileTree,
|
|
98205
|
+
GradientField,
|
|
98107
98206
|
GridOverlay,
|
|
98108
98207
|
HyperframesLoader,
|
|
98109
98208
|
HyperframesLogo,
|
|
@@ -98223,6 +98322,7 @@ export {
|
|
|
98223
98322
|
useProjectFileWriter,
|
|
98224
98323
|
useRenderClipContent,
|
|
98225
98324
|
useRenderQueue,
|
|
98325
|
+
useThumbnailStill,
|
|
98226
98326
|
useTimelineContext,
|
|
98227
98327
|
useTimelineEditing,
|
|
98228
98328
|
useTimelinePlayer
|