From 13d4ab76b32faa5f75d9a586b8d18ccafca2abdc Mon Sep 17 00:00:00 2001 From: Local Dev Date: Wed, 21 Oct 2026 21:55:10 +0300 Subject: [PATCH] Theseus 0.3.101: Grok + Gemini + ChatGPT, sidebar polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three more LLM engines land in the catalog and in DEFAULT_ENABLED, so the sparkle button's picker lists five AI engines out of the box: Perplexity, Phind, ChatGPT, Grok, Gemini. The first two answer a URL query without any login, the latter three need a free-tier account; all three accept the chat URL as the "open the home" target, which is what the sidebar button needs. A one-time offer migration adds them to any existing profile's enabledEngines (users who disable one keep it off across restarts via a new `enginesOffered` field). Sidebar strip polish. The gear button now opens Settings › Sidebar on a single click instead of popping a one-item native menu first. The "+" add button under the gear is removed — adding a web app goes through Settings › Sidebar anyway, which the gear opens. The quicklinks-add IPC is kept but repointed to Settings › Sidebar (it used to open General, which was the wrong page for the add-a-web-app flow). --- main.js | 84 ++++++++++++++++++++++++++++++++++++++++++++----- package.json | 2 +- quicklinks.html | 13 +++----- 3 files changed, 82 insertions(+), 17 deletions(-) diff --git a/main.js b/main.js index f9cf224f..97ee13c5 100644 --- a/main.js +++ b/main.js @@ -153,11 +153,19 @@ const SEARCH_ENGINES = { // default becomes stale as instances rate-limit / die (searx.be is anti-bot-locked). // Users who want SearXNG add their preferred instance via the custom URL form. wikipedia: { kind: "search", tier: "catalog", name: "Wikipedia", sym: "📖", fav: "en.wikipedia.org", url: (q) => "https://en.wikipedia.org/wiki/Special:Search?search=" + encodeURIComponent(q) }, - // AI / LLM answer engines that ANSWER the URL query without requiring a login. - // ChatGPT / Claude / You.com's youchat all bounce to sign-in before running - // ?q=, so they'd fail silently as a "search engine" — omitted deliberately. + // AI / LLM answer engines. The first group answers a URL query without any + // login, so they also work as a "search engine" in the toolbar. The second + // group needs an account (free tier) and bounces ?q= to the sign-in gate — + // they're useful as sidebar LLM targets (opens the chat home) but not as + // the toolbar default search; set one of the first group there instead. perplexity: { kind: "llm", tier: "catalog", name: "Perplexity", sym: "🧠", fav: "www.perplexity.ai", url: (q) => "https://www.perplexity.ai/search?q=" + encodeURIComponent(q) }, phind: { kind: "llm", tier: "catalog", name: "Phind", sym: "🧑‍💻", fav: "www.phind.com", url: (q) => "https://www.phind.com/search?q=" + encodeURIComponent(q) }, + // Account-gated LLMs — free tier available on all three. ChatGPT and Grok + // accept ?q= on their home URL and pre-fill the composer; Gemini's + // webapp ignores query strings so it just lands on the chat screen. + chatgpt: { kind: "llm", tier: "catalog", name: "ChatGPT", sym: "💬", fav: "chatgpt.com", url: (q) => "https://chatgpt.com/?q=" + encodeURIComponent(q) }, + grok: { kind: "llm", tier: "catalog", name: "Grok", sym: "🪐", fav: "grok.com", url: (q) => "https://grok.com/?q=" + encodeURIComponent(q) }, + gemini: { kind: "llm", tier: "catalog", name: "Gemini", sym: "✦", fav: "gemini.google.com", url: (q) => "https://gemini.google.com/app?q=" + encodeURIComponent(q) }, // ---- Extras: wider bank, discoverable via the Search filter box in Settings. // These are known-working engines that don't require sign-in on ?q= but aren't // first-class enough to sit in the primary catalog. Keep the list vetted — if @@ -174,7 +182,7 @@ const SEARCH_ENGINES = { }; // 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 = ["startpage", "duckduckgo", "google", "brave", "bing", "perplexity"]; +const DEFAULT_ENABLED = ["startpage", "duckduckgo", "google", "brave", "bing", "perplexity", "chatgpt", "grok", "gemini"]; // A frozen catalog engine (`frozen: ""`) stays visible in Settings so the user sees why it // is gone, but it is never enabled, never offered in the picker and never the default. Unfreeze by // deleting the field; the user's installed list is left alone, so the row comes back as it was. @@ -383,8 +391,14 @@ function fetchDnsRecords(name) { const seq = Number(j?.seq) || 0; // Rollback guard: a manifest with a lower seq than one already seen // for this name is stale (or replayed) — keep what we had. - if (j && j.dns && typeof j.dns === "object" && seq >= (c?.seq ?? -1)) { - value = { dns: j.dns, seq, updatedAt: j.updated_at || null, owner: j.verified_owner || null }; + if (j && typeof j === "object" && seq >= (c?.seq ?? -1)) { + // The gateway returns both the DNS block (A/AAAA/MX/TXT…) and the + // apex content records (h/u/s3/ip/tls/p) from the same signed and + // verified manifest. Both are optional. See + // Decentralized.DNS/INTEGRATION-signed-records-clients.md. + const dns = (j.dns && typeof j.dns === "object") ? j.dns : {}; + const apex = (j.apex && typeof j.apex === "object") ? j.apex : null; + value = { dns, apex, seq, updatedAt: j.updated_at || null, owner: j.verified_owner || null }; } } else if (r.status === 404 || r.status === 409) { value = null; // no manifest declared / nowhere to keep one @@ -396,6 +410,25 @@ function fetchDnsRecords(name) { dnsRecordsCache.set(name, { value: prev, at: c?.at ?? 0, seq: c?.seq ?? -1, pending }); return pending; } +// The owner-signed apex content records (h/u/s3/ip/tls/p) for a name, from +// the same /api/dns response. These OVERRIDE on-chain records per field at +// serve time — chain stays as a fallback for any field the manifest doesn't +// declare. Returns null when the name has no manifest or no apex block, so +// callers can short-circuit the merge. See serveBns for the merge call site; +// keep the rule aligned with Argus/src/gateway/public-gateway.mjs serve(). +async function apexFor(host) { + // Normalise to the registered name — a subdomain query ("www.hermes.x") + // shares its apex ("hermes.x") with every other label under it. + let name = host; + try { + const rec = entries.get(host); + if (rec?.entry?.name) name = rec.entry.name; + } catch { /* resolver not ready yet */ } + const cached = dnsRecordsCache.get(name); + if (cached && Date.now() - cached.at < DNS_RECORDS_TTL) return cached.value?.apex ?? null; + const v = await fetchDnsRecords(name).catch(() => null); + return v?.apex ?? null; +} // Kick off the fetch for a resolved entry and attach the answer when it lands. // `entry.dns` is undefined while unknown, null when the owner published no // manifest, or { dns, seq, updatedAt, owner }. @@ -683,6 +716,23 @@ function loadSettings() { settings.searchEngine = enabled.find((x) => !isFrozen(x)) || DEFAULT_ENABLED[0]; } } catch (e) { console.warn("engine normalize:", e?.message); } + // One-time LLM-engine offer (0.3.101): three new account-gated LLMs were + // added to DEFAULT_ENABLED, but an existing profile's enabledEngines is + // already set so the fresh default doesn't take effect. Add each one the + // first time this upgrade runs; `enginesOffered` remembers it so a user + // who disables one keeps it disabled across restarts. + try { + const OFFER = ["chatgpt", "grok", "gemini"]; + const offered = Array.isArray(settings.enginesOffered) ? settings.enginesOffered.slice() : []; + let changed = false; + for (const id of OFFER) { + if (offered.includes(id)) continue; + offered.push(id); changed = true; + if (!settings.enabledEngines.includes(id)) settings.enabledEngines.push(id); + if (!settings.installedEngines.includes(id)) settings.installedEngines.push(id); + } + if (changed) settings.enginesOffered = offered; + } catch (e) { console.warn("engines offer:", e?.message); } // Quick-links default set changed in 0.3.70: drop Messenger + Spotify and // reorder to Telegram, WhatsApp, X, YouTube. Users who kept the previous // untouched default (same 6 ids, same order) move to the new set; any @@ -2524,7 +2574,20 @@ async function serveBns(request) { const block = blockDecision(rec.entry, host); if (block) return new Response(blockedInlineHtml(block.flag, host), { status: 403, headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" } }); } - const r = rec.entry.records; + // Merge the owner-signed manifest's `apex` block over the chain records. + // The gateway's /api/dns/ verifies the manifest signature against the + // on-chain NFT holder and returns the apex block; we trust that for now, + // same way the DNS block below trusts it. Omitted apex fields fall back + // to chain, so a legacy name with no manifest is a no-op merge. + // + // Keep this merge rule in step with + // Argus/src/gateway/public-gateway.mjs serve() + // AriadneResolver/.../daemon/resolver.js + // AriadneResolver/mobile/.../BchFetcher.java + // All four surfaces must agree on precedence or a name serves different + // content depending on which resolver the user runs. + const manifestApex = await apexFor(host).catch(() => null); + const r = manifestApex ? { ...rec.entry.records, ...manifestApex } : rec.entry.records; // Subdomain inheritance: `checkers.game.x` collapses to `game.x` in the // registry (see resolver-web `normalizeName`). `ip` semantics apply to the // whole namespace via Host routing; `s3` semantics are exact-key per name. @@ -9753,7 +9816,12 @@ ipcMain.handle("quicklinks-open", (_e, id) => { return true; }); ipcMain.handle("quicklinks-close", () => { closeQuickPanel(); closeLeftPanel(); return true; }); -ipcMain.handle("quicklinks-add", () => { openSettingsTab("general"); return true; }); +// Opens Settings › Sidebar — the page that holds the web-apps list, the +// panel-mode radio and the reset-widths button. Called by the strip's gear +// button (0.3.101: direct open, no popover) and by the retired "+" button +// (also gone in 0.3.101, kept here as the stable IPC name for any later +// surface that wants to jump to Sidebar settings). +ipcMain.handle("quicklinks-add", () => { openSettingsTab("sidebar"); return true; }); function broadcastSettings() { sendAllChrome("settings-update", settings); for (const t of tabs) { diff --git a/package.json b/package.json index 26ef4e98..a223272c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "theseus-navigator", - "version": "0.3.100", + "version": "0.3.101", "license": "MPL-2.0", "description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.", "author": "Silent Mode", diff --git a/quicklinks.html b/quicklinks.html index 2f9107fa..2e5f07f3 100644 --- a/quicklinks.html +++ b/quicklinks.html @@ -65,20 +65,17 @@
- - +