diff --git a/src/components/nodes/OutputNode.tsx b/src/components/nodes/OutputNode.tsx index 463bf13b..81dc46cb 100644 --- a/src/components/nodes/OutputNode.tsx +++ b/src/components/nodes/OutputNode.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useState, useMemo } from "react"; +import { useCallback, useState, useMemo, useEffect, useRef } from "react"; import { Handle, Position, NodeProps, Node } from "@xyflow/react"; import { BaseNode } from "./BaseNode"; import { useCommentNavigation } from "@/hooks/useCommentNavigation"; @@ -14,7 +14,10 @@ export function OutputNode({ id, data, selected }: NodeProps) { const nodeData = data; const commentNavigation = useCommentNavigation(id); const updateNodeData = useWorkflowStore((state) => state.updateNodeData); + const regenerateNode = useWorkflowStore((state) => state.regenerateNode); + const edges = useWorkflowStore((state) => state.edges); const [showLightbox, setShowLightbox] = useState(false); + const previousEdgeCountRef = useRef(0); // Determine if content is audio const isAudio = useMemo(() => { @@ -43,6 +46,31 @@ export function OutputNode({ id, data, selected }: NodeProps) { const videoBlobUrl = useVideoBlobUrl(isVideo ? contentSrc ?? null : null); + // Initialize edge count ref on mount + useEffect(() => { + const connectedEdges = edges.filter((edge) => edge.target === id); + previousEdgeCountRef.current = connectedEdges.length; + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + // Auto-trigger execution when connected + useEffect(() => { + const connectedEdges = edges.filter((edge) => edge.target === id); + const currentEdgeCount = connectedEdges.length; + + // Only trigger if we gained a new connection (not on initial mount or disconnect) + if (currentEdgeCount > previousEdgeCountRef.current) { + // Auto-run when a new connection is made + regenerateNode(id); + } + + previousEdgeCountRef.current = currentEdgeCount; + }, [edges, id, regenerateNode]); + + // Handle Run button click + const handleRun = useCallback(() => { + regenerateNode(id); + }, [id, regenerateNode]); + const handleDownload = useCallback(async () => { if (!contentSrc) return; @@ -91,6 +119,7 @@ export function OutputNode({ id, data, selected }: NodeProps) { comment={nodeData.comment} onCustomTitleChange={(title) => updateNodeData(id, { customTitle: title || undefined })} onCommentChange={(comment) => updateNodeData(id, { comment: comment || undefined })} + onRun={handleRun} selected={selected} className="min-w-[200px]" commentNavigation={commentNavigation ?? undefined} diff --git a/src/store/workflowStore.ts b/src/store/workflowStore.ts index 8df3f97e..84579680 100644 --- a/src/store/workflowStore.ts +++ b/src/store/workflowStore.ts @@ -1167,6 +1167,11 @@ export const useWorkflowStore = create((set, get) => ({ set({ isRunning: false, currentNodeIds: [] }); await logger.endSession(); return; + } else if (node.type === "output") { + await executeOutput(executionCtx); + set({ isRunning: false, currentNodeIds: [] }); + await logger.endSession(); + return; } // After regeneration, execute directly connected downstream consumer nodes