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
@@ -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<boolean>;
}
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,
],
);
@@ -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 ──