mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 07:09:59 +00:00
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:
@@ -1,4 +1,4 @@
|
||||
import type { RefObject } from "react";
|
||||
import { useCallback, type RefObject } from "react";
|
||||
import { SourceEditor } from "./editor/SourceEditor";
|
||||
import { LeftSidebar, type LeftSidebarHandle } from "./sidebar/LeftSidebar";
|
||||
import { MediaPreview } from "./MediaPreview";
|
||||
@@ -6,6 +6,7 @@ import { isMediaFile } from "../utils/mediaTypes";
|
||||
import { usePanelLayoutContext } from "../contexts/PanelLayoutContext";
|
||||
import { useStudioContext } from "../contexts/StudioContext";
|
||||
import { useFileManagerContext } from "../contexts/FileManagerContext";
|
||||
import { getPersistedRenderSettings } from "./renders/renderSettings";
|
||||
|
||||
export interface StudioLeftSidebarProps {
|
||||
leftSidebarRef: RefObject<LeftSidebarHandle | null>;
|
||||
@@ -28,7 +29,7 @@ export function StudioLeftSidebar({
|
||||
handlePanelResizeMove,
|
||||
handlePanelResizeEnd,
|
||||
} = usePanelLayoutContext();
|
||||
const { projectId } = useStudioContext();
|
||||
const { projectId, renderQueue, waitForPendingDomEditSaves } = useStudioContext();
|
||||
const {
|
||||
compositions,
|
||||
assets,
|
||||
@@ -45,6 +46,15 @@ export function StudioLeftSidebar({
|
||||
handleContentChange,
|
||||
} = useFileManagerContext();
|
||||
|
||||
const handleRenderComposition = useCallback(
|
||||
async (comp: string) => {
|
||||
await waitForPendingDomEditSaves();
|
||||
const { format, quality, fps } = getPersistedRenderSettings();
|
||||
await renderQueue.startRender({ composition: comp, format, quality, fps });
|
||||
},
|
||||
[renderQueue, waitForPendingDomEditSaves],
|
||||
);
|
||||
|
||||
if (leftCollapsed) {
|
||||
return (
|
||||
<div className="flex w-10 flex-shrink-0 flex-col items-center border-r border-neutral-800/50 bg-neutral-950 pt-1">
|
||||
@@ -107,6 +117,8 @@ export function StudioLeftSidebar({
|
||||
)
|
||||
) : undefined
|
||||
}
|
||||
onRenderComposition={handleRenderComposition}
|
||||
isRendering={renderQueue.isRendering}
|
||||
onLint={onLint}
|
||||
linting={linting}
|
||||
onToggleCollapse={toggleLeftSidebar}
|
||||
|
||||
Reference in New Issue
Block a user