mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
* fix(sdk,studio): R5 cutover review fixes — fromTo dest, timing sync, parity Confirmed correctness findings from the R5 review of the SDK cutover stack, applied on top of #1539: - fromTo add via cutover dropped its destination: handleAddGsapTween read only `toProperties`; now falls back to `properties` like every other method. - handleSetTiming GSAP sync: a clip with no data-start skipped the shift (now treats start as 0, matching the server path) and a blank/non-numeric data-start wrote position: NaN (now sanitized). - handleSetTiming no longer appends an absolute position to an auto-sequenced (implicit-position) tween, which collapsed staggers. - handleSetTiming keeps data-end in sync when a clip carries BOTH data-duration and data-end (a stale data-end inverted the clip). - string/relative tween positions ("+=0.5", "<") documented as a known ceiling. - opacity/autoAlpha property seed no longer falsy-zero (`|| 1`): an element at opacity 0 seeds 0, not 1. - optimistic add-keyframe cache tolerance aligned to the writer's PCT_TOLERANCE (2%) so a near-neighbour keyframe no longer shows then vanishes on reload. - DOM-patch finiteness validation runs before the SDK cutover path. - attribute ops mapping to a reserved data-* name decline the cutover up front instead of throwing inside dispatch. Regression tests added for each SDK-side fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(studio): close two gaps in the reserved-attr cutover gate - Lowercase the mapped attribute name before the reserved check, matching the SDK's validateSetAttribute (which lowercases), so a case-variant reserved name is declined up front instead of throwing inside dispatch. - Also gate `html-attribute` ops (raw, non-prefixed names), not just bare `attribute` ops. Both the emitter and the gate now derive the name via one shared `sdkAttrName` helper so they can't drift. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(studio): match keyframe remove-path tolerance to the writer (mirror of add) The optimistic remove-keyframe cache filtered with `> 0.001`, dropping only a near-exact match, while the writer removes within PCT_TOLERANCE (2). Removing at e.g. 49% dropped a 50% keyframe on disk but left it in the cache — a phantom that vanished on reload, the inverted twin of the add-path tolerance fix. Now filters with `> 2` to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
8c981a451a
commit
e57e75b9b4
@@ -14,6 +14,46 @@ const CUTOVER_OP_TYPES = new Set<PatchOperation["type"]>([
|
||||
"html-attribute",
|
||||
]);
|
||||
|
||||
// Mirrors the SDK's RESERVED_ATTRS (mutate.ts): a bare `attribute` op is
|
||||
// force-prefixed `data-`, so e.g. property "end" → "data-end", which the SDK
|
||||
// rejects with a throw. Detect that up front and decline the whole batch so it
|
||||
// takes the server path cleanly, instead of throwing inside the dispatch and
|
||||
// silently falling back per op.
|
||||
// ponytail: small mirror of the SDK set; if the SDK adds a reserved attr, a new
|
||||
// op for it just reverts to the (working) throw→fallback path until synced.
|
||||
const RESERVED_CUTOVER_ATTRS = new Set<string>([
|
||||
"data-hf-id",
|
||||
"data-composition-id",
|
||||
"data-width",
|
||||
"data-height",
|
||||
"data-start",
|
||||
"data-end",
|
||||
"data-track-index",
|
||||
"data-hold-start",
|
||||
"data-hold-end",
|
||||
"data-hold-fill",
|
||||
]);
|
||||
|
||||
// The attribute name the SDK setAttribute op carries for this patch op (or null
|
||||
// if the op isn't an attribute). Shared by patchOpsToSdkEditOps and the reserved
|
||||
// gate so the name they reason about can't drift: a bare `attribute` op is
|
||||
// force-prefixed `data-`; an `html-attribute` op keeps its raw name.
|
||||
function sdkAttrName(op: PatchOperation): string | null {
|
||||
if (op.type === "attribute") {
|
||||
return op.property.startsWith("data-") ? op.property : `data-${op.property}`;
|
||||
}
|
||||
if (op.type === "html-attribute") return op.property;
|
||||
return null;
|
||||
}
|
||||
|
||||
function mapsToReservedAttr(op: PatchOperation): boolean {
|
||||
const name = sdkAttrName(op);
|
||||
// Lowercase to match the SDK's validateSetAttribute (it lowercases before the
|
||||
// reserved check), so "DATA-START" is declined up front too; covers both
|
||||
// `attribute` (prefixed) and `html-attribute` (raw) ops.
|
||||
return name !== null && RESERVED_CUTOVER_ATTRS.has(name.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Studio PatchOperations for a given hf-id to SDK EditOps.
|
||||
*
|
||||
@@ -31,15 +71,11 @@ function patchOpsToSdkEditOps(hfId: string, ops: PatchOperation[]): EditOp[] {
|
||||
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 });
|
||||
} else if (op.type === "attribute" || op.type === "html-attribute") {
|
||||
const name = sdkAttrName(op);
|
||||
if (name !== null) {
|
||||
result.push({ type: "setAttribute", target: hfId, name, value: op.value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +97,8 @@ export function shouldUseSdkCutover(
|
||||
hasSession &&
|
||||
!!hfId &&
|
||||
ops.length > 0 &&
|
||||
ops.every((o) => CUTOVER_OP_TYPES.has(o.type))
|
||||
ops.every((o) => CUTOVER_OP_TYPES.has(o.type)) &&
|
||||
!ops.some(mapsToReservedAttr)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user