Files
heygen-com--hyperframes/docs/sdk/reference/edit-operations.mdx
T
wehub-resource-sync 85453da49f
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:58:35 +08:00

558 lines
18 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Edit Operations"
description: "The complete EditOp catalog for dispatch(), can(), and batch()."
---
Every mutation in the SDK is expressed as an `EditOp` — a plain data object with a discriminated `type` field. You can submit ops individually through `dispatch()`, validate them with `can()`, or group them into a single undo/persist step with `batch()`.
<Tip>
Every element op requires an explicit `target` (an `HfId` string or `HfId[]` array). There is no selection-implicit mutation — the SDK never reads the current selection to decide what to edit. The typed methods on `Composition` (such as `comp.setText()` and `comp.setStyle()`) are convenience sugar that construct and dispatch these same ops.
</Tip>
## dispatch, batch, and can
### dispatch
```typescript
comp.dispatch(op: EditOp, opts?: { origin?: unknown }): void
```
Applies `op` immediately. Emits a `patch` event, persists if an adapter is attached, and records a history entry. The optional `origin` is forwarded verbatim in the resulting `PatchEvent`; use it to label the source of the change (e.g. `"user"`, `"agent"`, or your own string constant).
```typescript
comp.dispatch(
{ type: "setStyle", target: "hf-title", styles: { color: "#FFD60A" } },
{ origin: "agent" },
);
```
### batch
```typescript
comp.batch(fn: () => void, opts?: { origin?: unknown }): void
```
Groups all `dispatch()` calls made inside `fn` into a single undo step, a single persist write, and a single `patch` event. Use `batch()` when several mutations belong together logically.
```typescript
comp.batch(() => {
comp.dispatch({ type: "setText", target: "hf-title", value: "Launch Day" });
comp.dispatch({ type: "setStyle", target: "hf-title", styles: { fontSize: "96px" } });
comp.dispatch({ type: "setTiming", target: "hf-title", start: 0.5, duration: 3 });
});
```
### can
```typescript
comp.can(op: EditOp): CanResult
```
Dry-runs `op` without mutating the document. Returns `{ ok: true }` when `dispatch(op)` would succeed, or `{ ok: false; code: string; message: string; hint?: string }` when it would be a no-op or error.
Use `can()` as a feature-detection gate before rendering controls or applying optional operations:
```typescript
const result = comp.can({
type: "setGsapTween",
animationId: "anim-1",
properties: { ease: "power3.out" },
});
if (result.ok) {
comp.setGsapTween("anim-1", { ease: "power3.out" });
} else {
console.warn(result.code, result.message);
}
```
Stable `code` values for `ok: false`:
| Code | Meaning |
|------|---------|
| `E_TARGET_NOT_FOUND` | The `target` hf-id does not exist in the document. |
| `E_NO_ROOT` | The document has no root element (empty HTML). |
| `E_NO_GSAP_TIMELINE` | Op requires a parsed GSAP timeline; parser engine not yet active. |
| `E_NO_GSAP_SCRIPT` | Op requires a GSAP `<script>` block; none found in the document. |
<Note>
Several Phase-3b GSAP ops (`addGsapTween`, `setGsapTween`, `removeGsapTween`, `addGsapKeyframe`, `setGsapKeyframe`, `removeGsapKeyframe`, etc.) return `{ ok: false, code: 'E_NO_GSAP_TIMELINE' }` from `can()` until the parser engine ships. `dispatch()` still applies those ops structurally even when `can()` returns false.
</Note>
See also: [`Composition`](/sdk/reference/composition) for the typed-method wrappers, [`Types`](/sdk/reference/types) for `CanResult` and `EditOp`.
---
## Element edits
These ops target one or more elements by explicit hf-id. `target` accepts a single `HfId` string or an `HfId[]` array; when an array is given the op is applied to each id individually within a single batch.
| `type` | Key fields | What it does |
|--------|-----------|--------------|
| `setStyle` | `target`, `styles` | Merges CSS inline styles. `null` values remove that property. |
| `setText` | `target`, `value` | Replaces the element's direct text content. |
| `setAttribute` | `target`, `name`, `value` | Sets or removes an HTML attribute. `null` removes it. Does not touch `style`, `class`, or `data-hf-*`. |
| `setTiming` | `target`, `start?`, `duration?`, `trackIndex?` | Updates one or more timing attributes (`data-start`, `data-duration`, `data-track`). Omitted fields are unchanged. |
| `setHold` | `target`, `hold` | Sets an elastic hold window; see `ElasticHold` shape below. |
| `moveElement` | `target`, `x`, `y` | Repositions the element by setting `data-x` / `data-y` (not CSS `left`/`top`). |
| `removeElement` | `target` | Removes the element and all its children from the document. Inverse of `addElement`. |
### setStyle
```typescript
comp.dispatch({
type: "setStyle",
target: "hf-card",
styles: {
borderRadius: "24px",
backgroundColor: "#1A1A1A",
color: null, // removes the color property
},
});
```
`styles` keys are camelCase property names, matching `CSSStyleDeclaration` convention.
### setText
```typescript
comp.dispatch({
type: "setText",
target: ["hf-headline", "hf-sub"],
value: "Coming soon",
});
```
### setAttribute
```typescript
comp.dispatch({
type: "setAttribute",
target: "hf-logo",
name: "src",
value: "/assets/logo-v2.png",
});
// Remove an attribute:
comp.dispatch({
type: "setAttribute",
target: "hf-video",
name: "autoplay",
value: null,
});
```
### setTiming
```typescript
comp.dispatch({
type: "setTiming",
target: "hf-title",
start: 1.5,
duration: 3,
trackIndex: 0,
});
```
### setHold
`hold` is an `ElasticHold` object:
```typescript
comp.dispatch({
type: "setHold",
target: "hf-badge",
hold: {
start: 2,
end: 5,
fill: "freeze", // "freeze" | "loop"
},
});
```
### moveElement
Sets the element's position via `data-x` / `data-y` attributes. Coordinates are in composition-space pixels.
```typescript
comp.dispatch({
type: "moveElement",
target: "hf-logo",
x: 120,
y: 48,
});
```
---
## Structure
These ops mutate document structure (add/remove/reorder elements, apply class-level styles, or change composition metadata). They do **not** take a `target` field in the same form as element edits.
| `type` | Key fields | What it does |
|--------|-----------|--------------|
| `addElement` | `parent`, `index`, `html` | Inserts an HTML fragment. Returns the minted hf-id via the typed `comp.addElement()` method. |
| `reorderElements` | `entries` | Sets inline `z-index` on one or more elements to reorder their stacking. |
| `setClassStyle` | `selector`, `styles` | Merges CSS rule styles for a class selector. `null` values remove properties. |
| `deleteAllForSelector` | `selector` | Removes all elements matching the CSS selector from the document. |
| `setCompositionMetadata` | `width?`, `height?`, `duration?` | Updates top-level composition dimensions and/or total duration. |
### addElement
`parent` is the hf-id of the parent element, or `null` to insert at the document body root. `index` is the zero-based sibling index (append if `>= childCount`). `html` must be a single-root HTML fragment and must not contain `<script>`.
```typescript
comp.dispatch({
type: "addElement",
parent: "hf-scene-1",
index: 2,
html: '<div class="clip" data-start="3" data-duration="2">New layer</div>',
});
```
Use the typed `comp.addElement(parent, index, html)` method to get back the minted hf-id.
### reorderElements
Each entry sets `z-index` on one element. Elements must be non-statically positioned for `z-index` to take effect — the caller must ensure `position` is set.
```typescript
comp.dispatch({
type: "reorderElements",
entries: [
{ target: "hf-bg", zIndex: 0 },
{ target: "hf-logo", zIndex: 10 },
{ target: "hf-text", zIndex: 20 },
],
});
```
### setClassStyle
```typescript
comp.dispatch({
type: "setClassStyle",
selector: ".caption",
styles: { fontSize: "14px", fontWeight: "600" },
});
```
### deleteAllForSelector
```typescript
comp.dispatch({
type: "deleteAllForSelector",
selector: ".debug-overlay",
});
```
### setCompositionMetadata
```typescript
comp.dispatch({
type: "setCompositionMetadata",
width: 1920,
height: 1080,
duration: 30,
});
```
---
## Variables
| `type` | Key fields | What it does |
|--------|-----------|--------------|
| `setVariableValue` | `id`, `value` | Sets a composition variable's current value by id. Value may be a string, number, boolean, `FontValue`, or `ImageValue`. Refuses to create an undeclared variable. |
| `declareVariable` | `decl` | Creates a new variable declaration, or fully replaces an existing one (type, label, default, and all other schema fields — not just the value). The only op that creates the schema entry from scratch. |
| `removeVariable` | `id` | Removes a variable's declaration entirely. Live `var.{id}` overrides and `data-var-*` DOM references are left untouched. |
```typescript
// Scalar variable
comp.dispatch({
type: "setVariableValue",
id: "brandColor",
value: "#6C5CE7",
});
// Font variable (object-valued — never a CSS string)
comp.dispatch({
type: "setVariableValue",
id: "brand-font",
value: {
name: "Inter",
source: "https://fonts.googleapis.com/css2?family=Inter:wght@400;700",
},
});
// Image variable (object-valued)
comp.dispatch({
type: "setVariableValue",
id: "hero-image",
value: {
url: "/assets/hero.jpg",
alt: "Product hero",
fit: "cover",
},
});
// Create a new variable declaration
comp.dispatch({
type: "declareVariable",
decl: {
id: "brandColor",
type: "color",
label: "Brand color",
default: "#6C5CE7",
},
});
// Remove a variable's declaration
comp.dispatch({ type: "removeVariable", id: "brandColor" });
```
---
## GSAP tweens
These ops add, edit, and remove GSAP tween entries in the composition's GSAP script block. They operate by `animationId` — a stable string identifier minted when a tween is created. Use `comp.addGsapTween()` or `addWithKeyframes` to mint a new id; the typed wrapper returns it directly.
<Note>
Several GSAP ops require the parser engine to be active. Until it ships, `can()` returns `{ ok: false, code: 'E_NO_GSAP_TIMELINE' }` for these ops. `dispatch()` still applies the op structurally.
</Note>
| `type` | Key fields | What it does |
|--------|-----------|--------------|
| `addGsapTween` | `target`, `tween` | Adds a new tween for a target hf-id. Returns the minted `animationId` via the typed method. |
| `setGsapTween` | `animationId`, `properties` | Partially updates an existing tween's `GsapTweenSpec` fields. |
| `removeGsapTween` | `animationId` | Removes the tween entirely. |
| `removeGsapProperty` | `animationId`, `property`, `from?` | Removes one animated property from a tween. `from: true` removes from `fromProperties`; otherwise removes from `toProperties` / `properties`. |
### addGsapTween
`tween` is a `GsapTweenSpec` object:
```typescript
comp.dispatch({
type: "addGsapTween",
target: "hf-title",
tween: {
method: "from",
position: 0,
duration: 0.8,
ease: "power3.out",
fromProperties: { opacity: 0, y: 40 },
},
});
```
`GsapTweenSpec` fields:
| Field | Type | Description |
|-------|------|-------------|
| `method` | `"from" \| "to" \| "fromTo" \| "set"` | GSAP tween method. |
| `position` | `number \| string` | Timeline position. Accepts numbers (seconds) or label-relative strings (`"intro+=0.5"`). |
| `duration` | `number` | Tween duration in seconds. |
| `ease` | `string` | GSAP ease string (e.g. `"power3.out"`). |
| `fromProperties` | `Record<string, unknown>` | Start-state properties (used by `from` and `fromTo`). |
| `toProperties` | `Record<string, unknown>` | End-state properties (used by `fromTo`). |
| `properties` | `Record<string, unknown>` | Animated properties for `to` tweens. |
| `repeat` | `number` | Repeat count (`-1` = infinite). |
| `yoyo` | `boolean` | Reverse on alternating repeats. |
| `stagger` | `number \| Record<string, unknown>` | Stagger config for multi-target tweens. |
### setGsapTween
Only the fields you supply are changed; omit any field to leave it unchanged.
```typescript
comp.dispatch({
type: "setGsapTween",
animationId: "anim-1",
properties: { ease: "elastic.out(1, 0.3)", duration: 1.2 },
});
```
### removeGsapProperty
```typescript
// Remove 'scale' from the to-properties of an existing tween
comp.dispatch({
type: "removeGsapProperty",
animationId: "anim-1",
property: "scale",
});
// Remove 'opacity' from the from-properties
comp.dispatch({
type: "removeGsapProperty",
animationId: "anim-2",
property: "opacity",
from: true,
});
```
---
## Keyframes
Keyframe ops work with tweens that use CSS `@keyframes`-style percentage arrays rather than a single `fromProperties`/`toProperties` shape.
| `type` | Key fields | What it does |
|--------|-----------|--------------|
| `setGsapKeyframe` | `animationId`, `keyframeIndex`, `position?`, `value?`, `ease?` | Updates one keyframe by index inside an existing keyframed tween. |
| `addGsapKeyframe` | `animationId`, `position`, `value` | Appends a new keyframe at a given percentage position. |
| `removeGsapKeyframe` | `animationId`, `percentage` | Removes the keyframe at a specific percentage. |
| `removeAllKeyframes` | `animationId` | Clears all keyframes from a tween, leaving the tween shell intact. |
| `convertToKeyframes` | `animationId`, `resolvedFromValues?` | Converts a `from`/`to`/`fromTo` tween into keyframe form. `resolvedFromValues` provides live computed values for the 0% stop. |
| `materializeKeyframes` | `animationId`, `keyframes`, `easeEach?`, `resolvedSelector?` | Writes a complete keyframe set to an existing tween, replacing any previous keyframes. |
| `addWithKeyframes` | `targetSelector`, `position`, `duration`, `keyframes`, `ease?` | Creates a new keyframed tween for the given CSS selector. Returns minted `animationId` via typed method. |
| `replaceWithKeyframes` | `animationId`, `targetSelector`, `position`, `duration`, `keyframes`, `ease?` | Atomically removes an existing tween and inserts a new keyframed tween. |
| `splitIntoPropertyGroups` | `animationId` | Splits a multi-property keyframed tween into one tween per animated property. |
| `splitAnimations` | `originalId`, `newId`, `splitTime`, `elementStart`, `elementDuration` | Splits one tween into two at `splitTime`. The second half gets `newId`. |
| `unrollDynamicAnimations` | `animationId`, `elements` | Converts a selector-targeted tween that matches multiple elements into per-element keyframe tweens. |
### materializeKeyframes
`keyframes` is an array of `{ percentage, properties, ease? }` objects:
```typescript
comp.dispatch({
type: "materializeKeyframes",
animationId: "anim-3",
keyframes: [
{ percentage: 0, properties: { opacity: 0, y: 30 } },
{ percentage: 100, properties: { opacity: 1, y: 0 }, ease: "power2.out" },
],
easeEach: "none",
resolvedSelector: "#hf-title",
});
```
### addWithKeyframes / replaceWithKeyframes
`position` is a number (seconds) — unlike `GsapTweenSpec.position`, label-relative strings are not accepted here.
```typescript
// Create a new keyframed tween:
comp.dispatch({
type: "addWithKeyframes",
targetSelector: "#hf-card",
position: 1.5,
duration: 0.6,
keyframes: [
{ percentage: 0, properties: { scale: 0.8, opacity: 0 } },
{ percentage: 100, properties: { scale: 1, opacity: 1 } },
],
ease: "back.out(1.7)",
});
// Replace an existing tween atomically:
comp.dispatch({
type: "replaceWithKeyframes",
animationId: "anim-4",
targetSelector: "#hf-card",
position: 1.5,
duration: 0.6,
keyframes: [
{ percentage: 0, properties: { scale: 0.9 } },
{ percentage: 100, properties: { scale: 1 } },
],
});
```
<Note>
After `replaceWithKeyframes`, position-derived tween IDs renumber. Re-query `comp.getElement(id).animationIds` to discover the new ID rather than assuming it matches the old one.
</Note>
`KeyframeSpec` fields:
| Field | Type | Description |
|-------|------|-------------|
| `percentage` | `number` | Keyframe stop position (0100). |
| `properties` | `Record<string, number \| string>` | CSS / GSAP properties at this stop. |
| `ease` | `string` | Ease applied from this stop to the next. |
| `auto` | `boolean` | GSAP endpoint flag — emitted as numeric `_auto: 1`. |
---
## Labels
GSAP timeline labels mark named positions (in seconds) in the master timeline. Labels are referenced in `GsapTweenSpec.position` as strings like `"intro"` or `"intro+=0.5"`.
| `type` | Key fields | What it does |
|--------|-----------|--------------|
| `addLabel` | `name`, `position` | Adds a named label at a timeline position (seconds). |
| `removeLabel` | `name` | Removes a named label. |
```typescript
comp.dispatch({ type: "addLabel", name: "intro", position: 0 });
comp.dispatch({ type: "addLabel", name: "outro", position: 8 });
// Tween positioned relative to a label:
comp.dispatch({
type: "addGsapTween",
target: "hf-cta",
tween: {
method: "from",
position: "outro-=0.5",
duration: 0.4,
fromProperties: { opacity: 0 },
},
});
comp.dispatch({ type: "removeLabel", name: "intro" });
```
---
## Arc paths
Arc path ops control the motion path of a GSAP tween — the curve along which an element travels.
| `type` | Key fields | What it does |
|--------|-----------|--------------|
| `setArcPath` | `animationId`, `config` | Creates or replaces the arc-path config on a tween. |
| `updateArcSegment` | `animationId`, `segmentIndex`, `update` | Updates one segment's curviness or control points. |
| `removeArcPath` | `animationId` | Removes the arc-path from a tween, reverting to a straight-line path. |
### setArcPath
`config` shape:
```typescript
comp.dispatch({
type: "setArcPath",
animationId: "anim-5",
config: {
enabled: true,
autoRotate: true, // boolean, or a number (degrees offset)
segments: [
{
curviness: 1.5,
cp1: { x: 200, y: -80 }, // first control point
cp2: { x: 400, y: 20 }, // second control point
},
],
},
});
```
| Field | Type | Description |
|-------|------|-------------|
| `enabled` | `boolean` | Whether the arc path is active. |
| `autoRotate` | `boolean \| number` | `true` = auto-rotate to follow path tangent; a number offsets the rotation by that many degrees. |
| `segments` | `Array<{ curviness?, cp1?, cp2? }>` | Per-segment path definition. `curviness` is a GSAP Bezier curviness value; `cp1`/`cp2` are control-point coordinates in composition space. |
### updateArcSegment
```typescript
comp.dispatch({
type: "updateArcSegment",
animationId: "anim-5",
segmentIndex: 0,
update: { curviness: 2, cp1: { x: 220, y: -100 } },
});
```