From aea158ab052cb9de29ba0abee92479ab5030ab05 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Tue, 27 Oct 2026 20:50:40 +0200 Subject: [PATCH] Theseus 0.3.122 followups: AI engines page, Pick-AI edit button, Pithos panel, Did-you-mean on DNS failures, LLM picker UI polish, real-brand icon preference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shipped on top of the 0.3.122 Settings restructure commit. AI engines settings page. New top-level nav entry "AI engines" with its own Default picker, Enabled list, catalog, discover filter and + Add AI engine form. The Search page is now LLM-free — ENGINE_KINDS is `["search"]`, the Search catalog filters out kind="llm" entries, and the Search custom-URL form only adds kind="search" engines. add-engine IPC now accepts a kind: "llm" field (sets defaultLlmId instead of searchEngine for LLM customs). Pick-AI picker. Borders removed, corners rounded (16px), heavier shadow; "Pick AI engines…" footer row dropped. Edit button (pencil icon) added to the header; opens the new AI engines settings page via `llm-picker-settings`, which now routes to the "ai-engines" slug (openSettingsTab falls through to "search" for legacy profiles that lack the section). Icon priority flipped. allEngines() now prefers a fetched favicon (the site's own brand mark, cached per profile) over the bundled generic tile. Bundled tiles still carry the first paint while the fetch runs, and the emit after fetch swaps in the real brand. Offline profiles see the tile indefinitely — same reliability as before. Pithos sidebar panel. bundled-addons/pithos/addon.json declares a `panels` block so sidebarLeftPanels() returns Pithos's panel BEFORE the add-on's on-demand activation. Previously, the Sidebar settings' Theseus native apps card was missing Pithos: it only appeared after first launch, when the add-on's own registerSidebarPanel call ran. Hermes already did this. Did-you-mean on DNS failures. The error page's "Did you mean" row now merges BCNR warm-index matches with ICANN alternative-TLD swaps + history base-name matches. For my.mynymbox.com the suggestions are my.mynymbox.io, mynymbox.io, my.mynymbox.net, mynymbox.net — the subdomain-strip is interleaved so a mistake like typing my.mynymbox.com when the real site is client.mynymbox.io still surfaces mynymbox.io near the top. Both paths are local-only; no network, no leak of what the user is browsing. Sidebar settings tightened. "How panels open" collapsed to one row with two side-by-side option cards; "Reset panel widths" and "AI engine in the sidebar" are one-liners now. --- bundled-addons/pithos/addon.json | 9 ++ error-preload.js | 3 + error.html | 32 +++-- llm-picker.html | 29 ++--- main.js | 90 ++++++++++++-- settings.html | 199 ++++++++++++++++++++++++++++--- 6 files changed, 313 insertions(+), 49 deletions(-) diff --git a/bundled-addons/pithos/addon.json b/bundled-addons/pithos/addon.json index 04b6be20..44b860a0 100644 --- a/bundled-addons/pithos/addon.json +++ b/bundled-addons/pithos/addon.json @@ -7,6 +7,15 @@ "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjM2RkYzk3Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMWU4ZjZhIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHBhdGggZD0iTTIyIDZoMjB2NWMwIDItMiAzLTIgNSA4IDMgMTQgMTEgMTQgMjEgMCAxMi05IDIxLTIyIDIxUzEwIDQ5IDEwIDM3YzAtMTAgNi0xOCAxNC0yMSAwLTItMi0zLTItNXoiIGZpbGw9InVybCgjZykiLz48cGF0aCBkPSJNMTQgMzBoMzZNMTMgNDBoMzgiIHN0cm9rZT0iIzBiMWYxOCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii41NSIvPjwvc3ZnPgo=", "main": "index.js", "activation": "on-demand", + "panels": [ + { + "id": "main", + "title": "Pithos", + "page": "ui/index.html", + "side": "left", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjM2RkYzk3Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMWU4ZjZhIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHBhdGggZD0iTTIyIDZoMjB2NWMwIDItMiAzLTIgNSA4IDMgMTQgMTEgMTQgMjEgMCAxMi05IDIxLTIyIDIxUzEwIDQ5IDEwIDM3YzAtMTAgNi0xOCAxNC0yMSAwLTItMi0zLTItNXoiIGZpbGw9InVybCgjZykiLz48cGF0aCBkPSJNMTQgMzBoMzZNMTMgNDBoMzgiIHN0cm9rZT0iIzBiMWYxOCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii41NSIvPjwvc3ZnPgo=" + } + ], "capabilities": [ "sidebar-panel", "vault-derive", diff --git a/error-preload.js b/error-preload.js index 76f8f7a6..8f2238f3 100644 --- a/error-preload.js +++ b/error-preload.js @@ -9,4 +9,7 @@ contextBridge.exposeInMainWorld("errorpage", { registerOnSirius: (host) => ipcRenderer.invoke("error-register", host), openExternal: (url) => ipcRenderer.invoke("error-open-external", url), bnsSimilar: (host) => ipcRenderer.invoke("error-bns-similar", host), + // ICANN "did you mean?" — alternative-TLD swaps + history matches for a + // host whose DNS failed. See icannAlternatives() in main.js. + icannAlternatives: (host) => ipcRenderer.invoke("error-icann-alternatives", host), }); diff --git a/error.html b/error.html index 219fe8a2..350f0625 100644 --- a/error.html +++ b/error.html @@ -270,16 +270,30 @@ actionsBox.appendChild(b); } - // "Did you mean" — for BCNR-name failures, ask main for near-matches in - // the warm shared index (Levenshtein ≤ 2, same TLD). No network — the - // index is local, so this works offline too. Clicking a suggestion - // retries the load at that host, preserving the original path if any. - if ((kind === "name-not-registered" || kind === "name-unreachable") && host && window.errorpage?.bnsSimilar) { - window.errorpage.bnsSimilar(host).then((matches) => { - if (!Array.isArray(matches) || !matches.length) return; + // "Did you mean" — two sources, merged into one row: + // • BCNR warm shared index (Levenshtein ≤ 2, same TLD) — for a BCDN name + // typo like "games.x" → "game.x". Local index; works offline. + // • ICANN alternative-TLD swaps + history base-name matches — a DNS + // failure on my.mynymbox.com surfaces my.mynymbox.io, .net, .org, …, + // and any related host the user has visited. Local too. + // Clicking a suggestion retries at that host, preserving the original path. + if ((kind === "name-not-registered" || kind === "name-unreachable") && host) { + let origPath = "/"; + try { const u = new URL(url); origPath = u.pathname + u.search + u.hash; } catch {} + const sources = [ + window.errorpage?.bnsSimilar ? window.errorpage.bnsSimilar(host) : Promise.resolve([]), + window.errorpage?.icannAlternatives ? window.errorpage.icannAlternatives(host) : Promise.resolve([]), + ]; + Promise.all(sources).then(([bns, icann]) => { + const seen = new Set(); + const matches = []; + for (const name of [...(bns || []), ...(icann || [])]) { + if (!name || seen.has(name)) continue; + seen.add(name); matches.push(name); + if (matches.length >= 4) break; + } + if (!matches.length) return; const row = $("suggestRow"); - let origPath = "/"; - try { const u = new URL(url); origPath = u.pathname + u.search + u.hash; } catch {} for (const name of matches) { const a = el("a", null, name); a.href = "#"; diff --git a/llm-picker.html b/llm-picker.html index c1f56955..a87b64ce 100644 --- a/llm-picker.html +++ b/llm-picker.html @@ -19,16 +19,21 @@ } .pop { display: flex; flex-direction: column; - background: var(--bg); border: 1px solid var(--line); border-radius: 12px; - box-shadow: 0 12px 40px rgba(0,0,0,.5), 0 2px 8px rgba(0,0,0,.4); + background: var(--bg); border-radius: 16px; + box-shadow: 0 16px 48px rgba(0,0,0,.55), 0 4px 12px rgba(0,0,0,.4); overflow: hidden; width: 240px; } - .hdr { padding: 10px 14px 6px; font-size: 11px; font-weight: 600; + .hdr { padding: 10px 14px 4px; font-size: 11px; font-weight: 600; color: var(--mut); text-transform: uppercase; letter-spacing: .5px; - border-bottom: 1px solid var(--line); } + display: flex; align-items: center; justify-content: space-between; gap: 8px; } + .hdr .edit { background: transparent; border: 0; padding: 2px; margin: -2px; + color: var(--mut); cursor: pointer; border-radius: 6px; + display: inline-grid; place-items: center; } + .hdr .edit:hover { color: var(--ink); background: var(--card-hi); } + .hdr .edit svg { width: 14px; height: 14px; display: block; } .list { display: flex; flex-direction: column; padding: 4px; } .row { display: flex; align-items: center; gap: 10px; - padding: 8px 10px; border-radius: 8px; cursor: pointer; position: relative; + padding: 8px 10px; border-radius: 10px; cursor: pointer; position: relative; transition: background .08s; } .row:hover { background: var(--card-hi); } .row.cur { background: var(--acid-tint); } @@ -41,7 +46,7 @@ .name { flex: 1; font-weight: 600; font-size: 13px; color: var(--ink); } .check { flex: none; width: 14px; height: 14px; color: var(--acid); opacity: 0; } .row.cur .check { opacity: 1; } - .footer { border-top: 1px solid var(--line); padding: 4px; } + .footer { padding: 4px; } .footer .row { color: var(--mut); font-weight: 500; } .footer .ico { background: transparent; } .footer .ico svg { width: 18px; height: 18px; color: var(--mut); } @@ -50,14 +55,8 @@
-
AI engine
+
AI engine
-