"use client" import { getWebsocketUrl } from '@/server/generate' import { useCallback, useEffect, useRef, useState } from 'react' import useSWR from 'swr' import useWebSocket, { ReadyState } from 'react-use-websocket'; import { useDebounce } from "use-debounce"; import { Input } from './ui/input'; import { Badge } from './ui/badge'; import { Skeleton } from './ui/skeleton'; import { cn } from '@/lib/utils'; export function WebsocketDemo() { const { data } = useSWR("ws", getWebsocketUrl, { revalidateOnFocus: false, }) const [ws, setWs] = useState() const [status, setStatus] = useState("not-connected") const [prompt, setPrompt] = useState('A anime cat'); const [debouncedPrompt] = useDebounce(prompt, 200); const [currentLog, setCurrentLog] = useState(); const [reconnectCounter, setReconnectCounter] = useState(0) const canvasRef = useRef(null); // Reference to the canvas element const sendInput = useCallback(() => { if (status == "reconnecting" || status == "connecting") return if (ws?.readyState == ws?.CLOSED) { setStatus('reconnecting') setReconnectCounter(x => x + 1) return } if (status != "ready") return ws?.send(JSON.stringify( { "event": "input", "inputs": { "input_text": debouncedPrompt } } )) }, [ws, debouncedPrompt, status]) const preStatus = useRef(status) useEffect(() => { if (preStatus.current != status && status == "ready") sendInput(); preStatus.current = status }, [status, sendInput]) useEffect(() => { sendInput(); }, [debouncedPrompt]) const connectWS = useCallback((data: NonNullable>>) => { setStatus("connecting"); const websocket = new WebSocket(data.ws_connection_url); websocket.binaryType = "arraybuffer"; websocket.onopen = () => { setStatus("connected"); }; websocket.onmessage = (event) => { if (typeof event.data === "string") { const message = JSON.parse(event.data); if (message?.event == "status" && message?.data?.sid) { setStatus("ready"); } if (message?.event) { if (message?.event == "executing" && message?.data?.node == null) setCurrentLog("done") else if (message?.event == "live_status") setCurrentLog(`running - ${message.data?.current_node} ${(message.data.progress * 100).toFixed(2)}%`) else if (message?.event == "elapsed_time") setCurrentLog(`elapsed time: ${Math.ceil(message.data?.elapsed_time * 100) / 100}s`) } console.log("Received message:", message); } if (event.data instanceof ArrayBuffer) { console.log("Received binary message:"); drawImage(event.data); } }; websocket.onclose = () => setStatus("closed"); websocket.onerror = () => setStatus("error"); setWs(websocket); return () => { websocket.close(); }; }, [data]) const drawImage = useCallback((arrayBuffer: ArrayBuffer) => { const view = new DataView(arrayBuffer); const eventType = view.getUint32(0); const buffer = arrayBuffer.slice(4); switch (eventType) { case 1: const view2 = new DataView(arrayBuffer); const imageType = view2.getUint32(0) let imageMime switch (imageType) { case 1: default: imageMime = "image/jpeg"; break; case 2: imageMime = "image/png" break; case 3: imageMime = "image/webp" } const blob = new Blob([buffer.slice(4)], { type: imageMime }); const fileSize = blob.size; console.log(`Received image size: ${(fileSize / 1024).toFixed(2)} KB`); // const blob = new Blob([arrayBuffer], { type: 'image/png' }); // Assuming the image is a JPEG const url = URL.createObjectURL(blob); const canvas = canvasRef.current; const ctx = canvas?.getContext('2d'); if (ctx) { console.log("drawing"); const img = new Image(); img.onload = () => { if (canvas) { ctx.drawImage(img, 0, 0, canvas.width, canvas.height); } URL.revokeObjectURL(url); // Clean up }; img.src = url; } // this.dispatchEvent(new CustomEvent("b_preview", { detail: imageBlob })); break; default: throw new Error(`Unknown binary websocket message of type ${eventType}`); } }, []); useEffect(() => { if (!data) { setStatus("not-connected"); return; } return connectWS(data) }, [connectWS, reconnectCounter]) const pending = (status == "not-connected" || status == "connecting" || status == "reconnecting" || currentLog?.startsWith("running") || (!currentLog && status == "connected")) return (
Status: {status} {(currentLog || status == "connected" || status == "ready") && {currentLog} {status == "connected" && !currentLog && "stating comfy ui"} {status == "ready" && !currentLog && " running"} }
{/* { <> } */}
setPrompt(e.target.value)} />
) }