Commit Graph
2708 Commits
Author SHA1 Message Date
Miguel Angel Simon Sierra fa3848a33d fix(studio): keep the timeline multi-selection through a group edit
setSelectedElementId no longer resets the selection set when re-selecting an element
that is already a member: DOM-to-selection sync echoes fire on every pointer move during
a group drag and were collapsing the set to the grabbed clip.

Also drops the duplicate clearSelectedElementIds action in favor of clearSelection,
which the marquee now uses to clear on an empty drag.
2026-07-09 16:54:34 -04:00
Miguel Angel Simon Sierra 36413da7f6 feat(studio): resize selected timeline clips together
Apply handle deltas through a group resize session that snapshots selected members.

Clamp one shared delta against zero start and min duration before preview and commit.

Playback-start changes are calculated per member with the existing trim formula.
2026-07-09 16:54:34 -04:00
Miguel Angel Simon Sierra 5d27af30b2 feat(studio): move selected timeline clips together
Fan selected body drags through a group move session that snapshots all selected members.

The grabbed clip snap result is applied first, then one shared delta is clamped.

Live preview patches every moved member and release persists once through batch timing.
2026-07-09 16:54:34 -04:00
Miguel Angel Simon Sierra 45d09047b7 feat(studio): add timeline marquee selection 2026-07-09 16:54:34 -04:00
Miguel Angel Simon Sierra 1cf601202d feat(studio): batch timeline timing commits
Persist group timing edits through one coalesced write per source file.

Keep batch timing queued behind any z-index commit for the same gesture.
2026-07-09 16:54:34 -04:00
Miguel Angel Simon Sierra 1d858f004d feat(studio): highlight timeline selection sets
Render selected styling from selectedElementIds in the timeline.

Sync the set into preview group selection boxes without collapsing the anchor.
2026-07-09 16:54:34 -04:00
Miguel Angel Simon Sierra cca31feffe feat(studio): activate selected element set state
Use selectedElementIds as the live selection set with selectedElementId as anchor.

Keep single-click selection as a one-element set and clear both fields together.
2026-07-09 16:54:34 -04:00
Miguel Angel Simon Sierra 6080f5ad3e fix(studio): propagate z-index reorder save failures and drop dead targetTrack
handleDomZIndexReorderCommit no longer swallows per-entry save failures: it settles every
patch, and on any rejection rolls back the eager DOM z-index/position and the optimistic
store zIndex before rejecting, so a failed save cannot leave the UI showing a stacking order
that never persisted or let an ordered-after timing write proceed.

Also removes the dead targetTrack parameter threaded through the timeline edit helpers;
vertical placement is owned by the z-index intent.
2026-07-09 16:53:26 -04:00
James Russo 83da993695 Merge pull request #2084 from heygen-com/07-08-feat_subcomp_variable_render_path
feat(core): aggregate template sub-comp variable defaults from the root div on the render path
2026-07-09 13:44:27 -07:00
James Russo 4961a59bf6 Merge pull request #2081 from heygen-com/07-08-feat_editable_template_subcomps_and_subcomp_promote
feat(sdk,studio): editable template sub-compositions + promote sub-comp element properties
2026-07-09 13:44:08 -07:00
James Russo 2160907bc7 Merge pull request #2071 from heygen-com/07-08-feat_studio_promote_to_variable_from_the_design_panel
feat(studio): promote to variable from the design panel
2026-07-09 13:43:52 -07:00
James Russo 20afabfda0 Merge pull request #2055 from heygen-com/07-07-feat_studio_bind_selected_element_properties_to_variables
feat(studio): bind selected element properties to variables
2026-07-09 13:43:38 -07:00
James Russo ac0c8e9dd2 Merge pull request #2054 from heygen-com/07-07-feat_core_declarative_variable_bindings_data-var-src_data-var-text_css_custom_props
feat(core): declarative variable bindings — data-var-src, data-var-text, css custom props
2026-07-09 13:43:26 -07:00
James Russo cda7ee1a3a Merge pull request #2052 from heygen-com/07-07-fix_studio_code-review_and_live-test_fixes_for_the_variables_stack
fix(studio): code-review and live-test fixes for the variables stack
2026-07-09 13:43:13 -07:00
James Russo dbfe6d8cff Merge pull request #2051 from heygen-com/07-07-feat_studio_render_with_preview_variables_template_handoff_docs
feat(studio): render with preview variables + template handoff + docs
2026-07-09 13:42:59 -07:00
James Russo 51b596719b Merge pull request #2050 from heygen-com/07-07-feat_studio_variables_inspector_panel_with_live_preview_values
feat(studio): variables inspector panel with live preview values
2026-07-09 13:42:44 -07:00
James Russo 0da2937796 Merge pull request #2049 from heygen-com/07-07-feat_studio-server_preview_variable_injection_render_variables_forwarding
feat(studio-server): preview variable injection + render variables forwarding
2026-07-09 13:42:22 -07:00
James Russo 6a40ddcac3 Merge pull request #2048 from heygen-com/07-07-feat_sdk_variable_usage_scan_preview-values_adapter_seam
feat(sdk): variable usage scan + preview-values adapter seam
2026-07-09 13:32:04 -07:00
Vance IngallsandClaude Fable 5 1bb7688347 fix(core): name missing figma scope in 403, retry 429 with backoff
Two bugs from live figma-integration use:

1. `tokens` styles fallback 403s on non-Enterprise. /v1/files/:key/styles
   needs library_content:read — a scope the setup docs and the generic
   FORBIDDEN message both omitted, so the user saw "missing a read scope"
   with no way to know which. Each endpoint now carries a scope hint; the
   403 names the exact scope (styles → library_content:read). Setup text and
   skill scope list updated to include Library content: Read-only.

2. `asset` (and every per-node component render) had no 429 handling — the
   message said "back off and retry" but the client didn't. Two imports in
   a row tripped the per-minute limit and hard-failed. get() now retries 429
   with exponential backoff, honoring Retry-After when present, before
   surfacing RATE_LIMITED after maxRetries (default 3). sleep is injectable
   so tests don't wait.

Batch multi-node asset syntax (the documented /v1/images comma-ids rate
workaround) is a separate enhancement — retry makes the reported failure
self-heal, including the many-node component path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:31:13 -07:00
James a9901cb661 feat(core): sub-composition variable render path 2026-07-09 13:31:04 -07:00
James 8de80bf369 feat(sdk,studio): editable template sub-compositions + promote sub-comp element properties 2026-07-09 13:31:04 -07:00
James 7c144ecc30 feat(studio): promote to variable from the design panel 2026-07-09 13:31:03 -07:00
JamesandClaude Fable 5 267b289bb8 feat(studio): bind selected element properties to variables
Ninth PR of the template-variables stack: the promote-a-property gesture.
Select an element on the canvas/timeline, open the Variables tab, and the
panel offers per-property bind actions.

- "Bind selected" card in the Variables panel, built from the selection:
  image/media source (img/video/audio), text, text color, background, and
  font. Each action declares a variable whose default is the element's
  CURRENT value (promoting never changes the render — computed rgb colors
  convert to hex, the first computed font family becomes the font default)
  and writes the declarative binding the runtime resolves: data-var-src /
  data-var-text attributes or `<prop>: var(--id)` styles. Declare + bind
  run as one batched schema edit (one undo step); binding to an
  already-declared id skips the declare and just binds.
- guarded to selections from the composition the session models — a
  selection in another source file never writes bindings into this one.
- core: extract readVariablesForElement into runtime/variableScope.ts,
  shared by color grading and the declarative bindings (was duplicated).
- fix(studio-server): buildSubCompositionHtml's extractElementAttrs
  rebuilt html/body attributes without HTML-escaping values, shredding
  quote-bearing attributes — data-composition-variables (a JSON array)
  came out as mangled bogus attributes, so getVariables() silently
  returned {} on every /preview/comp/* page (no declared defaults, no
  runtime bindings). Pre-existing bug surfaced by live-testing this
  feature; regression test added.

Verified end-to-end in a live session: select headline → Bind text color
→ declaration + var(--headline-color) written to disk → override in the
panel → runtime applies the custom prop and the element renders the
override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:31:03 -07:00
James f7ee0768ae feat(core): declarative variable bindings — data-var-src, data-var-text, css custom props 2026-07-09 13:31:03 -07:00
James bc0e0b314b fix(studio): code-review and live-test fixes for the variables stack 2026-07-09 13:31:03 -07:00
JamesandClaude Fable 5 010d49327e feat(studio): render with preview variables + template handoff + docs
Sixth PR of the template-variables Studio stack — closing the loop from
preview to render to developer handoff.

- renders started from the Renders tab now carry the active preview
  variable overrides (StartRenderOptions.variables → POST /render →
  RenderConfig.variables), so "render" produces exactly what the user is
  previewing.
- Variables panel "Use this template" footer: copy the effective values
  (defaults merged with overrides) as JSON, or as a ready-to-run
  `npx hyperframes render <comp> --variables '<json>'` command.
- gitignore: negate the renders/ output rule for the tracked
  src/components/renders/ source dir — without it, pre-commit's format
  re-stage (`git add {staged_files}`) hard-fails on any change to those
  files.
- docs: the Studio panel docs/concepts/variables.mdx described was
  aspirational — replace with the real Variables-in-Studio section
  (declare/edit, render-truthful preview, render-with-values, handoff,
  usage badges); document the new SDK variable APIs in
  docs/sdk/reference/composition.mdx (declaration ops, read APIs,
  setPreviewVariables).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:31:03 -07:00
James b34ad85165 feat(studio): variables inspector panel with live preview values 2026-07-09 13:31:03 -07:00
JamesandClaude Fable 5 2e0b884521 feat(studio-server): preview variable injection + render variables forwarding
Fourth PR of the template-variables Studio stack — the HTTP plumbing.

- preview routes (/preview and /preview/comp/*) accept
  ?variables=<url-encoded json> and inject
  `window.__hfVariables = {...}` into <head>, before the runtime and any
  composition script — the exact global the engine sets via
  evaluateOnNewDocument at render time, so preview-with-values cannot
  diverge from render output. Values are escaped against </script>
  breakout, malformed payloads 400 instead of silently previewing
  defaults, and the ETag is salted with a hash of the payload so cached
  previews revalidate when values change.
- POST /projects/:id/render accepts variables ({variableId: value}) and
  forwards them through StudioApiAdapter.startRender into the producer's
  RenderConfig.variables — the same channel `hyperframes render
  --variables` uses. Wired in both adapters (CLI embedded server + vite
  dev adapter).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:31:03 -07:00
James 91831f3f8e feat(sdk): variable usage scan + preview-values adapter seam 2026-07-09 13:31:02 -07:00
James Russo 839881a98e feat(sdk): variable declaration edit ops (declare/update/remove) (#2047) 2026-07-09 13:29:24 -07:00
James Russo fcbd4cb0f6 feat(sdk): variable declaration read apis + browser-safe variables entry (#2046) 2026-07-09 13:15:21 -07:00
Vance Ingalls 030fded71d chore: release v0.7.46 v0.7.46 2026-07-09 12:01:33 -07:00
Vance Ingalls 65dd2dc77e Merge pull request #2093 from heygen-com/fix/static-dedup-tlcall-disqualify
fix(engine): disqualify static-frame dedup on any tl.call()
2026-07-09 12:00:16 -07:00
Vance Ingalls 7acce7e4f4 Merge pull request #2108 from heygen-com/docs/sdk-attachsync-and-variable-crud
docs(sdk): document attachSync, variable CRUD, and getRootElements/getAllAnimationIds
2026-07-09 11:59:28 -07:00
Vance Ingalls 70b9f4dad0 Merge pull request #2100 from heygen-com/feat/sdk-live-dom-sync
feat(sdk): attachSync mirrors composition edits onto a live document
2026-07-09 11:58:39 -07:00
Vance Ingalls bf936f5e3e Merge pull request #2098 from heygen-com/feat/sdk-variable-crud-and-exports
feat(sdk): variable CRUD (declare/remove/get/list) + export gaps
2026-07-09 11:53:58 -07:00
Vance Ingalls f169d5fad6 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."
2026-07-09 11:52:14 -07:00
Vance Ingalls c94de6034e fix(sdk): address PR #2100 review feedback on attachSync
- Script-mirror filter changed from an exact "/script/gsap" match to
  path.startsWith("/script/") — the documented contract is "never mirror
  script-tag rewrites," not just today's one known path; startsWith covers
  any future script-kind patch under the same intent.
- _syncDetach is now cleared when the caller invokes the returned detach
  function directly, not only on the next attachSync call — avoids holding
  a stale (already-unsubscribed) reference between an explicit detach() and
  a later attachSync(other).
- The initial applyOverrideSet call is now wrapped in try/catch: a bad
  initial snapshot no longer prevents the ongoing patch subscription from
  attaching, matching the SDK's existing swallow-and-warn precedent for
  silent-failure paths (adapters/iframe.ts's tainted-canvas warning).
- Added a test proving declareVariable/removeVariable (the /variable-decls/
  patches PR #2098 introduces) mirror onto the live document's
  data-composition-variables attribute — the existing suite only covered
  setVariableValue's CSS-custom-property path, not the schema-metadata path.
2026-07-09 11:52:14 -07:00
Vance Ingalls 91cc67a007 docs(sdk): document attachSync, variable CRUD, and getRootElements/getAllAnimationIds
These SDK reference docs were behind the API surface: PR #2100's attachSync
had zero documentation, and PR #2098/#2092's declareVariable, removeVariable,
getVariableValue, listVariables, and getRootElements were all missing from
composition.mdx despite being real public Composition methods. getAllAnimationIds
was also undocumented (pre-existing gap, unrelated to this stack).

- composition.mdx: adds getVariableValue, listVariables, declareVariable,
  removeVariable (Typed edit methods), getRootElements, getAllAnimationIds
  (Query section)
- adapters.mdx: adds attachSync to the PreviewAdapter interface + a
  ParamField documenting its contract (immediate sync, ongoing patch
  mirroring, script-patch exclusion, detach semantics)
- edit-operations.mdx: adds declareVariable/removeVariable rows + examples
  to the Variables op table
2026-07-09 11:52:14 -07:00
Vance Ingalls fbb6a33c91 test(sdk): cover attachSync mirroring for setVariableValue and setTiming
setVariableValue is the headline case the sync spec was built for and
had no coverage; setTiming (data-start/data-end mirroring) was also
untested. Both regression-checked by temporarily breaking the
underlying mutate/apply-patches code paths and confirming the new
assertions fail.
2026-07-09 11:52:14 -07:00
Vance Ingalls 393eced906 fix(sdk-playground): implement PreviewAdapter.attachSync no-op stub
PlaygroundPreview implements PreviewAdapter but was missing attachSync,
which this branch added to the interface — a real TS break (no
typecheck script wires sdk-playground into CI, so nothing caught it).
Mirrors the same no-op stub already added to HeadlessPreviewAdapter.
2026-07-09 11:52:13 -07:00
Vance Ingalls 9c691d2438 test(sdk): cover attachSync re-attach teardown and manual detach 2026-07-09 11:52:13 -07:00
Vance Ingalls 0e08543561 test(sdk): cover attachSync against a detached iframe 2026-07-09 11:52:13 -07:00
Vance Ingalls ed9df3f57d test(sdk): cover script-patch skip and stylesheet-patch mirroring in attachSync 2026-07-09 11:52:13 -07:00
Vance Ingalls 91ddf9e196 feat(sdk): attachSync mirrors composition edits onto a live document
Adds attachSync(comp) to PreviewAdapter/IframePreviewAdapter — does an
immediate full sync via the existing applyOverrideSet, then subscribes to
comp.on('patch', ...) and replays every future patch (forward or inverse —
undo/redo included) via the existing applyPatchesToDocument, pointed at the
iframe's live document instead of the offscreen linkedom one. No new
mutation logic; both functions already work against any
{document, wrapped, stamped}-shaped object.

Also adds a no-op attachSync stub to HeadlessPreviewAdapter, required to
keep it satisfying the widened PreviewAdapter interface.

Closes the gap that made pacific's canvas-react hand-roll its own
override-application code (applyOverrideToIframe.ts) with two separate
mechanisms (diffing for normal edits, verbatim op-replay for undo/redo) —
subscribing to the patch stream directly needs only one.
2026-07-09 11:52:13 -07:00
Vance Ingalls bcda11aa7c fix(sdk): address PR #2098 review feedback on variable CRUD
- validateOp now handles declareVariable/removeVariable (E_NO_ROOT when no
  composition root), matching setVariableValue's existing case — previously
  comp.can() returned E_UNKNOWN_OP for both.
- removeVariable's undo-inverse now tags its {decl, index} reinsert payload
  with __kind: "reinsert" instead of relying on structural "decl"/"index"
  key presence to disambiguate it from a plain declareVariable patch.
  VariableDecl has an open index signature, so a real variable schema could
  legally declare its own "decl"/"index" fields and be misinterpreted by the
  old structural check; a regression test pins the exact collision.
- getVariableValue's return type tightened from `unknown` to
  `string | number | boolean | FontValue | ImageValue | undefined`, matching
  setVariableValue's parameter type for round-trip symmetry. The underlying
  unknown-typed read is cast once at this SDK boundary.
- Added a redo test for declareVariable/removeVariable (existing tests only
  covered undo).
2026-07-09 11:52:12 -07:00
Vance Ingalls 19756faa5d feat(sdk): variable CRUD (declare/remove/get/list) + export gaps
Closes the remaining Tier 2/3 gaps from the SDK surface audit that motivated
#2092 — real, contained fixes short of the two genuinely architectural items
(a live-DOM apply adapter, structural editing ops) that need their own design
pass, not a quick patch.

Variable CRUD was write-only and creation-blocked: setVariableValue existed,
but there was no getVariableValue, listVariables, declareVariable, or
removeVariable — and writeVariableDefault intentionally refuses to create an
undeclared variable ("keep the schema authoritative"), so a variables panel
(list what exists, read current values, let someone add one) could not be
built against the SDK at all.

- getVariableValue(id) / listVariables(): thin reads over the existing
  readVariableDefault / a new listVariableDecls.
- declareVariable(decl) / removeVariable(id): new EditOps with full
  undo/redo support via a new patch path (/variable-decls/{id}, distinct
  from /variables/{id} which is default-only) — removeVariable's inverse
  bundles the original array index so undo reinserts at the same position
  instead of appending, mirroring handleRemoveElement's siblingIndex.

Export gaps (same shape as #2092's fixes — the logic already existed,
just wasn't reachable): resolveScoped, findById, escapeHfId from
engine/model.ts; readVariableDefault from engine/variableModel.ts.

17 new tests across mutate.test.ts (declareVariable/removeVariable engine
semantics + undo), session.test.ts (Composition-level API), and smoke.test.ts
(export-surface import check). 439/439 sdk tests passing. Full workspace
build (incl. studio) verified clean.
2026-07-09 11:52:12 -07:00
Vance Ingalls d32fb19b9c Merge pull request #2097 from heygen-com/fix/preview-adapter-relative-timing
fix(studio-server): previewAdapter getElementTimings ignores relative data-start refs
2026-07-09 11:46:05 -07:00
Vance Ingalls e4c4d2e15d fix(studio-server): address PR #2097 review feedback on relative-timing resolver
Documents the shared-pattern context (3rd copy of "resolve relative
data-start", after runtime startResolver.ts and the SDK's own
getElementTimings) and explains when the raw parseFloat fallback in
resolveStart's else branch can actually fire (a malformed grammar string
with a leading number). Adds a test pinning the "reference target exists
but its own timing is unresolvable" branch, which existing tests didn't
cover (only "target doesn't exist" was tested).

Cross-checked the negative-offset clamp concern raised in review: the
SDK's own resolveReferenceStart (session.ts) also clamps to
Math.max(0, ...), so this stays consistent with its sibling — no code
change needed there.
2026-07-09 11:27:53 -07:00
Vance Ingalls fbd21d5709 fix(studio-server): previewAdapter getElementTimings ignores relative data-start refs
Same bug class as the SDK's getElementTimings fix (#2092): data-start can be a
relative-reference expression ("intro", "intro + 2"), not just an absolute
number. The old code did a raw parseFloat on it, so any reference silently
resolved to undefined instead of an actual time.

Also: this function never read data-duration at all (only data-start/data-end
literally), so a reference to a duration-authored (not end-authored) clip was
unresolvable regardless of the parseFloat bug — resolving a reference needs
the target's END, which for a duration-authored clip requires start+duration.

Both fixed together via the shared parseStartExpression grammar parser
(@hyperframes/core/runtime/start-expression), with the same cycle-guard
pattern as the SDK fix. Reference resolution against other elements is scoped
to this file's existing findById (bare data-hf-id lookup).

6 new tests: duration-based end resolution, relative reference (with and
without offset), missing target, and a mutual-cycle termination check.
2026-07-09 11:27:53 -07:00