Files
hyperframes/packages/core
Miguel Ángel f4e00ce2b5 fix(runtime): a finished film rests on its last frame instead of a blank one (#4158)
* fix(runtime): a finished film rests on its last frame instead of a blank one

The clip-visibility check was half-open (start <= t < end), so a clip whose
authored end reached the composition's total duration hid at exactly that
instant. The player parks there when playback ends, so a finished film
went blank instead of holding its last frame, in both the published
player and Studio's scrub-to-end.

Extracts the shared half-open comparison used in seven places into
isInClipWindow (packages/core/src/runtime/clipWindow.ts) and adds
isClipVisibleAt, which additionally treats a clip reaching the
composition's terminal instant as visible. Only the DOM-visibility gate
uses the terminal rule; playback and media-activation windows keep the
plain half-open check, so two back-to-back clips still never both show
at their shared boundary and audio never re-activates past the end.

* fix(runtime): hold a terminal video on its last frame at the composition end

* refactor(player): route the proxy clip-window checks through the shared predicate

Makes the composition duration required on syncRuntimeMedia so a caller
cannot drop the terminal rule, exposes the predicate as a core subpath,
and adds a nested-timeline float-noise test.

* build(core): declare the runtime/clip-window subpath in the package-subpaths descriptor

* test(runtime): a speed-ramped video that runs to the composition end holds its last frame

Covers the composition of the terminal hold with a rate lane: at a time
past the clip end the video sits at the source time of its own end.
2026-09-19 10:35:26 -04:00
..

@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)

Generated composition trust

Composition generators require trusted authors for code-bearing inputs. styles and generateHyperframesStyles preserve authored CSS, which can load external resources. animations may contain __raw: values that are emitted as JavaScript; includeScripts: true includes executable timeline code. serializeGsapAnimations also accepts raw preamble, postamble, and a code-bearing timelineVar. Never fill these inputs with untrusted data. Attribute encoding and closing-tag containment are not a sandbox; render untrusted compositions in an appropriately isolated execution environment and never serve them on a privileged origin.

Text content retains the supported inline-formatting sanitizer contract. The clip parser intentionally flattens inner formatting to text, so parse/generate is not a lossless replacement for editing the source HTML.

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