@hyperframes/studio 0.8.138 → 0.8.140
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-B30apksz.js → hyperframes-player-BLuyxnK7.js} +1 -1
- package/dist/assets/{index-DXVvWfQG.js → index-CWfuDCoA.js} +1 -1
- package/dist/assets/{index-DIJuIBwI.js → index-Dr3Bo9uH.js} +1 -1
- package/dist/assets/{index-BdjA0CLD.js → index-VAHDM_zL.js} +210 -210
- package/dist/{chunk-4S3AYCOG.js → chunk-H2DUOXTF.js} +3 -2
- package/dist/{chunk-TDWGXBUT.js → chunk-TFLU2MGI.js} +1 -6
- package/dist/chunk-TFLU2MGI.js.map +1 -0
- package/dist/{chunk-7UQWVN52.js → chunk-UYWON5AX.js} +2 -2
- package/dist/{domEditingLayers-IEVLO54Z.js → domEditingLayers-KBTPQKGA.js} +3 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.html +1 -1
- package/dist/index.js +7327 -6265
- package/dist/index.js.map +1 -1
- package/dist/player.js +2 -2
- package/package.json +8 -8
- package/src/App.tsx +9 -4
- package/src/components/TimelineToolbar.test.tsx +79 -1
- package/src/components/TimelineToolbar.tsx +14 -29
- package/src/components/editor/DomEditOverlay.tsx +11 -7
- package/src/components/editor/PropertyPanel.test.tsx +600 -748
- package/src/components/editor/PropertyPanel.tsx +1 -0
- package/src/components/editor/PropertyPanelFlat.tsx +1 -0
- package/src/components/editor/anchoredResizeCommitFeedsOffset.test.ts +2 -0
- package/src/components/editor/domEditOverlayGeometry.ts +7 -7
- package/src/components/editor/domEditOverlayGestures.ts +18 -0
- package/src/components/editor/domEditOverlayStartGesture.blocked.test.ts +1 -0
- package/src/components/editor/domEditOverlayStartGesture.test.ts +3 -0
- package/src/components/editor/domEditOverlayStartGesture.ts +9 -1
- package/src/components/editor/dragUndoPaint.test.ts +2 -0
- package/src/components/editor/gestureHoldsPreviewReload.test.tsx +432 -15
- package/src/components/editor/groupDropKeepsSelection.test.ts +1 -0
- package/src/components/editor/manualEdits.ts +1 -1
- package/src/components/editor/manualEditsDom.ts +7 -7
- package/src/components/editor/manualEditsParsing.ts +14 -2
- package/src/components/editor/manualEditsTypes.ts +5 -0
- package/src/components/editor/manualOffsetDrag.test.ts +23 -0
- package/src/components/editor/manualOffsetDrag.ts +5 -0
- package/src/components/editor/previewGestureStarts.ts +284 -0
- package/src/components/editor/previewReadOnly.overlay.test.tsx +3 -2
- package/src/components/editor/propertyPanelFill.tsx +36 -13
- package/src/components/editor/propertyPanelFlatStyleSections.test.tsx +4 -0
- package/src/components/editor/propertyPanelFlatStyleSections.tsx +13 -7
- package/src/components/editor/propertyPanelImageFill.test.tsx +164 -0
- package/src/components/editor/propertyPanelStyleSections.tsx +10 -7
- package/src/components/editor/rotateInlineSave.test.ts +1 -0
- package/src/components/editor/shiftDragAxisLock.test.tsx +1 -0
- package/src/components/editor/useDomEditOverlayGestures.ts +67 -71
- package/src/components/nle/NLEContext.test.ts +26 -0
- package/src/components/nle/NLEContext.tsx +8 -3
- package/src/components/nle/NLEPreview.test.ts +44 -1
- package/src/components/nle/NLEPreview.tsx +7 -2
- package/src/hooks/draggedGsapPosition.ts +8 -0
- package/src/hooks/editMoment.ts +13 -0
- package/src/hooks/externalFileChangeOwnership.test.ts +25 -4
- package/src/hooks/gsapDragCommit.ts +2 -1
- package/src/hooks/gsapDragPositionCommit.ts +19 -7
- package/src/hooks/gsapPositionDetection.ts +5 -3
- package/src/hooks/gsapResizeIntercept.ts +11 -4
- package/src/hooks/gsapRuntimeBridge.ts +31 -11
- package/src/hooks/gsapValueAtPlayhead.browser.test.ts +3 -22
- package/src/hooks/gsapValueAtPlayhead.test.ts +15 -0
- package/src/hooks/gsapValueAtPlayhead.ts +12 -5
- package/src/hooks/timelineEditingHelpers.test.ts +22 -0
- package/src/hooks/timelineEditingHelpers.ts +18 -2
- package/src/hooks/useDomSelection.test.ts +21 -2
- package/src/hooks/useEditHistoryActions.test.tsx +20 -0
- package/src/hooks/useEditHistoryActions.ts +2 -0
- package/src/hooks/useExternalFileChangeCoordinator.test.tsx +536 -10
- package/src/hooks/useExternalFileChangeCoordinator.ts +72 -45
- package/src/hooks/useGsapAwareEditing.test.tsx +38 -0
- package/src/hooks/useGsapAwareEditing.ts +148 -136
- package/src/hooks/useGsapAwareGroupMove.ts +126 -113
- package/src/hooks/useGsapScriptCommits.ts +5 -4
- package/src/hooks/useMusicBeatAnalysis.ts +2 -1
- package/src/hooks/usePreviewPersistence.history.test.tsx +7 -2
- package/src/hooks/usePreviewPersistence.ts +1 -4
- package/src/hooks/useSdkSession.lifecycle.test.tsx +20 -0
- package/src/hooks/useSdkSession.ts +4 -1
- package/src/hooks/useThumbnailStripSize.test.tsx +65 -0
- package/src/hooks/useThumbnailStripSize.ts +23 -1
- package/src/hooks/useTimelineDeleteOps.test.tsx +10 -0
- package/src/hooks/useTimelineDeleteOps.ts +9 -10
- package/src/index.ts +2 -0
- package/src/player/components/AudioWaveform.test.tsx +153 -1
- package/src/player/components/AudioWaveform.tsx +93 -37
- package/src/player/components/Timeline.scrollRest.test.tsx +22 -0
- package/src/player/components/Timeline.test.ts +3 -1
- package/src/player/components/TimelineCanvas.tsx +4 -0
- package/src/player/components/TimelineClip.tsx +20 -6
- package/src/player/components/TimelineClipFades.test.tsx +11 -0
- package/src/player/components/TimelineClipFades.tsx +4 -4
- package/src/player/components/TimelineClipJoins.tsx +23 -18
- package/src/player/components/TimelineGestureOverlay.tsx +30 -27
- package/src/player/components/TimelineGroupRow.tsx +1 -0
- package/src/player/components/TimelineLaneClip.tsx +127 -0
- package/src/player/components/TimelineLanes.test.tsx +51 -6
- package/src/player/components/TimelineLanes.tsx +166 -178
- package/src/player/components/TimelineRuler.tsx +2 -0
- package/src/player/components/TimelineTimeLayer.tsx +25 -0
- package/src/player/components/VideoThumbnail.test.tsx +204 -12
- package/src/player/components/VideoThumbnail.tsx +37 -9
- package/src/player/components/thumbnailUtils.test.ts +17 -3
- package/src/player/components/thumbnailUtils.ts +17 -1
- package/src/player/components/timelineLayout.test.ts +3 -3
- package/src/player/components/timelineMotion.test.ts +27 -0
- package/src/player/components/timelineMotion.ts +62 -0
- package/src/player/components/timelineZoomInput.test.ts +438 -0
- package/src/player/components/timelineZoomInput.ts +456 -0
- package/src/player/components/useTimelineGeometry.ts +0 -3
- package/src/player/components/useTimelinePlayhead.test.tsx +97 -30
- package/src/player/components/useTimelinePlayhead.ts +53 -59
- package/src/player/components/useTimelineProviderState.tsx +16 -21
- package/src/player/components/useTimelineZoom.ts +7 -12
- package/src/player/hooks/useShadowPreviewReload.ts +6 -6
- package/src/player/hooks/useTimelinePlayer.shadowReload.test.ts +14 -0
- package/src/player/hooks/useTimelinePlayer.ts +8 -5
- package/src/player/lib/thumbnailScheduler.test.ts +20 -1
- package/src/player/lib/thumbnailScheduler.ts +1 -1
- package/src/player/lib/thumbnailVideoDecoder.test.ts +230 -30
- package/src/player/lib/thumbnailVideoDecoder.ts +197 -54
- package/src/player/lib/timelineElementHelpers.ts +0 -11
- package/src/player/lib/timelineViewportBudgets.test.ts +1 -1
- package/src/player/lib/timelineViewportBudgets.ts +1 -1
- package/src/player/previewReloading.test.ts +48 -0
- package/src/player/previewReloading.ts +48 -0
- package/src/utils/gsapUndoRestore.browser.test.ts +4 -17
- package/src/utils/rootDuration.ts +5 -4
- package/src/utils/studioPendingEdits.test.ts +27 -0
- package/src/utils/studioPendingEdits.ts +9 -0
- package/src/utils/timelineAssetDrop.test.ts +13 -4
- package/src/utils/timelineAssetDrop.ts +6 -2
- package/dist/chunk-TDWGXBUT.js.map +0 -1
- /package/dist/{chunk-4S3AYCOG.js.map → chunk-H2DUOXTF.js.map} +0 -0
- /package/dist/{chunk-7UQWVN52.js.map → chunk-UYWON5AX.js.map} +0 -0
- /package/dist/{domEditingLayers-IEVLO54Z.js.map → domEditingLayers-KBTPQKGA.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState, type MutableRefObject } from "react";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
mergeFileChangePayloads,
|
|
4
4
|
readFileChangeAffectedCompositions,
|
|
5
5
|
readFileChangeAffectsPreview,
|
|
6
6
|
readFileChangeField,
|
|
@@ -125,14 +125,18 @@ function decodeFileChange(delivery: unknown): unknown {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
*/
|
|
133
|
-
export function sseFileChangeChannel(onDelivery: (delivery: unknown) => void): () => void {
|
|
128
|
+
export function sseFileChangeChannel(
|
|
129
|
+
onDelivery: (delivery: unknown) => void,
|
|
130
|
+
onReconnect: () => void,
|
|
131
|
+
): () => void {
|
|
134
132
|
const eventSource = new EventSource("/api/events");
|
|
135
133
|
eventSource.addEventListener("file-change", onDelivery);
|
|
134
|
+
// Reopening the stream does not replay changes missed during a disconnect.
|
|
135
|
+
let opened = false;
|
|
136
|
+
eventSource.addEventListener("open", () => {
|
|
137
|
+
if (opened) onReconnect();
|
|
138
|
+
opened = true;
|
|
139
|
+
});
|
|
136
140
|
return () => eventSource.close();
|
|
137
141
|
}
|
|
138
142
|
|
|
@@ -168,18 +172,19 @@ export function useExternalFileChangeCoordinator({
|
|
|
168
172
|
const generationRef = useRef(0);
|
|
169
173
|
const mountedRef = useRef(true);
|
|
170
174
|
const lastEventIdentityRef = useRef<string | null>(null);
|
|
171
|
-
const blockedRef = useRef(
|
|
175
|
+
const blockedRef = useRef<ExternalFileChangeBlockedState | null>(null);
|
|
172
176
|
const snapshotWriteTailRef = useRef<Promise<void>>(Promise.resolve());
|
|
173
177
|
const drainingRef = useRef(false);
|
|
174
178
|
const pendingPayloadRef = useRef<{ payload: unknown } | null>(null);
|
|
175
|
-
|
|
176
|
-
// A newer blocked change replaces the held one, so it inherits the thumbnails the held one owed.
|
|
179
|
+
// A newer blocked change replaces the held one, so it inherits what the held one owed.
|
|
177
180
|
const setBlocked = useCallback((next: ExternalFileChangeBlockedState | null) => {
|
|
178
|
-
|
|
181
|
+
const held = blockedRef.current;
|
|
182
|
+
const current =
|
|
179
183
|
next && held
|
|
180
|
-
? { ...next, payload:
|
|
181
|
-
: next
|
|
182
|
-
|
|
184
|
+
? { ...next, payload: mergeFileChangePayloads(held.payload, next.payload) }
|
|
185
|
+
: next;
|
|
186
|
+
blockedRef.current = current;
|
|
187
|
+
setBlockedState(current);
|
|
183
188
|
}, []);
|
|
184
189
|
|
|
185
190
|
useEffect(() => {
|
|
@@ -190,6 +195,10 @@ export function useExternalFileChangeCoordinator({
|
|
|
190
195
|
};
|
|
191
196
|
}, []);
|
|
192
197
|
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
pendingPayloadRef.current = null;
|
|
200
|
+
}, [projectId]);
|
|
201
|
+
|
|
193
202
|
useEffect(() => {
|
|
194
203
|
generationRef.current += 1;
|
|
195
204
|
setBlocked(null);
|
|
@@ -240,8 +249,12 @@ export function useExternalFileChangeCoordinator({
|
|
|
240
249
|
};
|
|
241
250
|
}, [loadConflictSnapshot, projectId, recoveryFilePath, setBlocked]);
|
|
242
251
|
|
|
243
|
-
|
|
244
|
-
|
|
252
|
+
// What a settled change owes: thumbnails (unless its own write's echo brings them), then reloads.
|
|
253
|
+
const deliverOwed = useCallback(
|
|
254
|
+
(owed: unknown, accept = true) => {
|
|
255
|
+
const path = readStudioFileChangePath(owed) ?? ".";
|
|
256
|
+
const affectsPreview = readFileChangeAffectsPreview(owed);
|
|
257
|
+
if (accept) onAcceptedPersistedFileChange(path, readFileChangeAffectedCompositions(owed));
|
|
245
258
|
logReload(affectsPreview ? "reload" : "file-tree only", {
|
|
246
259
|
path,
|
|
247
260
|
by: "external-change coordinator",
|
|
@@ -255,7 +268,7 @@ export function useExternalFileChangeCoordinator({
|
|
|
255
268
|
// a watcher event the SSE never delivered).
|
|
256
269
|
void refreshFileTree?.();
|
|
257
270
|
},
|
|
258
|
-
[reloadPreview, reloadSdkSession, refreshFileTree],
|
|
271
|
+
[onAcceptedPersistedFileChange, reloadPreview, reloadSdkSession, refreshFileTree],
|
|
259
272
|
);
|
|
260
273
|
|
|
261
274
|
const persistSnapshotInOrder = useCallback(async (write: () => Promise<void>) => {
|
|
@@ -274,21 +287,28 @@ export function useExternalFileChangeCoordinator({
|
|
|
274
287
|
if (!path) return;
|
|
275
288
|
|
|
276
289
|
const generation = ++generationRef.current;
|
|
290
|
+
const savingPath = getPendingCandidate?.()?.path;
|
|
277
291
|
const result = await drainPendingChanges();
|
|
278
292
|
if (!mountedRef.current || generation !== generationRef.current) return;
|
|
279
293
|
|
|
280
294
|
if (result.status === "clean") {
|
|
281
295
|
const previousBlocked = blockedRef.current;
|
|
282
|
-
|
|
296
|
+
// Only saving the failed draft's file retires its copy; restored drafts are never queued.
|
|
297
|
+
if (
|
|
298
|
+
previousBlocked?.status === "failed" &&
|
|
299
|
+
!previousBlocked.recovered &&
|
|
300
|
+
previousBlocked.path === savingPath &&
|
|
301
|
+
deleteConflictSnapshot
|
|
302
|
+
) {
|
|
283
303
|
try {
|
|
284
|
-
await deleteConflictSnapshot(projectId!, path);
|
|
304
|
+
await deleteConflictSnapshot(projectId!, previousBlocked.path);
|
|
285
305
|
} catch (error) {
|
|
286
306
|
if (mountedRef.current && generation === generationRef.current) {
|
|
287
307
|
setBlocked({
|
|
288
308
|
...previousBlocked,
|
|
289
309
|
generation,
|
|
290
310
|
error,
|
|
291
|
-
payload:
|
|
311
|
+
payload: mergeFileChangePayloads(payload, previousBlocked.payload),
|
|
292
312
|
});
|
|
293
313
|
}
|
|
294
314
|
return;
|
|
@@ -296,27 +316,30 @@ export function useExternalFileChangeCoordinator({
|
|
|
296
316
|
}
|
|
297
317
|
if (!mountedRef.current || generation !== generationRef.current) return;
|
|
298
318
|
setBlocked(null);
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
319
|
+
const restored = previousBlocked?.status === "failed" && previousBlocked.recovered;
|
|
320
|
+
deliverOwed(
|
|
321
|
+
previousBlocked && !restored
|
|
322
|
+
? mergeFileChangePayloads(previousBlocked.payload, payload)
|
|
323
|
+
: payload,
|
|
324
|
+
);
|
|
304
325
|
return;
|
|
305
326
|
}
|
|
306
|
-
const content = readFileChangeContent(payload);
|
|
307
327
|
if (result.status === "failed") {
|
|
328
|
+
// The draft names the file to recover; the payload stays the scope to reload.
|
|
308
329
|
const candidate = getPendingCandidate?.();
|
|
309
|
-
const
|
|
330
|
+
const failedPath = candidate?.path ?? (path === "." ? (recoveryFilePath ?? path) : path);
|
|
331
|
+
const studioContent = candidate?.content ?? null;
|
|
332
|
+
const external = failedPath === path ? payload : null;
|
|
310
333
|
let error = result.error;
|
|
311
334
|
if (studioContent != null && persistFailureSnapshot) {
|
|
312
335
|
try {
|
|
313
336
|
await persistSnapshotInOrder(() =>
|
|
314
337
|
persistFailureSnapshot(
|
|
315
338
|
projectId!,
|
|
316
|
-
|
|
339
|
+
failedPath,
|
|
317
340
|
studioContent,
|
|
318
|
-
readFileChangeVersion(
|
|
319
|
-
|
|
341
|
+
readFileChangeVersion(external),
|
|
342
|
+
readFileChangeContent(external),
|
|
320
343
|
result.error,
|
|
321
344
|
),
|
|
322
345
|
);
|
|
@@ -333,7 +356,7 @@ export function useExternalFileChangeCoordinator({
|
|
|
333
356
|
setBlocked({
|
|
334
357
|
status: "failed",
|
|
335
358
|
generation,
|
|
336
|
-
path,
|
|
359
|
+
path: failedPath,
|
|
337
360
|
error,
|
|
338
361
|
payload,
|
|
339
362
|
studioContent,
|
|
@@ -348,7 +371,7 @@ export function useExternalFileChangeCoordinator({
|
|
|
348
371
|
setBlocked({
|
|
349
372
|
status: "failed",
|
|
350
373
|
generation,
|
|
351
|
-
path,
|
|
374
|
+
path: result.error.filePath,
|
|
352
375
|
error,
|
|
353
376
|
payload,
|
|
354
377
|
studioContent: result.error.attemptedContent,
|
|
@@ -365,14 +388,17 @@ export function useExternalFileChangeCoordinator({
|
|
|
365
388
|
projectId,
|
|
366
389
|
deleteConflictSnapshot,
|
|
367
390
|
getPendingCandidate,
|
|
391
|
+
recoveryFilePath,
|
|
368
392
|
persistConflictSnapshot,
|
|
369
393
|
persistFailureSnapshot,
|
|
370
394
|
persistSnapshotInOrder,
|
|
371
|
-
|
|
372
|
-
onAcceptedPersistedFileChange,
|
|
395
|
+
deliverOwed,
|
|
373
396
|
],
|
|
374
397
|
);
|
|
375
398
|
|
|
399
|
+
const drainOnePendingRef = useRef(drainOnePending);
|
|
400
|
+
drainOnePendingRef.current = drainOnePending;
|
|
401
|
+
|
|
376
402
|
const startDrainLoop = useCallback(async () => {
|
|
377
403
|
if (drainingRef.current) return;
|
|
378
404
|
drainingRef.current = true;
|
|
@@ -381,12 +407,12 @@ export function useExternalFileChangeCoordinator({
|
|
|
381
407
|
const pending = pendingPayloadRef.current;
|
|
382
408
|
if (!pending) break;
|
|
383
409
|
pendingPayloadRef.current = null;
|
|
384
|
-
await
|
|
410
|
+
await drainOnePendingRef.current(pending.payload);
|
|
385
411
|
}
|
|
386
412
|
} finally {
|
|
387
413
|
drainingRef.current = false;
|
|
388
414
|
}
|
|
389
|
-
}, [
|
|
415
|
+
}, []);
|
|
390
416
|
|
|
391
417
|
const processChange = useCallback(
|
|
392
418
|
// fallow-ignore-next-line complexity
|
|
@@ -439,8 +465,7 @@ export function useExternalFileChangeCoordinator({
|
|
|
439
465
|
!readFileChangeAffectsPreview(payload);
|
|
440
466
|
if (!waitingChangeOutranksThis) {
|
|
441
467
|
pendingPayloadRef.current = {
|
|
442
|
-
payload:
|
|
443
|
-
waiting == null ? payload : mergeFileChangeAffectedCompositions(waiting, payload),
|
|
468
|
+
payload: waiting == null ? payload : mergeFileChangePayloads(waiting, payload),
|
|
444
469
|
};
|
|
445
470
|
}
|
|
446
471
|
void startDrainLoop();
|
|
@@ -462,7 +487,8 @@ export function useExternalFileChangeCoordinator({
|
|
|
462
487
|
import.meta.hot.on("hf:file-change", handler);
|
|
463
488
|
return () => import.meta.hot?.off?.("hf:file-change", handler);
|
|
464
489
|
}
|
|
465
|
-
|
|
490
|
+
const catchUp = () => processChangeRef.current({ path: "." });
|
|
491
|
+
return sseFileChangeChannel(handler, catchUp);
|
|
466
492
|
}, []);
|
|
467
493
|
|
|
468
494
|
const retry = useCallback(async () => {
|
|
@@ -489,18 +515,16 @@ export function useExternalFileChangeCoordinator({
|
|
|
489
515
|
onUseExternalFile?.(path, external);
|
|
490
516
|
await deleteConflictSnapshot?.(projectId, path);
|
|
491
517
|
setBlocked(null);
|
|
492
|
-
|
|
493
|
-
reloadAcceptedGeneration(path, readFileChangeAffectsPreview(current.payload));
|
|
518
|
+
deliverOwed(current.payload);
|
|
494
519
|
},
|
|
495
520
|
[
|
|
496
521
|
deleteConflictSnapshot,
|
|
497
522
|
setBlocked,
|
|
498
523
|
discardPendingChanges,
|
|
499
524
|
onUseExternalFile,
|
|
500
|
-
onAcceptedPersistedFileChange,
|
|
501
525
|
projectId,
|
|
502
526
|
readProjectFile,
|
|
503
|
-
|
|
527
|
+
deliverOwed,
|
|
504
528
|
resetSaveQueues,
|
|
505
529
|
],
|
|
506
530
|
);
|
|
@@ -550,7 +574,10 @@ export function useExternalFileChangeCoordinator({
|
|
|
550
574
|
resetSaveQueues?.();
|
|
551
575
|
await deleteConflictSnapshot?.(projectId, conflict.filePath);
|
|
552
576
|
setBlocked(null);
|
|
553
|
-
|
|
577
|
+
deliverOwed(
|
|
578
|
+
mergeFileChangePayloads(current.payload, { path: conflict.filePath, affectsPreview: true }),
|
|
579
|
+
readStudioFileChangePath(current.payload) !== conflict.filePath,
|
|
580
|
+
);
|
|
554
581
|
}, [
|
|
555
582
|
deleteConflictSnapshot,
|
|
556
583
|
setBlocked,
|
|
@@ -558,7 +585,7 @@ export function useExternalFileChangeCoordinator({
|
|
|
558
585
|
overwriteConflict,
|
|
559
586
|
projectId,
|
|
560
587
|
readProjectFile,
|
|
561
|
-
|
|
588
|
+
deliverOwed,
|
|
562
589
|
resetSaveQueues,
|
|
563
590
|
]);
|
|
564
591
|
|
|
@@ -42,6 +42,7 @@ vi.mock("./useSafeGsapCommitMutation", () => ({
|
|
|
42
42
|
}));
|
|
43
43
|
|
|
44
44
|
import { useGsapAwareEditing } from "./useGsapAwareEditing";
|
|
45
|
+
import { isPreviewChanging } from "../player/previewReloading";
|
|
45
46
|
import { tryGsapRotationIntercept } from "./gsapRuntimeBridge";
|
|
46
47
|
|
|
47
48
|
afterEach(() => {
|
|
@@ -235,6 +236,43 @@ describe("useGsapAwareEditing keeps the route a gesture chose at press", () => {
|
|
|
235
236
|
});
|
|
236
237
|
});
|
|
237
238
|
|
|
239
|
+
describe("useGsapAwareEditing holds a canvas press while a drop saves", () => {
|
|
240
|
+
it.each([
|
|
241
|
+
["a move through the script", false, "move"],
|
|
242
|
+
["a group move through the script", false, "group"],
|
|
243
|
+
["a move saved as CSS", true, "move"],
|
|
244
|
+
])("%s", async (_, plainTranslate, kind) => {
|
|
245
|
+
let land: () => void = () => {};
|
|
246
|
+
const landed = new Promise<void>((resolve) => (land = resolve));
|
|
247
|
+
mocks.drag.mockImplementation(async () => {
|
|
248
|
+
await landed;
|
|
249
|
+
return { status: "persisted" };
|
|
250
|
+
});
|
|
251
|
+
const save = vi.fn().mockReturnValue(landed);
|
|
252
|
+
const stageElementPositionOffset = vi.fn(() => ({ save, rollback: vi.fn() }));
|
|
253
|
+
const { pathOffsetCommit, groupCommit, root } = mountGroupHandler({
|
|
254
|
+
stageElementPositionOffset,
|
|
255
|
+
});
|
|
256
|
+
const box = { element: document.createElement("div"), id: "box", selector: "#box" };
|
|
257
|
+
const selection = box as unknown as DomEditSelection;
|
|
258
|
+
const to = { x: 40, y: 20 };
|
|
259
|
+
let saved!: Promise<unknown>;
|
|
260
|
+
act(() => {
|
|
261
|
+
saved =
|
|
262
|
+
kind === "group"
|
|
263
|
+
? groupCommit([{ selection, next: to, plainTranslate }])
|
|
264
|
+
: pathOffsetCommit(selection, to, { plainTranslate });
|
|
265
|
+
});
|
|
266
|
+
expect(isPreviewChanging(), "from the drop").toBe(!plainTranslate);
|
|
267
|
+
await act(async () => {
|
|
268
|
+
land();
|
|
269
|
+
await saved;
|
|
270
|
+
});
|
|
271
|
+
expect(isPreviewChanging()).toBe(false);
|
|
272
|
+
act(() => root.unmount());
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
238
276
|
describe("useGsapAwareEditing keeps the box route a resize chose at press", () => {
|
|
239
277
|
it.each([
|
|
240
278
|
["the CSS route, on an element GSAP has since taken over", true],
|