const gridForm = document.getElementById("grid-form"); const descriptorEl = document.getElementById("descriptor"); const gridMetaEl = document.getElementById("grid-meta"); const summaryEl = document.getElementById("summary"); const historyEl = document.getElementById("history"); const planOutput = document.getElementById("plan-output"); const preferredInput = document.getElementById("preferred-label"); const refreshScreenshot = document.getElementById("refresh-screenshot"); const refreshMemo = document.getElementById("refresh-memo"); const logEl = document.getElementById("ws-log"); let currentGrid = null; let lastPlan = null; let ws = null; let keepAliveId = null; const log = (message) => { const timestamp = new Date().toLocaleTimeString(); logEl.textContent = `[${timestamp}] ${message}\n${logEl.textContent}`; }; const headers = { "Content-Type": "application/json", }; const subscribeToGrid = (gridId) => { if (!gridId) return; if (ws) { ws.close(); } const protocol = window.location.protocol === "https:" ? "wss" : "ws"; ws = new WebSocket(`${protocol}://${window.location.host}/stream/screenshots?grid_id=${gridId}`); ws.addEventListener("open", () => { log(`WebSocket listening for grid ${gridId}`); ws.send("ready"); keepAliveId = setInterval(() => ws.send("ping"), 15000); }); ws.addEventListener("message", (event) => { log(`Update received → ${event.data}`); }); ws.addEventListener("close", () => { log("WebSocket disconnected"); if (keepAliveId) { clearInterval(keepAliveId); keepAliveId = null; } }); }; const updateDescriptor = (descriptor) => { descriptorEl.textContent = JSON.stringify(descriptor, null, 2); gridMetaEl.textContent = `Grid ${descriptor.grid_id} (${descriptor.rows}x${descriptor.columns}) · ${descriptor.cells.length} cells`; }; const updateSummary = async () => { if (!currentGrid) return; const [summaryResponse, historyResponse] = await Promise.all([ fetch(`/grid/${currentGrid}/summary`), fetch(`/grid/${currentGrid}/history`), ]); if (summaryResponse.ok) { const payload = await summaryResponse.json(); summaryEl.textContent = payload.summary; } if (historyResponse.ok) { const payload = await historyResponse.json(); historyEl.textContent = JSON.stringify(payload.history, null, 2); } }; const initGrid = async (event) => { event.preventDefault(); const formData = new FormData(gridForm); const payload = { width: Number(formData.get("width")), height: Number(formData.get("height")), rows: Number(formData.get("rows")), columns: Number(formData.get("columns")), screenshot_base64: formData.get("screenshot"), }; const response = await fetch("/grid/init", { method: "POST", headers, body: JSON.stringify(payload), }); const descriptor = await response.json(); currentGrid = descriptor.grid_id; updateDescriptor(descriptor); await updateSummary(); subscribeToGrid(currentGrid); planOutput.textContent = "Plan preview will appear here."; log(`Grid ${currentGrid} initialized.`); }; document.getElementById("plan-button").addEventListener("click", async () => { if (!currentGrid) { log("Initialize a grid first."); return; } const response = await fetch(`/grid/${currentGrid}/plan`, { method: "POST", headers, body: JSON.stringify({ preferred_label: preferredInput.value || null, action: "click", text: "ui-trigger", }), }); const result = await response.json(); lastPlan = result.plan; planOutput.textContent = JSON.stringify(result, null, 2); }); document.getElementById("run-action").addEventListener("click", async () => { if (!lastPlan) { log("Run the planner first."); return; } const payload = { grid_id: lastPlan.grid_id, action: lastPlan.action, target_cell: lastPlan.target_cell, text: "from-ui", comment: "UI action", }; const response = await fetch("/grid/action", { method: "POST", headers, body: JSON.stringify(payload), }); const result = await response.json(); log(`Action ${result.detail} at ${result.coordinates}`); await updateSummary(); }); document.getElementById("refresh-button").addEventListener("click", async () => { if (!currentGrid) { log("Start a grid first."); return; } const payload = { screenshot_base64: refreshScreenshot.value || "", memo: refreshMemo.value || undefined, }; const response = await fetch(`/grid/${currentGrid}/refresh`, { method: "POST", headers, body: JSON.stringify(payload), }); const data = await response.json(); log(`Refresh acknowledged: ${JSON.stringify(data)}`); }); gridForm.addEventListener("submit", initGrid);