From b2cfcd6dbf450695dbec070607b89abbd689770c Mon Sep 17 00:00:00 2001 From: Local Dev Date: Tue, 22 Sep 2026 21:25:33 +0200 Subject: [PATCH] fix(theseus): extension updates re-check, announce and install in place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates published after launch never showed: the add-on channel was checked once, 30 s after boot, and staged copies only applied on the next launch with nothing telling the user. On 2026-09-22 Aegis 0.8.3 and VPN 0.1.3 landed minutes after the app's only check and stayed invisible through manual scans made earlier and a restart made before they were published. Now: the check repeats every 4 hours; every check (boot, timer, manual, add-on-driven) reports what is staged to the chrome, which shows a chip for staged extensions; clicking it, or the "Update to vX" button that appears on the extension's row and detail in Settings, promotes the staged folder over the installed one and rebuilds the add-on host, so the new version runs without a restart. Plug-ins (Aegis) are excluded from the chip and the hot swap — a wallet updates from its own panel and applies on the next launch. Also from the same review: the new-tab button follows the last tab and parks after the scroll arrow only when the strip overflows; Tor sits left of the Aegis chip; plug-ins no longer appear in Settings › Extensions (they have Plug-ins); the extension detail view has a labelled Back button, a close button and a Check now button; the promotion helper returns what it promoted and accepts a filter. --- addon-updater.js | 16 +++++++---- chrome.html | 58 +++++++++++++++++++++++++++++++++++---- main.js | 67 +++++++++++++++++++++++++++++++++------------ preload.js | 7 +++++ settings-preload.js | 2 ++ settings.html | 45 ++++++++++++++++++++++++------ 6 files changed, 159 insertions(+), 36 deletions(-) diff --git a/addon-updater.js b/addon-updater.js index 873341c..8b0c085 100644 --- a/addon-updater.js +++ b/addon-updater.js @@ -53,12 +53,16 @@ function readAddonJson(dir) { // Called BEFORE seedBundledAddons at boot. Any staged folder whose version // beats the currently installed copy is promoted; older or matching stages // are cleaned up so they don't loop on every boot. -function promoteStagedUpdates({ addonsDir, backupsDir, stagedDir, logger }) { +// `only(manifest)` limits the promotion to some staged entries (the live +// "Install update" path applies extensions in place but leaves plug-ins for +// the next launch). Returns the list of { id, version } actually promoted. +function promoteStagedUpdates({ addonsDir, backupsDir, stagedDir, logger, only }) { const log = logger || (() => {}); - if (!fs.existsSync(stagedDir)) return; + const promoted = []; + if (!fs.existsSync(stagedDir)) return promoted; let entries; try { entries = fs.readdirSync(stagedDir, { withFileTypes: true }); } - catch (e) { log("promote: readdir failed:", e?.message); return; } + catch (e) { log("promote: readdir failed:", e?.message); return promoted; } for (const de of entries) { if (!de.isDirectory()) continue; const from = path.join(stagedDir, de.name); @@ -68,6 +72,7 @@ function promoteStagedUpdates({ addonsDir, backupsDir, stagedDir, logger }) { try { fs.rmSync(from, { recursive: true, force: true }); } catch {} continue; } + if (typeof only === "function" && !only(manifest)) continue; const target = path.join(addonsDir, manifest.id); const currentVer = readAddonJson(target)?.version; if (currentVer && cmpVer(currentVer, manifest.version) >= 0) { @@ -82,13 +87,14 @@ function promoteStagedUpdates({ addonsDir, backupsDir, stagedDir, logger }) { try { fs.renameSync(target, backup); } catch (e) { log(`promote: backup ${manifest.id} failed, keeping staged for next boot:`, e?.message); continue; } } - try { fs.renameSync(from, target); log(`promote: ${manifest.id} -> ${manifest.version}`); } + try { fs.renameSync(from, target); log(`promote: ${manifest.id} -> ${manifest.version}`); promoted.push({ id: manifest.id, version: manifest.version }); } catch (e) { // Cross-drive rename can fail on Windows; copy then rm. - try { fs.cpSync(from, target, { recursive: true }); fs.rmSync(from, { recursive: true, force: true }); log(`promote: ${manifest.id} -> ${manifest.version} (via copy)`); } + try { fs.cpSync(from, target, { recursive: true }); fs.rmSync(from, { recursive: true, force: true }); promoted.push({ id: manifest.id, version: manifest.version }); log(`promote: ${manifest.id} -> ${manifest.version} (via copy)`); } catch (ee) { log(`promote: move ${manifest.id} failed:`, ee.message); } } } + return promoted; } // ---------- HTTP helpers -------------------------------------------------- diff --git a/chrome.html b/chrome.html index 8c8ce46..80179fd 100644 --- a/chrome.html +++ b/chrome.html @@ -55,6 +55,7 @@ matter how many tabs are open; the row scrolls (wheel) once tabs hit their minimum width, like Chrome. */ .tabs > .newtab { flex: none; margin-left: 2px; } + .tabrow > .newtab { flex: none; align-self: center; margin-left: 2px; } /* Firefox-style overflow: once tabs hit their minimum width the row scrolls, the earliest tabs slide out on the left, and an arrow at each end moves the row (they only appear when there is something to scroll). */ @@ -492,10 +493,11 @@ +
- +