mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 07:09:59 +00:00
fix(sdk): resolve composition-id targets + emit canonical data-hf-id for GSAP tweens (#1526)
A sub-composition ROOT is addressed by its data-composition-id, but the SDK's whole element<->tween attribution is data-hf-id based, so the prior fix's [data-composition-id] selector was invisible to three readers (validateOp/can, selectorMatchesId -> setTiming + removeElement cascade, buildAnimationIdMap -> getElement.animationIds), diverging can from apply and orphaning tweens. Root fix: make composition ids first-class resolvable addresses and emit the canonical selector everywhere. - resolveScoped (model.ts): for a bare id with no data-hf-id match, fall back to [data-composition-id]. data-hf-id keeps precedence; scoped-path and canonical behavior intact. Fixes validateOp gating, findById/getElement, and every op handler for comp-root targets in one place. - gsapTargetSelector (mutate.ts): resolve the target and emit [data-hf-id="<resolved host hf-id>"] (canonical). Normal targets unchanged; comp-root targets resolve via comp-id -> host -> host hf-id. Defensive [data-composition-id] only when the resolved element has no hf-id. - setTiming syncs the GSAP tween via the resolved element's data-hf-id so a comp-root target matches its host tween; removeElement cascade already covers the host hf-id via collectSubtreeHfIds. - export escapeHfId; escape both the querySelector probe and the emitted selector string. Tests: comp-id resolveScoped fallback + precedence (session.subcomp), canonical selector, validateOp accept, setTiming sync, removeElement cascade, and getElement.animationIds for comp-root tweens (mutate.gsap). The prior test only called applyOp, masking all of this. 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
9cc3550f7e
commit
6e32142334
@@ -37,7 +37,7 @@ export function findById(document: Document, id: string): Element | null {
|
||||
return resolveScoped(document, id);
|
||||
}
|
||||
|
||||
function escapeHfId(id: string): string {
|
||||
export function escapeHfId(id: string): string {
|
||||
return id.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
||||
}
|
||||
|
||||
@@ -76,8 +76,15 @@ export function resolveScoped(document: Document, id: string): Element | null {
|
||||
if (parts.length === 1) {
|
||||
const escaped = escapeHfId(id);
|
||||
const matches = Array.from(document.querySelectorAll(`[data-hf-id="${escaped}"]`));
|
||||
if (matches.length === 0) return null;
|
||||
return matches.find((el) => isCanonicalScope(el)) ?? matches[0] ?? null;
|
||||
if (matches.length > 0) {
|
||||
return matches.find((el) => isCanonicalScope(el)) ?? matches[0] ?? null;
|
||||
}
|
||||
// Fall back to a sub-composition ROOT addressed by its composition id. A
|
||||
// host element carries data-hf-id (its own leaf id) AND data-composition-id
|
||||
// (the id studio passes when targeting the sub-comp root). data-hf-id takes
|
||||
// precedence above; only when no hf-id matches do we treat the bare id as a
|
||||
// composition id, making comp-ids first-class resolvable addresses.
|
||||
return document.querySelector(`[data-composition-id="${escaped}"]`);
|
||||
}
|
||||
|
||||
let context: Element | Document = document;
|
||||
|
||||
Reference in New Issue
Block a user