Skip to content

Commit 0a7ebe1

Browse files
fix(studio): batch shadow dispatch, rename runShadowDispatch, add PatchOperation import
Wrap the shadow dispatch loop in session.batch() so a mid-loop throw cannot leave the SDK session in a partially-applied state. Without the batch boundary, one failing op would update some elements but not others, diverging the shadow session from the real one. Rename reportShadowDispatch → runShadowDispatch to eliminate the misleading 'report' prefix — the function mutates the SDK session, it is not read-only. Update the only caller (useDomEditSession). Add missing PatchOperation import to useDomEditCommits (the type was already used in the onDomEditPersisted interface but never imported). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
1 parent f070ccd commit 0a7ebe1

3 files changed

Lines changed: 10 additions & 7 deletions

File tree

packages/studio/src/hooks/useDomEditCommits.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { buildDomEditPatchTarget, type DomEditSelection } from "../components/ed
99
import { fontFamilyFromAssetPath, type ImportedFontAsset } from "../components/editor/fontAssets";
1010
import type { EditHistoryKind } from "../utils/editHistory";
1111
import type { PersistDomEditOperations } from "./domEditCommitTypes";
12+
import type { PatchOperation } from "../utils/sourcePatcher";
1213
import { useDomEditPositionPatchCommit } from "./useDomEditPositionPatchCommit";
1314
import { useDomEditTextCommits } from "./useDomEditTextCommits";
1415
import { useDomGeometryCommits } from "./useDomGeometryCommits";

packages/studio/src/hooks/useDomEditSession.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { useAskAgentModal } from "./useAskAgentModal";
99
import { useDomSelection } from "./useDomSelection";
1010
import { usePreviewInteraction } from "./usePreviewInteraction";
1111
import { useDomEditCommits } from "./useDomEditCommits";
12-
import { reportShadowDispatch } from "../utils/sdkShadow";
12+
import { runShadowDispatch } from "../utils/sdkShadow";
1313
import { useGsapScriptCommits } from "./useGsapScriptCommits";
1414
import { useGsapCacheVersion } from "./useGsapTweenCache";
1515
import { useDomEditWiring } from "./useDomEditWiring";
@@ -233,7 +233,7 @@ export function useDomEditSession({
233233
refreshDomEditSelectionFromPreview,
234234
buildDomSelectionFromTarget,
235235
onDomEditPersisted: sdkSession
236-
? (sel, ops) => reportShadowDispatch(sdkSession, sel, ops)
236+
? (sel, ops) => runShadowDispatch(sdkSession, sel, ops)
237237
: undefined,
238238
});
239239

packages/studio/src/utils/sdkShadow.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,10 @@ export function sdkShadowDispatch(
151151
return { dispatched: false, mismatches: [{ kind: "element_not_found", hfId }] };
152152
}
153153
try {
154-
for (const op of patchOpsToSdkEditOps(hfId, ops)) {
155-
session.dispatch(op);
156-
}
154+
const sdkOps = patchOpsToSdkEditOps(hfId, ops);
155+
session.batch(() => {
156+
for (const op of sdkOps) session.dispatch(op);
157+
});
157158
} catch (err) {
158159
return {
159160
dispatched: false,
@@ -171,9 +172,10 @@ export function sdkShadowDispatch(
171172

172173
/**
173174
* Shadow-dispatch ops to the SDK session and emit sdk_shadow_dispatch telemetry.
174-
* No-op when STUDIO_SDK_SHADOW_ENABLED is false.
175+
* Despite the telemetry focus, this function does mutate the SDK session — it
176+
* is not read-only. No-op when STUDIO_SDK_SHADOW_ENABLED is false.
175177
*/
176-
export function reportShadowDispatch(
178+
export function runShadowDispatch(
177179
session: Composition,
178180
selection: DomEditSelection,
179181
ops: PatchOperation[],

0 commit comments

Comments
 (0)