Files
hyperframes/packages/core
Vance IngallsandClaude Opus 5 a387850032 fix(core,studio): a voice track is never a carve bed, and never carves its own group
A narration clip inside a Voiceover group had a carve pointed at that
group — a member ducking the bus it feeds. Three faults, each sufficient
on its own.

**No bed-eligibility rule.** `couldBeCarveSource` has said since it was
written that music and sfx cannot be sources, and it is called from
nowhere — exported, tested, dead. Nothing ever asked the near-end
question: can this track be the BED. `showCarve` only asked "is anything
already carving against me, and is there anything to listen to", so a
voice track was offered the control like any other. Added
`couldBeCarveBed` beside its sibling and wired it in.

**Offering is not applying.** A bed with exactly one candidate carves
itself unasked, which is right for a track named `music-bed` and wrong
for one named `a1` — a decision taken off a name that said nothing is how
a carve appears that nobody remembers configuring. `isNamedCarveBed`
gates self-application on a name that positively reads as a bed; the
picker stays looser, the same split the source side already makes between
`sourceOptions` and `autoSourceIds`.

**A bed was offered its own group.** The candidate scan excluded exactly
one element, the bed itself. Its siblings survived that filter and rolled
up into the very group the bed belongs to, which came back as a
candidate — and being the only one, was applied. The mirror case too: a
group bed's id matches no <audio> id, so nothing stopped a group carving
against itself. `collectCarveCandidates` now takes the bed's id and drops
both it and its group.

An existing carve still shows its module (`carve !== null`), so nothing
already configured becomes unreachable — only newly offered and
self-applied ones are refused.

The bed/relationship predicates moved to `useFxCarveGrouping.ts`, next to
the source-eligibility rules they belong with. That is also what puts
`useFxCarve.ts` back under the 600-line ceiling it crossed here.

Five tests, each mutation-checked against the pre-fix code. Verified live:
selecting `vo-2` renders no carve module; `music-bed` still gets one,
listening to `Voiceover (4)`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 16:40:15 -07: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)

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