From 2757cd8c640303a901d6387a4645374f3bcefd72 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Thu, 30 Jul 2026 22:00:01 +0200 Subject: [PATCH] Theseus: light/dark theme, redesigned search box, trimmed engines, WebRTC modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 - + @@ -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) => ``; 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; diff --git a/home.html b/home.html index 933b7f8..d136493 100644 --- a/home.html +++ b/home.html @@ -2,7 +2,7 @@ Theseus
diff --git a/main.js b/main.js index 510563f..3cf8510 100644 --- a/main.js +++ b/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(); diff --git a/popover.html b/popover.html index 849790d..8448522 100644 --- a/popover.html +++ b/popover.html @@ -1,7 +1,7 @@
diff --git a/settings.html b/settings.html index 7411b4d..f518f1f 100644 --- a/settings.html +++ b/settings.html @@ -2,7 +2,7 @@ Theseus β€” Settings
@@ -60,6 +70,10 @@

General

Changes apply immediately and are saved for next time.

+
+
Appearance
Light, dark, or follow your system theme.
+
+
Search engine
Used when you type a search into the address bar.
@@ -93,8 +107,14 @@

Privacy

Reduce tracking and lock down device access.

-
WebRTC leak protection
Stops sites from discovering your real IP through WebRTC. Strongest with Tor on.
- +
WebRTC IP policy
+
Controls which IP addresses WebRTC may reveal β€” the same thing the "WebRTC Network Limiter" extension does, built in. Public interface only hides your local IP; Disable non-proxied UDP is strongest. Tor forces the strongest automatically.
+
Block camera
Deny camera by default β€” also hides its name from fingerprinting.
@@ -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();