"use client"; 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 { showcaseMediaNullable, 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, Edit, ExternalLink, Info, MoreVertical, Play, } 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"; import { workflowVersionInputsToZod } from "../lib/workflowVersionInputsToZod"; import { callServerPromise } from "./callServerPromise"; import fetcher from "./fetcher"; import { ButtonAction } from "@/components/ButtonActionLoader"; import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine"; 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 ( ); } type SelectedMachineStore = { selectedMachine: string | undefined; setSelectedMachine: (machine: string) => void; }; export const selectedMachineStore = create((set) => ({ selectedMachine: undefined, setSelectedMachine: (machine) => set(() => ({ selectedMachine: machine })), })); export function useSelectedMachine( machines: Awaited>, ): [string, (v: string) => void] { const { selectedMachine, setSelectedMachine } = selectedMachineStore(); return [selectedMachine ?? machines?.[0]?.id ?? "", setSelectedMachine]; // const searchParams = useSearchParams(); // const pathname = usePathname(); // const router = useRouter(); // const createQueryString = useCallback( // (name: string, value: string) => { // const params = new URLSearchParams(searchParams.toString()); // params.set(name, value); // return params.toString(); // }, // [searchParams], // ); // return [ // searchParams.get("machine") ?? machines?.[0]?.id ?? "", // (v: string) => { // // window.history.pushState( // // "new url", // // "", // // pathname + "?" + createQueryString("machine", v), // // ); // // router.push(pathname + "?" + createQueryString("machine", v)); // router.replace(pathname + "?" + createQueryString("machine", v)); // }, // ]; } 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(props: { preview: z.infer; }) { 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 && props.preview && props.preview.length > 0 && ( <> Generated image )} {!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, runOrigin: "manual", }), ); // 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 OpenEditButton({ workflow, machines, }: { workflow: Awaited>; machines: Awaited>; }) { const [version] = useQueryState("version", { defaultValue: workflow?.versions[0].version ?? 1, ...parseAsInteger, }); const [machine] = useSelectedMachine(machines); const workflow_version_id = workflow?.versions.find( (x) => x.version == version, )?.id; const [isLoading, setIsLoading] = useState(false); return ( workflow_version_id && machine && ( ) ); } 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}
*/}
); }