@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
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type ElementMatchSelection,
|
|
17
17
|
} from "../utils/studioHelpers";
|
|
18
18
|
import type { TimelineElement } from "../player";
|
|
19
|
+
import { studioApiFetch } from "../utils/studioApiFetch";
|
|
19
20
|
|
|
20
21
|
interface UseGroupCommitsParams extends DomEditCommitBaseParams {
|
|
21
22
|
/** Resync the SDK session after a server-side write (the wrapper/unwrap changes
|
|
@@ -132,7 +133,7 @@ async function commitStructuralMutation(
|
|
|
132
133
|
writeFile: deps.writeProjectFile,
|
|
133
134
|
recordEdit: deps.editHistory.recordEdit,
|
|
134
135
|
rewrite: async (originalContent) => {
|
|
135
|
-
const mutateResponse = await
|
|
136
|
+
const mutateResponse = await studioApiFetch(
|
|
136
137
|
buildProjectApiPath(pid, `/file-mutations/${route}/${encodeURIComponent(targetPath)}`),
|
|
137
138
|
{
|
|
138
139
|
method: "POST",
|
|
@@ -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,6 +74,7 @@ function mountResizeHandler(
|
|
|
74
74
|
size: { width: number; height: number },
|
|
75
75
|
offset?: { x: number; y: number },
|
|
76
76
|
restore?: () => void,
|
|
77
|
+
route?: { plainTranslate: boolean },
|
|
77
78
|
) => Promise<void | import("../utils/previewFeatureUsage").GeometryCommitResult>)
|
|
78
79
|
| null = null;
|
|
79
80
|
let property: ReturnType<typeof useGsapAwareEditing>["commitAnimatedProperty"] | null = null;
|
|
@@ -119,18 +120,21 @@ function mountResizeHandler(
|
|
|
119
120
|
type AwareEditingParams = Parameters<typeof useGsapAwareEditing>[0];
|
|
120
121
|
|
|
121
122
|
function mountGroupHandler({
|
|
122
|
-
gsapCommitMutation,
|
|
123
|
-
makeFetchFallback,
|
|
123
|
+
gsapCommitMutation = vi.fn().mockResolvedValue(undefined),
|
|
124
|
+
makeFetchFallback = () => vi.fn().mockResolvedValue([]),
|
|
124
125
|
trackGsapInteractionFailure = vi.fn(),
|
|
125
126
|
stageElementPositionOffset = vi.fn(),
|
|
126
127
|
handleDomRotationCommit = vi.fn(),
|
|
127
|
-
}:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
}: Partial<
|
|
129
|
+
Pick<
|
|
130
|
+
AwareEditingParams,
|
|
131
|
+
| "gsapCommitMutation"
|
|
132
|
+
| "makeFetchFallback"
|
|
133
|
+
| "trackGsapInteractionFailure"
|
|
134
|
+
| "stageElementPositionOffset"
|
|
135
|
+
| "handleDomRotationCommit"
|
|
136
|
+
>
|
|
137
|
+
> = {}) {
|
|
134
138
|
let groupCommit!: (
|
|
135
139
|
updates: DomEditGroupPathOffsetCommit[],
|
|
136
140
|
) => Promise<void | import("../utils/previewFeatureUsage").GeometryCommitResult>;
|
|
@@ -216,8 +220,6 @@ describe("useGsapAwareEditing keeps the route a gesture chose at press", () => {
|
|
|
216
220
|
const save = vi.fn().mockResolvedValue(undefined);
|
|
217
221
|
const stageElementPositionOffset = vi.fn(() => ({ save, rollback: vi.fn() }));
|
|
218
222
|
const { pathOffsetCommit, groupCommit, root } = mountGroupHandler({
|
|
219
|
-
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
220
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
221
223
|
stageElementPositionOffset,
|
|
222
224
|
});
|
|
223
225
|
const box = {
|
|
@@ -233,14 +235,25 @@ describe("useGsapAwareEditing keeps the route a gesture chose at press", () => {
|
|
|
233
235
|
});
|
|
234
236
|
});
|
|
235
237
|
|
|
238
|
+
describe("useGsapAwareEditing keeps the box route a resize chose at press", () => {
|
|
239
|
+
it.each([
|
|
240
|
+
["the CSS route, on an element GSAP has since taken over", true],
|
|
241
|
+
["the GSAP route, on an element that now looks GSAP-free", false],
|
|
242
|
+
])("%s", async (_, plainTranslate) => {
|
|
243
|
+
mocks.resize.mockResolvedValue({ status: "persisted" });
|
|
244
|
+
const h = mountResizeHandler([], [], plainTranslate);
|
|
245
|
+
const size = { width: 300, height: 200 };
|
|
246
|
+
await act(() => h.resize(h.selection, size, undefined, vi.fn(), { plainTranslate }));
|
|
247
|
+
expect(h.fallback).toHaveBeenCalledTimes(plainTranslate ? 1 : 0);
|
|
248
|
+
expect(mocks.resize).toHaveBeenCalledTimes(plainTranslate ? 0 : 1);
|
|
249
|
+
act(() => h.root.unmount());
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
236
253
|
describe("useGsapAwareEditing refuses a group GSAP took over before writing any member", () => {
|
|
237
254
|
it("writes no member when one CSS-route member has been folded since the press", async () => {
|
|
238
255
|
const stageElementPositionOffset = vi.fn(() => ({ save: vi.fn(), rollback: vi.fn() }));
|
|
239
|
-
const { groupCommit, root } = mountGroupHandler({
|
|
240
|
-
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
241
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
242
|
-
stageElementPositionOffset,
|
|
243
|
-
});
|
|
256
|
+
const { groupCommit, root } = mountGroupHandler({ stageElementPositionOffset });
|
|
244
257
|
const card = {
|
|
245
258
|
element: document.createElement("div"),
|
|
246
259
|
id: "card",
|
|
@@ -268,7 +281,6 @@ describe("useGsapAwareEditing rotation routing", () => {
|
|
|
268
281
|
const gsapCommitMutation = vi.fn();
|
|
269
282
|
const h = mountGroupHandler({
|
|
270
283
|
gsapCommitMutation,
|
|
271
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
272
284
|
handleDomRotationCommit,
|
|
273
285
|
});
|
|
274
286
|
const box = { element, id: "box", selector: "#box" } as unknown as DomEditSelection;
|
|
@@ -300,6 +312,15 @@ describe("useGsapAwareEditing rotation routing", () => {
|
|
|
300
312
|
act(() => h.root.unmount());
|
|
301
313
|
});
|
|
302
314
|
|
|
315
|
+
it("saves a turn the press sent down the GSAP route there, even once the element looks GSAP-free", async () => {
|
|
316
|
+
vi.mocked(tryGsapRotationIntercept).mockResolvedValue({ status: "persisted" });
|
|
317
|
+
const h = rotate(document.createElement("div"));
|
|
318
|
+
await act(() => h.rotationCommit(h.box, { angle: 55, plain: null }));
|
|
319
|
+
expect(tryGsapRotationIntercept).toHaveBeenCalledTimes(1);
|
|
320
|
+
expect(h.handleDomRotationCommit).not.toHaveBeenCalled();
|
|
321
|
+
act(() => h.root.unmount());
|
|
322
|
+
});
|
|
323
|
+
|
|
303
324
|
it("saves a turn the press drew as CSS by the CSS writer, even once a GSAP tween turns the element", async () => {
|
|
304
325
|
const element = document.createElement("div");
|
|
305
326
|
const h = rotate(element);
|
|
@@ -332,7 +353,6 @@ describe("useGsapAwareEditing shared-tween moves", () => {
|
|
|
332
353
|
const commitMutation = vi.fn();
|
|
333
354
|
const { pathOffsetCommit, root } = mountGroupHandler({
|
|
334
355
|
gsapCommitMutation: commitMutation,
|
|
335
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
336
356
|
stageElementPositionOffset,
|
|
337
357
|
});
|
|
338
358
|
const word = { element: gsapPositioned("span"), hfId: "w0", selector: ".w" };
|
|
@@ -351,11 +371,7 @@ describe("useGsapAwareEditing shared-tween moves", () => {
|
|
|
351
371
|
});
|
|
352
372
|
const save = vi.fn().mockResolvedValue(undefined);
|
|
353
373
|
const stageElementPositionOffset = vi.fn(() => ({ save, rollback: vi.fn() }));
|
|
354
|
-
const { groupCommit, root } = mountGroupHandler({
|
|
355
|
-
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
356
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
357
|
-
stageElementPositionOffset,
|
|
358
|
-
});
|
|
374
|
+
const { groupCommit, root } = mountGroupHandler({ stageElementPositionOffset });
|
|
359
375
|
const word = { element: gsapPositioned("span"), hfId: "w0", selector: ".w" };
|
|
360
376
|
const box = { element: gsapPositioned("div"), id: "box", selector: "#box" };
|
|
361
377
|
const plain = { element: document.createElement("div"), id: "plain", selector: "#plain" };
|
|
@@ -406,6 +422,7 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
406
422
|
null,
|
|
407
423
|
expect.any(Function),
|
|
408
424
|
expect.any(Function),
|
|
425
|
+
undefined,
|
|
409
426
|
);
|
|
410
427
|
act(() => h.root.unmount());
|
|
411
428
|
});
|
|
@@ -658,7 +675,6 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
658
675
|
);
|
|
659
676
|
const { groupCommit, root } = mountGroupHandler({
|
|
660
677
|
gsapCommitMutation: commitMutation,
|
|
661
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
662
678
|
});
|
|
663
679
|
const updates = [
|
|
664
680
|
{
|
|
@@ -759,11 +775,7 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
759
775
|
mocks.drag.mockImplementation(async (selection) => {
|
|
760
776
|
throw selection.id === "a" ? failures[0] : failures[1];
|
|
761
777
|
});
|
|
762
|
-
const { groupCommit, root } = mountGroupHandler({
|
|
763
|
-
gsapCommitMutation: vi.fn().mockResolvedValue(undefined),
|
|
764
|
-
makeFetchFallback: () => vi.fn().mockResolvedValue([]),
|
|
765
|
-
trackGsapInteractionFailure,
|
|
766
|
-
});
|
|
778
|
+
const { groupCommit, root } = mountGroupHandler({ trackGsapInteractionFailure });
|
|
767
779
|
const updates = [
|
|
768
780
|
{
|
|
769
781
|
selection: { element: gsapPositioned("div"), id: "a", selector: "#a" },
|
|
@@ -782,6 +794,7 @@ describe("useGsapAwareEditing anchored resize", () => {
|
|
|
782
794
|
updates[0]?.selection,
|
|
783
795
|
"drag",
|
|
784
796
|
"Move animated layer (group)",
|
|
797
|
+
true,
|
|
785
798
|
);
|
|
786
799
|
mocks.drag.mockReset();
|
|
787
800
|
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
|
+
}
|
|
@@ -30,9 +30,7 @@ import { logResize, logResizeSettle } from "../utils/resizeDebug";
|
|
|
30
30
|
import type { MoveCommitOptions } from "../components/editor/domEditOverlayGestures";
|
|
31
31
|
import { runGestureTransaction } from "./gestureTransaction";
|
|
32
32
|
import {
|
|
33
|
-
|
|
34
|
-
gsapWritesPosition,
|
|
35
|
-
gsapWritesRotation,
|
|
33
|
+
editsPlainCss,
|
|
36
34
|
hasNonHoldTweenForElement,
|
|
37
35
|
POSITION_CHANNELS,
|
|
38
36
|
} from "./gsapRuntimeKeyframes";
|
|
@@ -49,6 +47,7 @@ export interface UseGsapAwareEditingParams {
|
|
|
49
47
|
domEditSelection: DomEditSelection | null;
|
|
50
48
|
selectedGsapAnimations: GsapAnimation[];
|
|
51
49
|
gsapCommitMutation: CommitMutation | null;
|
|
50
|
+
activeCompPath?: string | null;
|
|
52
51
|
previewIframeRef: React.RefObject<HTMLIFrameElement | null>;
|
|
53
52
|
showToast: (message: string, tone?: "error" | "info") => void;
|
|
54
53
|
bumpGsapCache: () => void;
|
|
@@ -61,6 +60,7 @@ export interface UseGsapAwareEditingParams {
|
|
|
61
60
|
selection: DomEditSelection | null,
|
|
62
61
|
mutationType: string,
|
|
63
62
|
label: string,
|
|
63
|
+
toast?: boolean,
|
|
64
64
|
) => void;
|
|
65
65
|
// DOM fallbacks (from useDomEditCommits)
|
|
66
66
|
stageElementPositionOffset: (
|
|
@@ -117,6 +117,7 @@ export function useGsapAwareEditing({
|
|
|
117
117
|
domEditSelection,
|
|
118
118
|
selectedGsapAnimations,
|
|
119
119
|
gsapCommitMutation,
|
|
120
|
+
activeCompPath,
|
|
120
121
|
previewIframeRef,
|
|
121
122
|
showToast,
|
|
122
123
|
bumpGsapCache,
|
|
@@ -148,7 +149,7 @@ export function useGsapAwareEditing({
|
|
|
148
149
|
modifiers?: MoveCommitOptions,
|
|
149
150
|
) => {
|
|
150
151
|
const writes = observeGsapGesture(gsapCommitMutation);
|
|
151
|
-
if (modifiers?.plainTranslate ??
|
|
152
|
+
if (modifiers?.plainTranslate ?? editsPlainCss(selection.element, "move")) {
|
|
152
153
|
const result = await stageElementPositionOffset(selection, next, true).save();
|
|
153
154
|
return writes.finish(result?.changed === true);
|
|
154
155
|
}
|
|
@@ -190,6 +191,7 @@ export function useGsapAwareEditing({
|
|
|
190
191
|
|
|
191
192
|
const handleGsapAwareGroupPathOffsetCommit = useGsapAwareGroupMove({
|
|
192
193
|
gsapCommitMutation,
|
|
194
|
+
activeCompPath,
|
|
193
195
|
previewIframeRef,
|
|
194
196
|
makeFetchFallback,
|
|
195
197
|
trackGsapInteractionFailure,
|
|
@@ -203,9 +205,10 @@ export function useGsapAwareEditing({
|
|
|
203
205
|
next: { width: number; height: number },
|
|
204
206
|
offset?: { x: number; y: number },
|
|
205
207
|
restore: () => void = () => undefined,
|
|
208
|
+
route?: { plainTranslate: boolean },
|
|
206
209
|
) => {
|
|
207
210
|
const writes = observeGsapGesture(gsapCommitMutation);
|
|
208
|
-
if (
|
|
211
|
+
if (route?.plainTranslate ?? editsPlainCss(selection.element, "resize")) {
|
|
209
212
|
const result = await handleDomBoxSizeCommit(selection, next, offset, restore);
|
|
210
213
|
return writes.finish(result?.changed === true);
|
|
211
214
|
}
|
|
@@ -254,8 +257,13 @@ export function useGsapAwareEditing({
|
|
|
254
257
|
);
|
|
255
258
|
// Saved after the size, under its undo key, so the two are one step.
|
|
256
259
|
await saveMove(dragOutcome, async () => {
|
|
257
|
-
const
|
|
258
|
-
anchorMove = stageElementPositionOffset(
|
|
260
|
+
const plainAfterSettle = editsPlainCss(selection.element, "move");
|
|
261
|
+
anchorMove = stageElementPositionOffset(
|
|
262
|
+
selection,
|
|
263
|
+
offset,
|
|
264
|
+
plainAfterSettle,
|
|
265
|
+
coalesceKey,
|
|
266
|
+
);
|
|
259
267
|
});
|
|
260
268
|
}
|
|
261
269
|
};
|
|
@@ -285,6 +293,7 @@ export function useGsapAwareEditing({
|
|
|
285
293
|
previewIframeRef.current,
|
|
286
294
|
commitMutation,
|
|
287
295
|
makeFetchFallback(selection),
|
|
296
|
+
offset,
|
|
288
297
|
);
|
|
289
298
|
assertGsapEditPersisted(outcome);
|
|
290
299
|
// Saved before the buffered GSAP writes, so their reload stays the gesture's last render.
|
|
@@ -355,7 +364,7 @@ export function useGsapAwareEditing({
|
|
|
355
364
|
const handleGsapAwareRotationCommit = useCallback(
|
|
356
365
|
async (selection: DomEditSelection, next: RotationCommit) => {
|
|
357
366
|
const writes = observeGsapGesture(gsapCommitMutation);
|
|
358
|
-
if (next.plain
|
|
367
|
+
if (next.plain === undefined ? editsPlainCss(selection.element, "rotate") : next.plain) {
|
|
359
368
|
const result = await handleDomRotationCommit(selection, next);
|
|
360
369
|
return writes.finish(result?.changed === true);
|
|
361
370
|
}
|
|
@@ -5,28 +5,18 @@ import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
|
|
|
5
5
|
import type { CommitMutationCall, CommitMutationOptions } from "./gsapScriptCommitTypes";
|
|
6
6
|
import type { UseGsapAwareEditingParams } from "./useGsapAwareEditing";
|
|
7
7
|
import { observeGsapGesture } from "./gsapGestureOutcome";
|
|
8
|
-
import {
|
|
8
|
+
import { editsPlainCss } from "./gsapRuntimeKeyframes";
|
|
9
9
|
import { refuseGsapTakeover } from "./elementOffsetStager";
|
|
10
10
|
import { tryGsapDragIntercept } from "./gsapRuntimeBridge";
|
|
11
11
|
import { assertGsapEditPersisted } from "./gsapEditOutcome";
|
|
12
|
+
import { firstPreflightFailure } from "./gsapGroupPreflight";
|
|
12
13
|
|
|
13
14
|
// A distinct key keeps consecutive group drags in separate undo entries.
|
|
14
15
|
let groupDragCommitCounter = 0;
|
|
15
16
|
|
|
16
|
-
function firstPreflightFailure(
|
|
17
|
-
results: PromiseSettledResult<void>[],
|
|
18
|
-
updates: DomEditGroupPathOffsetCommit[],
|
|
19
|
-
): { error: unknown; selection: DomEditSelection } | null {
|
|
20
|
-
for (const [index, result] of results.entries()) {
|
|
21
|
-
if (result.status !== "rejected") continue;
|
|
22
|
-
const selection = updates[index]?.selection;
|
|
23
|
-
if (selection) return { error: result.reason, selection };
|
|
24
|
-
}
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
17
|
export function useGsapAwareGroupMove({
|
|
29
18
|
gsapCommitMutation,
|
|
19
|
+
activeCompPath,
|
|
30
20
|
previewIframeRef,
|
|
31
21
|
makeFetchFallback,
|
|
32
22
|
trackGsapInteractionFailure,
|
|
@@ -35,6 +25,7 @@ export function useGsapAwareGroupMove({
|
|
|
35
25
|
}: Pick<
|
|
36
26
|
UseGsapAwareEditingParams,
|
|
37
27
|
| "gsapCommitMutation"
|
|
28
|
+
| "activeCompPath"
|
|
38
29
|
| "previewIframeRef"
|
|
39
30
|
| "makeFetchFallback"
|
|
40
31
|
| "trackGsapInteractionFailure"
|
|
@@ -44,10 +35,12 @@ export function useGsapAwareGroupMove({
|
|
|
44
35
|
const handleGsapAwareGroupPathOffsetCommit = useCallback(
|
|
45
36
|
async (
|
|
46
37
|
updates: DomEditGroupPathOffsetCommit[],
|
|
38
|
+
options: { refusalToast?: boolean } = {},
|
|
47
39
|
): Promise<import("../utils/previewFeatureUsage").GeometryCommitResult> => {
|
|
48
40
|
const writes = observeGsapGesture(gsapCommitMutation);
|
|
49
41
|
const writer = writes.commit;
|
|
50
42
|
if (!writer) return { ok: true, changed: false };
|
|
43
|
+
const toastRefusal = options.refusalToast !== false;
|
|
51
44
|
// One coalesce key across slow writes keeps the group drag in one undo entry.
|
|
52
45
|
const coalesceKey = `group-drag:${++groupDragCommitCounter}`;
|
|
53
46
|
// Hold the render until every member is saved, or unwritten members snap back.
|
|
@@ -87,16 +80,16 @@ export function useGsapAwareGroupMove({
|
|
|
87
80
|
// mutation, so a blocked member never leaves earlier siblings partially moved. Preflights
|
|
88
81
|
// write nothing and share one in-flight parse per file, so they run together.
|
|
89
82
|
const preflightResults = await Promise.allSettled(
|
|
90
|
-
updates.map(async ({ selection, plainTranslate }) => {
|
|
91
|
-
if (plainTranslate ??
|
|
92
|
-
refuseGsapTakeover(selection.element, showToast);
|
|
83
|
+
updates.map(async ({ selection, next, plainTranslate }) => {
|
|
84
|
+
if (plainTranslate ?? editsPlainCss(selection.element, "move")) {
|
|
85
|
+
refuseGsapTakeover(selection.element, toastRefusal ? showToast : () => {});
|
|
93
86
|
return void offsetMembers.set(selection, true);
|
|
94
87
|
}
|
|
95
88
|
const animations = await makeFetchFallback(selection, { failOnFetchError: true })();
|
|
96
89
|
preflightAnimations.set(selection, animations);
|
|
97
90
|
const outcome = await tryGsapDragIntercept(
|
|
98
91
|
selection,
|
|
99
|
-
|
|
92
|
+
next,
|
|
100
93
|
animations,
|
|
101
94
|
previewIframeRef.current,
|
|
102
95
|
coalescedCommit,
|
|
@@ -107,13 +100,19 @@ export function useGsapAwareGroupMove({
|
|
|
107
100
|
assertGsapEditPersisted(outcome);
|
|
108
101
|
}),
|
|
109
102
|
);
|
|
110
|
-
const preflightFailure = firstPreflightFailure(
|
|
103
|
+
const preflightFailure = firstPreflightFailure(
|
|
104
|
+
preflightResults,
|
|
105
|
+
updates,
|
|
106
|
+
offsetMembers,
|
|
107
|
+
activeCompPath,
|
|
108
|
+
);
|
|
111
109
|
if (preflightFailure) {
|
|
112
110
|
trackGsapInteractionFailure(
|
|
113
111
|
preflightFailure.error,
|
|
114
112
|
preflightFailure.selection,
|
|
115
113
|
"drag",
|
|
116
114
|
"Move animated layer (group)",
|
|
115
|
+
toastRefusal,
|
|
117
116
|
);
|
|
118
117
|
throw preflightFailure.error;
|
|
119
118
|
}
|
|
@@ -167,6 +166,7 @@ export function useGsapAwareGroupMove({
|
|
|
167
166
|
},
|
|
168
167
|
[
|
|
169
168
|
gsapCommitMutation,
|
|
169
|
+
activeCompPath,
|
|
170
170
|
previewIframeRef,
|
|
171
171
|
makeFetchFallback,
|
|
172
172
|
trackGsapInteractionFailure,
|
|
@@ -4,8 +4,8 @@ import { trackStudioEditBlocked, trackStudioSaveFailure } from "../utils/studioS
|
|
|
4
4
|
import { isGsapEditBlockedError } from "./gsapEditOutcome";
|
|
5
5
|
import { wasAlreadyToasted } from "./domEditPersistFailure";
|
|
6
6
|
|
|
7
|
-
function failureToast(error: unknown): string | null {
|
|
8
|
-
if (wasAlreadyToasted(error)) return null;
|
|
7
|
+
function failureToast(error: unknown, toast: boolean): string | null {
|
|
8
|
+
if (!toast || wasAlreadyToasted(error)) return null;
|
|
9
9
|
return isGsapEditBlockedError(error) ? error.message : "Failed to save animated edit.";
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -14,7 +14,13 @@ export function useGsapInteractionFailureTelemetry(
|
|
|
14
14
|
showToast: (message: string, tone?: "error" | "info") => void,
|
|
15
15
|
) {
|
|
16
16
|
return useCallback(
|
|
17
|
-
(
|
|
17
|
+
(
|
|
18
|
+
error: unknown,
|
|
19
|
+
selection: DomEditSelection | null,
|
|
20
|
+
mutationType: string,
|
|
21
|
+
label: string,
|
|
22
|
+
toast?: boolean,
|
|
23
|
+
) => {
|
|
18
24
|
const report = isGsapEditBlockedError(error)
|
|
19
25
|
? trackStudioEditBlocked
|
|
20
26
|
: trackStudioSaveFailure;
|
|
@@ -28,7 +34,7 @@ export function useGsapInteractionFailureTelemetry(
|
|
|
28
34
|
targetSelector: selection?.selector,
|
|
29
35
|
targetSourceFile: selection?.sourceFile,
|
|
30
36
|
});
|
|
31
|
-
const message = failureToast(error);
|
|
37
|
+
const message = failureToast(error, toast !== false);
|
|
32
38
|
if (message) showToast(message, "error");
|
|
33
39
|
},
|
|
34
40
|
[activeCompPath, showToast],
|