Skip to content

Commit b16fca2

Browse files
committed
fix(studio): every GSAP script commit counts as a pending edit, so undo waits for it
1 parent 7025479 commit b16fca2

2 files changed

Lines changed: 24 additions & 4 deletions

File tree

‎packages/studio/src/hooks/useGsapScriptCommits.test.tsx‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type { DomEditSelection } from "../components/editor/domEditingTypes";
3030
import type { MutationResult } from "./gsapScriptCommitTypes";
3131
import { persistSdkSerialize } from "../utils/sdkCutover";
3232
import { applyPreviewSync, useGsapScriptCommits } from "./useGsapScriptCommits";
33+
import { hasStudioPendingEdits } from "../utils/studioPendingEdits";
3334

3435
// ── applyPreviewSync (pure preview-sync decision) ────────────────────────────
3536

@@ -426,6 +427,24 @@ function mockFetchResult(over: Partial<MutationResult> = {}): void {
426427
);
427428
}
428429

430+
describe("a GSAP script commit", () => {
431+
it("counts as a pending edit from its call until it lands, so a quick Cmd+Z waits for it", async () => {
432+
mockFetchResult();
433+
const deps = renderCommitHook();
434+
let committed!: Promise<unknown>;
435+
act(() => {
436+
committed = deps.api.commitMutation(
437+
selection,
438+
{ type: "remove-all-keyframes", animationId: "a" },
439+
{ label: "Remove all keyframes" },
440+
);
441+
});
442+
expect(hasStudioPendingEdits()).toBe(true);
443+
await act(async () => void (await committed));
444+
expect(hasStudioPendingEdits()).toBe(false);
445+
});
446+
});
447+
429448
describe("runCommit — instantPatch wiring", () => {
430449
it("explains a deliberate mutation that the server safely rejected as unchanged", async () => {
431450
mockFetchResult({ changed: false });

‎packages/studio/src/hooks/useGsapScriptCommits.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { applySoftReload, extractGsapScriptText } from "../utils/gsapSoftReload"
77
import type { SoftReloadResult } from "../utils/gsapSoftReload";
88
import { trackStudioEvent } from "../utils/studioTelemetry";
99
import { serializeStudioFileMutation } from "../utils/studioFileMutationCoordinator";
10+
import { trackedStudioEdit } from "../utils/studioPendingEdits";
1011
import {
1112
getStudioSaveErrorMessage,
1213
isStudioSaveErrorAlreadyToasted,
@@ -416,20 +417,20 @@ export function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIfra
416417
}
417418
return run();
418419
};
419-
const commit: CommitMutation = (selection, mutation, options) => {
420+
const commit: CommitMutation = trackedStudioEdit((selection, mutation, options) => {
420421
if (!activeProjectId) return Promise.resolve();
421422
const file = selection.sourceFile || activeCompPath || "index.html";
422423
return serializeCommit(file, options.serializeKey, () =>
423424
runCommit(activeProjectId, activeCompPath, file, selection, mutation, options),
424425
);
425-
};
426-
commit.batch = (calls, options) => {
426+
}) as CommitMutation;
427+
commit.batch = trackedStudioEdit((calls: CommitMutationCall[], options: CommitMutationOptions) => {
427428
if (!activeProjectId) return Promise.resolve();
428429
const file = calls[0]?.selection.sourceFile || activeCompPath || "index.html";
429430
return serializeCommit(file, options.serializeKey, () =>
430431
runBatchCommit(activeProjectId, activeCompPath, file, calls, options),
431432
);
432-
};
433+
});
433434
return commit;
434435
}, [runCommit, runBatchCommit, activeCompPath, activeProjectId, writeProjectFile]);
435436
const trackGsapSaveFailure = useGsapSaveFailureTelemetry(activeCompPath);

0 commit comments

Comments
 (0)