// Theseus Navigator β€” Electron main process. // Native .bch via a custom `bns://` protocol: resolves names with the shared // portable resolver (Argus/resolver-web.js) and serves content itself (on-chain // 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, nativeTheme } = require("electron"); const path = require("path"); const http = require("http"); const https = require("https"); const { spawn } = require("child_process"); const fs = require("fs"); const WebSocket = require("ws"); // Packaged builds ship the resolver and tor/ as unpacked resources (they can't // run from inside app.asar); dev runs read them from the repo. const RES_DIR = app.isPackaged ? process.resourcesPath : __dirname; // Bundled as .mjs so it loads as ES module in the packaged app (no package.json // sits next to it in resources/, so a bare .js would be treated as CommonJS and // fail on `export`). Dev reads the engine copy directly (Argus is type:module). const RESOLVER = app.isPackaged ? path.join(RES_DIR, "resolver-web.mjs") : path.join(__dirname, "..", "Argus", "src", "lib", "resolver-web.js"); // Built-in search engines. Users can also add their own (settings.customEngines, // each { id, name, url } where the url contains "%s" for the query). // Catalog of built-in engines (users pick which to enable + can add their own). // fav = the domain to load a real favicon from; sym = emoji fallback. const SEARCH_ENGINES = { duckduckgo: { name: "DuckDuckGo", sym: "πŸ¦†", fav: "duckduckgo.com", url: (q) => "https://duckduckgo.com/?q=" + encodeURIComponent(q) }, google: { name: "Google", sym: "πŸ”΅", fav: "www.google.com", url: (q) => "https://www.google.com/search?q=" + encodeURIComponent(q) }, brave: { name: "Brave", sym: "🦁", fav: "search.brave.com", url: (q) => "https://search.brave.com/search?q=" + encodeURIComponent(q) }, bing: { name: "Bing", sym: "πŸ”Ž", fav: "www.bing.com", url: (q) => "https://www.bing.com/search?q=" + encodeURIComponent(q) }, startpage: { name: "Startpage", sym: "πŸ›‘οΈ", fav: "www.startpage.com", url: (q) => "https://www.startpage.com/sp/search?query=" + encodeURIComponent(q) }, yandex: { name: "Yandex", sym: "πŸ”΄", fav: "yandex.com", url: (q) => "https://yandex.com/search/?text=" + encodeURIComponent(q) }, ecosia: { name: "Ecosia", sym: "🌱", fav: "www.ecosia.org", url: (q) => "https://www.ecosia.org/search?q=" + encodeURIComponent(q) }, mojeek: { name: "Mojeek", sym: "🧭", fav: "www.mojeek.com", url: (q) => "https://www.mojeek.com/search?q=" + encodeURIComponent(q) }, searxng: { name: "SearXNG", sym: "🧩", fav: "searx.be", url: (q) => "https://searx.be/search?q=" + encodeURIComponent(q) }, wikipedia: { name: "Wikipedia", sym: "πŸ“–", fav: "en.wikipedia.org", url: (q) => "https://en.wikipedia.org/wiki/Special:Search?search=" + encodeURIComponent(q) }, perplexity: { name: "Perplexity", sym: "🧠", fav: "www.perplexity.ai", url: (q) => "https://www.perplexity.ai/search?q=" + encodeURIComponent(q) }, // AI / LLM answer engines (accept a URL query and answer it directly). chatgpt: { name: "ChatGPT", sym: "πŸ€–", fav: "chatgpt.com", url: (q) => "https://chatgpt.com/?q=" + encodeURIComponent(q) }, claude: { name: "Claude", sym: "✳️", fav: "claude.ai", url: (q) => "https://claude.ai/new?q=" + encodeURIComponent(q) }, phind: { name: "Phind", sym: "πŸ§‘β€πŸ’»", fav: "www.phind.com", url: (q) => "https://www.phind.com/search?q=" + encodeURIComponent(q) }, you: { name: "You.com", sym: "🟣", fav: "you.com", url: (q) => "https://you.com/search?q=" + encodeURIComponent(q) + "&tbm=youchat" }, }; // Engines enabled by default (shown in the toolbar dropdown). The rest are in the // catalog and can be turned on from Settings. Custom + detected engines are always on. const DEFAULT_ENABLED = ["duckduckgo", "google", "brave", "bing", "startpage"]; // DuckDuckGo's icon service reliably returns a favicon for ANY domain from one // privacy-respecting host β€” far more robust than guessing /favicon.ico per site. const faviconUrl = (domain) => (domain ? `https://icons.duckduckgo.com/ip3/${domain}.ico` : null); function customFavicon(url) { try { return faviconUrl(new URL(String(url).replace("%s", "x")).hostname); } catch { return null; } } function isEnabled(id) { return (settings.enabledEngines || DEFAULT_ENABLED).includes(id); } // Built-in + user-added, as flat metadata for the pickers. Built-in obey // enabledEngines; custom engines are always enabled. function allEngines() { const list = Object.entries(SEARCH_ENGINES).map(([id, e]) => ({ id, name: e.name, sym: e.sym, favicon: faviconUrl(e.fav), builtin: true, enabled: isEnabled(id) })); for (const c of settings.customEngines || []) list.push({ id: c.id, name: c.name, sym: c.sym || "πŸ”", favicon: customFavicon(c.url), builtin: false, enabled: true }); // Apply the user's custom order; ids not in engineOrder keep their natural order (stable sort). const order = settings.engineOrder || []; return list.slice().sort((a, b) => { const ia = order.indexOf(a.id), ib = order.indexOf(b.id); if (ia === -1 && ib === -1) return 0; if (ia === -1) return 1; if (ib === -1) return -1; return ia - ib; }); } function enabledEnginesList() { return allEngines().filter((e) => e.enabled); } function engineUrl(id, q) { if (SEARCH_ENGINES[id]) return SEARCH_ENGINES[id].url(q); const c = (settings.customEngines || []).find((e) => e.id === id); return c ? c.url.replace(/%s/g, encodeURIComponent(q)) : SEARCH_ENGINES.duckduckgo.url(q); } const SEARCH = (q) => engineUrl(settings.searchEngine, q); // Public content relay (secret-free): serves s3/ip/h/u without shipping keys. const GATEWAY = "https://navigate.st"; // ---- BNS name detection (multi-TLD) -------------------------------------- // Theseus is a BNS-native browser: BCNR is the priority registry for EVERY // dotted host, regardless of TLD. The engine (resolver-web.js) resolves any //