Theseus: light/dark theme, redesigned search box, trimmed engines, WebRTC modes
- Light/dark/system theme (Settings > General) via nativeTheme.themeSource driving prefers-color-scheme across chrome, settings, popover, and home. - Search box redesigned: a magnifier icon opens the engine dropdown, the rest is a wider typing field; styled to match the browser (was a raw <select>). - Trimmed built-in engines to DuckDuckGo/Google/Brave/Bing/Startpage/Yandex; custom engines still add/remove via "Add / edit engines…". - WebRTC: the "WebRTC Network Limiter" extension can't run in Electron (chrome.privacy API is unavailable), so its function is now a native 4-mode WebRTC IP policy in Settings > Privacy. - Address-bar placeholder: "Ask a search engine or enter web address".
This commit is contained in:
parent
01429990ea
commit
2757cd8c64
5 changed files with 133 additions and 52 deletions
87
chrome.html
87
chrome.html
|
|
@ -2,28 +2,40 @@
|
|||
<html>
|
||||
<head><meta charset="utf-8">
|
||||
<style>
|
||||
:root { color-scheme: dark; font-family: system-ui, sans-serif; }
|
||||
:root {
|
||||
color-scheme: light dark; font-family: system-ui, sans-serif;
|
||||
--bg:#0f1420; --surface:#1b2330; --surface2:#171e2a; --active:#263041cc;
|
||||
--ink:#e7eaf1; --mut:#b9c2d0; --dim:#8b98a9; --faint:#5e6678;
|
||||
--line:rgba(255,255,255,.20); --line2:rgba(255,255,255,.13); --hover:rgba(255,255,255,.08);
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--bg:#e9ecf1; --surface:#ffffff; --surface2:#f1f3f7; --active:#dbe1ec;
|
||||
--ink:#1a1f28; --mut:#3c4453; --dim:#697280; --faint:#98a1b0;
|
||||
--line:rgba(0,0,0,.18); --line2:rgba(0,0,0,.11); --hover:rgba(0,0,0,.06);
|
||||
}
|
||||
}
|
||||
[hidden] { display: none !important; }
|
||||
body { margin: 0; height: 100vh; background: #0f1420; color: #e7eaf1; user-select: none; overflow: hidden; }
|
||||
body { margin: 0; height: 100vh; background: var(--bg); color: var(--ink); user-select: none; overflow: hidden; }
|
||||
.tabs { display: flex; align-items: flex-end; gap: 4px; padding: 6px 8px 0; height: 34px; }
|
||||
.tab { display: flex; align-items: center; gap: 8px; max-width: 200px; min-width: 90px; padding: 6px 10px;
|
||||
background: #1b2330; border: 1px solid #ffffff14; border-bottom: none; border-radius: 8px 8px 0 0;
|
||||
font-size: 12.5px; cursor: default; color: #b9c2d0; }
|
||||
.tab.active { background: #26304180; color: #fff; }
|
||||
background: var(--surface); border: 1px solid var(--line2); border-bottom: none; border-radius: 8px 8px 0 0;
|
||||
font-size: 12.5px; cursor: default; color: var(--mut); }
|
||||
.tab.active { background: var(--active); color: var(--ink); }
|
||||
.tab .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
|
||||
.tab .spin { width: 10px; height: 10px; flex: none; border: 1.5px solid #ffffff2e; border-top-color: #d6ff3d; border-radius: 50%; animation: spin .7s linear infinite; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.tab .x { opacity: .5; cursor: pointer; padding: 0 2px; border-radius: 4px; }
|
||||
.tab .x:hover { opacity: 1; background: #ffffff1a; }
|
||||
.newtab { padding: 4px 10px; cursor: pointer; color: #8b98a9; border-radius: 6px; font-size: 16px; }
|
||||
.newtab:hover { background: #ffffff12; color: #fff; }
|
||||
.tab .x:hover { opacity: 1; background: var(--line2); }
|
||||
.newtab { padding: 4px 10px; cursor: pointer; color: var(--dim); border-radius: 6px; font-size: 16px; }
|
||||
.newtab:hover { background: var(--hover); color: var(--ink); }
|
||||
.bar { display: flex; gap: 6px; align-items: center; padding: 6px 10px; }
|
||||
.nav { display: flex; gap: 1px; }
|
||||
.ic { width: 34px; height: 32px; display: grid; place-items: center; border-radius: 8px; cursor: pointer;
|
||||
color: #c5cdda; background: transparent; border: none; padding: 0; }
|
||||
color: var(--mut); background: transparent; border: none; padding: 0; }
|
||||
.ic svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.ic:hover { background: #ffffff14; color: #fff; }
|
||||
.ic:active { background: #ffffff22; }
|
||||
.ic:hover { background: var(--line2); color: var(--ink); }
|
||||
.ic:active { background: var(--line); }
|
||||
.ic:disabled { opacity: .28; cursor: default; background: transparent; }
|
||||
/* indeterminate loading bar under the toolbar */
|
||||
.loadbar { height: 2px; overflow: hidden; background: transparent; }
|
||||
|
|
@ -31,31 +43,36 @@
|
|||
background: linear-gradient(90deg, transparent, #4b7bec, #d6ff3d, transparent); animation: loadslide 1.1s infinite linear; }
|
||||
@keyframes loadslide { 0% { transform: translateX(-110%); } 100% { transform: translateX(400%); } }
|
||||
/* address bar */
|
||||
.urlwrap { flex: 1; display: flex; align-items: center; gap: 4px; background: #1b2330; border: 1px solid #ffffff22;
|
||||
.urlwrap { flex: 1; display: flex; align-items: center; gap: 4px; background: var(--surface); border: 1px solid var(--line);
|
||||
border-radius: 999px; padding: 0 6px 0 4px; }
|
||||
.urlwrap:focus-within { border-color: #4b7bec; }
|
||||
/* Firefox-style padlock — always present (the "site security" button) */
|
||||
.secbadge { display: grid; place-items: center; width: 28px; height: 26px; border-radius: 6px; border: none;
|
||||
background: transparent; cursor: pointer; padding: 0; }
|
||||
.secbadge:hover { background: #ffffff14; }
|
||||
.secbadge:hover { background: var(--line2); }
|
||||
.secbadge svg { width: 14px; height: 14px; }
|
||||
.secbadge svg .shk { fill: none; stroke: #a9b3c2; stroke-width: 1.3; }
|
||||
.secbadge svg .bdy { fill: #a9b3c2; }
|
||||
.secbadge svg .shk { fill: none; stroke: var(--dim); stroke-width: 1.3; }
|
||||
.secbadge svg .bdy { fill: var(--dim); }
|
||||
.secbadge.warn svg .shk { stroke: #f6ad55; } .secbadge.warn svg .bdy { fill: #f6ad55; }
|
||||
input { padding: 7px 6px; border-radius: 999px; border: none; background: transparent; color: inherit; font-size: 13.5px; outline: none; }
|
||||
#url { flex: 1; }
|
||||
/* minimal registry indicator at the END of the address bar */
|
||||
.reg { font-size: 10.5px; letter-spacing: .03em; padding: 2px 9px; border-radius: 999px; white-space: nowrap;
|
||||
background: rgba(214,255,61,.13); color: #d6ff3d; border: 1px solid #d6ff3d33; }
|
||||
.reg.icann { background: rgba(139,152,169,.14); color: #9aa6b6; border-color: #ffffff1a; }
|
||||
.star { border: none; background: transparent; cursor: pointer; color: #8b98a9; font-size: 15px; padding: 2px 4px; border-radius: 6px; }
|
||||
.star:hover { background: #ffffff14; color: #fff; } .star.on { color: #ffd23d; }
|
||||
/* compact search-engine picker */
|
||||
.engine { display: flex; align-items: center; gap: 4px; background: #1b2330; border: 1px solid #ffffff22;
|
||||
border-radius: 8px; padding: 0 4px 0 8px; }
|
||||
.engine .mag { color: #8b98a9; font-size: 12px; }
|
||||
.engine select { background: transparent; color: #c5cdda; border: none; font-size: 12px; padding: 7px 2px; outline: none; cursor: pointer; }
|
||||
.tor { padding: 7px 10px; border-radius: 8px; border: 1px solid #ffffff22; background: #2b2f3a; color: #fff; cursor: pointer; white-space: nowrap; font-size: 12.5px; }
|
||||
.reg.icann { background: rgba(139,152,169,.14); color: #9aa6b6; border-color: var(--line2); }
|
||||
.star { border: none; background: transparent; cursor: pointer; color: var(--dim); font-size: 15px; padding: 2px 4px; border-radius: 6px; }
|
||||
.star:hover { background: var(--line2); color: var(--ink); } .star.on { color: #ffd23d; }
|
||||
/* search box: engine icon (click = pick engine) + a wide typing area */
|
||||
.searchbox { display: flex; align-items: center; width: 300px; background: var(--surface); border: 1px solid var(--line);
|
||||
border-radius: 999px; padding: 0 12px 0 3px; }
|
||||
.searchbox:focus-within { border-color: #4b7bec; }
|
||||
.engine-sel { appearance: none; -webkit-appearance: none; width: 34px; height: 30px; border: none; cursor: pointer;
|
||||
color: transparent; border-radius: 999px 0 0 999px;
|
||||
background: transparent url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238b98a9' stroke-width='1.6'%3E%3Ccircle cx='7' cy='7' r='4.2'/%3E%3Cpath d='M10.2 10.2 L14 14'/%3E%3C/svg%3E") no-repeat center / 15px; }
|
||||
.engine-sel:hover { background-color: var(--hover); }
|
||||
.engine-sel option { color: var(--ink); background: var(--surface); }
|
||||
#search { flex: 1; padding: 7px 4px; }
|
||||
.tor { padding: 7px 10px; border-radius: 8px; border: 1px solid var(--line); background: #2b2f3a; color: var(--ink); cursor: pointer; white-space: nowrap; font-size: 12.5px; }
|
||||
.tor.connecting { background: #7a5c14; } .tor.on { background: #6b3fa0; }
|
||||
.logo { display: flex; align-items: center; gap: 6px; font-weight: 700; color: #d6ff3d; white-space: nowrap;
|
||||
padding: 6px 12px; border: 1px solid #d6ff3d33; border-radius: 8px; cursor: pointer; background: rgba(214,255,61,.08); font-size: 13px; }
|
||||
|
|
@ -64,23 +81,23 @@
|
|||
/* favorites bar — new-tab page only, single compact row */
|
||||
.bookmarks { display: flex; align-items: center; gap: 4px; padding: 3px 10px 5px; height: 26px; overflow: hidden; }
|
||||
.bm { display: flex; align-items: center; gap: 6px; max-width: 180px; padding: 3px 9px; border-radius: 6px;
|
||||
background: #171e2a; border: 1px solid #ffffff10; color: #c5cdda; font-size: 12px; cursor: pointer; white-space: nowrap; }
|
||||
.bm:hover { background: #202a3a; color: #fff; }
|
||||
background: var(--surface2); border: 1px solid var(--hover); color: var(--mut); font-size: 12px; cursor: pointer; white-space: nowrap; }
|
||||
.bm:hover { background: var(--active); color: var(--ink); }
|
||||
.bm .bt { overflow: hidden; text-overflow: ellipsis; }
|
||||
.bm .bx { opacity: 0; cursor: pointer; font-size: 11px; } .bm:hover .bx { opacity: .55; }
|
||||
.bm .bx:hover { opacity: 1; color: #f6768a; }
|
||||
.bm-empty { color: #5e6678; font-size: 11.5px; white-space: nowrap; }
|
||||
.bm-empty { color: var(--faint); font-size: 11.5px; white-space: nowrap; }
|
||||
.tordisc { font-size: 11.5px; color: #d9c7f2; background: #2a1c40; border-top: 1px solid #6b3fa055; padding: 5px 14px; }
|
||||
.tordisc a { color: #d6ff3d; }
|
||||
.bcnrbar { display: flex; align-items: center; gap: 10px; font-size: 11.5px; color: #dbead0;
|
||||
.bcnrbar { display: flex; align-items: center; gap: 10px; font-size: 11.5px; color: var(--mut);
|
||||
background: #1a2417; border-top: 1px solid #d6ff3d33; padding: 6px 14px; }
|
||||
.bcnrbar .breg { font-size: 10px; letter-spacing: .03em; padding: 1px 7px; border-radius: 999px;
|
||||
background: rgba(214,255,61,.14); color: #d6ff3d; border: 1px solid #d6ff3d33; }
|
||||
.bcnrbar b { color: #fff; }
|
||||
.bcnrbar b { color: var(--ink); }
|
||||
.bcnrbar .bopen { margin-left: auto; padding: 4px 11px; border-radius: 7px; border: 1px solid #d6ff3d55;
|
||||
background: rgba(214,255,61,.12); color: #eaffb0; cursor: pointer; font-size: 11.5px; }
|
||||
.bcnrbar .bopen:hover { background: rgba(214,255,61,.22); }
|
||||
.bcnrbar .bdismiss { color: #8b98a9; text-decoration: none; } .bcnrbar .bdismiss:hover { color: #fff; }
|
||||
.bcnrbar .bdismiss { color: var(--dim); text-decoration: none; } .bcnrbar .bdismiss:hover { color: var(--ink); }
|
||||
</style></head>
|
||||
<body>
|
||||
<div class="tabs" id="tabs"></div>
|
||||
|
|
@ -99,9 +116,10 @@
|
|||
<span class="reg" id="reg" hidden></span>
|
||||
<button class="star" id="star" title="Save this page">☆</button>
|
||||
</div>
|
||||
<span class="engine" title="Search engine (type a search in the address bar)">
|
||||
<span class="mag">🔍</span><select id="engine"></select>
|
||||
</span>
|
||||
<div class="searchbox">
|
||||
<select id="engine" class="engine-sel" title="Choose search engine"></select>
|
||||
<input id="search" placeholder="Search" spellcheck="false">
|
||||
</div>
|
||||
<button class="tor" id="tor" title="Route traffic through Tor">🧅 Tor: Off</button>
|
||||
<button class="logo" id="logo" title="Settings"><span>⛓ Theseus</span><span class="gear">⚙</span></button>
|
||||
</div>
|
||||
|
|
@ -127,6 +145,7 @@
|
|||
|
||||
function goURL() { const v = $("url").value.trim(); if (v) T.navigate(v); }
|
||||
$("url").addEventListener("keydown", (e) => { if (e.key === "Enter") goURL(); });
|
||||
$("search").addEventListener("keydown", (e) => { if (e.key === "Enter") { const q = $("search").value.trim(); if (q) { T.search(q); $("search").value = ""; } } });
|
||||
$("back").onclick = () => T.back();
|
||||
$("fwd").onclick = () => T.forward();
|
||||
$("reload").onclick = () => T.reload();
|
||||
|
|
@ -142,6 +161,8 @@
|
|||
sel.innerHTML = d.engines.map((e) => `<option value="${e.id}">${e.name.replace(/</g,"<")}</option>`).join("")
|
||||
+ `<option disabled>──────────</option><option value="__manage">+ Add / edit engines…</option>`;
|
||||
sel.value = d.current;
|
||||
const cur = d.engines.find((e) => e.id === d.current);
|
||||
$("search").placeholder = cur ? "Search with " + cur.name : "Search";
|
||||
}
|
||||
$("engine").onchange = () => {
|
||||
const v = $("engine").value;
|
||||
|
|
|
|||
12
home.html
12
home.html
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head><meta charset="utf-8"><title>Theseus</title>
|
||||
<style>
|
||||
:root { color-scheme: dark; }
|
||||
:root { color-scheme: light dark; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; min-height: 100vh; font-family: system-ui, sans-serif;
|
||||
background: radial-gradient(1000px 500px at 50% -10%, #16202e, #0b0e14);
|
||||
|
|
@ -30,6 +30,16 @@
|
|||
.b-srv { background: #4fd1a522; color: #4fd1a5; }
|
||||
footer { color: #47505f; font-size: 12px; padding-bottom: 2rem; text-align: center; }
|
||||
footer b { color: #6b7688; }
|
||||
/* light theme (placed last so it wins over the dark base rules) */
|
||||
@media (prefers-color-scheme: light) {
|
||||
body { background: radial-gradient(1000px 500px at 50% -10%, #dce6f2, #eef0f4); color: #1a1f28; }
|
||||
h1 .g { color: #4a8a00; }
|
||||
.tag { color: #4a5262; } .hint { color: #8a93a2; }
|
||||
input { background: #ffffff; border-color: rgba(0,0,0,.14); color: #1a1f28; }
|
||||
.card { background: #ffffff; border-color: rgba(0,0,0,.10); }
|
||||
.card:hover { background: #f4f7fb; } .card .n { color: #1a1f28; } .card .d { color: #4a5262; }
|
||||
footer { color: #8a93a2; } footer b { color: #4a5262; }
|
||||
}
|
||||
</style></head>
|
||||
<body>
|
||||
<div class="hero">
|
||||
|
|
|
|||
39
main.js
39
main.js
|
|
@ -4,7 +4,7 @@
|
|||
// h, Sia s3, direct ip, redirect u). Tabs, nav controls, a search box, a home
|
||||
// page, and optional Tor onion routing. No system daemon; the app is the trust
|
||||
// boundary.
|
||||
const { app, BrowserWindow, WebContentsView, ipcMain, protocol, session, Menu, clipboard } = require("electron");
|
||||
const { app, BrowserWindow, WebContentsView, ipcMain, protocol, session, Menu, clipboard, nativeTheme } = require("electron");
|
||||
const path = require("path");
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
|
|
@ -26,13 +26,10 @@ const RESOLVER = app.isPackaged
|
|||
const SEARCH_ENGINES = {
|
||||
duckduckgo: { name: "DuckDuckGo", url: (q) => "https://duckduckgo.com/?q=" + encodeURIComponent(q) },
|
||||
google: { name: "Google", url: (q) => "https://www.google.com/search?q=" + encodeURIComponent(q) },
|
||||
bing: { name: "Bing", url: (q) => "https://www.bing.com/search?q=" + encodeURIComponent(q) },
|
||||
yandex: { name: "Yandex", url: (q) => "https://yandex.com/search/?text=" + encodeURIComponent(q) },
|
||||
brave: { name: "Brave", url: (q) => "https://search.brave.com/search?q=" + encodeURIComponent(q) },
|
||||
bing: { name: "Bing", url: (q) => "https://www.bing.com/search?q=" + encodeURIComponent(q) },
|
||||
startpage: { name: "Startpage", url: (q) => "https://www.startpage.com/sp/search?query=" + encodeURIComponent(q) },
|
||||
ecosia: { name: "Ecosia", url: (q) => "https://www.ecosia.org/search?q=" + encodeURIComponent(q) },
|
||||
mojeek: { name: "Mojeek", url: (q) => "https://www.mojeek.com/search?q=" + encodeURIComponent(q) },
|
||||
presearch: { name: "Presearch", url: (q) => "https://presearch.com/search?q=" + encodeURIComponent(q) },
|
||||
yandex: { name: "Yandex", url: (q) => "https://yandex.com/search/?text=" + encodeURIComponent(q) },
|
||||
};
|
||||
// Built-in + user-added, as flat metadata for the pickers.
|
||||
function allEngines() {
|
||||
|
|
@ -56,8 +53,8 @@ const GATEWAY = "https://navigate.st";
|
|||
// DUAL — real ICANN TLDs we ALSO offer on BCNR. These never hijack the real
|
||||
// web: the ICANN site loads normally, and if a BCNR name also exists
|
||||
// we surface a passive "also on BCNR" switch (see navigateTab).
|
||||
const BNS_NATIVE_TLDS = new Set(["bch", "p2p", "bit", "nav", "x"]);
|
||||
const BNS_DUAL_TLDS = new Set(["de", "dev", "ltd"]);
|
||||
const BNS_NATIVE_TLDS = new Set(["bch", "p2p", "bit", "nav", "x", "sia", "dgb", "nt"]);
|
||||
const BNS_DUAL_TLDS = new Set(["de", "dev", "ltd", "ru"]);
|
||||
const REGISTRY = "BCNR"; // user-facing registry label (Bitcoin Cash Name Registry)
|
||||
const tldOf = (host) => {
|
||||
const h = String(host).toLowerCase().replace(/\.$/, "");
|
||||
|
|
@ -85,7 +82,7 @@ function looksLikeUrl(q) {
|
|||
|
||||
// ---- persistent user settings (userData/settings.json) ----
|
||||
const SETTINGS_DEFAULTS = {
|
||||
webrtcProtect: true, // reduce WebRTC IP leaks even without Tor (Tor strengthens it)
|
||||
webrtcMode: "public_only", // WebRTC IP policy: default | public_only | public_private | disable_udp
|
||||
blockCamera: true, // deny camera by default (also hides camera labels from fingerprinting)
|
||||
blockMicrophone: true, // deny microphone by default (also hides mic labels)
|
||||
restoreSession: true, // reopen last session's tabs on launch
|
||||
|
|
@ -96,7 +93,13 @@ const SETTINGS_DEFAULTS = {
|
|||
locationMode: "hide", locationLat: "40.7128", locationLon: "-74.0060", // coords for manual (default: deny)
|
||||
searchEngine: "duckduckgo",// default search engine (built-in id or a custom id)
|
||||
customEngines: [], // user-added: [{ id, name, url-with-%s }]
|
||||
theme: "dark", // dark | light | system — drives prefers-color-scheme in all views
|
||||
};
|
||||
// Applying the theme via nativeTheme.themeSource makes prefers-color-scheme update
|
||||
// in every renderer (chrome, settings, popover, page views) with no per-view IPC.
|
||||
function applyTheme() {
|
||||
try { nativeTheme.themeSource = ["dark", "light", "system"].includes(settings.theme) ? settings.theme : "dark"; } catch {}
|
||||
}
|
||||
// Auto decoys used by "spoof" mode (plausible but not the user's real values).
|
||||
const SPOOF = { tz: "America/New_York", lang: "en-US", lat: 40.7128, lon: -74.0060 };
|
||||
let settings = { ...SETTINGS_DEFAULTS };
|
||||
|
|
@ -115,10 +118,18 @@ function loadBookmarks() { try { if (fs.existsSync(bookmarksFile())) bookmarks =
|
|||
function saveBookmarks() { try { fs.writeFileSync(bookmarksFile(), JSON.stringify(bookmarks, null, 2)); } catch (e) { console.error("bookmarks save failed:", e.message); } }
|
||||
function emitBookmarks() { try { chrome?.webContents.send("bookmarks", bookmarks); } catch {} }
|
||||
function emitEngines() { try { chrome?.webContents.send("engines", { engines: allEngines(), current: settings.searchEngine }); } catch {} }
|
||||
// WebRTC IP-handling policy: protect by default (independent of Tor), strongest under Tor.
|
||||
// WebRTC IP-handling policy — the same control the "WebRTC Network Limiter"
|
||||
// Chrome extension provides, done natively (that extension's chrome.privacy API
|
||||
// isn't available in Electron, and this is more reliable). Tor forces the strongest.
|
||||
const WEBRTC_POLICIES = {
|
||||
default: "default", // allow all (may expose local IP)
|
||||
public_only: "default_public_interface_only", // only the default public interface
|
||||
public_private: "default_public_and_private_interfaces",
|
||||
disable_udp: "disable_non_proxied_udp", // strongest (only proxied UDP)
|
||||
};
|
||||
function webrtcPolicy() {
|
||||
if (!settings.webrtcProtect) return "default";
|
||||
return torState === "on" ? "disable_non_proxied_udp" : "default_public_interface_only";
|
||||
if (torState === "on") return "disable_non_proxied_udp";
|
||||
return WEBRTC_POLICIES[settings.webrtcMode] || "default_public_interface_only";
|
||||
}
|
||||
// ---- anti-fingerprinting: timezone + language + location ----
|
||||
// Show/Hide/Spoof/Manual. Effective override, or null = "show" (real value).
|
||||
|
|
@ -695,7 +706,8 @@ ipcMain.handle("bookmark-remove", (_e, url) => {
|
|||
ipcMain.handle("settings-get", () => settings);
|
||||
ipcMain.handle("settings-set", (_e, key, val) => {
|
||||
if (key in SETTINGS_DEFAULTS) { settings[key] = val; saveSettings(); }
|
||||
if (key === "webrtcProtect") applyWebRTCPolicy();
|
||||
if (key === "webrtcMode") applyWebRTCPolicy();
|
||||
if (key === "theme") applyTheme();
|
||||
if (key === "backgroundThrottle") applyThrottle();
|
||||
if (["timezoneMode", "timezoneValue", "languageMode", "languageValue",
|
||||
"locationMode", "locationLat", "locationLon"].includes(key)) { applyFingerprintAll(); applyAcceptLanguage(); }
|
||||
|
|
@ -719,6 +731,7 @@ if (!process.env.THESEUS_NO_AUTOSTART) {
|
|||
app.whenReady().then(() => {
|
||||
Menu.setApplicationMenu(null); // drop the native File/Edit/View/Help menu bar
|
||||
loadSettings();
|
||||
applyTheme();
|
||||
loadBookmarks();
|
||||
applyPermissions();
|
||||
applyAcceptLanguage();
|
||||
|
|
|
|||
11
popover.html
11
popover.html
|
|
@ -1,7 +1,7 @@
|
|||
<!doctype html>
|
||||
<html><head><meta charset="utf-8">
|
||||
<style>
|
||||
:root { color-scheme: dark; font-family: system-ui, sans-serif; }
|
||||
:root { color-scheme: light dark; font-family: system-ui, sans-serif; }
|
||||
html, body { margin: 0; background: transparent; }
|
||||
.card { display: flex; flex-direction: column; background: #1c222c;
|
||||
border: 1px solid #ffffff26; border-radius: 8px; box-shadow: 0 12px 34px #000c; overflow: hidden; color: #e7eaf1; }
|
||||
|
|
@ -21,6 +21,15 @@
|
|||
.details { border-top: 1px solid #ffffff12; padding: 11px 16px 13px; display: grid; grid-template-columns: auto 1fr; gap: 7px 14px; font-size: 12px; }
|
||||
.details .k { color: #7f8aa0; } .details .v { color: #e7eaf1; }
|
||||
.details .v code { font-family: ui-monospace, monospace; color: #bfeae4; word-break: break-all; }
|
||||
/* light theme (placed last so it wins over the dark base rules) */
|
||||
@media (prefers-color-scheme: light) {
|
||||
.card { background: #ffffff; border-color: rgba(0,0,0,.15); color: #1a1f28; }
|
||||
.close { color: #697280; } .close:hover { background: rgba(0,0,0,.06); }
|
||||
.hero .host { color: #3c4453; } .hero .sub { color: #697280; }
|
||||
.details { border-top-color: rgba(0,0,0,.10); } .details .k { color: #7b8494; } .details .v { color: #1a1f28; }
|
||||
.details .v code { color: #0a7d73; }
|
||||
.hero.neutral .ico svg { stroke: #697280; } .hero.neutral .ico .bdy { fill: #697280; }
|
||||
}
|
||||
</style></head>
|
||||
<body>
|
||||
<div class="card">
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<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);
|
||||
:root{ color-scheme: light 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}
|
||||
|
|
@ -45,6 +45,16 @@
|
|||
.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}
|
||||
/* light theme (placed last so these win over the dark base rules) */
|
||||
@media (prefers-color-scheme: light){
|
||||
:root{ --bg:#eef0f4; --panel:#ffffff; --line:rgba(0,0,0,.11); --ink:#1a1f28; --mut:#4a5262; --dim:#7b8494; }
|
||||
.side{ background:#f6f8fb; }
|
||||
.side a:hover{ background:rgba(0,0,0,.05); }
|
||||
select, .ctl input, .addeng input{ background:#f1f3f7; }
|
||||
.track{ background:#c8cfdb; }
|
||||
.knob{ background:#8a93a2; }
|
||||
code, .ceng{ background:#eef1f6; }
|
||||
}
|
||||
</style></head>
|
||||
<body>
|
||||
<div class="app">
|
||||
|
|
@ -60,6 +70,10 @@
|
|||
<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">Appearance</div><div class="d">Light, dark, or follow your system theme.</div></div>
|
||||
<div class="ctl"><select id="theme"><option value="dark">Dark</option><option value="light">Light</option><option value="system">System</option></select></div>
|
||||
</div>
|
||||
<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>
|
||||
|
|
@ -93,8 +107,14 @@
|
|||
<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 class="txt"><div class="t">WebRTC IP policy</div>
|
||||
<div class="d">Controls which IP addresses WebRTC may reveal — the same thing the "WebRTC Network Limiter" extension does, built in. <b>Public interface only</b> hides your local IP; <b>Disable non-proxied UDP</b> is strongest. Tor forces the strongest automatically.</div></div>
|
||||
<div class="ctl"><select id="webrtcMode">
|
||||
<option value="default">Allow all (default)</option>
|
||||
<option value="public_only">Public interface only</option>
|
||||
<option value="public_private">Public + private interfaces</option>
|
||||
<option value="disable_udp">Disable non-proxied UDP</option>
|
||||
</select></div>
|
||||
</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>
|
||||
|
|
@ -145,7 +165,7 @@
|
|||
for (const s of sections) document.getElementById(s).hidden = (s !== a.dataset.sec);
|
||||
});
|
||||
|
||||
const TOGGLES = ["restoreSession", "backgroundThrottle", "webrtcProtect", "blockCamera", "blockMicrophone"];
|
||||
const TOGGLES = ["restoreSession", "backgroundThrottle", "blockCamera", "blockMicrophone"];
|
||||
C.get().then((s) => {
|
||||
for (const k of TOGGLES) {
|
||||
const el = document.getElementById(k); if (!el) continue;
|
||||
|
|
@ -166,6 +186,14 @@
|
|||
list.querySelectorAll(".cx").forEach((b) => b.onclick = () => C.removeEngine(b.dataset.id).then(renderEngines));
|
||||
}
|
||||
sel.onchange = () => C.set("searchEngine", sel.value);
|
||||
// appearance (theme)
|
||||
const th = document.getElementById("theme");
|
||||
th.value = s.theme || "dark";
|
||||
th.onchange = () => C.set("theme", th.value);
|
||||
// WebRTC IP policy
|
||||
const wm = document.getElementById("webrtcMode");
|
||||
wm.value = s.webrtcMode || "public_only";
|
||||
wm.onchange = () => C.set("webrtcMode", wm.value);
|
||||
document.getElementById("engAdd").onclick = () => {
|
||||
const name = document.getElementById("engName").value.trim();
|
||||
const url = document.getElementById("engUrl").value.trim();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue