const VECTORS = { canvas: "Canvas", webgl: "WebGL", audio: "Audio", navigator: "Navigator", screen: "Screen", timezone: "Timezone", webrtc: "WebRTC", fonts: "Fonts", clientRects: "Client Rects", plugins: "Plugins", battery: "Battery", connection: "Connection" }; async function loadContainers() { const containers = await browser.runtime.sendMessage({ type: "getContainerList" }); const list = document.getElementById("container-list"); list.innerHTML = ""; for (const c of containers) { const row = document.createElement("div"); row.className = "row"; const dot = document.createElement("span"); dot.className = `dot dot-${c.color}`; row.appendChild(dot); const nameWrap = document.createElement("div"); nameWrap.className = "name"; const name = document.createElement("div"); name.className = "name-primary"; name.textContent = c.name; nameWrap.appendChild(name); if (c.domain) { const domain = document.createElement("div"); domain.className = "name-domain"; domain.textContent = c.domain; nameWrap.appendChild(domain); } row.appendChild(nameWrap); const toggle = document.createElement("input"); toggle.type = "checkbox"; toggle.className = "toggle"; toggle.checked = c.enabled; toggle.addEventListener("change", async () => { await browser.runtime.sendMessage({ type: "toggleContainer", cookieStoreId: c.cookieStoreId, enabled: toggle.checked }); }); row.appendChild(toggle); const gear = document.createElement("button"); gear.className = "gear"; gear.textContent = "\u2699"; gear.title = "Vector settings"; gear.addEventListener("click", () => toggleSettings(c.cookieStoreId, row, gear)); row.appendChild(gear); const regen = document.createElement("button"); regen.className = "regen"; regen.textContent = "New"; regen.title = "Generate new fingerprint"; regen.addEventListener("click", async () => { regen.textContent = "..."; await browser.runtime.sendMessage({ type: "regenerateFingerprint", cookieStoreId: c.cookieStoreId }); regen.textContent = "OK"; setTimeout(() => { regen.textContent = "New"; }, 800); }); row.appendChild(regen); const del = document.createElement("button"); del.className = "del"; del.textContent = "\u00D7"; del.title = "Delete container"; del.addEventListener("click", () => { // Show inline confirmation const existing = row.nextElementSibling; if (existing && existing.classList.contains("confirm-bar")) { existing.remove(); return; } // Remove any open panels if (existing && existing.classList.contains("vector-panel")) existing.remove(); const bar = document.createElement("div"); bar.className = "confirm-bar"; const msg = document.createElement("span"); msg.className = "confirm-msg"; msg.textContent = `Delete "${c.name}"?`; bar.appendChild(msg); const yes = document.createElement("button"); yes.className = "confirm-yes"; yes.textContent = "Delete"; yes.addEventListener("click", async () => { await browser.runtime.sendMessage({ type: "deleteContainer", cookieStoreId: c.cookieStoreId }); bar.remove(); row.remove(); }); bar.appendChild(yes); const no = document.createElement("button"); no.className = "confirm-no"; no.textContent = "Cancel"; no.addEventListener("click", () => bar.remove()); bar.appendChild(no); row.after(bar); }); row.appendChild(del); list.appendChild(row); } } async function toggleSettings(cookieStoreId, row, gearBtn) { const existing = row.nextElementSibling; if (existing && existing.classList.contains("vector-panel")) { existing.remove(); gearBtn.classList.remove("active"); return; } gearBtn.classList.add("active"); const panel = document.createElement("div"); panel.className = "vector-panel"; const { global, overrides } = await browser.runtime.sendMessage({ type: "getContainerVectors", cookieStoreId }); for (const [key, label] of Object.entries(VECTORS)) { const item = document.createElement("div"); item.className = "vector-row"; const span = document.createElement("span"); span.className = "vector-label"; span.textContent = label; item.appendChild(span); const hasOverride = overrides[key] !== undefined && overrides[key] !== null; const globalEnabled = global[key] !== false; const effectiveValue = hasOverride ? overrides[key] : globalEnabled; const toggle = document.createElement("input"); toggle.type = "checkbox"; toggle.className = "toggle toggle-sm"; toggle.checked = effectiveValue; if (!hasOverride) toggle.classList.add("inherited"); toggle.addEventListener("change", async () => { overrides[key] = toggle.checked; toggle.classList.remove("inherited"); await browser.runtime.sendMessage({ type: "setContainerVectors", cookieStoreId, vectors: overrides }); }); item.appendChild(toggle); const resetBtn = document.createElement("button"); resetBtn.className = "vector-reset"; resetBtn.textContent = "\u21A9"; resetBtn.title = "Reset to global default"; if (!hasOverride) resetBtn.style.visibility = "hidden"; resetBtn.addEventListener("click", async () => { delete overrides[key]; toggle.checked = globalEnabled; toggle.classList.add("inherited"); resetBtn.style.visibility = "hidden"; await browser.runtime.sendMessage({ type: "setContainerVectors", cookieStoreId, vectors: overrides }); }); item.appendChild(resetBtn); panel.appendChild(item); } row.after(panel); } document.getElementById("regen-all").addEventListener("click", async (e) => { e.target.textContent = "Regenerating..."; await browser.runtime.sendMessage({ type: "regenerateAll" }); e.target.textContent = "Done!"; setTimeout(() => { e.target.textContent = "Regenerate All"; }, 800); }); document.getElementById("prune").addEventListener("click", async (e) => { e.target.textContent = "Pruning..."; const result = await browser.runtime.sendMessage({ type: "pruneContainers" }); e.target.textContent = `Removed ${result.pruned}`; setTimeout(() => { e.target.textContent = "Prune Unused"; loadContainers(); }, 1200); }); document.getElementById("reset").addEventListener("click", (e) => { const actions = e.target.closest(".actions"); const existing = actions.querySelector(".confirm-bar"); if (existing) { existing.remove(); return; } const bar = document.createElement("div"); bar.className = "confirm-bar"; const msg = document.createElement("span"); msg.className = "confirm-msg"; msg.textContent = "Remove all containers and data?"; bar.appendChild(msg); const yes = document.createElement("button"); yes.className = "confirm-yes"; yes.textContent = "Reset"; yes.addEventListener("click", async () => { bar.remove(); e.target.textContent = "Resetting..."; await browser.runtime.sendMessage({ type: "resetAll" }); e.target.textContent = "Done!"; setTimeout(() => { e.target.textContent = "Reset All"; loadContainers(); }, 1200); }); bar.appendChild(yes); const no = document.createElement("button"); no.className = "confirm-no"; no.textContent = "Cancel"; no.addEventListener("click", () => bar.remove()); bar.appendChild(no); actions.appendChild(bar); }); document.getElementById("search").addEventListener("input", (e) => { const q = e.target.value.toLowerCase(); const rows = document.querySelectorAll("#container-list .row"); for (const row of rows) { const text = row.textContent.toLowerCase(); row.classList.toggle("hidden", q && !text.includes(q)); } }); loadContainers();