@ -431,16 +431,16 @@ export function VideoStitchNode({ id, data, selected }: NodeProps<VideoStitchNod
{ renderHandles ( ) }
< div className = "flex-1 flex flex-col min-h-0 gap-2" >
{ /* Filmstrip UI */ }
< div className = "flex-1 flex flex-col min-h-0 gap-2" >
{ /* Filmstrip + controls area (shrink-0: only takes space it needs) */ }
< div className = "shrink-0 flex flex-col gap-2" >
{ orderedClips . length === 0 ? (
< div className = "flex-1 flex items-center justify-center border border-dashed border-neutral-600 rounded" >
< div className = "h-16 flex items-center justify-center border border-dashed border-neutral-600 rounded" >
< span className = "text-[10px] text-neutral-500" > Connect videos to stitch < / span >
< / div >
) : (
< >
{ /* Filmstrip */ }
< div className = "flex-1 overflow-y-auto nowheel grid grid-cols-4 content-start gap-2 p-2 bg-neutral-900/50 rounded" >
< div className = "overflow-y-auto nowheel grid grid-cols-4 content-start gap-2 p-2 bg-neutral-900/50 rounded" >
{ orderedClips . map ( ( clip ) = > {
const thumbnail = thumbnails . get ( clip . edgeId ) ;
return (
@ -511,14 +511,6 @@ export function VideoStitchNode({ id, data, selected }: NodeProps<VideoStitchNod
} ) }
< / div >
{ /* Stitch button */ }
< button
onClick = { handleStitch }
disabled = { orderedClips . length < 2 || nodeData . status === "loading" || isRunning }
className = "px-3 py-1.5 bg-blue-600 hover:bg-blue-500 disabled:bg-neutral-700 disabled:text-neutral-500 disabled:cursor-not-allowed rounded text-white text-xs font-medium transition-colors"
>
{ nodeData . status === "loading" ? "Processing..." : "Stitch" }
< / button >
< / >
) }
< / div >
@ -549,16 +541,16 @@ export function VideoStitchNode({ id, data, selected }: NodeProps<VideoStitchNod
< / div >
) }
{ /* Output preview */ }
{ /* Output preview (flex-1: grows with node) */ }
{ nodeData . outputVideo && nodeData . status !== "loading" && (
< div className = "relative w- ful l" >
< div className = "relative flex-1 min-h-0 " >
< video
src = { nodeData . outputVideo }
controls
autoPlay
loop
muted
className = "w-full h-auto max-h-40 object-contain rounded"
className = "w-full h-full object-contain rounded"
playsInline
/ >
< button
@ -572,6 +564,36 @@ export function VideoStitchNode({ id, data, selected }: NodeProps<VideoStitchNod
< / button >
< / div >
) }
{ /* Controls row: Loop selector + Stitch button (below video, right-aligned) */ }
{ orderedClips . length > 0 && (
< div className = "shrink-0 flex items-center justify-end gap-2" >
< div className = "flex items-center gap-1" >
< span className = "text-[10px] text-neutral-400" > Loop < / span >
{ ( [ 1 , 2 , 3 ] as const ) . map ( ( count ) = > (
< button
key = { count }
onClick = { ( ) = > updateNodeData ( id , { loopCount : count } ) }
className = { ` nodrag px-1.5 py-0.5 rounded text-[10px] font-medium transition-colors ${
( nodeData . loopCount || 1 ) === count
? "bg-blue-600 text-white"
: "bg-neutral-700 text-neutral-400 hover:bg-neutral-600 hover:text-neutral-300"
} ` }
>
{ count } x
< / button >
) ) }
< / div >
< button
onClick = { handleStitch }
disabled = { orderedClips . length < 2 || nodeData . status === "loading" || isRunning }
className = "px-3 py-1.5 bg-blue-600 hover:bg-blue-500 disabled:bg-neutral-700 disabled:text-neutral-500 disabled:cursor-not-allowed rounded text-white text-xs font-medium transition-colors"
>
{ nodeData . status === "loading" ? "Processing..." : "Stitch" }
< / button >
< / div >
) }
< / div >
< / BaseNode >
) ;