mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-14 18:01:20 +08:00
c73138633f
Split the animation guide into anime.js default and GSAP adapter paths. Align docs, README, and agent templates on the new registration contract.
541 lines
18 KiB
Plaintext
541 lines
18 KiB
Plaintext
---
|
||
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 adapter timeline. |
|
||
| `E_NO_GSAP_SCRIPT` | Op requires a GSAP `<script>` block; none found in the document. |
|
||
|
||
<Note>
|
||
Several GSAP adapter ops (`addGsapTween`, `setGsapTween`, `removeGsapTween`, `addGsapKeyframe`, `setGsapKeyframe`, `removeGsapKeyframe`, etc.) return `{ ok: false, code: 'E_NO_GSAP_TIMELINE' }` from `can()` when the parser engine is unavailable. `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 by id. Value may be a string, number, boolean, `FontValue`, or `ImageValue`. |
|
||
|
||
```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",
|
||
},
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## GSAP adapter tweens
|
||
|
||
These ops add, edit, and remove GSAP tween entries in the composition's GSAP script block. anime.js is the first-party default runtime for new composition authoring; these operations are the supported SDK editing surface for existing or migrated GSAP adapter scripts. 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 adapter ops require the parser engine to be active. When it is unavailable, `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 (0–100). |
|
||
| `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 adapter 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 adapter 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 } },
|
||
});
|
||
```
|