- Security popover redesigned to resemble Firefox's site-info panel: lock/shield hero, "Connection secure" status, host, plain-language subtitle, and a details grid; the overlay view auto-sizes to content. - Search-engine picker returned to the toolbar with an "Add / edit engines…" entry that opens Settings; added Ecosia, Mojeek, Presearch. - Custom search engines: add (name + URL template with %s) / remove in Settings > General; used by address-bar search. - Address-bar placeholder: "Ask a search engine or enter web address".
191 lines
12 KiB
HTML
191 lines
12 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><title>Theseus — Settings</title>
|
|
<style>
|
|
:root{ color-scheme: dark; --bg:#0b0e14; --panel:#141a24; --line:rgba(255,255,255,.09);
|
|
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d; }
|
|
*{box-sizing:border-box}
|
|
body{margin:0;height:100vh;background:var(--bg);color:var(--ink);font:15px/1.6 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
|
|
.app{display:flex;height:100vh}
|
|
/* left sidebar menu */
|
|
.side{flex:none;width:220px;background:#0e131c;border-right:1px solid var(--line);padding:20px 12px;display:flex;flex-direction:column;gap:2px}
|
|
.brand{font-weight:700;color:var(--acid);font-size:15px;padding:4px 12px 16px}
|
|
.side a{display:block;padding:9px 12px;border-radius:8px;color:var(--mut);text-decoration:none;font-size:14px;cursor:pointer}
|
|
.side a:hover{background:#ffffff0a;color:var(--ink)}
|
|
.side a.active{background:rgba(214,255,61,.12);color:var(--acid)}
|
|
/* content */
|
|
.content{flex:1;overflow-y:auto;padding:2.4rem 2.4rem 4rem}
|
|
section{max-width:640px}
|
|
h1{font-size:1.4rem;margin:0 0 .2rem}
|
|
.lede{color:var(--mut);margin:0 0 1.8rem;font-size:13.5px}
|
|
.row{display:flex;align-items:center;gap:16px;background:var(--panel);border:1px solid var(--line);
|
|
border-radius:12px;padding:14px 18px;margin-bottom:10px}
|
|
.row .txt{flex:1}
|
|
.row .t{font-weight:600}
|
|
.row .d{color:var(--mut);font-size:13px;margin-top:2px}
|
|
.ctl{display:flex;flex-direction:column;gap:6px;align-items:flex-end;flex:none}
|
|
.ctl .coords{display:flex;gap:6px}
|
|
select,.ctl input{background:#1b2330;color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:13px;outline:none}
|
|
select:focus,.ctl input:focus{border-color:#4b7bec}
|
|
.ctl input{width:150px} .ctl .coords input{width:92px}
|
|
code{font-family:ui-monospace,monospace;font-size:12px;background:#0e131b;border:1px solid var(--line);border-radius:5px;padding:1px 5px;color:#bfeae4}
|
|
.addeng{display:flex;gap:6px} .addeng input{flex:1;background:#1b2330;color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font-size:13px;outline:none}
|
|
.addeng input:focus{border-color:#4b7bec}
|
|
.btn{border:1px solid #d6ff3d55;background:rgba(214,255,61,.12);color:#eaffb0;border-radius:8px;padding:8px 14px;font-size:13px;cursor:pointer}
|
|
.btn:hover{background:rgba(214,255,61,.22)}
|
|
.ceng{display:flex;align-items:center;gap:8px;background:#10151f;border:1px solid var(--line);border-radius:8px;padding:6px 10px;margin-bottom:6px;font-size:13px}
|
|
.ceng .cn{font-weight:600} .ceng .cu{color:var(--dim);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
|
|
.ceng .cx{cursor:pointer;color:#8b98a9;border:none;background:transparent;font-size:13px} .ceng .cx:hover{color:#f6768a}
|
|
.cempty{color:var(--dim);font-size:12.5px}
|
|
/* toggle */
|
|
.sw{position:relative;width:46px;height:26px;flex:none;cursor:pointer}
|
|
.sw input{opacity:0;width:0;height:0}
|
|
.track{position:absolute;inset:0;background:#2b3444;border:1px solid var(--line);border-radius:999px;transition:.15s}
|
|
.knob{position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:#8b98a9;transition:.15s}
|
|
.sw input:checked + .track{background:rgba(214,255,61,.25);border-color:#d6ff3d55}
|
|
.sw input:checked + .track .knob{transform:translateX(20px);background:var(--acid)}
|
|
.note{color:var(--dim);font-size:12.5px;margin-top:1.4rem;border-top:1px solid var(--line);padding-top:1rem}
|
|
</style></head>
|
|
<body>
|
|
<div class="app">
|
|
<nav class="side">
|
|
<div class="brand">⛓ Theseus</div>
|
|
<a data-sec="general" class="active">General</a>
|
|
<a data-sec="performance">Performance</a>
|
|
<a data-sec="privacy">Privacy</a>
|
|
<a data-sec="fingerprint">Anti-fingerprinting</a>
|
|
</nav>
|
|
<div class="content">
|
|
<!-- GENERAL -->
|
|
<section id="general">
|
|
<h1>General</h1>
|
|
<p class="lede">Changes apply immediately and are saved for next time.</p>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">Search engine</div><div class="d">Used when you type a search into the address bar.</div></div>
|
|
<div class="ctl"><select id="searchEngine"></select></div>
|
|
</div>
|
|
<div class="row" style="flex-direction:column;align-items:stretch;gap:12px">
|
|
<div class="txt"><div class="t">Add a search engine</div>
|
|
<div class="d">Name it, and give a URL with <code>%s</code> where the query goes — e.g. <code>https://searx.be/search?q=%s</code></div></div>
|
|
<div id="customList"></div>
|
|
<div class="addeng">
|
|
<input id="engName" placeholder="Name (e.g. SearXNG)">
|
|
<input id="engUrl" placeholder="https://example.com/search?q=%s">
|
|
<button id="engAdd" class="btn">Add</button>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">Reopen tabs on launch</div><div class="d">Restore the tabs from your last session when Theseus starts.</div></div>
|
|
<label class="sw"><input type="checkbox" id="restoreSession"><span class="track"><span class="knob"></span></span></label>
|
|
</div>
|
|
</section>
|
|
<!-- PERFORMANCE -->
|
|
<section id="performance" hidden>
|
|
<h1>Performance</h1>
|
|
<p class="lede">Keep Theseus light on resources.</p>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">Throttle inactive tabs</div><div class="d">Background and inactive tabs use far less CPU. Recommended.</div></div>
|
|
<label class="sw"><input type="checkbox" id="backgroundThrottle"><span class="track"><span class="knob"></span></span></label>
|
|
</div>
|
|
</section>
|
|
<!-- PRIVACY -->
|
|
<section id="privacy" hidden>
|
|
<h1>Privacy</h1>
|
|
<p class="lede">Reduce tracking and lock down device access.</p>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">WebRTC leak protection</div><div class="d">Stops sites from discovering your real IP through WebRTC. Strongest with Tor on.</div></div>
|
|
<label class="sw"><input type="checkbox" id="webrtcProtect"><span class="track"><span class="knob"></span></span></label>
|
|
</div>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">Block camera</div><div class="d">Deny camera by default — also hides its name from fingerprinting.</div></div>
|
|
<label class="sw"><input type="checkbox" id="blockCamera"><span class="track"><span class="knob"></span></span></label>
|
|
</div>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">Block microphone</div><div class="d">Deny microphone by default — also hides its name from fingerprinting.</div></div>
|
|
<label class="sw"><input type="checkbox" id="blockMicrophone"><span class="track"><span class="knob"></span></span></label>
|
|
</div>
|
|
</section>
|
|
<!-- ANTI-FINGERPRINTING -->
|
|
<section id="fingerprint" hidden>
|
|
<h1>Anti-fingerprinting</h1>
|
|
<p class="lede">For each, choose <b>Show</b> (real), <b>Hide</b> (neutral value), <b>Spoof</b> (an automatic decoy), or <b>Manual</b> (set your own).</p>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">Timezone</div><div class="d">What sites read via JavaScript (Intl / Date).</div></div>
|
|
<div class="ctl">
|
|
<select id="timezoneMode"><option value="show">Show real</option><option value="hide">Hide (UTC)</option><option value="spoof">Spoof (auto)</option><option value="manual">Manual…</option></select>
|
|
<input id="timezoneValue" placeholder="e.g. Europe/Berlin" hidden>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">Language</div><div class="d">navigator.language and the Accept-Language header.</div></div>
|
|
<div class="ctl">
|
|
<select id="languageMode"><option value="show">Show real</option><option value="hide">Hide (en-US)</option><option value="spoof">Spoof (auto)</option><option value="manual">Manual…</option></select>
|
|
<input id="languageValue" placeholder="e.g. de-DE" hidden>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="txt"><div class="t">Location</div><div class="d"><b>Hide</b> denies geolocation; <b>Spoof</b>/<b>Manual</b> report coordinates you choose.</div></div>
|
|
<div class="ctl">
|
|
<select id="locationMode"><option value="show">Show real</option><option value="hide">Hide (block)</option><option value="spoof">Spoof (auto)</option><option value="manual">Manual…</option></select>
|
|
<div class="coords" id="locationCoords" hidden>
|
|
<input id="locationLat" placeholder="lat"><input id="locationLon" placeholder="lon">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="note">These reduce tracking and hide your IP, but a custom browser can still be fingerprinted. For maximum anonymity, use the Tor Browser.</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
const C = window.cfg;
|
|
// sidebar navigation
|
|
const sections = ["general", "performance", "privacy", "fingerprint"];
|
|
document.querySelectorAll(".side a").forEach((a) => a.onclick = () => {
|
|
document.querySelectorAll(".side a").forEach((x) => x.classList.toggle("active", x === a));
|
|
for (const s of sections) document.getElementById(s).hidden = (s !== a.dataset.sec);
|
|
});
|
|
|
|
const TOGGLES = ["restoreSession", "backgroundThrottle", "webrtcProtect", "blockCamera", "blockMicrophone"];
|
|
C.get().then((s) => {
|
|
for (const k of TOGGLES) {
|
|
const el = document.getElementById(k); if (!el) continue;
|
|
el.checked = !!s[k];
|
|
el.addEventListener("change", () => C.set(k, el.checked));
|
|
}
|
|
// search engines: default picker + custom-engine list + add form
|
|
const sel = document.getElementById("searchEngine");
|
|
const esc = (s) => String(s || "").replace(/</g, "<");
|
|
function renderEngines(d) {
|
|
sel.innerHTML = d.engines.map((e) => `<option value="${e.id}">${esc(e.name)}</option>`).join("");
|
|
sel.value = d.current;
|
|
const custom = d.engines.filter((e) => !e.builtin);
|
|
const list = document.getElementById("customList");
|
|
list.innerHTML = custom.length
|
|
? custom.map((e) => `<div class="ceng"><span class="cn">${esc(e.name)}</span><span class="cu">${esc(e.id)}</span><button class="cx" data-id="${e.id}" title="Remove">✕</button></div>`).join("")
|
|
: `<div class="cempty">No custom engines yet.</div>`;
|
|
list.querySelectorAll(".cx").forEach((b) => b.onclick = () => C.removeEngine(b.dataset.id).then(renderEngines));
|
|
}
|
|
sel.onchange = () => C.set("searchEngine", sel.value);
|
|
document.getElementById("engAdd").onclick = () => {
|
|
const name = document.getElementById("engName").value.trim();
|
|
const url = document.getElementById("engUrl").value.trim();
|
|
if (!name || !url.includes("%s")) { alert("Enter a name and a URL containing %s (where the query goes)."); return; }
|
|
C.addEngine({ name, url }).then((d) => { document.getElementById("engName").value = ""; document.getElementById("engUrl").value = ""; renderEngines(d); });
|
|
};
|
|
C.engines().then(renderEngines);
|
|
// anti-fingerprinting mode selectors, with value field(s) shown on "manual"
|
|
const bind = (mode, showValIf, apply) => {
|
|
const m = document.getElementById(mode);
|
|
m.value = s[mode] || "show";
|
|
apply(m.value === "manual");
|
|
m.addEventListener("change", () => { C.set(mode, m.value); apply(m.value === "manual"); });
|
|
};
|
|
const val = (id) => { const v = document.getElementById(id); v.value = s[id] ?? ""; v.addEventListener("change", () => C.set(id, v.value.trim())); return v; };
|
|
const tzV = val("timezoneValue"), lgV = val("languageValue"), laV = val("locationLat"), loV = val("locationLon");
|
|
bind("timezoneMode", null, (manual) => tzV.hidden = !manual);
|
|
bind("languageMode", null, (manual) => lgV.hidden = !manual);
|
|
bind("locationMode", null, (manual) => document.getElementById("locationCoords").hidden = !manual);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|