const $ = (id) => document.getElementById(id); chrome.storage.local.get(["base", "key", "mode"], (v) => { if (v.base) $("base").value = v.base; if (v.key) $("key").value = v.key; if (v.mode) $("mode").value = v.mode; }); $("mode").addEventListener("change", () => chrome.storage.local.set({ mode: $("mode").value })); const save = () => chrome.storage.local.set({ base: $("base").value.trim(), key: $("key").value.trim() }); $("base").addEventListener("change", save); $("key").addEventListener("change", save); function refresh() { chrome.runtime.sendMessage({ type: "status" }, (s) => { $("status").textContent = s.active ? `Recording — ${s.count} steps captured. Click through your flow, then Finish.` : "Not recording."; $("start").hidden = s.active; $("finish").hidden = !s.active; $("discard").hidden = !s.active; }); } refresh(); $("start").addEventListener("click", async () => { save(); const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); chrome.runtime.sendMessage({ type: "start", tabId: tab.id, mode: $("mode").value }, refresh); }); $("discard").addEventListener("click", () => chrome.runtime.sendMessage({ type: "discard" }, refresh)); $("finish").addEventListener("click", () => { chrome.runtime.sendMessage({ type: "stop" }, async ({ steps, mode }) => { const base = $("base").value.trim().replace(/\/$/, ""); const key = $("key").value.trim(); if (!base || !key) { $("status").textContent = "Set platform URL and API key first."; return; } if (!steps.length) { $("status").textContent = "No steps captured."; return; } try { $("status").textContent = "Uploading…"; const auth = { Authorization: `Bearer ${key}` }; const demo = await (await fetch(`${base}/api/demos`, { method: "POST", headers: { ...auth, "Content-Type": "application/json" }, body: JSON.stringify({ name: `Capture ${new Date().toLocaleString()}` }), })).json(); for (const s of steps) { let step; if (mode === "dom") { step = await (await fetch(`${base}/api/demos/${demo.id}/steps`, { method: "POST", headers: { ...auth, "Content-Type": "application/json" }, body: JSON.stringify({ snapshot_type: "dom", dom_html: s.html }), })).json(); } else { const blob = await (await fetch(s.dataUrl)).blob(); const fd = new FormData(); fd.append("file", blob, "step.png"); const asset = await (await fetch(`${base}/api/assets`, { method: "POST", headers: auth, body: fd })).json(); step = await (await fetch(`${base}/api/demos/${demo.id}/steps`, { method: "POST", headers: { ...auth, "Content-Type": "application/json" }, body: JSON.stringify({ snapshot_type: "image", asset_id: asset.id }), })).json(); } // pre-place a hotspot centred on the recorded click await fetch(`${base}/api/steps/${step.id}`, { method: "PATCH", headers: { ...auth, "Content-Type": "application/json" }, body: JSON.stringify({ hotspot: { x: Math.max(0, s.xPct - 4), y: Math.max(0, s.yPct - 4), w: 8, h: 8 } }), }); } chrome.runtime.sendMessage({ type: "discard" }, () => {}); $("status").textContent = `Done — demo "${demo.name}" created with ${steps.length} steps. Open the editor to refine.`; refresh(); } catch (e) { $("status").textContent = `Upload failed: ${e}`; } }); });