fix(parsers): preserve authored keyframe intent

This commit is contained in:
Miguel Angel Simon Sierra
2026-07-25 14:12:17 +02:00
parent 5acbf240cb
commit d84e999f72
7 changed files with 346 additions and 67 deletions
@@ -283,6 +283,21 @@ function mockFetchResult(over: Partial<MutationResult> = {}): void {
}
describe("runCommit — instantPatch wiring", () => {
it("explains a deliberate mutation that the server safely rejected as unchanged", async () => {
mockFetchResult({ changed: false });
const deps = renderCommitHook();
await act(async () => {
await deps.api.commitMutation(
selection,
{ type: "move-keyframe", fromPercentage: 50, toPercentage: 100 },
{ label: "Move keyframe" },
);
});
expect(deps.showToast).toHaveBeenCalledWith("A keyframe already exists at that time", "info");
});
it("no-op commit with an instantPatch still patches the runtime (paired x/y commits)", async () => {
patchRuntimeTweenInPlace.mockReturnValue(true);
mockFetchResult({ changed: false });
@@ -77,6 +77,17 @@ async function mutateGsapScriptBatch(
type ShowToast = (message: string, tone?: "error" | "info") => void;
function showUnchangedMutationFeedback(
mutations: Record<string, unknown>[],
result: MutationResult,
showToast: ShowToast | undefined,
): void {
if (result.changed !== false || mutations.length !== 1) return;
if (mutations[0]?.type === "move-keyframe") {
showToast?.("A keyframe already exists at that time", "info");
}
}
async function runMutationRequest(
mutations: Record<string, unknown>[],
options: CommitMutationOptions,
@@ -94,7 +105,9 @@ async function runMutationRequest(
);
}
try {
return await request();
const result = await request();
showUnchangedMutationFeedback(mutations, result, showToast);
return result;
} catch (error) {
if (error instanceof GsapMutationHttpError) {
showToast?.(formatGsapMutationRejectionToast(error), "error");