"use client"; import { useRef, useState, useEffect, useMemo } from "react"; import { useWorkflowStore } from "@/store/workflowStore"; import { NodeType } from "@/types"; import { useReactFlow } from "@xyflow/react"; import { ModelSearchDialog } from "./modals/ModelSearchDialog"; // Get the center of the React Flow pane in screen coordinates function getPaneCenter() { const pane = document.querySelector('.react-flow'); if (pane) { const rect = pane.getBoundingClientRect(); return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2, }; } return { x: window.innerWidth / 2, y: window.innerHeight / 2 }; } interface NodeButtonProps { type: NodeType; label: string; } function NodeButton({ type, label }: NodeButtonProps) { const addNode = useWorkflowStore((state) => state.addNode); const { screenToFlowPosition } = useReactFlow(); const handleClick = () => { const center = getPaneCenter(); const position = screenToFlowPosition({ x: center.x + Math.random() * 100 - 50, y: center.y + Math.random() * 100 - 50, }); addNode(type, position); }; const handleDragStart = (event: React.DragEvent) => { event.dataTransfer.setData("application/node-type", type); event.dataTransfer.effectAllowed = "copy"; }; return ( ); } function GenerateComboButton() { const [isOpen, setIsOpen] = useState(false); const menuRef = useRef(null); const addNode = useWorkflowStore((state) => state.addNode); const { screenToFlowPosition } = useReactFlow(); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(event.target as Node)) { setIsOpen(false); } }; if (isOpen) { document.addEventListener("mousedown", handleClickOutside); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [isOpen]); const handleAddNode = (type: NodeType) => { const center = getPaneCenter(); const position = screenToFlowPosition({ x: center.x + Math.random() * 100 - 50, y: center.y + Math.random() * 100 - 50, }); addNode(type, position); setIsOpen(false); }; const handleDragStart = (event: React.DragEvent, type: NodeType) => { event.dataTransfer.setData("application/node-type", type); event.dataTransfer.effectAllowed = "copy"; setIsOpen(false); }; return (
{isOpen && (
)}
); } export function FloatingActionBar() { const { nodes, isRunning, currentNodeIds, executeWorkflow, regenerateNode, executeSelectedNodes, stopWorkflow, validateWorkflow, edgeStyle, setEdgeStyle, setModelSearchOpen, modelSearchOpen, modelSearchProvider, } = useWorkflowStore(); // Get display text for running nodes const runningNodeCount = currentNodeIds.length; const getRunningLabel = () => { if (runningNodeCount === 0) return "Running..."; if (runningNodeCount === 1) { const node = nodes.find((n) => n.id === currentNodeIds[0]); const nodeName = node?.data?.customTitle || node?.type || "node"; return `Running ${nodeName}...`; } return `Running ${runningNodeCount} nodes...`; }; const [runMenuOpen, setRunMenuOpen] = useState(false); const runMenuRef = useRef(null); const { valid, errors } = validateWorkflow(); // Get the selected nodes const selectedNodes = useMemo(() => { return nodes.filter((n) => n.selected); }, [nodes]); // Get the selected node (if exactly one is selected) const selectedNode = useMemo(() => { return selectedNodes.length === 1 ? selectedNodes[0] : null; }, [selectedNodes]); // Close run menu when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (runMenuRef.current && !runMenuRef.current.contains(event.target as Node)) { setRunMenuOpen(false); } }; if (runMenuOpen) { document.addEventListener("mousedown", handleClickOutside); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [runMenuOpen]); const toggleEdgeStyle = () => { setEdgeStyle(edgeStyle === "angular" ? "curved" : "angular"); }; const handleRunClick = () => { if (isRunning) { stopWorkflow(); } else { executeWorkflow(); } }; const handleRunFromSelected = () => { if (selectedNode) { executeWorkflow(selectedNode.id); setRunMenuOpen(false); } }; const handleRunSelectedOnly = () => { if (selectedNode) { regenerateNode(selectedNode.id); setRunMenuOpen(false); } }; const handleRunSelectedNodes = () => { if (selectedNodes.length > 0) { executeSelectedNodes(selectedNodes.map((n) => n.id)); setRunMenuOpen(false); } }; return (
{/* Browse models button */}
{/* Dropdown chevron button */} {!isRunning && valid && ( )} {/* Dropdown menu */} {runMenuOpen && !isRunning && (
)}
{/* Model search dialog */} setModelSearchOpen(false)} initialProvider={modelSearchProvider} />
); }