feat: add preview image builder

This commit is contained in:
BennyKok
2024-01-04 22:29:22 +08:00
parent 314eb9fd16
commit 9937252777
18 changed files with 1085 additions and 56 deletions
+46
View File
@@ -0,0 +1,46 @@
"use client";
import React, { useEffect, useRef } from "react";
export type LogsType = {
machine_id?: string;
logs: string;
timestamp: number;
}[];
export function LogsViewer({ logs }: { logs: LogsType }) {
const container = useRef<HTMLDivElement | null>(null);
useEffect(() => {
// console.log(logs.length, container.current);
if (container.current) {
const scrollHeight = container.current.scrollHeight;
container.current.scrollTo({
top: scrollHeight,
behavior: "smooth",
});
}
}, [logs.length]);
return (
<div
ref={(ref) => {
if (!container.current && ref) {
const scrollHeight = ref.scrollHeight;
ref.scrollTo({
top: scrollHeight,
behavior: "instant",
});
}
container.current = ref;
}}
className="flex flex-col text-xs p-2 overflow-y-scroll max-h-[400px] whitespace-break-spaces"
>
{logs.map((x, i) => (
<div key={i}>{x.logs}</div>
))}
</div>
);
}
+48
View File
@@ -0,0 +1,48 @@
"use client";
import type { LogsType } from "@/components/LogsViewer";
import { LogsViewer } from "@/components/LogsViewer";
import { getConnectionStatus } from "@/components/getConnectionStatus";
import { useEffect, useState } from "react";
import useWebSocket from "react-use-websocket";
export function MachineBuildLog({
machine_id,
endpoint,
}: {
machine_id: string;
endpoint: string;
}) {
const [logs, setLogs] = useState<LogsType>([]);
const wsEndpoint = endpoint.replace(/^http/, "ws");
const { lastMessage, readyState } = useWebSocket(
`${wsEndpoint}/ws/${machine_id}`,
{
shouldReconnect: () => true,
reconnectAttempts: 20,
reconnectInterval: 1000,
}
);
const connectionStatus = getConnectionStatus(readyState);
useEffect(() => {
if (!lastMessage?.data) return;
const message = JSON.parse(lastMessage.data);
console.log(message);
if (message?.event === "LOGS") {
setLogs((logs) => [...(logs ?? []), message.data]);
}
}, [lastMessage]);
return (
<div>
{connectionStatus}
<LogsViewer logs={logs} />
</div>
);
}
+25 -2
View File
@@ -23,8 +23,12 @@ import {
TableRow,
} from "@/components/ui/table";
import { type MachineType } from "@/db/schema";
import { addMachineSchema } from "@/server/addMachineSchema";
import {
addCustomMachineSchema,
addMachineSchema,
} from "@/server/addMachineSchema";
import {
addCustomMachine,
addMachine,
deleteMachine,
disableMachine,
@@ -92,10 +96,15 @@ export const columns: ColumnDef<Machine>[] = [
return (
// <a className="hover:underline" href={`/${row.original.id}`}>
<div className="flex flex-row gap-2 items-center">
<div>{row.getValue("name")}</div>
<a href={`/machines/${row.original.id}`} className="hover:underline">
{row.getValue("name")}
</a>
{row.original.disabled && (
<Badge variant="destructive">Disabled</Badge>
)}
{!row.original.disabled && row.original.status && (
<Badge variant="outline">{row.original.status}</Badge>
)}
</div>
// </a>
);
@@ -254,6 +263,20 @@ export function MachineList({ data }: { data: Machine[] }) {
serverAction={addMachine}
formSchema={addMachineSchema}
/>
<InsertModal
title="Custom Machine"
description="Add custom Comfyui machines to your account."
serverAction={addCustomMachine}
formSchema={addCustomMachineSchema}
fieldConfig={{
type: {
fieldType: "fallback",
inputProps: {
disabled: true,
},
},
}}
/>
</div>
</div>
<div className="rounded-md border overflow-x-auto w-full">
+6 -44
View File
@@ -1,5 +1,7 @@
"use client";
import { LogsViewer } from "./LogsViewer";
import { getConnectionStatus } from "./getConnectionStatus";
import { Badge } from "@/components/ui/badge";
import {
Dialog,
@@ -10,9 +12,8 @@ import {
DialogTrigger,
} from "@/components/ui/dialog";
import type { getMachines } from "@/server/curdMachine";
import { Check, CircleOff, SatelliteDish } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import useWebSocket, { ReadyState } from "react-use-websocket";
import React, { useEffect, useState } from "react";
import useWebSocket from "react-use-websocket";
import { create } from "zustand";
type State = {
@@ -98,17 +99,7 @@ function MachineWS({
}
);
const connectionStatus = {
[ReadyState.CONNECTING]: (
<SatelliteDish size={14} className="text-orange-500" />
),
[ReadyState.OPEN]: <Check size={14} className="text-green-500" />,
[ReadyState.CLOSING]: <CircleOff size={14} className="text-orange-500" />,
[ReadyState.CLOSED]: <CircleOff size={14} className="text-red-500" />,
[ReadyState.UNINSTANTIATED]: "Uninstantiated",
}[readyState];
const container = useRef<HTMLDivElement | null>(null);
const connectionStatus = getConnectionStatus(readyState);
useEffect(() => {
if (!lastMessage?.data) return;
@@ -130,18 +121,6 @@ function MachineWS({
}
}, [lastMessage]);
useEffect(() => {
// console.log(logs.length, container.current);
if (container.current) {
const scrollHeight = container.current.scrollHeight;
container.current.scrollTo({
top: scrollHeight,
behavior: "smooth",
});
}
}, [logs.length]);
return (
<Dialog>
<DialogTrigger asChild className="">
@@ -156,24 +135,7 @@ function MachineWS({
You can view your run&apos;s outputs here
</DialogDescription>
</DialogHeader>
<div
ref={(ref) => {
if (!container.current && ref) {
const scrollHeight = ref.scrollHeight;
ref.scrollTo({
top: scrollHeight,
behavior: "instant",
});
}
container.current = ref;
}}
className="flex flex-col text-xs p-2 overflow-y-scroll max-h-[400px] whitespace-break-spaces"
>
{logs.map((x, i) => (
<div key={i}>{x.logs}</div>
))}
</div>
<LogsViewer logs={logs} />
</DialogContent>
</Dialog>
);
@@ -0,0 +1,17 @@
import { Check, CircleOff, SatelliteDish } from "lucide-react";
import React from "react";
import { ReadyState } from "react-use-websocket";
export function getConnectionStatus(readyState: ReadyState) {
const connectionStatus = {
[ReadyState.CONNECTING]: (
<SatelliteDish size={14} className="text-orange-500" />
),
[ReadyState.OPEN]: <Check size={14} className="text-green-500" />,
[ReadyState.CLOSING]: <CircleOff size={14} className="text-orange-500" />,
[ReadyState.CLOSED]: <CircleOff size={14} className="text-red-500" />,
[ReadyState.UNINSTANTIATED]: "Uninstantiated",
}[readyState];
return connectionStatus;
}