diff --git a/addons-host.js b/addons-host.js index 19d108d..3c38448 100644 --- a/addons-host.js +++ b/addons-host.js @@ -537,12 +537,12 @@ class AddonHost { staged: (full?.staged || []).find((s) => s.id === manifest.id) || null, }; }, - // Cleanly relaunch Theseus. Used by the plug-in card's "apply - // update" chip to activate a staged build without asking the user - // to hunt for the app menu. + // 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. restartApp: () => { if (!this._restartApp) throw new Error("restartApp unavailable (host not wired)"); - this._restartApp(); + return Promise.resolve(this._restartApp(manifest.name || manifest.id)); }, // Resolves once the browser chrome has painted (immediately if it // already has). Put expensive dependency loading behind this so it diff --git a/main.js b/main.js index b72ebd4..76c2c2a 100644 --- a/main.js +++ b/main.js @@ -2063,7 +2063,25 @@ function initAddons() { return { report: [], skipped: "unexpected-error", staged: [] }; } }, - restartApp: () => { console.log("[restart] requested by an add-on"); try { app.relaunch(); } catch {} app.quit(); }, + // An add-on may ask for a relaunch (Aegis does after staging its own + // 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. + restartApp: async (addonName) => { + const who = String(addonName || "An add-on").slice(0, 60); + console.log(`[restart] ${who} asked to relaunch Theseus`); + const parent = win && !win.isDestroyed() ? win : undefined; + const { response } = await dialog.showMessageBox(parent, { + type: "question", title: "Restart Theseus?", + message: `${who} wants to restart Theseus.`, + detail: "Usually to finish installing its own update. Open tabs are restored after the restart. If you choose Later, the update still applies the next time Theseus starts.", + buttons: ["Restart now", "Later"], defaultId: 1, cancelId: 1, noLink: true, + }); + if (response !== 0) { console.log(`[restart] ${who}: user chose Later`); return { restarted: false, deferred: true }; } + try { app.relaunch(); } catch {} + app.quit(); + return { restarted: true }; + }, // open-tab (addon-file variant): open one of the add-on's OWN files in a // full tab. The path is joined against the resolved add-on folder and // rejected if the result escapes it — belt-and-braces with the sanity