import React, { useEffect, useRef, useState } from "react"; export default function App() { const canvasRef = useRef(null); const wsRef = useRef(null); const [connected, setConnected] = useState(false); const [playerPos, setPlayerPos] = useState([50, 50]); const [status, setStatus] = useState("Disconnected"); useEffect(() => { // Connect to backend WebSocket. When deploying to HF Spaces, // use the absolute URL or relative path ("/ws"). const proto = window.location.protocol === "https:" ? "wss" : "ws"; const host = window.location.host; // same host as Space const wsUrl = `${proto}://${host}/ws`; const ws = new WebSocket(wsUrl); wsRef.current = ws; ws.addEventListener("open", () => { setConnected(true); setStatus("Connected"); // ask for initial frame ws.send(JSON.stringify({ type: "noop" })); }); ws.addEventListener("message", (ev) => { const msg = JSON.parse(ev.data); if (msg.type === "frame") { drawFrame(msg.image_b64); if (Array.isArray(msg.player_pos)) setPlayerPos(msg.player_pos); } else if (msg.type === "status") { setStatus(msg.text); } }); ws.addEventListener("close", () => { setConnected(false); setStatus("Disconnected"); }); return () => { ws.close(); }; }, []); // draw base64 png on canvas function drawFrame(image_b64) { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); const img = new Image(); img.onload = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); }; img.src = `data:image/png;base64,${image_b64}`; } // send action down the websocket function sendAction(action) { if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return; wsRef.current.send(JSON.stringify({ type: "action", action })); } }