feat(studio): per-composition render button in compositions tab (#874)

* feat(studio): add per-composition render button in compositions tab

Thread composition path through the full render pipeline so individual
compositions can be rendered independently from the studio UI.

- Add download icon button on each comp card (visible on hover)
- Accept `composition` field in POST /projects/:id/render
- Pass composition as `entryFile` to the producer's createRenderJob
- Make the Export button in the Renders panel composition-aware
  (renders the active composition instead of always index.html)

* fix(studio): make composition render buttons always visible

The hover-only opacity made them undiscoverable.

* fix(studio): address PR review — CLI adapter, path guard, a11y, tests, settings sync

- Wire `composition` → `entryFile` in CLI studio adapter (studioServer.ts)
  so `hyperframes preview` renders the correct composition, not always index.html
- Add path-traversal guard: reject composition paths that resolve outside projectDir
- Add `aria-label` to the icon-only render button for screen readers
- Add 4 tests: forwarding, empty/missing → undefined, path-traversal → 400
- Persist render settings (format/quality/fps) to localStorage so comp card
  buttons use the same settings as the Export panel

* refactor(studio): extract render settings persistence to own module

Move getPersistedRenderSettings/persistRenderSettings out of
RenderQueue.tsx into renderSettings.ts so code-splitting the
component doesn't drag along the helper.
This commit is contained in:
Miguel Ángel
2026-05-15 22:55:54 +02:00
committed by GitHub
parent 9b23ccf665
commit 4fd9520a90
12 changed files with 235 additions and 12 deletions
@@ -43,6 +43,8 @@ interface LeftSidebarProps {
onDuplicateFile?: (path: string) => void;
onMoveFile?: (oldPath: string, newPath: string) => void;
codeChildren?: ReactNode;
onRenderComposition?: (comp: string) => void;
isRendering?: boolean;
onLint?: () => void;
linting?: boolean;
onToggleCollapse?: () => void;
@@ -69,6 +71,8 @@ export const LeftSidebar = memo(
onDuplicateFile,
onMoveFile,
codeChildren,
onRenderComposition,
isRendering,
onLint,
linting,
onToggleCollapse,
@@ -169,6 +173,8 @@ export const LeftSidebar = memo(
compositions={compositions}
activeComposition={activeComposition}
onSelect={onSelectComposition}
onRenderComposition={onRenderComposition}
isRendering={isRendering}
/>
)}
{tab === "assets" && (