106 lines
7.8 KiB
HTML
106 lines
7.8 KiB
HTML
|
|
<!doctype html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<title>Cookie Pop-ups</title>
|
||
|
|
<style>
|
||
|
|
:root { color-scheme: light dark;
|
||
|
|
--bg:#0e131c; --panel:#141a24; --panel2:#191f2b; --line:rgba(255,255,255,.09);
|
||
|
|
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#0ac18e; --warn:#f5a524; --danger:#ff5b5b; }
|
||
|
|
@media (prefers-color-scheme: light) {
|
||
|
|
:root { --bg:#f8faff; --panel:#ffffff; --panel2:#eff3fb; --line:rgba(0,0,0,.10);
|
||
|
|
--ink:#1a1f2b; --mut:#5c6577; --dim:#8a93a5; --warn:#d97706; }
|
||
|
|
}
|
||
|
|
* { box-sizing: border-box; }
|
||
|
|
html, body { margin: 0; height: 100%; }
|
||
|
|
body { background: var(--bg); color: var(--ink); font: 13px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; display: flex; flex-direction: column; }
|
||
|
|
header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--line); background: var(--panel); }
|
||
|
|
header h1 { margin: 0; font-size: 14px; font-weight: 600; }
|
||
|
|
header .pill { margin-left: auto; }
|
||
|
|
.pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px; font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; background: rgba(139,152,169,.14); color: var(--mut); }
|
||
|
|
.pill.on { color: #0b0e14; background: var(--acid); }
|
||
|
|
.pill.off { color: #fff; background: var(--danger); }
|
||
|
|
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
||
|
|
.body { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }
|
||
|
|
.card { border: 1px solid var(--line); background: var(--panel); border-radius: 14px; padding: 16px; }
|
||
|
|
.site { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
|
||
|
|
.site .host { font: 600 13px/1.3 ui-monospace, "Cascadia Code", Consolas, monospace; color: var(--mut); overflow-wrap: anywhere; }
|
||
|
|
.site .num { font-size: 40px; font-weight: 700; line-height: 1.1; color: var(--warn); font-variant-numeric: tabular-nums; }
|
||
|
|
.site .num.muted { color: var(--dim); }
|
||
|
|
.site .lbl { color: var(--mut); }
|
||
|
|
.site .sub { color: var(--dim); font-size: 11.5px; }
|
||
|
|
.btn { border: 1px solid var(--line); background: var(--panel2); color: var(--ink); padding: 9px 16px; border-radius: 10px; font: inherit; font-weight: 600; cursor: pointer; }
|
||
|
|
.btn:hover { border-color: rgb(from var(--warn) r g b / .5); }
|
||
|
|
.btn.primary { background: var(--warn); color: #0b0e14; border-color: transparent; }
|
||
|
|
.btn.wide { margin-top: 10px; min-width: 200px; }
|
||
|
|
.row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||
|
|
.row + .row { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
|
||
|
|
.row .k { font-weight: 600; }
|
||
|
|
.row .d { color: var(--dim); font-size: 11.5px; }
|
||
|
|
.row .v { color: var(--mut); font-variant-numeric: tabular-nums; }
|
||
|
|
.switch { position: relative; width: 40px; height: 22px; border-radius: 11px; background: var(--dim); border: none; cursor: pointer; flex: none; transition: background .15s; }
|
||
|
|
.switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: #fff; transition: left .15s; }
|
||
|
|
.switch.on { background: var(--acid); } .switch.on::after { left: 20px; }
|
||
|
|
.modes { display: grid; gap: 8px; margin-top: 10px; }
|
||
|
|
.mode { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel2); cursor: pointer; }
|
||
|
|
.mode.sel { border-color: rgb(from var(--warn) r g b / .6); }
|
||
|
|
.mode input { margin-top: 3px; accent-color: var(--warn); }
|
||
|
|
.mode .t { font-weight: 600; } .mode .d { color: var(--dim); font-size: 11.5px; }
|
||
|
|
.note { color: var(--dim); font-size: 11.5px; line-height: 1.45; }
|
||
|
|
h2 { margin: 0 0 8px; font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--dim); }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<header><span>🍪</span><h1>Cookie Pop-ups</h1><span class="pill" id="pill"><span class="dot"></span><span id="pillText">…</span></span></header>
|
||
|
|
<div class="body">
|
||
|
|
<div class="card site" id="siteCard"><div class="lbl">Open a site to see what was handled there.</div></div>
|
||
|
|
<div class="card">
|
||
|
|
<div class="row">
|
||
|
|
<div><div class="k">Answer cookie pop-ups for me</div><div class="d">On every site, before the banner gets in the way.</div></div>
|
||
|
|
<button class="switch" id="enabled" role="switch" aria-checked="true" title="Turn on or off"></button>
|
||
|
|
</div>
|
||
|
|
<div class="modes" id="modes">
|
||
|
|
<label class="mode"><input type="radio" name="mode" value="optOut"><span><div class="t">Reject all but essentials</div><div class="d">Declines tracking and marketing cookies wherever the pop-up allows it. Recommended.</div></span></label>
|
||
|
|
<label class="mode"><input type="radio" name="mode" value="optIn"><span><div class="t">Just make it go away</div><div class="d">Accepts the defaults. Fastest, but you keep whatever the site wants to set.</div></span></label>
|
||
|
|
</div>
|
||
|
|
<div class="row" style="margin-top:12px; padding-top:12px; border-top:1px solid var(--line)">
|
||
|
|
<div><div class="k">Pop-ups handled</div><div class="d">Since the add-on was installed.</div></div>
|
||
|
|
<div class="v" id="total">0</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="note" id="rules"></div>
|
||
|
|
<div class="note">Built on DuckDuckGo's autoconsent rules for hundreds of consent dialogs, with a fallback that looks for reject buttons on unknown ones. Some banners still need a click; if one keeps reappearing, exclude the site here.</div>
|
||
|
|
</div>
|
||
|
|
<script>
|
||
|
|
const $ = (id) => document.getElementById(id);
|
||
|
|
const esc = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&":"&","<":"<",">":">",'"':""","'":"'" })[c]);
|
||
|
|
let state = null;
|
||
|
|
function render(s) {
|
||
|
|
state = s;
|
||
|
|
$("pill").className = "pill " + (s.enabled ? "on" : "off");
|
||
|
|
$("pillText").textContent = s.enabled ? "On" : "Off";
|
||
|
|
$("enabled").classList.toggle("on", s.enabled); $("enabled").setAttribute("aria-checked", String(s.enabled));
|
||
|
|
for (const l of document.querySelectorAll(".mode")) { const on = l.querySelector("input").value === s.mode; l.classList.toggle("sel", on); l.querySelector("input").checked = on; }
|
||
|
|
$("total").textContent = Number(s.handledTotal || 0).toLocaleString();
|
||
|
|
$("rules").textContent = s.rulesVersion ? `Rules: autoconsent ${s.rulesVersion}.` : "";
|
||
|
|
const card = $("siteCard");
|
||
|
|
if (!s.site) card.innerHTML = `<div class="lbl">Open a site to see what was handled there.</div>`;
|
||
|
|
else if (s.site.skipped) card.innerHTML = `<div class="host">${esc(s.site.host)}</div><div class="num muted">—</div><div class="lbl">Excluded: pop-ups are left alone on this site.</div><button class="btn primary wide" id="siteBtn">Handle pop-ups here again</button>`;
|
||
|
|
else {
|
||
|
|
const n = s.site.handled;
|
||
|
|
card.innerHTML = `<div class="host">${esc(s.site.host)}</div><div class="num${n ? "" : " muted"}">${n}</div>
|
||
|
|
<div class="lbl">${n === 1 ? "pop-up answered on this site" : "pop-ups answered on this site"}${s.site.cmp ? ` · ${esc(s.site.cmp)}` : ""}</div>
|
||
|
|
${s.site.failed ? `<div class="sub">${s.site.failed} attempt(s) did not complete — the banner may need a click.</div>` : ""}
|
||
|
|
<button class="btn wide" id="siteBtn">Exclude this site</button>`;
|
||
|
|
}
|
||
|
|
const b = $("siteBtn"); if (b) b.onclick = () => window.silentmode.invoke("skip-site", { host: s.site.host, skipped: !s.site.skipped }).then(render);
|
||
|
|
}
|
||
|
|
$("enabled").onclick = () => window.silentmode.invoke("set-enabled", { enabled: !(state && state.enabled) }).then(render);
|
||
|
|
$("modes").addEventListener("change", (e) => { if (e.target.name === "mode") window.silentmode.invoke("set-mode", { mode: e.target.value }).then(render); });
|
||
|
|
window.silentmode.on("state", render);
|
||
|
|
window.silentmode.invoke("state").then(render);
|
||
|
|
window.silentmode.onVisibility((v) => { if (v) window.silentmode.invoke("state").then(render); });
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|