mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
docs(sdk): document CompositionVariable, id/variable utilities, and attachSync in canvas guide
Follow-up to the previous commit in this PR — found while auditing whether any SDK-surface documentation gaps existed beyond this stack: - types.mdx: EditOp's own union listing was missing declareVariable/ removeVariable (present in edit-operations.mdx's table but not mirrored here). Adds a full CompositionVariable reference section (base fields + all 7 variants) since composition.mdx's new declareVariable/listVariables docs reference it without it being defined anywhere in the type reference. - utilities.mdx: documents 6 exported functions with zero prior docs — resolveScoped, findById, bareId, escapeHfId, isNewHostBoundary (Id & Scope Utilities) and readVariableDefault (Variable Utilities). Pre-existing gaps, unrelated to this stack. - canvas-integration.mdx: adds a "Keeping the preview in sync" section covering attachSync right where the guide already sets up preview + comp — previously the guide never mentioned it despite being exactly the answer to "how do I keep the iframe in sync with edits."
This commit is contained in:
@@ -32,6 +32,23 @@ The callback references `comp` before it is declared — that is intentional and
|
||||
|
||||
The `dispatch` callback is optional. Omitting it means `commitPreview()` is a no-op, which is useful if you want to handle op derivation yourself.
|
||||
|
||||
## Keeping the preview in sync
|
||||
|
||||
Everything above wires up hit-testing and drag — but the iframe still won't reflect edits made any other way (an inspector panel calling `comp.setStyle()` directly, an undo, a collaborator's change replayed via `applyPatches()`). `attachSync` closes that gap: call it once you have both `preview` and `comp`, and every future edit — including undo/redo — mirrors onto the live iframe automatically.
|
||||
|
||||
```typescript
|
||||
const detach = preview.attachSync(comp);
|
||||
|
||||
// later, when the editor unmounts or swaps compositions:
|
||||
detach();
|
||||
```
|
||||
|
||||
`attachSync` does an immediate full sync of `comp`'s current state first (so re-opening a composition with existing overrides isn't a blank iframe), then subscribes to the same `patch` event your other listeners use. You don't need to write your own mirroring code, and you don't need a separate mechanism for undo/redo — both flow through the same subscription. Script-tag edits (GSAP script rewrites) are the one thing it never mirrors, since replaying a live `<script>` tag doesn't re-execute it.
|
||||
|
||||
<Note>
|
||||
Calling `attachSync` again with a different `comp` detaches the previous subscription first — useful if your editor swaps which composition an iframe is bound to without remounting it.
|
||||
</Note>
|
||||
|
||||
## Hit-testing: finding what the user clicked
|
||||
|
||||
`preview.elementAtPoint(x, y)` performs a synchronous hit-test at coordinates in the iframe's own coordinate space and returns the nearest `[data-hf-id]` element, or `null` for a transparent hit.
|
||||
|
||||
@@ -350,6 +350,105 @@ interface ElasticHold {
|
||||
|
||||
---
|
||||
|
||||
## CompositionVariable
|
||||
|
||||
The full schema for one declared variable — used by `comp.declareVariable()`, returned from `comp.listVariables()`. A closed union discriminated on `type`; every variant shares the same base fields plus its own type-specific ones.
|
||||
|
||||
```typescript
|
||||
interface CompositionVariableBase {
|
||||
id: string;
|
||||
type: "string" | "number" | "color" | "boolean" | "enum" | "font" | "image";
|
||||
label: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface StringVariable extends CompositionVariableBase {
|
||||
type: "string";
|
||||
default: string;
|
||||
placeholder?: string;
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
interface NumberVariable extends CompositionVariableBase {
|
||||
type: "number";
|
||||
default: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
unit?: string;
|
||||
}
|
||||
|
||||
interface ColorVariable extends CompositionVariableBase {
|
||||
type: "color";
|
||||
default: string;
|
||||
brandRole?: string;
|
||||
}
|
||||
|
||||
interface BooleanVariable extends CompositionVariableBase {
|
||||
type: "boolean";
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
interface EnumVariable extends CompositionVariableBase {
|
||||
type: "enum";
|
||||
default: string;
|
||||
options: { value: string; label: string }[];
|
||||
}
|
||||
|
||||
interface FontVariable extends CompositionVariableBase {
|
||||
type: "font";
|
||||
default: string;
|
||||
source?: string;
|
||||
default_name?: string;
|
||||
default_source?: string;
|
||||
}
|
||||
|
||||
interface ImageVariable extends CompositionVariableBase {
|
||||
type: "image";
|
||||
default: string;
|
||||
brandRole?: string;
|
||||
}
|
||||
|
||||
type CompositionVariable =
|
||||
| StringVariable
|
||||
| NumberVariable
|
||||
| ColorVariable
|
||||
| BooleanVariable
|
||||
| EnumVariable
|
||||
| FontVariable
|
||||
| ImageVariable;
|
||||
```
|
||||
|
||||
<ResponseField name="id" type="string">
|
||||
Stable identifier, referenced by `setVariableValue`, `getVariableValue`, `removeVariable`, and `var.{id}` override-set keys.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="type" type="string">
|
||||
Discriminant. Determines which variant's extra fields apply and what shape `default` takes.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="label" type="string">
|
||||
Human-readable name for a variables panel UI.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="default" type="string | number | boolean">
|
||||
The variable's fallback value. `font` and `image` variants store their fallback as a plain string here (a font-family name / image URL) — the richer `FontValue`/`ImageValue` object shapes are only used as `setVariableValue`'s runtime argument, not as `default`'s stored type.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="brandRole" type="string (color, image)">
|
||||
Optional semantic label for brand-system tooling, e.g. `"color:primary"` or `"logo:primary"`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="options" type="{ value: string; label: string }[] (enum only)">
|
||||
The selectable choices for an `enum` variable.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="source" type="string (font only)">
|
||||
Font stylesheet URL (e.g. a Google Fonts CSS link) paired with the `default` font-family name.
|
||||
</ResponseField>
|
||||
|
||||
---
|
||||
|
||||
## FontValue
|
||||
|
||||
Object value for a `font` composition variable. Always an object — never a raw CSS string.
|
||||
@@ -619,6 +718,8 @@ type EditOp =
|
||||
| { type: "setClassStyle"; selector: string; styles: Record<string, string | null> }
|
||||
| { type: "setCompositionMetadata"; width?: number; height?: number; duration?: number }
|
||||
| { type: "setVariableValue"; id: string; value: string | number | boolean | FontValue | ImageValue }
|
||||
| { type: "declareVariable"; decl: CompositionVariable }
|
||||
| { type: "removeVariable"; id: string }
|
||||
| { type: "addGsapTween"; target: HfId; tween: GsapTweenSpec }
|
||||
| { type: "setGsapTween"; animationId: string; properties: Partial<GsapTweenSpec> }
|
||||
| { type: "removeGsapTween"; animationId: string }
|
||||
|
||||
@@ -230,6 +230,72 @@ const images = all.filter((el) => el.tag === "img");
|
||||
|
||||
---
|
||||
|
||||
## Id & Scope Utilities
|
||||
|
||||
```typescript
|
||||
import { resolveScoped, findById, bareId, escapeHfId, isNewHostBoundary } from "@hyperframes/sdk";
|
||||
```
|
||||
|
||||
Low-level id-resolution helpers used internally by dispatch, patch replay, and query — exposed for hosts building their own DOM-facing tooling against the same document model.
|
||||
|
||||
### resolveScoped
|
||||
|
||||
```typescript
|
||||
function resolveScoped(document: Document, id: string): Element | null;
|
||||
```
|
||||
|
||||
Resolve an `HfId` — bare (`"hf-title"`) or scoped (`"hf-host/hf-leaf"`) — to its `Element`. For an ambiguous bare id (the same id appearing both top-level and inside a sub-composition), prefers the canonical top-level match, matching `getElement()`'s own preference. This is the single resolution rule every mutation and patch-replay path shares — using your own `querySelector` instead can silently target the wrong duplicate.
|
||||
|
||||
### findById
|
||||
|
||||
```typescript
|
||||
function findById(document: Document, id: string): Element | null;
|
||||
```
|
||||
|
||||
Thin alias for `resolveScoped` kept for call-site clarity where "find" reads better than "resolve." Identical behavior.
|
||||
|
||||
### bareId
|
||||
|
||||
```typescript
|
||||
function bareId(scopedId: string): string;
|
||||
```
|
||||
|
||||
Strip a scoped id down to its leaf segment: `bareId("hf-host/hf-leaf")` returns `"hf-leaf"`. A no-op on an already-bare id.
|
||||
|
||||
### escapeHfId
|
||||
|
||||
```typescript
|
||||
function escapeHfId(id: string): string;
|
||||
```
|
||||
|
||||
Escape an id for safe interpolation into a `querySelectorAll` attribute-value selector (backslashes and double quotes). Use this if you're writing a raw `[data-hf-id="..."]` selector yourself instead of going through `resolveScoped`/`findById`.
|
||||
|
||||
### isNewHostBoundary
|
||||
|
||||
```typescript
|
||||
function isNewHostBoundary(el: Element): boolean;
|
||||
```
|
||||
|
||||
Returns `true` when `el` is the root of an inlined sub-composition — that is, it carries a `data-composition-file` attribute whose value differs from its parent's (or the parent has none). Use this to detect "entering a new sub-composition" while walking the tree, without hardcoding the attribute name.
|
||||
|
||||
---
|
||||
|
||||
## Variable Utilities
|
||||
|
||||
```typescript
|
||||
import { readVariableDefault } from "@hyperframes/sdk";
|
||||
```
|
||||
|
||||
### readVariableDefault
|
||||
|
||||
```typescript
|
||||
function readVariableDefault(document: Document, id: string): unknown;
|
||||
```
|
||||
|
||||
Read a declared variable's current `default` value directly from the document's `data-composition-variables` schema attribute, bypassing the session layer. This is the same function `comp.getVariableValue()` calls internally; prefer the typed `Composition` method in session code — use this only when you're working against a raw `Document` outside of an open session (matching `buildDocument`/`buildRoots`'s "same functions the SDK uses internally" pattern above).
|
||||
|
||||
---
|
||||
|
||||
## Constants
|
||||
|
||||
### ORIGIN\_APPLY\_PATCHES
|
||||
|
||||
Reference in New Issue
Block a user