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>
This commit is contained in:
James
2026-07-09 13:31:03 -07:00
co-authored by Claude Fable 5
parent b34ad85165
commit 010d49327e
7 changed files with 153 additions and 14 deletions
@@ -34,6 +34,12 @@ export interface StartRenderOptions {
resolution?: ResolutionPreset | "auto";
/** Render a specific composition file instead of index.html. */
composition?: string;
/**
* Composition-variable overrides ({variableId: value}), forwarded to the
* render route and injected as window.__hfVariables — the same channel
* `hyperframes render --variables` uses.
*/
variables?: Record<string, unknown>;
}
// "Hide" (formerly "Clear") is a view operation, not a delete: hidden ids are
@@ -126,7 +132,9 @@ export function useRenderQueue(projectId: string | null) {
}, [loadRenders]);
// Start a render and track progress via SSE
// Pre-existing branchy fetch/poll flow — the variables passthrough added one branch.
const startRender = useCallback(
// fallow-ignore-next-line complexity
async (opts: StartRenderOptions = {}) => {
if (!projectId) return;
@@ -154,6 +162,7 @@ export function useRenderQueue(projectId: string | null) {
format: string;
resolution?: string;
composition?: string;
variables?: Record<string, unknown>;
telemetryDistinctId: string;
} = {
fps,
@@ -166,6 +175,9 @@ export function useRenderQueue(projectId: string | null) {
};
if (resolution && resolution !== "auto") body.resolution = resolution;
if (composition) body.composition = composition;
if (opts.variables && Object.keys(opts.variables).length > 0) {
body.variables = opts.variables;
}
let res: Response;
try {
res = await fetch(`/api/projects/${projectId}/render`, {