diff --git a/packages/studio/src/App.tsx b/packages/studio/src/App.tsx index 1da56bb99..316ee1aa2 100644 --- a/packages/studio/src/App.tsx +++ b/packages/studio/src/App.tsx @@ -304,6 +304,7 @@ export function StudioApp() { openSourceForSelection: fileManager.openSourceForSelection, selectSidebarTab: selectSidebarTabStable, getSidebarTab: getSidebarTabStable, + sdkSession: sdkHandle.session, }); domEditSelectionBridgeRef.current = domEditSession.domEditSelection; clearDomSelectionRef.current = domEditSession.clearDomSelection; diff --git a/packages/studio/src/hooks/useDomEditCommits.ts b/packages/studio/src/hooks/useDomEditCommits.ts index 7e97930b0..9292a8c3e 100644 --- a/packages/studio/src/hooks/useDomEditCommits.ts +++ b/packages/studio/src/hooks/useDomEditCommits.ts @@ -77,6 +77,13 @@ export interface UseDomEditCommitsParams { onDomEditPersisted?: (selection: DomEditSelection, operations: PatchOperation[]) => void; /** Stage 7 Step 3b: called after a successful server-side element delete. */ onElementDeleted?: (selection: DomEditSelection) => void; + /** Stage 7 Step 3c: called before the server-side patch path; returns true if SDK handled it. */ + onTrySdkPersist?: ( + selection: DomEditSelection, + operations: PatchOperation[], + originalContent: string, + targetPath: string, + ) => Promise; } export function useDomEditCommits({ @@ -99,6 +106,7 @@ export function useDomEditCommits({ buildDomSelectionFromTarget, onDomEditPersisted, onElementDeleted, + onTrySdkPersist, }: UseDomEditCommitsParams) { const resolveImportedFontAsset = useCallback( (fontFamilyValue: string): ImportedFontAsset | null => { @@ -149,6 +157,18 @@ export function useDomEditCommits({ if (options?.shouldSave && !options.shouldSave()) return; + // Skip the SDK path when prepareContent is set (e.g. @font-face injection + // for a custom font): sdkCutoverPersist serializes only the patched DOM + // and would drop the injected content. Let the server path run prepareContent. + if ( + onTrySdkPersist && + !options?.prepareContent && + (await onTrySdkPersist(selection, operations, originalContent, targetPath)) + ) { + // SDK handled it — its in-memory doc is already current. + return; + } + const patchTarget = buildDomEditPatchTarget(selection); const patchBody = { target: patchTarget, operations }; const unsafeFields = findUnsafeDomPatchValues(patchBody); @@ -235,6 +255,7 @@ export function useDomEditCommits({ reloadPreview, showToast, onDomEditPersisted, + onTrySdkPersist, ], ); diff --git a/packages/studio/src/hooks/useDomEditSession.ts b/packages/studio/src/hooks/useDomEditSession.ts index df08968f2..d51743483 100644 --- a/packages/studio/src/hooks/useDomEditSession.ts +++ b/packages/studio/src/hooks/useDomEditSession.ts @@ -4,6 +4,8 @@ import type { EditHistoryKind } from "../utils/editHistory"; import type { RightPanelTab } from "../utils/studioHelpers"; import type { PatchTarget } from "../utils/sourcePatcher"; import type { SidebarTab } from "../components/sidebar/LeftSidebar"; +import type { Composition } from "@hyperframes/sdk"; +import { sdkCutoverPersist } from "../utils/sdkCutover"; import { useAskAgentModal } from "./useAskAgentModal"; import { useDomSelection } from "./useDomSelection"; import { usePreviewInteraction } from "./usePreviewInteraction"; @@ -58,6 +60,7 @@ export interface UseDomEditSessionParams { openSourceForSelection?: (sourceFile: string, target: PatchTarget) => void; selectSidebarTab?: (tab: SidebarTab) => void; getSidebarTab?: () => SidebarTab; + sdkSession?: Composition | null; } // ── Hook ── @@ -96,6 +99,7 @@ export function useDomEditSession({ openSourceForSelection, selectSidebarTab, getSidebarTab, + sdkSession, }: UseDomEditSessionParams) { void _setRefreshKey; void _readProjectFile; @@ -228,6 +232,15 @@ export function useDomEditSession({ clearDomSelection, refreshDomEditSelectionFromPreview, buildDomSelectionFromTarget, + onTrySdkPersist: sdkSession + ? (selection, operations, originalContent, targetPath) => + sdkCutoverPersist(selection, operations, originalContent, targetPath, sdkSession, { + editHistory, + writeProjectFile, + reloadPreview, + domEditSaveTimestampRef, + }) + : undefined, }); // ── Wiring: selection sync, GSAP cache, preview sync, selection handlers ──