Ship Theseus 0.2.4 7fd323a8 (home cards refresh + engine-picker sync + proxy auth support)
Setup 7fd323a87bd32b780e147de18e16ecd82f89960bbd8e9a619c5d25d374597cd2
Portable 3166e64cf56badd7b26c4c793dc79bbed6f9d6c48fbd467d97f845385b91b1dd
Home cards: DEFAULT_HOME_CARDS replaced with the .x sibling grid the
user asked for -- hello.bch, siatest.bch (the "types of BCDN" pair),
then silentmode.x / theseus.x / sirius.x / hephaestus.x /
prometheus.x / helios.x / hermes.x. Existing installs with a saved
home-cards.json keep their edits (defaults only seed fresh profiles).
Search engine picker sync: user reported the toolbar dropdown listed
engines as active that Settings > Search showed differently. Root
cause: settings.searchEngine could be pointing at an id not in the
currently-enabled set (stale settings.json after DEFAULT_ENABLED
changes across versions). loadSettings now normalizes on boot -- if
searchEngine isn't enabled, fall back to enabled[0]; and
installedEngines gets unioned with enabledEngines so the two lists
can't disagree in ways that make toolbar and Settings render
different rows.
Proxy auth support in the framework: setSessionProxy accepts
`{ proxyRules, auth: { username, password } }` or an inline
`socks5://user:pass@host:port` URL. When creds are present, the
handler strips them from the URL, installs a session#login listener
on the default session that answers with them, then calls setProxy.
Chromium's SOCKS5 client doesn't consume proxy auth (known Chromium
limitation), but HTTP proxies work; SOCKS-based extensions need to
gate by IP allowlist at their server. Log line masks the password.
Update chip note: the "download opens in a different browser" was
0.2.0-era behavior. 0.2.1 rewired it to session.downloadURL. Anyone
still seeing it needs to install 0.2.1+ once.
Deployed: scp + sia-upload both trees, verified HEAD 200 + manifest
0.2.4 live.
This commit is contained in:
parent
49dde73c6b
commit
454255e963
2 changed files with 64 additions and 11 deletions
73
main.js
73
main.js
|
|
@ -237,6 +237,22 @@ function loadSettings() {
|
|||
// uses whatever the user left it at last time.
|
||||
const w = Number(settings.sidebarWidth) || SIDEBAR_W_DEFAULT;
|
||||
sidebarW = Math.max(SIDEBAR_W_MIN, Math.min(SIDEBAR_W_MAX, w));
|
||||
// Normalize the search-engine state so the toolbar picker and Settings tab
|
||||
// can never disagree. Two invariants:
|
||||
// 1. Every enabledEngines id must also be in installedEngines. If a user
|
||||
// manually edited settings.json (or an upgrade left the two out of
|
||||
// sync), we add the missing installed rows now.
|
||||
// 2. settings.searchEngine must be an enabled engine. If the default from
|
||||
// SETTINGS_DEFAULTS points at an id the user has disabled, fall back
|
||||
// to the first currently-enabled engine.
|
||||
try {
|
||||
const enabled = Array.isArray(settings.enabledEngines) ? settings.enabledEngines : DEFAULT_ENABLED.slice();
|
||||
const installed = Array.isArray(settings.installedEngines) ? settings.installedEngines : DEFAULT_ENABLED.slice();
|
||||
settings.installedEngines = [...new Set([...installed, ...enabled])];
|
||||
if (!enabled.includes(settings.searchEngine)) {
|
||||
settings.searchEngine = enabled[0] || DEFAULT_ENABLED[0];
|
||||
}
|
||||
} catch (e) { console.warn("engine normalize:", e?.message); }
|
||||
}
|
||||
function saveSettings() {
|
||||
try { fs.writeFileSync(settingsFile(), JSON.stringify(settings, null, 2)); } catch (e) { console.error("settings save failed:", e.message); }
|
||||
|
|
@ -310,14 +326,15 @@ function emitUpdateAvailable() {
|
|||
// can add/edit/remove via the page's edit mode. First-run seed = the classic
|
||||
// Silent Mode showcase (hello.bch, theseus.bch, silentmode.bch, etc.).
|
||||
const DEFAULT_HOME_CARDS = [
|
||||
{ title: "hello.bch", url: "https://hello.bch/", sub: "On the blockchain itself.", badge: "on-chain" },
|
||||
{ title: "theseus.bch", url: "https://theseus.bch/", sub: "This browser's own name.", badge: "on-chain" },
|
||||
{ title: "silentmode.bch", url: "https://silentmode.bch/", sub: "The division behind this stack.", badge: "on-chain" },
|
||||
{ title: "argo.bch", url: "https://argo.bch/", sub: "Host network, crewed by Argonauts.", badge: "on-chain" },
|
||||
{ title: "coinspectrum.deviant.bch", url: "https://coinspectrum.deviant.bch/", sub: "A full site from Sia.", badge: "Sia" },
|
||||
{ title: "coinspectrum.bch", url: "https://coinspectrum.bch/", sub: "Same site, direct server.", badge: "server" },
|
||||
{ title: "faucet.deviant.bch", url: "https://faucet.deviant.bch/", sub: "Testnet faucet hub, on Sia.", badge: "Sia" },
|
||||
{ title: "siatest.bch", url: "https://siatest.bch/", sub: "A page with no server.", badge: "Sia" },
|
||||
{ title: "hello.bch", url: "https://hello.bch/", sub: "A small page on the blockchain itself.", badge: "on-chain" },
|
||||
{ title: "siatest.bch", url: "https://siatest.bch/", sub: "A page with no server, backed by Sia.", badge: "Sia" },
|
||||
{ title: "silentmode.x", url: "https://silentmode.x/", sub: "The project's .x address.", badge: ".x" },
|
||||
{ title: "theseus.x", url: "https://theseus.x/", sub: "This browser's own address.", badge: ".x" },
|
||||
{ title: "sirius.x", url: "https://sirius.x/", sub: "Register and manage BCDN names.", badge: ".x" },
|
||||
{ title: "hephaestus.x", url: "https://hephaestus.x/", sub: "Code host — Silent Mode's Forgejo.", badge: ".x" },
|
||||
{ title: "prometheus.x", url: "https://prometheus.x/", sub: "Storage-as-a-product.", badge: ".x" },
|
||||
{ title: "helios.x", url: "https://helios.x/", sub: "Discovery + navigation.", badge: ".x" },
|
||||
{ title: "hermes.x", url: "https://hermes.x/", sub: "Messaging — NIP-17 over Nostr.", badge: ".x" },
|
||||
];
|
||||
const homeCardsFile = () => path.join(app.getPath("userData"), "home-cards.json");
|
||||
function loadHomeCards() {
|
||||
|
|
@ -1156,6 +1173,11 @@ let sidebarW = SIDEBAR_W_DEFAULT;
|
|||
// The add-on host is the single point of truth for what's installed and
|
||||
// active. Populated by initAddons() at app-ready time.
|
||||
let addonHost = null;
|
||||
// One-shot proxy-login handler installed by setSessionProxy when the
|
||||
// extension provided credentials. Removed and re-installed on every
|
||||
// setSessionProxy call so the current credentials always match the
|
||||
// current proxy.
|
||||
let proxyLoginHandler = null;
|
||||
const { AddonHost } = require("./addons-host.js");
|
||||
function addonsUserDir() { return path.join(app.getPath("userData"), "addons"); }
|
||||
function addonsDataDir() { return path.join(app.getPath("userData"), "addons-data"); }
|
||||
|
|
@ -1189,15 +1211,46 @@ function initAddons() {
|
|||
// Session-proxy capability. Add-ons that declare "session-proxy" in
|
||||
// their manifest can call api.setSessionProxy(rules) to swap
|
||||
// Chromium's outbound network path. Same primitive Tor uses.
|
||||
//
|
||||
// Authentication: Chromium's setProxy does NOT parse credentials from
|
||||
// `socks5://user:pass@host:port` — it rejects it as ERR_NO_SUPPORTED_
|
||||
// PROXIES. Add-ons pass auth separately either as an object:
|
||||
// api.setSessionProxy({ proxyRules, auth: { username, password } })
|
||||
// or inline URL — this hook strips the user:pass@ and installs a
|
||||
// one-shot login handler on the default session that answers with
|
||||
// the extracted credentials next time Chromium asks the proxy for auth.
|
||||
setSessionProxy: async (rules, addonId) => {
|
||||
const ses = session.defaultSession;
|
||||
// Always clear any prior proxy-login handler before swapping.
|
||||
if (proxyLoginHandler) { ses.off("login", proxyLoginHandler); proxyLoginHandler = null; }
|
||||
if (rules == null || rules === "") {
|
||||
console.log(`[addons] [${addonId}] clearing session proxy`);
|
||||
try { await ses.setProxy({ proxyRules: "" }); } catch (e) { console.warn("proxy clear failed:", e?.message); }
|
||||
return;
|
||||
}
|
||||
const opts = typeof rules === "string" ? { proxyRules: rules } : rules;
|
||||
console.log(`[addons] [${addonId}] setting session proxy:`, opts.proxyRules || JSON.stringify(opts));
|
||||
let opts;
|
||||
let auth = null;
|
||||
if (typeof rules === "string") {
|
||||
// Parse inline creds: "scheme://user:pass@host:port".
|
||||
const m = rules.match(/^([a-z0-9+.-]+:\/\/)([^:@\/]+):([^@\/]+)@(.+)$/i);
|
||||
if (m) { opts = { proxyRules: m[1] + m[4] }; auth = { username: m[2], password: decodeURIComponent(m[3]) }; }
|
||||
else opts = { proxyRules: rules };
|
||||
} else {
|
||||
opts = { proxyRules: rules.proxyRules };
|
||||
if (rules.auth && rules.auth.username != null) auth = { username: String(rules.auth.username), password: String(rules.auth.password || "") };
|
||||
}
|
||||
const publicRules = opts.proxyRules; // never log the password
|
||||
console.log(`[addons] [${addonId}] setting session proxy:`, publicRules, auth ? "(auth pending)" : "");
|
||||
if (auth) {
|
||||
// Chromium fires session#login with `authenticationResponseDetails.isProxy === true`
|
||||
// when the proxy asks for creds. Answer once per session.
|
||||
proxyLoginHandler = (event, _details, authInfo, callback) => {
|
||||
if (!authInfo || !authInfo.isProxy) return;
|
||||
event.preventDefault();
|
||||
callback(auth.username, auth.password);
|
||||
};
|
||||
ses.on("login", proxyLoginHandler);
|
||||
}
|
||||
try { await ses.setProxy(opts); } catch (e) { console.warn("proxy set failed:", e?.message); }
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "theseus-navigator",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.",
|
||||
"author": "Silent Mode",
|
||||
"main": "main.js",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue