"use strict"; const rows = document.getElementById("rows"); const nameInput = document.getElementById("name"); const createBtn = document.getElementById("create"); const errorEl = document.getElementById("error"); const statusEl = document.getElementById("status"); const portInput = document.getElementById("port"); const savePortBtn = document.getElementById("save-port"); const modal = document.getElementById("secret-modal"); const secretEl = document.getElementById("secret"); const copyBtn = document.getElementById("copy"); const closeBtn = document.getElementById("close"); function applyStatus(info) { if (info && info.listenPort) { portInput.value = String(info.listenPort); } if (info && info.bindError) { statusEl.textContent = `Could not listen on ${info.listenPort}: ${info.bindError}`; return; } statusEl.textContent = info && info.hostUp ? `This add-on is ready on 127.0.0.1:${info.listenPort} while Firefox is open.` : "You can issue and revoke secrets here anytime."; } function showError(text) { errorEl.hidden = !text; errorEl.textContent = text || ""; } function render(clients) { rows.replaceChildren(); if (!clients.length) { const tr = document.createElement("tr"); const td = document.createElement("td"); td.colSpan = 4; td.className = "muted"; td.textContent = "No clients yet. Generate a secret to get started."; tr.appendChild(td); rows.appendChild(tr); return; } for (const client of clients) { const tr = document.createElement("tr"); for (const text of [client.name, client.created || "", client.last_used || "never"]) { const td = document.createElement("td"); td.textContent = text; tr.appendChild(td); } const action = document.createElement("td"); const btn = document.createElement("button"); btn.type = "button"; btn.textContent = "Revoke"; btn.dataset.revoke = client.id; action.appendChild(btn); tr.appendChild(action); rows.appendChild(tr); } } async function refresh() { render(await listClients()); try { applyStatus(await browser.runtime.sendMessage({ type: "status" })); } catch (err) { statusEl.textContent = "You can issue and revoke secrets here anytime."; } } savePortBtn.addEventListener("click", async () => { showError(""); try { applyStatus(await browser.runtime.sendMessage({ type: "set-port", port: portInput.value })); } catch (err) { showError(err.message); } }); createBtn.addEventListener("click", async () => { showError(""); try { const created = await createClient(nameInput.value.trim()); nameInput.value = ""; secretEl.value = created.token; modal.showModal(); await refresh(); } catch (err) { showError(err.message); } }); rows.addEventListener("click", async (event) => { const id = event.target && event.target.getAttribute("data-revoke"); if (!id) { return; } showError(""); try { await revokeClient(id); await refresh(); } catch (err) { showError(err.message); } }); copyBtn.addEventListener("click", async () => { try { await navigator.clipboard.writeText(secretEl.value); copyBtn.textContent = "Copied"; } catch (err) { showError(err.message); } }); closeBtn.addEventListener("click", () => { secretEl.value = ""; copyBtn.textContent = "Copy"; modal.close(); }); refresh().catch((err) => showError(err.message));