160 lines
4.7 KiB
JavaScript
160 lines
4.7 KiB
JavaScript
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);
|