@hyperframes/studio 0.8.114 → 0.8.116
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-aXNJKiIg.js → hyperframes-player-mGYvPVpw.js} +1 -1
- package/dist/assets/{index-Bn2RJNTJ.js → index-BWHt0-ir.js} +1 -1
- package/dist/assets/{index-Dz7doz9l.js → index-DYNaXPVf.js} +203 -203
- package/dist/assets/{index-C9N-DGjC.js → index-Dr3hNpvS.js} +1 -1
- package/dist/assets/{index-BYnsJnJV.css → index-t1LH6H5H.css} +1 -1
- package/dist/{chunk-BIUXP3PG.js → chunk-3F47EFCT.js} +142 -277
- package/dist/chunk-3F47EFCT.js.map +1 -0
- package/dist/{chunk-K47E2ZJA.js → chunk-5EKWJZLM.js} +504 -17
- package/dist/chunk-5EKWJZLM.js.map +1 -0
- package/dist/{chunk-VWT7STY3.js → chunk-J6Q572FA.js} +2 -2
- package/dist/{domEditingLayers-V67MS4WP.js → domEditingLayers-OL7GLSS4.js} +3 -5
- package/dist/index.d.ts +146 -40
- package/dist/index.html +2 -2
- package/dist/index.js +4021 -3591
- package/dist/index.js.map +1 -1
- package/dist/player.js +2 -2
- package/dist/styles.css +1 -1
- package/package.json +9 -8
- package/src/App.tsx +2 -2
- package/src/captions/hooks/useCaptionSync.ts +10 -6
- package/src/components/LinkedSelectionToggle.tsx +3 -2
- package/src/components/ProjectUnreachableBanner.tsx +3 -3
- package/src/components/dock/Dock.tsx +5 -7
- package/src/components/editor/CanvasContextMenu.tsx +3 -2
- package/src/components/editor/DomEditCropHandles.test.tsx +27 -0
- package/src/components/editor/DomEditCropHandles.tsx +7 -4
- package/src/components/editor/DomEditOverlay.tsx +5 -1
- package/src/components/editor/FileTreeNodes.tsx +13 -10
- package/src/components/editor/MotionPathOverlay.tsx +7 -2
- package/src/components/editor/PropertyPanelEmptyState.tsx +12 -2
- package/src/components/editor/SnapToolbar.test.tsx +100 -1
- package/src/components/editor/SnapToolbar.tsx +43 -5
- package/src/components/editor/anchoredResizeCommitFeedsOffset.test.ts +48 -1
- package/src/components/editor/audioFxTelemetry.ts +2 -3
- package/src/components/editor/commitPreflightHandles.test.tsx +2 -2
- package/src/components/editor/cropResize.ts +4 -3
- package/src/components/editor/domEditingDom.ts +4 -0
- package/src/components/editor/domEditingLayers.ts +1 -5
- 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/manualEdits.ts +1 -0
- package/src/components/editor/manualEditsParsing.ts +7 -0
- package/src/components/editor/manualOffsetDrag.ts +5 -6
- package/src/components/editor/marqueeCommit.test.tsx +106 -37
- package/src/components/editor/marqueeCommit.ts +24 -8
- package/src/components/editor/motionPathCommit.test.ts +29 -1
- package/src/components/editor/motionPathCommit.ts +21 -9
- package/src/components/editor/motionPathSelection.test.ts +6 -31
- package/src/components/editor/probeSourceElement.test.ts +45 -0
- package/src/components/editor/probeSourceElement.ts +41 -16
- package/src/components/editor/propertyPanelFlatTextSection.tsx +10 -7
- package/src/components/editor/propertyPanelFont.tsx +3 -2
- package/src/components/editor/propertyPanelFxAddMenu.tsx +11 -2
- package/src/components/editor/propertyPanelFxCarveModule.tsx +15 -5
- package/src/components/editor/propertyPanelFxNodeOpenBody.tsx +3 -0
- package/src/components/editor/propertyPanelFxNodeParams.tsx +15 -2
- package/src/components/editor/propertyPanelFxNodeRow.tsx +3 -0
- package/src/components/editor/propertyPanelFxPresetRun.tsx +10 -2
- package/src/components/editor/propertyPanelFxSection.test.tsx +56 -0
- package/src/components/editor/propertyPanelFxSection.tsx +30 -28
- package/src/components/editor/propertyPanelTypes.ts +6 -3
- package/src/components/editor/useColorGradingController.ts +2 -1
- package/src/components/editor/useDomEditNudge.test.tsx +37 -0
- package/src/components/editor/useDomEditNudge.ts +2 -0
- package/src/components/editor/useDomEditOverlayGestures.ts +5 -0
- package/src/components/editor/useDomEditZOrder.ts +12 -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/nle/useTimelineEditCallbacks.test.tsx +1 -0
- package/src/components/nle/useTimelineEditCallbacks.ts +7 -1
- package/src/components/renders/useFfmpegStatus.ts +2 -1
- package/src/components/renders/useRenderQueue.ts +5 -4
- package/src/components/sidebar/AssetContextMenu.tsx +4 -4
- package/src/components/sidebar/AssetsTab.tsx +2 -1
- package/src/components/sidebar/GlobalAssetsView.tsx +2 -1
- package/src/components/ui/menuStyle.test.ts +61 -0
- package/src/components/ui/menuStyle.ts +10 -0
- package/src/contexts/DomEditContext.tsx +5 -1
- package/src/contexts/TimelineEditContext.tsx +1 -0
- package/src/domEditExports.test.tsx +9 -1
- package/src/hooks/domEditCommitRunner.ts +1 -1
- 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/elementOffsetStager.ts +5 -4
- package/src/hooks/gsapDragCommit.test.ts +75 -164
- package/src/hooks/gsapDragCommit.ts +4 -125
- package/src/hooks/gsapDragPositionCommit.ts +36 -384
- package/src/hooks/gsapEditOutcome.ts +22 -2
- package/src/hooks/gsapGestureOutcome.test.ts +161 -0
- package/src/hooks/gsapGestureOutcome.ts +64 -0
- package/src/hooks/gsapGroupPreflight.ts +34 -0
- package/src/hooks/gsapKeyframeCommit.ts +3 -12
- 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 +96 -11
- package/src/hooks/gsapResizeIntercept.ts +79 -168
- 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/gsapScriptCommitHelpers.ts +2 -1
- package/src/hooks/gsapScriptCommitTypes.ts +13 -0
- package/src/hooks/gsapShared.ts +7 -0
- package/src/hooks/gsapShared.writeTarget.test.ts +45 -57
- 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 +8 -6
- package/src/hooks/newTweenTarget.test.ts +41 -76
- package/src/hooks/plainRotation.ts +3 -2
- package/src/hooks/previewPromotionSelection.test.tsx +1 -1
- package/src/hooks/refreshTimelineRowText.test.ts +65 -0
- package/src/hooks/refreshTimelineRowText.ts +29 -0
- package/src/hooks/timelineTimingSync.test.ts +1 -0
- package/src/hooks/timelineTimingSync.ts +2 -1
- package/src/hooks/useAnimatedPropertyCommit.test.tsx +46 -5
- package/src/hooks/useAnimatedPropertyCommit.ts +2 -2
- package/src/hooks/useAskAgentModal.ts +2 -1
- package/src/hooks/useBlockCatalog.ts +2 -1
- package/src/hooks/useBlockedTimelineEditToast.ts +12 -3
- package/src/hooks/useCaptionDetection.ts +2 -1
- package/src/hooks/useCompositionContentLoader.ts +2 -1
- package/src/hooks/useDomEditCommitsHelpers.ts +3 -2
- package/src/hooks/useDomEditPositionPatchCommit.ts +18 -23
- package/src/hooks/useDomEditSession.test.tsx +30 -1
- package/src/hooks/useDomEditSession.ts +10 -0
- package/src/hooks/useDomEditTextCommits.test.tsx +41 -0
- package/src/hooks/useDomEditTextCommits.ts +13 -3
- package/src/hooks/useDomEditWiring.ts +4 -2
- package/src/hooks/useDomGeometryCommit.test.tsx +25 -6
- package/src/hooks/useDomGeometryCommit.ts +8 -4
- package/src/hooks/useDomGeometryCommits.test.tsx +1 -0
- package/src/hooks/useDomGeometryCommits.ts +3 -2
- package/src/hooks/useDomSelection.test.ts +26 -0
- package/src/hooks/useDomSelection.ts +10 -3
- package/src/hooks/useDomSelectionTypes.ts +6 -1
- 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 +34 -3
- package/src/hooks/useEnableKeyframes.ts +7 -4
- package/src/hooks/useExternalFileChangeCoordinator.test.tsx +121 -0
- package/src/hooks/useExternalFileChangeCoordinator.ts +35 -8
- 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.ts +3 -2
- package/src/hooks/useGestureCommit.test.tsx +225 -55
- package/src/hooks/useGestureCommit.ts +86 -55
- package/src/hooks/useGroupCommits.ts +2 -1
- package/src/hooks/useGsapAwareEditing.groupPlan.test.tsx +160 -0
- package/src/hooks/useGsapAwareEditing.test.tsx +28 -35
- package/src/hooks/useGsapAwareEditing.testHelpers.tsx +42 -0
- package/src/hooks/useGsapAwareEditing.ts +101 -195
- package/src/hooks/useGsapAwareGroupMove.ts +179 -0
- package/src/hooks/useGsapInteractionFailureTelemetry.ts +10 -4
- package/src/hooks/useGsapKeyframeOps.sdkUsage.test.tsx +114 -0
- package/src/hooks/useGsapKeyframeOps.ts +36 -10
- package/src/hooks/useGsapScriptCommits.test.tsx +60 -1
- package/src/hooks/useGsapScriptCommits.ts +15 -10
- package/src/hooks/useGsapSelectionHandlers.test.tsx +1 -1
- package/src/hooks/useGsapSelectionHandlers.ts +24 -18
- package/src/hooks/useGsapTweenCache.ts +18 -12
- package/src/hooks/useLintModal.ts +2 -1
- package/src/hooks/usePersistentEditHistory.test.ts +277 -1
- package/src/hooks/usePersistentEditHistory.ts +47 -8
- package/src/hooks/usePopulateKeyframeCacheForFile.test.tsx +76 -2
- package/src/hooks/usePreviewInteraction.test.ts +21 -0
- package/src/hooks/usePreviewInteraction.ts +2 -0
- 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/useRenderClipContent.test.ts +18 -0
- package/src/hooks/useRenderClipContent.ts +4 -5
- 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.test.tsx +30 -22
- package/src/hooks/useTimelineDeleteOps.ts +22 -27
- package/src/hooks/useTimelineEditing.test.tsx +1 -1
- package/src/index.ts +4 -0
- package/src/player/components/AudioWaveform.tsx +3 -2
- package/src/player/components/AutomationSelectionMenu.tsx +3 -2
- package/src/player/components/ClipContextMenu.test.tsx +73 -0
- package/src/player/components/ClipContextMenu.tsx +33 -31
- package/src/player/components/CompositionThumbnail.tsx +2 -1
- package/src/player/components/KeyframeDiamondContextMenu.tsx +8 -9
- package/src/player/components/OutOfSyncBadge.tsx +5 -5
- package/src/player/components/TextClipContent.test.ts +19 -0
- package/src/player/components/TextClipContent.tsx +27 -0
- package/src/player/components/TrackGapContextMenu.tsx +3 -6
- package/src/player/components/clipMenuNormalize.ts +2 -1
- package/src/player/components/clipMenuToolItems.test.tsx +23 -0
- package/src/player/components/clipMenuToolItems.tsx +13 -9
- package/src/player/components/clipPeakMap.ts +2 -1
- package/src/player/components/timelineCallbacks.ts +1 -0
- package/src/player/components/timelineClipDragGestureLifecycle.ts +1 -1
- package/src/player/components/timelineClipGestureHandlers.test.ts +94 -0
- package/src/player/components/timelineClipGestureHandlers.ts +20 -2
- package/src/player/components/timelineEditing.ts +3 -1
- package/src/player/components/timelineLinkSelection.ts +35 -1
- package/src/player/components/useTimelineClipDrag.keyboard.test.tsx +14 -0
- package/src/player/components/useTimelineClipDrag.ts +6 -7
- package/src/player/hooks/timelinePlayerSync.test.ts +6 -0
- package/src/player/hooks/timelinePlayerSync.ts +6 -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/lib/timelineElementHelpers.test.ts +87 -1
- package/src/player/lib/timelineElementHelpers.ts +6 -15
- package/src/player/lib/timelineText.ts +40 -0
- package/src/player/sceneSwap.ts +2 -1
- package/src/player/store/timelineElement.ts +12 -0
- package/src/styles/theme.css +3 -0
- 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.ts +2 -3
- package/src/utils/gsapUndoRestore.browser.test.ts +51 -45
- package/src/utils/gsapUndoRestore.ts +3 -6
- package/src/utils/keyframeUsage.ts +79 -0
- package/src/utils/previewFeatureUsage.test.ts +32 -0
- package/src/utils/previewFeatureUsage.ts +46 -0
- package/src/utils/previewPrefetch.ts +2 -1
- package/src/utils/razorSplitTransaction.ts +12 -8
- package/src/utils/softReloadTargets.ts +1 -3
- package/src/utils/studioApiFetch.test.ts +75 -0
- package/src/utils/studioApiFetch.ts +13 -0
- package/src/utils/studioFileHistory.ts +4 -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.test.ts +107 -0
- package/src/utils/studioTelemetry.ts +51 -5
- package/src/utils/timelineCompositionInsert.ts +3 -2
- package/src/webmcp/StudioAgentTools.tsx +4 -3
- package/dist/chunk-BIUXP3PG.js.map +0 -1
- package/dist/chunk-K47E2ZJA.js.map +0 -1
- /package/dist/{chunk-VWT7STY3.js.map → chunk-J6Q572FA.js.map} +0 -0
- /package/dist/{domEditingLayers-V67MS4WP.js.map → domEditingLayers-OL7GLSS4.js.map} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { act } from "react";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
|
|
6
|
+
import type { DomEditSelection } from "../components/editor/domEditingTypes";
|
|
7
|
+
import type { DomEditGroupPathOffsetCommit } from "../components/editor/domEditOverlayGestures";
|
|
8
|
+
import { usePlayerStore } from "../player/store/playerStore";
|
|
9
|
+
import { trackStudioEditBlocked } from "../utils/studioSaveDiagnostics";
|
|
10
|
+
import { GSAP_EDIT_BLOCK_COPY } from "./gsapEditOutcome";
|
|
11
|
+
import { mountGsapAwareEditing } from "./useGsapAwareEditing.testHelpers";
|
|
12
|
+
|
|
13
|
+
vi.mock("../utils/studioSaveDiagnostics", async (importOriginal) => ({
|
|
14
|
+
...(await importOriginal<typeof import("../utils/studioSaveDiagnostics")>()),
|
|
15
|
+
trackStudioEditBlocked: vi.fn(),
|
|
16
|
+
trackStudioSaveFailure: vi.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
usePlayerStore.setState({ autoKeyframeEnabled: true, currentTime: 1, activeKeyframePct: null });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
vi.clearAllMocks();
|
|
27
|
+
document.body.innerHTML = "";
|
|
28
|
+
Reflect.deleteProperty(window, "__timelines");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const positionTween = (id: string, extra: Partial<GsapAnimation> = {}) =>
|
|
32
|
+
({
|
|
33
|
+
id: `#${id}-to-0-position`,
|
|
34
|
+
targetSelector: `#${id}`,
|
|
35
|
+
propertyGroup: "position",
|
|
36
|
+
method: "to",
|
|
37
|
+
properties: { x: 100 },
|
|
38
|
+
position: 0,
|
|
39
|
+
resolvedStart: 0,
|
|
40
|
+
duration: 2,
|
|
41
|
+
ease: "none",
|
|
42
|
+
...extra,
|
|
43
|
+
}) as unknown as GsapAnimation;
|
|
44
|
+
|
|
45
|
+
/** Three members GSAP is visibly moving, one `to` tween each in the file. */
|
|
46
|
+
function mountGroup(animations: GsapAnimation[]) {
|
|
47
|
+
const elements = ["a", "b", "c"].map((id) => {
|
|
48
|
+
const el = Object.assign(document.createElement("div"), { id });
|
|
49
|
+
document.body.append(el);
|
|
50
|
+
return el;
|
|
51
|
+
});
|
|
52
|
+
const live = elements.map((el) => ({
|
|
53
|
+
targets: () => [el],
|
|
54
|
+
vars: { x: 100, duration: 2 },
|
|
55
|
+
duration: () => 2,
|
|
56
|
+
startTime: () => 0,
|
|
57
|
+
}));
|
|
58
|
+
const timelines = { root: { getChildren: () => live, duration: () => 2 } };
|
|
59
|
+
Reflect.set(window, "__timelines", timelines);
|
|
60
|
+
const iframe = {
|
|
61
|
+
contentWindow: { gsap: { getProperty: () => 0, set: vi.fn() }, __timelines: timelines },
|
|
62
|
+
contentDocument: document,
|
|
63
|
+
} as unknown as HTMLIFrameElement;
|
|
64
|
+
const commitMutation = Object.assign(vi.fn().mockResolvedValue(undefined), {
|
|
65
|
+
batch: vi.fn().mockResolvedValue(undefined),
|
|
66
|
+
});
|
|
67
|
+
const showToast = vi.fn();
|
|
68
|
+
const stageElementPositionOffset = vi.fn(() => ({ save: vi.fn(), rollback: vi.fn() }));
|
|
69
|
+
const { editing, root } = mountGsapAwareEditing({
|
|
70
|
+
gsapCommitMutation: commitMutation,
|
|
71
|
+
activeCompPath: "index.html",
|
|
72
|
+
previewIframeRef: { current: iframe },
|
|
73
|
+
showToast,
|
|
74
|
+
makeFetchFallback: () => async () => animations,
|
|
75
|
+
stageElementPositionOffset,
|
|
76
|
+
});
|
|
77
|
+
const groupCommit = (updates: DomEditGroupPathOffsetCommit[]) =>
|
|
78
|
+
editing().handleGsapAwareGroupPathOffsetCommit(updates);
|
|
79
|
+
const updates = elements.map((element) => ({
|
|
80
|
+
selection: {
|
|
81
|
+
element,
|
|
82
|
+
id: element.id,
|
|
83
|
+
selector: `#${element.id}`,
|
|
84
|
+
} as unknown as DomEditSelection,
|
|
85
|
+
next: { x: 30, y: 0 },
|
|
86
|
+
}));
|
|
87
|
+
const written = () => [
|
|
88
|
+
...commitMutation.mock.calls.map((call) => call[1]),
|
|
89
|
+
...commitMutation.batch.mock.calls.flatMap(([calls]) =>
|
|
90
|
+
calls.map((c: { mutation: unknown }) => c.mutation),
|
|
91
|
+
),
|
|
92
|
+
];
|
|
93
|
+
return { elements, iframe, updates, groupCommit, commitMutation, written, showToast, root };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
describe("a group drag plans every member before its first write", () => {
|
|
97
|
+
it("refuses the whole group when one member's tween cannot take a keyframe", async () => {
|
|
98
|
+
const h = mountGroup([
|
|
99
|
+
positionTween("a"),
|
|
100
|
+
positionTween("b", { ease: undefined }),
|
|
101
|
+
positionTween("c"),
|
|
102
|
+
]);
|
|
103
|
+
const styles = h.elements.map((el) => el.getAttribute("style"));
|
|
104
|
+
|
|
105
|
+
await expect(h.groupCommit(h.updates)).rejects.toMatchObject({
|
|
106
|
+
name: "GsapEditBlockedError",
|
|
107
|
+
reason: "keyframes-uneditable",
|
|
108
|
+
detail: "unknown-ease",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(h.written()).toEqual([]);
|
|
112
|
+
expect(h.commitMutation.batch).not.toHaveBeenCalled();
|
|
113
|
+
const gsap = (h.iframe.contentWindow as unknown as { gsap: { set: unknown } }).gsap;
|
|
114
|
+
expect(gsap.set).not.toHaveBeenCalled();
|
|
115
|
+
expect(h.elements.map((el) => el.getAttribute("style"))).toEqual(styles);
|
|
116
|
+
expect(h.showToast).toHaveBeenCalledTimes(1);
|
|
117
|
+
expect(h.showToast).toHaveBeenCalledWith(GSAP_EDIT_BLOCK_COPY["keyframes-uneditable"], "error");
|
|
118
|
+
expect(trackStudioEditBlocked).toHaveBeenCalledTimes(1);
|
|
119
|
+
expect(trackStudioEditBlocked).toHaveBeenCalledWith(expect.objectContaining({ targetId: "b" }));
|
|
120
|
+
act(() => h.root.unmount());
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("refuses members animated in two files, since the batch writes one file", async () => {
|
|
124
|
+
const h = mountGroup(["a", "b", "c"].map((id) => positionTween(id)));
|
|
125
|
+
const updates = h.updates.map((u, i) => ({
|
|
126
|
+
...u,
|
|
127
|
+
selection: { ...u.selection, sourceFile: i === 2 ? "scenes/intro.html" : "index.html" },
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
await expect(h.groupCommit(updates)).rejects.toMatchObject({ reason: "mixed-files" });
|
|
131
|
+
|
|
132
|
+
expect(h.written()).toEqual([]);
|
|
133
|
+
expect(trackStudioEditBlocked).toHaveBeenCalledWith(expect.objectContaining({ targetId: "c" }));
|
|
134
|
+
act(() => h.root.unmount());
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("writes a group whose members name the open file or leave it implied", async () => {
|
|
138
|
+
const h = mountGroup(["a", "b", "c"].map((id) => positionTween(id)));
|
|
139
|
+
const updates = h.updates.map((u, i) => ({
|
|
140
|
+
...u,
|
|
141
|
+
selection: { ...u.selection, sourceFile: i === 0 ? "" : "index.html" },
|
|
142
|
+
}));
|
|
143
|
+
|
|
144
|
+
await h.groupCommit(updates);
|
|
145
|
+
|
|
146
|
+
expect(h.written()).toHaveLength(3);
|
|
147
|
+
act(() => h.root.unmount());
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("writes every member when each plan holds", async () => {
|
|
151
|
+
const h = mountGroup(["a", "b", "c"].map((id) => positionTween(id)));
|
|
152
|
+
|
|
153
|
+
await h.groupCommit(h.updates);
|
|
154
|
+
|
|
155
|
+
const ids = h.written().map((m) => (m as { animationId?: string }).animationId);
|
|
156
|
+
expect(ids).toEqual(["#a-to-0-position", "#b-to-0-position", "#c-to-0-position"]);
|
|
157
|
+
expect(h.showToast).not.toHaveBeenCalled();
|
|
158
|
+
act(() => h.root.unmount());
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -74,7 +74,7 @@ function mountResizeHandler(
|
|
|
74
74
|
size: { width: number; height: number },
|
|
75
75
|
offset?: { x: number; y: number },
|
|
76
76
|
restore?: () => void,
|
|
77
|
-
) => Promise<void>)
|
|
77
|
+
) => Promise<void | import("../utils/previewFeatureUsage").GeometryCommitResult>)
|
|
78
78
|
| null = null;
|
|
79
79
|
let property: ReturnType<typeof useGsapAwareEditing>["commitAnimatedProperty"] | null = null;
|
|
80
80
|
function Harness() {
|
|
@@ -119,25 +119,33 @@ function mountResizeHandler(
|
|
|
119
119
|
type AwareEditingParams = Parameters<typeof useGsapAwareEditing>[0];
|
|
120
120
|
|
|
121
121
|
function mountGroupHandler({
|
|
122
|
-
gsapCommitMutation,
|
|
123
|
-
makeFetchFallback,
|
|
122
|
+
gsapCommitMutation = vi.fn().mockResolvedValue(undefined),
|
|
123
|
+
makeFetchFallback = () => vi.fn().mockResolvedValue([]),
|
|
124
124
|
trackGsapInteractionFailure = vi.fn(),
|
|
125
125
|
stageElementPositionOffset = vi.fn(),
|
|
126
126
|
handleDomRotationCommit = vi.fn(),
|
|
127
|
-
}:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
}: Partial<
|
|
128
|
+
Pick<
|
|
129
|
+
AwareEditingParams,
|
|
130
|
+
| "gsapCommitMutation"
|
|
131
|
+
| "makeFetchFallback"
|
|
132
|
+
| "trackGsapInteractionFailure"
|
|
133
|
+
| "stageElementPositionOffset"
|
|
134
|
+
| "handleDomRotationCommit"
|
|
135
|
+
>
|
|
136
|
+
> = {}) {
|
|
137
|
+
let groupCommit!: (
|
|
138
|
+
updates: DomEditGroupPathOffsetCommit[],
|
|
139
|
+
) => Promise<void | import("../utils/previewFeatureUsage").GeometryCommitResult>;
|
|
135
140
|
let pathOffsetCommit!: (
|
|
136
141
|
selection: DomEditSelection,
|
|
137
142
|
next: { x: number; y: number },
|
|
138
143
|
route?: { plainTranslate?: boolean },
|
|
139
|
-
) => Promise<void>;
|
|
140
|
-
let rotationCommit!: (
|
|
144
|
+
) => Promise<void | import("../utils/previewFeatureUsage").GeometryCommitResult>;
|
|
145
|
+
let rotationCommit!: (
|
|
146
|
+
selection: DomEditSelection,
|
|
147
|
+
next: RotationCommit,
|
|
148
|
+
) => Promise<void | import("../utils/previewFeatureUsage").GeometryCommitResult>;
|
|
141
149
|
function Harness() {
|
|
142
150
|
const editing = useGsapAwareEditing({
|
|
143
151
|
domEditSelection: null,
|
|
@@ -211,8 +219,6 @@ describe("useGsapAwareEditing keeps the route a gesture chose at press", () => {
|
|
|
211
219
|
const save = vi.fn().mockResolvedValue(undefined);
|
|
212
220
|
const stageElementPositionOffset = vi.fn(() => ({ save, rollback: vi.fn() }));
|
|
213
221
|
const { pathOffsetCommit, groupCommit, root } = mountGroupHandler({
|
|
214
|
-
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
215
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
216
222
|
stageElementPositionOffset,
|
|
217
223
|
});
|
|
218
224
|
const box = {
|
|
@@ -231,11 +237,7 @@ describe("useGsapAwareEditing keeps the route a gesture chose at press", () => {
|
|
|
231
237
|
describe("useGsapAwareEditing refuses a group GSAP took over before writing any member", () => {
|
|
232
238
|
it("writes no member when one CSS-route member has been folded since the press", async () => {
|
|
233
239
|
const stageElementPositionOffset = vi.fn(() => ({ save: vi.fn(), rollback: vi.fn() }));
|
|
234
|
-
const { groupCommit, root } = mountGroupHandler({
|
|
235
|
-
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
236
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
237
|
-
stageElementPositionOffset,
|
|
238
|
-
});
|
|
240
|
+
const { groupCommit, root } = mountGroupHandler({ stageElementPositionOffset });
|
|
239
241
|
const card = {
|
|
240
242
|
element: document.createElement("div"),
|
|
241
243
|
id: "card",
|
|
@@ -263,7 +265,6 @@ describe("useGsapAwareEditing rotation routing", () => {
|
|
|
263
265
|
const gsapCommitMutation = vi.fn();
|
|
264
266
|
const h = mountGroupHandler({
|
|
265
267
|
gsapCommitMutation,
|
|
266
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
267
268
|
handleDomRotationCommit,
|
|
268
269
|
});
|
|
269
270
|
const box = { element, id: "box", selector: "#box" } as unknown as DomEditSelection;
|
|
@@ -327,7 +328,6 @@ describe("useGsapAwareEditing shared-tween moves", () => {
|
|
|
327
328
|
const commitMutation = vi.fn();
|
|
328
329
|
const { pathOffsetCommit, root } = mountGroupHandler({
|
|
329
330
|
gsapCommitMutation: commitMutation,
|
|
330
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
331
331
|
stageElementPositionOffset,
|
|
332
332
|
});
|
|
333
333
|
const word = { element: gsapPositioned("span"), hfId: "w0", selector: ".w" };
|
|
@@ -346,11 +346,7 @@ describe("useGsapAwareEditing shared-tween moves", () => {
|
|
|
346
346
|
});
|
|
347
347
|
const save = vi.fn().mockResolvedValue(undefined);
|
|
348
348
|
const stageElementPositionOffset = vi.fn(() => ({ save, rollback: vi.fn() }));
|
|
349
|
-
const { groupCommit, root } = mountGroupHandler({
|
|
350
|
-
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
351
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
352
|
-
stageElementPositionOffset,
|
|
353
|
-
});
|
|
349
|
+
const { groupCommit, root } = mountGroupHandler({ stageElementPositionOffset });
|
|
354
350
|
const word = { element: gsapPositioned("span"), hfId: "w0", selector: ".w" };
|
|
355
351
|
const box = { element: gsapPositioned("div"), id: "box", selector: "#box" };
|
|
356
352
|
const plain = { element: document.createElement("div"), id: "plain", selector: "#plain" };
|
|
@@ -401,6 +397,7 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
401
397
|
null,
|
|
402
398
|
expect.any(Function),
|
|
403
399
|
expect.any(Function),
|
|
400
|
+
undefined,
|
|
404
401
|
);
|
|
405
402
|
act(() => h.root.unmount());
|
|
406
403
|
});
|
|
@@ -589,7 +586,7 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
589
586
|
h.selection.element.setAttribute("data-hf-drag-initial-offset-x", "0");
|
|
590
587
|
h.selection.element.setAttribute("data-hf-drag-initial-offset-y", "0");
|
|
591
588
|
|
|
592
|
-
let commit!: Promise<void>;
|
|
589
|
+
let commit!: Promise<void | import("../utils/previewFeatureUsage").GeometryCommitResult>;
|
|
593
590
|
act(() => {
|
|
594
591
|
commit = h.resize(h.selection, { width: 300, height: 200 }, { x: -50.2, y: -25.6 });
|
|
595
592
|
});
|
|
@@ -636,7 +633,7 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
636
633
|
s: DomEditSelection,
|
|
637
634
|
m: unknown,
|
|
638
635
|
o: { coalesceKey?: string; label?: string; softReload?: boolean },
|
|
639
|
-
) => Promise<void>,
|
|
636
|
+
) => Promise<void | import("../utils/previewFeatureUsage").GeometryCommitResult>,
|
|
640
637
|
_fetch: unknown,
|
|
641
638
|
options?: { preflightOnly?: boolean },
|
|
642
639
|
) => {
|
|
@@ -653,7 +650,6 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
653
650
|
);
|
|
654
651
|
const { groupCommit, root } = mountGroupHandler({
|
|
655
652
|
gsapCommitMutation: commitMutation,
|
|
656
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
657
653
|
});
|
|
658
654
|
const updates = [
|
|
659
655
|
{
|
|
@@ -754,11 +750,7 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
754
750
|
mocks.drag.mockImplementation(async (selection) => {
|
|
755
751
|
throw selection.id === "a" ? failures[0] : failures[1];
|
|
756
752
|
});
|
|
757
|
-
const { groupCommit, root } = mountGroupHandler({
|
|
758
|
-
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
759
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
760
|
-
trackGsapInteractionFailure,
|
|
761
|
-
});
|
|
753
|
+
const { groupCommit, root } = mountGroupHandler({ trackGsapInteractionFailure });
|
|
762
754
|
const updates = [
|
|
763
755
|
{
|
|
764
756
|
selection: { element: gsapPositioned("div"), id: "a", selector: "#a" },
|
|
@@ -777,6 +769,7 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
777
769
|
updates[0]?.selection,
|
|
778
770
|
"drag",
|
|
779
771
|
"Move animated layer (group)",
|
|
772
|
+
true,
|
|
780
773
|
);
|
|
781
774
|
mocks.drag.mockReset();
|
|
782
775
|
if (priorDragImplementation) mocks.drag.mockImplementation(priorDragImplementation);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { vi } from "vitest";
|
|
2
|
+
import { mountReactHarness } from "./domSelectionTestHarness";
|
|
3
|
+
import { useGsapAwareEditing } from "./useGsapAwareEditing";
|
|
4
|
+
import { useGsapInteractionFailureTelemetry } from "./useGsapInteractionFailureTelemetry";
|
|
5
|
+
|
|
6
|
+
type Params = Parameters<typeof useGsapAwareEditing>[0];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Mounts useGsapAwareEditing with no-op callbacks for every param not given, and the real
|
|
10
|
+
* failure telemetry toasting through `showToast`. `editing()` reads the latest render.
|
|
11
|
+
*/
|
|
12
|
+
export function mountGsapAwareEditing(
|
|
13
|
+
params: Partial<Omit<Params, "trackGsapInteractionFailure">> & Pick<Params, "showToast">,
|
|
14
|
+
) {
|
|
15
|
+
let editing!: ReturnType<typeof useGsapAwareEditing>;
|
|
16
|
+
function Harness() {
|
|
17
|
+
editing = useGsapAwareEditing({
|
|
18
|
+
domEditSelection: null,
|
|
19
|
+
selectedGsapAnimations: [],
|
|
20
|
+
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
21
|
+
previewIframeRef: { current: null },
|
|
22
|
+
bumpGsapCache: vi.fn(),
|
|
23
|
+
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
24
|
+
stageElementPositionOffset: vi.fn(),
|
|
25
|
+
handleDomBoxSizeCommit: vi.fn(),
|
|
26
|
+
handleDomRotationCommit: vi.fn(),
|
|
27
|
+
commitPositionPatchToHtml: vi.fn(),
|
|
28
|
+
addGsapAnimation: vi.fn(),
|
|
29
|
+
convertToKeyframes: vi.fn(),
|
|
30
|
+
setArcPath: vi.fn(),
|
|
31
|
+
updateArcSegment: vi.fn(),
|
|
32
|
+
...params,
|
|
33
|
+
trackGsapInteractionFailure: useGsapInteractionFailureTelemetry(
|
|
34
|
+
"index.html",
|
|
35
|
+
params.showToast,
|
|
36
|
+
),
|
|
37
|
+
});
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const root = mountReactHarness(<Harness />);
|
|
41
|
+
return { editing: () => editing, root };
|
|
42
|
+
}
|