feat(theseus/settings): Check for updates button in Settings > General

New "Updates" card under Startup: a single "Check for updates" button
that hits the release manifest immediately (rather than waiting for
the boot-time and 6h-interval auto-check). Reuses the existing
recheck-update IPC; extended it to un-dismiss any chip the user closed
this session AND to return the current app version so the button can
render either:

- "You're on the latest (v0.3.21)." when nothing newer exists
- "vX.Y.Z is available — the update chip in the toolbar will offer it."

Button disables + shows "Checking…" during the fetch. Silent failures
report their reason ("Check failed: …") so the user isn't left staring
at a dash.
This commit is contained in:
Local Dev 2026-09-08 00:43:26 +02:00
parent e8f7c49c46
commit 5cd60f1df3
3 changed files with 43 additions and 1 deletions

10
main.js
View file

@ -2859,7 +2859,15 @@ ipcMain.handle("open-update-download", (_e, url) => {
} catch (e) { console.warn("update download failed:", e?.message); return false; } } catch (e) { console.warn("update download failed:", e?.message); return false; }
}); });
ipcMain.handle("dismiss-update", () => { updateDismissedThisSession = true; emitUpdateAvailable(); return true; }); ipcMain.handle("dismiss-update", () => { updateDismissedThisSession = true; emitUpdateAvailable(); return true; });
ipcMain.handle("recheck-update", async () => { await checkForUpdate(); return updateAvailable; }); ipcMain.handle("recheck-update", async () => {
// Manual "Check for updates" also un-dismisses any chip the user closed
// in this session — they're actively asking to see the status, so honour
// that. Report current app version too so the UI can render "you're on
// vN.M.O" when no newer build is out.
updateDismissedThisSession = false;
await checkForUpdate();
return { updateAvailable, currentVersion: app.getVersion() };
});
// One-click "Install & restart". Requires the silent pre-fetch to have // One-click "Install & restart". Requires the silent pre-fetch to have
// finished (updateDownloadState === "ready"). Launches the setup with /S // finished (updateDownloadState === "ready"). Launches the setup with /S
// (skips the wizard; our nsis/installer.nsh's Ariadne prompt is bypassed // (skips the wizard; our nsis/installer.nsh's Ariadne prompt is bypassed

View file

@ -36,6 +36,9 @@ contextBridge.exposeInMainWorld("cfg", {
ariadneInstall: () => ipcRenderer.invoke("ariadne-install"), ariadneInstall: () => ipcRenderer.invoke("ariadne-install"),
ariadneUpdate: () => ipcRenderer.invoke("ariadne-update"), ariadneUpdate: () => ipcRenderer.invoke("ariadne-update"),
ariadneUninstall: () => ipcRenderer.invoke("ariadne-uninstall"), ariadneUninstall: () => ipcRenderer.invoke("ariadne-uninstall"),
// Manual "Check for updates" — un-dismisses any existing chip and re-
// fetches the release manifest. Returns { updateAvailable, currentVersion }.
recheckUpdate: () => ipcRenderer.invoke("recheck-update"),
// Add-ons management (Settings > Add-ons tab). // Add-ons management (Settings > Add-ons tab).
listAddons: () => ipcRenderer.invoke("addons-list"), listAddons: () => ipcRenderer.invoke("addons-list"),
setAddonEnabled: (id, enabled) => ipcRenderer.invoke("addons-set-enabled", id, !!enabled), setAddonEnabled: (id, enabled) => ipcRenderer.invoke("addons-set-enabled", id, !!enabled),

View file

@ -159,6 +159,16 @@
<div class="txt"><div class="t">Open previous windows and tabs</div><div class="d">Restore the tabs from your last session when Theseus starts.</div></div> <div class="txt"><div class="t">Open previous windows and tabs</div><div class="d">Restore the tabs from your last session when Theseus starts.</div></div>
<label class="sw"><input type="checkbox" id="restoreSession"><span class="track"><span class="knob"></span></span></label> <label class="sw"><input type="checkbox" id="restoreSession"><span class="track"><span class="knob"></span></span></label>
</div> </div>
<div class="row" style="flex-direction:column;align-items:stretch;gap:8px">
<div class="txt">
<div class="t">Updates</div>
<div class="d">Theseus already checks the release manifest at boot and every 6h. Click below to check right now.</div>
</div>
<div id="updStatus" class="pmuted" style="font-size:12.5px"></div>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<button id="updCheck" class="btn">Check for updates</button>
</div>
</div>
<h2 class="sub">Appearance</h2> <h2 class="sub">Appearance</h2>
<p class="subd">Choose a theme for the browser. <b>System</b> follows your operating system's light/dark setting.</p> <p class="subd">Choose a theme for the browser. <b>System</b> follows your operating system's light/dark setting.</p>
<div class="themeCards" id="theme" role="radiogroup" aria-label="Theme"> <div class="themeCards" id="theme" role="radiogroup" aria-label="Theme">
@ -940,6 +950,27 @@
arRefresh.onclick = refreshAriadne; arRefresh.onclick = refreshAriadne;
refreshAriadne(); refreshAriadne();
// ---- Manual "Check for updates" -----------------------------------------
// Fires an IPC that un-dismisses any lingering session chip and hits the
// release manifest. Renders either "you're on the latest (vN.M.O)" or
// "vX.Y.Z is available — the update chip in the toolbar will offer it".
const updStatus = document.getElementById("updStatus");
const updCheck = document.getElementById("updCheck");
updCheck.onclick = async () => {
const orig = updCheck.textContent;
updCheck.disabled = true; updCheck.textContent = "Checking…";
updStatus.textContent = "Checking…";
try {
const r = await C.recheckUpdate();
if (r && r.updateAvailable && r.updateAvailable.version) {
updStatus.innerHTML = `<b style="color:var(--acid)">v${r.updateAvailable.version}</b> is available — the update chip in the toolbar will offer it.`;
} else {
updStatus.textContent = `You're on the latest (v${(r && r.currentVersion) || "?"}).`;
}
} catch (e) { updStatus.textContent = "Check failed: " + (e?.message || e); }
finally { updCheck.disabled = false; updCheck.textContent = orig; }
};
// ---- Passwords section: three states (setup / locked / unlocked) --------- // ---- Passwords section: three states (setup / locked / unlocked) ---------
// The vault lives in main.js — this UI just calls IPC. No plaintext ever // The vault lives in main.js — this UI just calls IPC. No plaintext ever
// sits in this DOM except the value produced by a specific Show/Copy click. // sits in this DOM except the value produced by a specific Show/Copy click.