fix(studio): wire onTrySdkPersist to sdkCutoverPersist (cutover was unwired) (#1463)

Stage 7 s7.5 removed the feature flag and declared cutover 'always-on',
but onTrySdkPersist was never actually passed to useDomEditCommits — the
sdkCutoverPersist function was dead code in production.

Thread sdkSession through useDomEditSession params, build the
onTrySdkPersist closure there (all CutoverDeps are already in scope),
and pass sdkSession from App.tsx. Style/text/attribute/html-attribute
commits now route through SDK dispatch instead of the server patch path.

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
This commit is contained in:
Vance Ingalls
2026-06-17 16:31:57 -07:00
committed by GitHub
co-authored by Miguel Ángel
parent ca1a8a6879
commit 8585fffc92
3 changed files with 35 additions and 0 deletions
+1
View File
@@ -304,6 +304,7 @@ export function StudioApp() {
openSourceForSelection: fileManager.openSourceForSelection, openSourceForSelection: fileManager.openSourceForSelection,
selectSidebarTab: selectSidebarTabStable, selectSidebarTab: selectSidebarTabStable,
getSidebarTab: getSidebarTabStable, getSidebarTab: getSidebarTabStable,
sdkSession: sdkHandle.session,
}); });
domEditSelectionBridgeRef.current = domEditSession.domEditSelection; domEditSelectionBridgeRef.current = domEditSession.domEditSelection;
clearDomSelectionRef.current = domEditSession.clearDomSelection; clearDomSelectionRef.current = domEditSession.clearDomSelection;
@@ -77,6 +77,13 @@ export interface UseDomEditCommitsParams {
onDomEditPersisted?: (selection: DomEditSelection, operations: PatchOperation[]) => void; onDomEditPersisted?: (selection: DomEditSelection, operations: PatchOperation[]) => void;
/** Stage 7 Step 3b: called after a successful server-side element delete. */ /** Stage 7 Step 3b: called after a successful server-side element delete. */
onElementDeleted?: (selection: DomEditSelection) => void; 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<boolean>;
} }
export function useDomEditCommits({ export function useDomEditCommits({
@@ -99,6 +106,7 @@ export function useDomEditCommits({
buildDomSelectionFromTarget, buildDomSelectionFromTarget,
onDomEditPersisted, onDomEditPersisted,
onElementDeleted, onElementDeleted,
onTrySdkPersist,
}: UseDomEditCommitsParams) { }: UseDomEditCommitsParams) {
const resolveImportedFontAsset = useCallback( const resolveImportedFontAsset = useCallback(
(fontFamilyValue: string): ImportedFontAsset | null => { (fontFamilyValue: string): ImportedFontAsset | null => {
@@ -149,6 +157,18 @@ export function useDomEditCommits({
if (options?.shouldSave && !options.shouldSave()) return; 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 patchTarget = buildDomEditPatchTarget(selection);
const patchBody = { target: patchTarget, operations }; const patchBody = { target: patchTarget, operations };
const unsafeFields = findUnsafeDomPatchValues(patchBody); const unsafeFields = findUnsafeDomPatchValues(patchBody);
@@ -235,6 +255,7 @@ export function useDomEditCommits({
reloadPreview, reloadPreview,
showToast, showToast,
onDomEditPersisted, onDomEditPersisted,
onTrySdkPersist,
], ],
); );
@@ -4,6 +4,8 @@ import type { EditHistoryKind } from "../utils/editHistory";
import type { RightPanelTab } from "../utils/studioHelpers"; import type { RightPanelTab } from "../utils/studioHelpers";
import type { PatchTarget } from "../utils/sourcePatcher"; import type { PatchTarget } from "../utils/sourcePatcher";
import type { SidebarTab } from "../components/sidebar/LeftSidebar"; import type { SidebarTab } from "../components/sidebar/LeftSidebar";
import type { Composition } from "@hyperframes/sdk";
import { sdkCutoverPersist } from "../utils/sdkCutover";
import { useAskAgentModal } from "./useAskAgentModal"; import { useAskAgentModal } from "./useAskAgentModal";
import { useDomSelection } from "./useDomSelection"; import { useDomSelection } from "./useDomSelection";
import { usePreviewInteraction } from "./usePreviewInteraction"; import { usePreviewInteraction } from "./usePreviewInteraction";
@@ -58,6 +60,7 @@ export interface UseDomEditSessionParams {
openSourceForSelection?: (sourceFile: string, target: PatchTarget) => void; openSourceForSelection?: (sourceFile: string, target: PatchTarget) => void;
selectSidebarTab?: (tab: SidebarTab) => void; selectSidebarTab?: (tab: SidebarTab) => void;
getSidebarTab?: () => SidebarTab; getSidebarTab?: () => SidebarTab;
sdkSession?: Composition | null;
} }
// ── Hook ── // ── Hook ──
@@ -96,6 +99,7 @@ export function useDomEditSession({
openSourceForSelection, openSourceForSelection,
selectSidebarTab, selectSidebarTab,
getSidebarTab, getSidebarTab,
sdkSession,
}: UseDomEditSessionParams) { }: UseDomEditSessionParams) {
void _setRefreshKey; void _setRefreshKey;
void _readProjectFile; void _readProjectFile;
@@ -228,6 +232,15 @@ export function useDomEditSession({
clearDomSelection, clearDomSelection,
refreshDomEditSelectionFromPreview, refreshDomEditSelectionFromPreview,
buildDomSelectionFromTarget, 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 ── // ── Wiring: selection sync, GSAP cache, preview sync, selection handlers ──