"use client"; import { workflowVersionInputsToZod } from "../lib/workflowVersionInputsToZod"; import { callServerPromise } from "./callServerPromise"; import fetcher from "./fetcher"; import { LoadingIcon } from "@/components/LoadingIcon"; import AutoForm, { AutoFormSubmit } from "@/components/ui/auto-form"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Skeleton } from "@/components/ui/skeleton"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import type { workflowAPINodeType } from "@/db/schema"; import { checkStatus, createRun } from "@/server/createRun"; import { createDeployments } from "@/server/curdDeploments"; import type { getMachines } from "@/server/curdMachine"; import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion"; import { Copy, ExternalLink, Info, MoreVertical, Play, Share, } from "lucide-react"; import { parseAsInteger, useQueryState } from "next-usequerystate"; import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import useSWR from "swr"; import type { z } from "zod"; import { create } from "zustand"; export function VersionSelect({ workflow, }: { workflow: Awaited>; }) { const [version, setVersion] = useQueryState("version", { defaultValue: workflow?.versions[0].version?.toString() ?? "", }); return ( ); } export function MachineSelect({ machines, }: { machines: Awaited>; }) { const [machine, setMachine] = useSelectedMachine(machines); return ( ); } function useSelectedMachine(machines: Awaited>) { const a = useQueryState("machine", { defaultValue: machines?.[0]?.id ?? "", }); return a; } type PublicRunStore = { image: string; loading: boolean; runId: string; status: string; setImage: (image: string) => void; setLoading: (loading: boolean) => void; setRunId: (runId: string) => void; setStatus: (status: string) => void; }; export const publicRunStore = create((set) => ({ image: "", loading: false, runId: "", status: "", setImage: (image) => set({ image }), setLoading: (loading) => set({ loading }), setRunId: (runId) => set({ runId }), setStatus: (status) => set({ status }), })); export function PublicRunOutputs() { const { image, loading, runId, status, setStatus, setImage, setLoading } = publicRunStore(); useEffect(() => { if (!runId) return; const interval = setInterval(() => { checkStatus(runId).then((res) => { console.log(res?.status); if (res) setStatus(res.status); if (res && res.status === "success") { setImage(res.outputs[0]?.data.images[0].url); setLoading(false); clearInterval(interval); } }); }, 2000); return () => clearInterval(interval); }, [runId]); return (
{!loading && image && ( Generated image )} {loading && (
{status}
)} {loading && }
); } export function RunWorkflowButton({ workflow, machines, }: { workflow: Awaited>; machines: Awaited>; }) { const [version] = useQueryState("version", { defaultValue: workflow?.versions[0].version ?? 1, ...parseAsInteger, }); const [machine] = useSelectedMachine(machines); const [isLoading, setIsLoading] = useState(false); const [values, setValues] = useState>({}); const [open, setOpen] = useState(false); const schema = useMemo(() => { const workflow_version = getWorkflowVersionFromVersionIndex( workflow, version ); if (!workflow_version) return null; return workflowVersionInputsToZod(workflow_version); }, [version]); const runWorkflow = async () => { console.log(values); const val = Object.keys(values).length > 0 ? values : undefined; const workflow_version_id = workflow?.versions.find( (x) => x.version === version )?.id; console.log(workflow_version_id); if (!workflow_version_id) return; setIsLoading(true); try { const origin = window.location.origin; await callServerPromise( createRun({ origin, workflow_version_id, machine_id: machine, inputs: val, isManualRun: true, }) ); // console.log(res.json()); setIsLoading(false); } catch (error) { setIsLoading(false); } setOpen(false); }; return ( Confirm run {schema ? "Run your workflow with custom inputs" : "Confirm to run your workflow"} {/*
*/} {schema && (
Run {isLoading ? : }
)} {!schema && ( )}
); } export function CreateDeploymentButton({ workflow, machines, }: { workflow: Awaited>; machines: Awaited>; }) { const [version] = useQueryState("version", { defaultValue: workflow?.versions[0].version ?? 1, ...parseAsInteger, }); const [machine] = useSelectedMachine(machines); const [isLoading, setIsLoading] = useState(false); const workflow_version_id = workflow?.versions.find( (x) => x.version === version )?.id; return ( { if (!workflow_version_id) return; setIsLoading(true); await callServerPromise( createDeployments( workflow.id, workflow_version_id, machine, "production" ) ); setIsLoading(false); }} > Production { if (!workflow_version_id) return; setIsLoading(true); await callServerPromise( createDeployments( workflow.id, workflow_version_id, machine, "staging" ) ); setIsLoading(false); }} > Staging ); } export function CreateShareButton({ workflow, machines, }: { workflow: Awaited>; machines: Awaited>; }) { const [version] = useQueryState("version", { defaultValue: workflow?.versions[0].version ?? 1, ...parseAsInteger, }); const [machine] = useSelectedMachine(machines); const [isLoading, setIsLoading] = useState(false); const workflow_version_id = workflow?.versions.find( (x) => x.version === version )?.id; return ( { if (!workflow_version_id) return; setIsLoading(true); await callServerPromise( createDeployments( workflow.id, workflow_version_id, machine, "public-share" ) ); setIsLoading(false); }} > Public ); } export function CopyWorkflowVersion({ workflow, }: { workflow: Awaited>; }) { const [version] = useQueryState("version", { defaultValue: workflow?.versions[0].version ?? 1, ...parseAsInteger, }); const workflow_version = workflow?.versions.find( (x) => x.version === version ); return ( { if (!workflow) return; // console.log(workflow_version?.workflow); workflow_version?.workflow?.nodes.forEach((x: any) => { if (x?.type === "ComfyDeploy") { x.widgets_values[1] = workflow.id; x.widgets_values[2] = workflow_version.version; } }); navigator.clipboard.writeText( JSON.stringify(workflow_version?.workflow) ); toast("Copied to clipboard"); }} > Copy (JSON) { navigator.clipboard.writeText( JSON.stringify(workflow_version?.workflow_api) ); toast("Copied to clipboard"); }} > Copy API (JSON) ); } export function getWorkflowVersionFromVersionIndex( workflow: Awaited>, version: number ) { const workflow_version = workflow?.versions.find((x) => x.version == version); return workflow_version; } export function ViewWorkflowDetailsButton({ workflow, }: { workflow: Awaited>; }) { const [version] = useQueryState("version", { defaultValue: workflow?.versions[0].version ?? 1, ...parseAsInteger, }); const [isLoading, setIsLoading] = useState(false); const [open, setOpen] = useState(false); const { data, error, isLoading: isNodesIndexLoading, } = useSWR( "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/extension-node-map.json", fetcher ); const groupedByAuxName = useMemo(() => { if (!data) return null; // console.log(data); const workflow_version = getWorkflowVersionFromVersionIndex( workflow, version ); const api = workflow_version?.workflow_api; if (!api) return null; const crossCheckedApi = Object.entries(api) .map(([_, value]) => { const classType = value.class_type; const classTypeData = Object.entries(data).find(([_, nodeArray]) => nodeArray[0].includes(classType) ); return classTypeData ? { node: value, classTypeData } : null; }) .filter((item) => item !== null); // console.log(crossCheckedApi); const groupedByAuxName = crossCheckedApi.reduce( (acc, data) => { if (!data) return acc; const { node, classTypeData } = data; const auxName = classTypeData[1][1].title_aux; // console.log(auxName); if (!acc[auxName]) { acc[auxName] = { url: classTypeData[0], node: [], }; } acc[auxName].node.push(node); return acc; }, {} as Record< string, { node: z.infer[]; url: string; } > ); // console.log(groupedByAuxName); return groupedByAuxName; }, [version, data]); return ( Workflow Details View your custom nodes, models, external files used in this workflow
File Output {groupedByAuxName && Object.entries(groupedByAuxName).map(([key, group]) => { // const filePath return ( {key} {group.node.map((x) => ( {x.class_type} ))} ); })}
{/* */} {/*
{view}
*/}
); }