"use client"; import { Handle, Position, type NodeProps } from "@xyflow/react"; import { AlertTriangle, CheckCircle2, Loader2, PauseCircle, XCircle, } from "lucide-react"; import { lookup } from "@/lib/node-catalog"; import { cn } from "@/lib/utils"; import type { FlowNodeData } from "@/lib/pipeline-graph"; export type NodeRunStatus = | "pending" | "running" | "success" | "failed" | "paused"; export function FlowNode({ data, selected }: NodeProps) { const fd = data as FlowNodeData & { runStatus?: NodeRunStatus }; const pn = fd.pipelineNode; const status = fd.runStatus; const entry = lookup(pn.type); const Icon = entry?.icon ?? AlertTriangle; const outputs = entry?.outputs ?? 1; const isTrigger = pn.type === "flow-nodes-base.trigger"; // Per-type accent colour for the TYPE label. Keeps the existing // catalog `color` (full bg) but extracts the hue for header text. const typeAccent = typeAccentClass(entry?.color); return (
{(entry?.label ?? "Unsupported")}
{pn.name}
{/* Input handle: triggers have no inputs */} {!isTrigger && ( )} {/* Output handle(s) */} {Array.from({ length: outputs }).map((_, i) => { const top = outputs === 1 ? "50%" : `${((i + 1) / (outputs + 1)) * 100}%`; return ( ); })}
); } function StatusPill({ status }: { status?: NodeRunStatus }) { if (!status || status === "pending") { return ( pending ); } if (status === "running") { return ( running ); } if (status === "success") { return ( succeeded ); } if (status === "failed") { return ( failed ); } if (status === "paused") { return ( paused ); } return null; } // typeAccentClass picks a tailwind text-color class that pairs with the // node's catalog `color` (bg-X-500) so the TYPE row reads as a distinct // accent against the card background. function typeAccentClass(bg?: string): string { switch (bg) { case "bg-emerald-500": return "text-emerald-500"; case "bg-amber-500": return "text-amber-500"; case "bg-sky-500": return "text-sky-500"; case "bg-violet-500": return "text-violet-500"; case "bg-indigo-500": return "text-indigo-500"; case "bg-fuchsia-500": return "text-fuchsia-500"; case "bg-rose-500": case "bg-red-600": return "text-rose-500"; case "bg-orange-500": return "text-orange-500"; case "bg-slate-500": case "bg-slate-400": return "text-slate-500"; default: return "text-primary"; } }