"use client"; import React, { useMemo } from "react"; import { Handle, Position, NodeProps, Node } from "@xyflow/react"; import { BaseNode } from "./BaseNode"; import { useWorkflowStore } from "@/store/workflowStore"; import { VideoFrameGrabNodeData } from "@/types"; import { useAdaptiveImageSrc } from "@/hooks/useAdaptiveImageSrc"; type VideoFrameGrabNodeType = Node; export function VideoFrameGrabNode({ id, data, selected }: NodeProps) { const nodeData = data; const adaptiveOutputImage = useAdaptiveImageSrc(nodeData.outputImage, id); const updateNodeData = useWorkflowStore((state) => state.updateNodeData); const regenerateNode = useWorkflowStore((state) => state.regenerateNode); const isRunning = useWorkflowStore((state) => state.isRunning); const edges = useWorkflowStore((state) => state.edges); const nodes = useWorkflowStore((state) => state.nodes); // Find connected source video from incoming edges const sourceVideoUrl = useMemo(() => { const incomingEdge = edges.find((e) => e.target === id && e.targetHandle === "video"); if (!incomingEdge) return null; const sourceNode = nodes.find((n) => n.id === incomingEdge.source); if (!sourceNode) return null; const d = sourceNode.data as Record; return (d.outputVideo as string | null) ?? null; }, [edges, nodes, id]); const hasSourceVideo = Boolean(sourceVideoUrl); const canExtract = hasSourceVideo && nodeData.status !== "loading" && !isRunning; const handleExtract = () => { regenerateNode(id); }; return ( {/* Video In (target, left, 50%) */}
Video In
{/* Image Out (source, right, 50%) */}
Image Out
{/* Image preview area */}
{nodeData.outputImage ? ( <> Extracted frame {/* Clear output button */} ) : (
Connect a video and extract a frame
)}
{/* Frame position toggle (only when source video connected) */} {hasSourceVideo && (
)} {/* Extract Frame button */}
{/* Processing overlay */} {nodeData.status === "loading" && (
Extracting frame...
)} {/* Error display */} {nodeData.status === "error" && nodeData.error && (

{nodeData.error}

)}
); }