feat(studio): s7.5 — delete shadow scaffolding; keep cutover flag (dark launch) (#1462)

Removes the SDK shadow telemetry: STUDIO_SDK_SHADOW_ENABLED, sdkShadow.ts +
sdkShadowGsapFidelity/GsapKeyframe/Numeric and their tests, the runShadow*
call-sites across the GSAP/timeline hooks, and the onDomEditPersisted shadow
callback in useDomEditSession. Moves patchOpsToSdkEditOps into sdkCutover.ts.

KEEPS STUDIO_SDK_CUTOVER_ENABLED as a dark-launch kill-switch — default false,
enable per-environment via VITE_STUDIO_SDK_CUTOVER_ENABLED=true. shouldUseSdkCutover
stays flag-gated. The stack can merge with zero behavior change; cutover is
validated by flipping the flag, not by removing it.

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
This commit is contained in:
Vance Ingalls
2026-06-17 16:27:03 -07:00
committed by GitHub
co-authored by Miguel Ángel
parent 0ca1a8a9d1
commit ca1a8a6879
15 changed files with 54 additions and 2110 deletions
+37 -2
View File
@@ -1,10 +1,9 @@
import type { MutableRefObject } from "react";
import type { Composition } from "@hyperframes/sdk";
import type { Composition, EditOp } from "@hyperframes/sdk";
import type { DomEditSelection } from "../components/editor/domEditing";
import type { EditHistoryKind } from "./editHistory";
import type { PatchOperation } from "./sourcePatcher";
import { STUDIO_SDK_CUTOVER_ENABLED } from "../components/editor/manualEditingAvailability";
import { patchOpsToSdkEditOps } from "./sdkShadow";
import { trackStudioEvent } from "./studioTelemetry";
const CUTOVER_OP_TYPES = new Set<PatchOperation["type"]>([
@@ -14,6 +13,42 @@ const CUTOVER_OP_TYPES = new Set<PatchOperation["type"]>([
"html-attribute",
]);
/**
* Map Studio PatchOperations for a given hf-id to SDK EditOps.
*
* Multiple inline-style ops are coalesced into a single setStyle (SDK batches
* style changes naturally). One SDK op is emitted per non-style op.
*/
function patchOpsToSdkEditOps(hfId: string, ops: PatchOperation[]): EditOp[] {
const result: EditOp[] = [];
const styles: Record<string, string | null> = {};
let hasStyles = false;
for (const op of ops) {
if (op.type === "inline-style") {
styles[op.property] = op.value;
hasStyles = true;
} else if (op.type === "text-content") {
result.push({ type: "setText", target: hfId, value: op.value ?? "" });
} else if (op.type === "attribute") {
result.push({
type: "setAttribute",
target: hfId,
name: op.property.startsWith("data-") ? op.property : `data-${op.property}`,
value: op.value,
});
} else if (op.type === "html-attribute") {
result.push({ type: "setAttribute", target: hfId, name: op.property, value: op.value });
}
}
if (hasStyles) {
result.unshift({ type: "setStyle", target: hfId, styles });
}
return result;
}
export function shouldUseSdkCutover(
flagEnabled: boolean,
hasSession: boolean,