Theseus: an extension update applied in place reloads its open panels
Applying an extension update without a restart swapped the files and restarted the add-on, but a panel or tab that was already open kept running the previous version's page, so the extension looked unchanged until Theseus restarted (Pithos showed its old layout this way). The add-on's open left panel, sidebar panel and tabs now reload after the swap. Extensions can also apply their own staged update with api.applySelfUpdate(), so their own update button needs no restart; plug-ins such as wallets still update on the next launch.
This commit is contained in:
parent
f3cd63134a
commit
99d7684590
2 changed files with 48 additions and 5 deletions
|
|
@ -282,7 +282,7 @@ function validateManifest(raw, folderName) {
|
|||
// Loader singleton. `discoverAndActivate(opts)` returns a snapshot the rest
|
||||
// of the app queries via `getActive()` / `getInstalled()`.
|
||||
class AddonHost {
|
||||
constructor({ addonsDir, dataDir, isDisabled, activationFor, onActivated, logger, setSessionProxy, vaultDerive, vaultImports, approvalModal, emitToPanel, hostRequire, hostImport, openTab, openAddonTab, openSettings, captureTab, saveCapture, scanTabForUris, checkAndStageUpdates, restartApp, revealSidebar, requestFilter, tabs }) {
|
||||
constructor({ addonsDir, dataDir, isDisabled, activationFor, onActivated, logger, setSessionProxy, vaultDerive, vaultImports, approvalModal, emitToPanel, hostRequire, hostImport, openTab, openAddonTab, openSettings, captureTab, saveCapture, scanTabForUris, checkAndStageUpdates, restartApp, applySelfUpdate, revealSidebar, requestFilter, tabs }) {
|
||||
this.addonsDir = addonsDir;
|
||||
this.dataDir = dataDir;
|
||||
this.isDisabled = isDisabled || (() => false);
|
||||
|
|
@ -351,6 +351,7 @@ class AddonHost {
|
|||
// add-on ever gets to hand-write into its own installed folder.
|
||||
this._checkAndStageUpdates = typeof checkAndStageUpdates === "function" ? checkAndStageUpdates : null;
|
||||
this._restartApp = typeof restartApp === "function" ? restartApp : null;
|
||||
this._applySelfUpdate = typeof applySelfUpdate === "function" ? applySelfUpdate : null;
|
||||
// revealSidebar: opens the sidebar and switches to the given panel id.
|
||||
// Signature: (addonId, panelId) => void. Add-ons use this from their
|
||||
// context-menu handler to surface a result in their sidebar UI.
|
||||
|
|
@ -837,6 +838,13 @@ class AddonHost {
|
|||
staged: (full?.staged || []).find((s) => s.id === manifest.id) || null,
|
||||
};
|
||||
},
|
||||
// Apply this add-on's staged update now, without a restart: Theseus
|
||||
// swaps it in, restarts the add-on and reloads its open panels and
|
||||
// tabs. Plug-ins (wallets) still update on the next launch.
|
||||
applySelfUpdate: async () => {
|
||||
if (!this._applySelfUpdate) throw new Error("applySelfUpdate unavailable (host not wired)");
|
||||
return this._applySelfUpdate(manifest.id);
|
||||
},
|
||||
// Ask Theseus to relaunch (the plug-in card's "apply update" chip).
|
||||
// The host asks the user first and resolves { restarted, deferred }
|
||||
// — an add-on cannot restart the browser on its own.
|
||||
|
|
|
|||
43
main.js
43
main.js
|
|
@ -2746,6 +2746,15 @@ function initAddons() {
|
|||
// update) but never gets to perform one: the user is asked first, in a
|
||||
// native dialog the panel cannot draw over. Declining costs nothing —
|
||||
// a staged update applies on the next normal launch anyway.
|
||||
// An add-on applying its own staged update in place (no restart). Runs a
|
||||
// moment later so the add-on's own call can return before the host is
|
||||
// rebuilt underneath it.
|
||||
applySelfUpdate: (addonId) => {
|
||||
if (isPluginAddon(addonId)) return { ok: false, reason: "plug-ins update on the next launch" };
|
||||
if (!listStagedAddons(addonsStagedDir()).some((s) => s.id === addonId)) return { ok: false, reason: "nothing staged" };
|
||||
setTimeout(() => applyStagedNow(addonId), 250);
|
||||
return { ok: true, scheduled: true };
|
||||
},
|
||||
restartApp: async (addonName) => {
|
||||
const who = String(addonName || "An add-on").slice(0, 60);
|
||||
console.log(`[restart] ${who} asked to relaunch Theseus`);
|
||||
|
|
@ -5924,9 +5933,9 @@ ipcMain.handle("addons-check-updates", () => pollAddonUpdates("manual"));
|
|||
// launch — a wallet mid-session is not something to hot-swap. `id` limits
|
||||
// the apply to one extension (Settings row button); omitted = every
|
||||
// staged extension (toolbar chip).
|
||||
ipcMain.handle("addons-apply-staged", (_e, id) => {
|
||||
ipcMain.handle("addons-apply-staged", (_e, id) => applyStagedNow(typeof id === "string" && id ? id : null));
|
||||
function applyStagedNow(want) {
|
||||
if (!addonHost) return { ok: false, error: "add-ons not ready", applied: [] };
|
||||
const want = typeof id === "string" && id ? id : null;
|
||||
const applied = addonUpdater.promoteStagedUpdates({
|
||||
addonsDir: addonsUserDir(),
|
||||
backupsDir: addonsBackupDir(),
|
||||
|
|
@ -5934,10 +5943,36 @@ ipcMain.handle("addons-apply-staged", (_e, id) => {
|
|||
logger: (...a) => console.log("[addons]", ...a),
|
||||
only: (m) => (want ? m.id === want : true) && !isPluginAddon(m.id),
|
||||
});
|
||||
if (applied.length) { addonHost.discoverAndActivate(); emitSidebarState(); }
|
||||
if (applied.length) {
|
||||
addonHost.discoverAndActivate();
|
||||
emitSidebarState();
|
||||
reloadAddonViews(applied.map((a) => a.id || a));
|
||||
}
|
||||
notifyStagedAddons();
|
||||
return { ok: true, applied, pending: listStagedAddons(addonsStagedDir()).map((s) => ({ id: s.id, version: s.version })) };
|
||||
});
|
||||
}
|
||||
// After an update is applied in place, an open panel or tab of that add-on
|
||||
// still runs the previous version's page (it was loaded before the swap),
|
||||
// so it shows the old UI until Theseus restarts. Reload them from disk.
|
||||
function reloadAddonViews(ids) {
|
||||
const mine = (panelId) => panelId && ids.some((id) => String(panelId).startsWith(`${id}:`));
|
||||
try {
|
||||
if (mine(leftPanelLoadedId)) {
|
||||
const open = leftPanelId;
|
||||
leftPanelLoadedId = null;
|
||||
if (open && mine(open)) openLeftPanel(open);
|
||||
}
|
||||
} catch (e) { console.warn("[addons] left panel reload failed:", e?.message); }
|
||||
try {
|
||||
if (mine(sidebarActivePanelId) && sidebar) {
|
||||
const p = addonHost.getSidebarPanels().find((x) => x.panelId === sidebarActivePanelId);
|
||||
if (p) sidebar.webContents.loadFile(p.pageFile);
|
||||
}
|
||||
} catch (e) { console.warn("[addons] sidebar reload failed:", e?.message); }
|
||||
for (const t of tabs) {
|
||||
try { if (t.addonId && ids.includes(t.addonId)) t.view.webContents.reload(); } catch {}
|
||||
}
|
||||
}
|
||||
ipcMain.handle("addons-list-staged", () => listStagedAddons(addonsStagedDir()));
|
||||
function listStagedAddons(stagedDir) {
|
||||
const out = [];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue