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
-