mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 15:20:13 +00:00
feat(sdk): export getRootElements/isNewHostBoundary/bareId, fix relative data-start
Closes gaps surfaced by pacific#30298 (hyperframes layer panel), where consumer
code had to hand-roll fixes for things the SDK/core already solve or nearly solve:
- getRootElements(): getElements() flattens the tree, so every descendant also
appears as its own top-level entry. buildRoots() already computes true roots
internally; this exposes it directly instead of making consumers re-derive
roots by filtering out descendant ids.
- Export isNewHostBoundary + bareId from @hyperframes/sdk: both already existed
internally (engine/model.ts) but weren't exported, so consumers were
duplicating sub-composition-boundary detection and scoped-id-to-DOM-leaf
conversion by hand.
- Export stripEmbeddedRuntimeScripts + RUNTIME_BOOTSTRAP_ATTR from
@hyperframes/core, and wire serialize({ stripRuntime: true }) on the SDK
session: a proper tokenizing implementation already existed in
compiler/htmlDocument.ts (handles more runtime-script marker variants than a
naive regex), just never exported. The SDK itself imports these via narrow
subpaths (./runtime/start-expression, ./compiler/html-document) rather than
the wide ./compiler barrel, matching the SDK's existing import convention and
avoiding pulling Node-only compiler code (fs/path) into browser bundles.
- Fix getElementTimings(): data-start can be a relative-reference expression
("intro", "intro + 2" — see parseStartExpression's grammar), not just an
absolute number. The old code did a raw parseFloat() on it, which silently
resolved any reference expression to 0. Now resolves references recursively
against the target element's own resolved start + duration, Node-safe (no
live GSAP timeline needed for this case).
14 new tests (session.timings.test.ts, session.subcomp.test.ts). Full sdk
suite: 417/417 passing. Full workspace build (incl. studio) verified clean.
This commit is contained in:
@@ -113,6 +113,46 @@ describe("getElementTimings — GSAP labels", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─── getElementTimings — relative data-start references ──────────────────────
|
||||
|
||||
/** "intro" starts at data-start=1 for 3s (ends at 4). "outro" starts 2s after intro ends. */
|
||||
const RELATIVE_START_HTML = `
|
||||
<div data-hf-id="hf-stage" data-hf-root style="width:1280px;height:720px" data-duration="20">
|
||||
<h1 data-hf-id="hf-intro" data-start="1" data-duration="3">Intro</h1>
|
||||
<p data-hf-id="hf-outro" data-start="hf-intro + 2" data-duration="4">Outro</p>
|
||||
<p data-hf-id="hf-right-after" data-start="hf-intro" data-duration="1">Right after</p>
|
||||
</div>
|
||||
`.trim();
|
||||
|
||||
describe("getElementTimings — relative data-start references", () => {
|
||||
it("resolves 'ref + offset' against the referenced element's resolved end", async () => {
|
||||
const comp = await openComposition(RELATIVE_START_HTML);
|
||||
const timings = comp.getElementTimings();
|
||||
|
||||
// hf-intro: enterAt=1, exitAt=4
|
||||
expect(timings["hf-intro"]).toMatchObject({ enterAt: 1, exitAt: 4 });
|
||||
// hf-outro: "hf-intro + 2" = intro's exitAt (4) + 2 = 6
|
||||
expect(timings["hf-outro"]).toMatchObject({ enterAt: 6, exitAt: 10 });
|
||||
});
|
||||
|
||||
it("resolves a bare reference (no offset) to the referenced element's exitAt", async () => {
|
||||
const comp = await openComposition(RELATIVE_START_HTML);
|
||||
const timings = comp.getElementTimings();
|
||||
expect(timings["hf-right-after"]).toMatchObject({ enterAt: 4, exitAt: 5 });
|
||||
});
|
||||
|
||||
it("resolves to 0 (not NaN) when the reference target doesn't exist", async () => {
|
||||
const html = `
|
||||
<div data-hf-id="hf-stage" data-hf-root style="width:1280px;height:720px">
|
||||
<p data-hf-id="hf-orphan" data-start="hf-nonexistent + 5" data-duration="2"></p>
|
||||
</div>
|
||||
`.trim();
|
||||
const comp = await openComposition(html);
|
||||
const timings = comp.getElementTimings();
|
||||
expect(timings["hf-orphan"]).toMatchObject({ enterAt: 0, exitAt: 2 });
|
||||
});
|
||||
});
|
||||
|
||||
// ─── setElementTiming — sparse map + batched dispatch ────────────────────────
|
||||
|
||||
describe("setElementTiming", () => {
|
||||
|
||||
Reference in New Issue
Block a user