mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
Rendering a composition and mounting one now derive root discovery, scope identity, asset sources and order, hoisted links, variable carriers and nested-host enumeration from the same module. Each keeps its own I/O, which is where they genuinely differ. The compiler's local depth cap and root lookup and the runtime's three pre-filtered head parameters are gone; the runtime hands over the head node and lets the module decide what comes out of it. Four divergences are settled here. Each changes behaviour, so each is stated with what actually differs rather than with a description of the edit. Inline head scripts. The compiler looped head scripts with a src branch and no else, so an inline one was silently discarded on render while the runtime ran it. The compiler was losing code, not holding a convention. Head and content scripts now run through one loop, head first, order preserved. A non-templated sub-composition with an inline head script went from zero collected scripts to one, wrapped, with its body intact. Link hoisting. Conditional on render, unconditional on mount, so a templated sub-composition's webfont link was dropped in video and kept in preview. Hoisting is the superset and matches what the author declared, so hoisting wins. A templated composition with a stylesheet link in its head went from no external links to that link. The parity fixture that previously recorded this shape as a known exclusion now gates it instead. Anonymous hosts. With a host naming no id, the compiler fell back to the first declared composition and scoped to it; the mount left the content unflattened and injected its stylesheet into the host head unscoped, so a composition's CSS leaked into whatever mounted it. The compiler's answer wins. The mount now flattens like every other mount and restores the declared id afterwards. The injected rule went from a bare class selector to one scoped to the composition. This flips an assertion that documented the old behaviour as intentional. Its premise no longer holds. What that test actually cared about, the root and its content being present under the host, still holds and is still asserted; the claim that nothing was flattened is now false and the test asserts the scoping instead. Scope ids. The compiler splits the CSS scope id from the script composition id; they differ only when a host names an id the content does not declare, and there the scripts follow the declared id so their self-referencing queries resolve. The runtime used one for both. The split wins: a host naming captions-comp over content declaring captions now emits scripts bound to captions while its CSS still scopes to captions-comp. Left alone deliberately: the variable-carrier divergence and its TODO, recursion on the mount path, and the three copies each of the flattened-root helper and the id assignment. The first two are behaviour changes with their own units. The third looks mergeable and is not cheaply, and it touches the instancing contract the pixel harness guards. Verified: core 1694 passing, producer 573 passing, the parity contract now nine tests with fixtures gating the two divergences it can observe. Lint clean, typecheck:runtime and the runtime preview guards clean, package cycles unchanged. A trap worth recording: the parity test's compiler arms import core's built dist while the mount arm imports source, so core must be rebuilt before that lane means anything after a compiler change. Skipping it produces a phantom divergence.
@hyperframes/core
Types, parsers, generators, compiler, linter, runtime, and frame adapters for the Hyperframes video framework.
Install
npm install @hyperframes/core
Most users don't need to install core directly — the CLI, producer, and studio packages depend on it internally.
What's inside
| Module | Description |
|---|---|
| Types | TimelineElement, CompositionSpec, Asset, canvas dimensions, defaults |
| Parsers | parseHtml — extract timeline elements from HTML; parseGsapScript — parse GSAP animations |
| Generators | generateHyperframesHtml — produce valid Hyperframes HTML from a composition spec |
| Compiler | compileTimingAttrs — resolve data-start / data-duration into absolute times |
| Linter | lintHyperframeHtml — validate Hyperframes HTML (missing attributes, overlapping tracks, etc.) |
| Runtime | IIFE script injected into the browser — manages seek, media playback, and the window.__hf protocol |
| Frame Adapters | Pluggable animation drivers (GSAP, Lottie, CSS, or custom) |
Frame Adapters
A frame adapter tells the engine how to seek your animation to a specific frame:
import { createGSAPFrameAdapter } from "@hyperframes/core";
const adapter = createGSAPFrameAdapter({
getTimeline: () => gsap.timeline(),
compositionId: "my-video",
});
Implement FrameAdapter for custom animation runtimes:
import type { FrameAdapter } from "@hyperframes/core";
const myAdapter: FrameAdapter = {
id: "my-adapter",
getDurationFrames: () => 300,
seekFrame: (frame) => {
/* seek your animation */
},
};
Parsing and generating HTML
import { parseHtml, generateHyperframesHtml } from "@hyperframes/core";
const { elements, metadata } = parseHtml(htmlString);
const html = generateHyperframesHtml(spec);
Linting
import { lintHyperframeHtml } from "@hyperframes/core/lint";
const result = lintHyperframeHtml(htmlString);
// result.findings: { severity, message, elementId }[]
Documentation
Full documentation: hyperframes.heygen.com/packages/core
Related packages
@hyperframes/engine— rendering engine that drives the browser@hyperframes/producer— full render pipeline (capture + encode)hyperframes— CLI