feat: add preview image builder
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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'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;
|
||||
}
|
||||
Reference in New Issue
Block a user