diff --git a/bundled-addons/aegis/addon.json b/bundled-addons/aegis/addon.json
index ac3ccce7..37c555c5 100644
--- a/bundled-addons/aegis/addon.json
+++ b/bundled-addons/aegis/addon.json
@@ -1,7 +1,7 @@
{
"id": "aegis",
"name": "Aegis Wallet",
- "version": "0.8.5",
+ "version": "0.8.6",
"category": "plugin",
"description": "Multi-chain wallet (BCH, BTC, TRX, ETH, SOL, SC, DGB) derived from your Theseus vault. Dapps get window.bitcoincash on .x sites; window.tronWeb / window.tronLink / window.ethereum / window.solana on any https page.",
"author": "Silent Mode",
diff --git a/bundled-addons/aegis/panel.js b/bundled-addons/aegis/panel.js
index 38b5b55e..8feed09c 100644
--- a/bundled-addons/aegis/panel.js
+++ b/bundled-addons/aegis/panel.js
@@ -831,6 +831,68 @@ function fillPicker() {
if (impKs) impKs.addEventListener("click", (e) => { e.stopPropagation(); d.hidden = true; openKeystoreImportModal(); });
}
+// 0.8.6: in-panel replacement for window.confirm(). The native dialog is
+// chrome-owned, so it renders as a Theseus-branded OS box outside the
+// sidebar โ jarring next to the wallet's own UI, and it can't carry an
+// icon or a danger-styled button. Resolves true/false like confirm(),
+// so callers just `await` it.
+// opts: { title, body, confirmLabel, cancelLabel, danger, icon }
+function aegisConfirm(opts) {
+ const o = opts || {};
+ return new Promise((resolve) => {
+ const overlay = document.createElement("div");
+ overlay.style.cssText = "position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:flex-start;justify-content:center;z-index:100000;padding-top:24px";
+ overlay.innerHTML = `
+
+
+ ${o.icon || (o.danger ? "โ " : "๐ก")}
+
${esc(o.title || "Are you sure?")}
+
+ ${o.body ? `
${o.body}
` : ""}
+
+ ${o.alertOnly ? "" : ``}
+
+
+
`;
+ document.body.appendChild(overlay);
+ let done = false;
+ const finish = (val) => {
+ if (done) return;
+ done = true;
+ document.removeEventListener("keydown", onKey, true);
+ try { overlay.remove(); } catch {}
+ resolve(val);
+ };
+ const onKey = (e) => {
+ if (e.key === "Escape") { e.stopPropagation(); finish(false); }
+ else if (e.key === "Enter") { e.stopPropagation(); finish(true); }
+ };
+ document.addEventListener("keydown", onKey, true);
+ overlay.addEventListener("click", (e) => { if (e.target === overlay) finish(false); });
+ overlay.querySelectorAll("[data-ac]").forEach((b) => b.addEventListener("click", (e) => {
+ e.stopPropagation();
+ finish(b.dataset.ac === "yes");
+ }));
+ const yes = overlay.querySelector('[data-ac="yes"]');
+ if (yes) yes.focus();
+ });
+}
+
+// Single-button sibling of aegisConfirm, for the error notices that used
+// window.alert(). Fire-and-forget: callers don't need the result, so it
+// works from sync handlers too.
+function aegisAlert(message, opts) {
+ const o = opts || {};
+ return aegisConfirm({
+ title: o.title || "Something went wrong",
+ icon: o.icon || "โ ",
+ body: esc(String(message == null ? "" : message)),
+ confirmLabel: o.confirmLabel || "OK",
+ cancelLabel: null,
+ alertOnly: true,
+ });
+}
+
// Import modal โ M.1 UX. Paste mnemonic + path OR WIF, choose network + label
// + category. Backend derives cashaddr and stores signer material in
// wallet-imports.enc (design ยง3.2). Modal is a plain overlay div injected
@@ -893,7 +955,13 @@ function openWalletManageModal(w) {
});
if (canRemove) overlay.querySelector("#mwRemove").addEventListener("click", async () => {
const msg = overlay.querySelector("#mwMsg"); msg.hidden = true;
- if (!confirm(`Remove "${w.label}" from Aegis?\n\nOn-chain funds stay where they are โ this only unlinks the wallet from Aegis. Add it back later on the same coin + network to derive the same addresses (${w.kind === "imported" ? "or re-import if this was imported" : "from your vault seed"}).`)) return;
+ const ok = await aegisConfirm({
+ title: `Remove "${w.label}"?`,
+ danger: true,
+ confirmLabel: "Remove wallet",
+ body: `On-chain funds stay exactly where they are โ this only unlinks the wallet from Aegis.
You can add it back later on the same coin + network to derive the same addresses ${w.kind === "imported" ? "(or re-import it, since this one was imported)" : "from your vault seed"}.`,
+ });
+ if (!ok) return;
try {
state = await S.invoke("removeWallet", { id: w.id });
close();
@@ -3360,14 +3428,20 @@ $("gsPinChange") && $("gsPinChange").addEventListener("click", async () => {
await handlePinSet(true);
});
$("gsPinRemove") && $("gsPinRemove").addEventListener("click", async () => {
- if (!confirm("Remove the quick-access PIN? You'll have to type the master password on every unlock again.")) return;
+ const ok = await aegisConfirm({
+ title: "Remove the quick-access PIN?",
+ danger: true,
+ confirmLabel: "Remove PIN",
+ body: "You'll have to type the master password on every unlock again.",
+ });
+ if (!ok) return;
try {
await S.invoke("pinBlobClear");
// Also disable the send-time PIN policy โ it depends on having a PIN.
await S.invoke("securitySet", { requirePinForSending: false });
await refreshSecurityState();
renderGeneralSecurity();
- } catch (e) { alert("Could not remove PIN: " + cleanErr(e)); }
+ } catch (e) { aegisAlert("Could not remove PIN: " + cleanErr(e)); }
});
$("gsRequirePin") && $("gsRequirePin").addEventListener("change", async () => {
const on = $("gsRequirePin").checked;
@@ -3376,7 +3450,7 @@ $("gsRequirePin") && $("gsRequirePin").addEventListener("change", async () => {
renderGeneralSecurity();
} catch (e) {
$("gsRequirePin").checked = !on;
- alert("Could not save setting: " + cleanErr(e));
+ aegisAlert("Could not save setting: " + cleanErr(e));
}
});
$("gsOpenPasswords") && $("gsOpenPasswords").addEventListener("click", () => {
@@ -3408,7 +3482,7 @@ $("gsLockOnClose") && $("gsLockOnClose").addEventListener("change", async () =>
bindIdleAutoLock();
} catch (e) {
$("gsLockOnClose").checked = !on;
- alert("Could not save setting: " + cleanErr(e));
+ aegisAlert("Could not save setting: " + cleanErr(e));
}
});
$("gsIdleMinutes") && $("gsIdleMinutes").addEventListener("change", async () => {
@@ -3417,10 +3491,16 @@ $("gsIdleMinutes") && $("gsIdleMinutes").addEventListener("change", async () =>
sessionState = await S.invoke("sessionConfigSet", { idleMinutes: mins });
renderSessionSettings();
bindIdleAutoLock();
- } catch (e) { alert("Could not save idle timeout: " + cleanErr(e)); }
+ } catch (e) { aegisAlert("Could not save idle timeout: " + cleanErr(e)); }
});
$("gsSignOut") && $("gsSignOut").addEventListener("click", async () => {
- if (!confirm("Sign out of Aegis? The vault will re-lock and you'll need the master password (or PIN) to open it again.")) return;
+ const ok = await aegisConfirm({
+ title: "Sign out of Aegis?",
+ icon: "๐",
+ confirmLabel: "Sign out",
+ body: "The vault will re-lock and you'll need the master password (or PIN) to open it again.",
+ });
+ if (!ok) return;
try {
state = await S.invoke("vaultLock");
stripView = { mode: "coins", groupKey: null };
@@ -3428,7 +3508,7 @@ $("gsSignOut") && $("gsSignOut").addEventListener("click", async () => {
// Session blob was cleared server-side; refresh our cached view.
sessionState = await S.invoke("sessionStatus");
renderSessionSettings();
- } catch (e) { alert("Could not sign out: " + cleanErr(e)); }
+ } catch (e) { aegisAlert("Could not sign out: " + cleanErr(e)); }
});
// Setting or changing a PIN needs the master password to encrypt against.
@@ -3459,7 +3539,7 @@ async function handlePinSet(replacing) {
await refreshSecurityState();
renderGeneralSecurity();
} catch (e) {
- alert("Could not save PIN: " + cleanErr(e));
+ aegisAlert("Could not save PIN: " + cleanErr(e));
} finally {
// Drop the buffered password sooner rather than later โ we only kept
// it around to enroll a PIN without a re-prompt.
@@ -3514,7 +3594,7 @@ function promptMasterPassword({ title, subtitle }) {
async function verifyPinInteractively(subtitle) {
const remain = await pinLockoutRemainingMs();
if (remain > 0) {
- alert(`PIN entry is locked for ${Math.ceil(remain / 60000)} min. Use "Remove" in Settings or wait it out.`);
+ aegisAlert(`PIN entry is locked for ${Math.ceil(remain / 60000)} min. Use "Remove" in Settings or wait it out.`);
return false;
}
return new Promise((resolve) => {
@@ -3731,7 +3811,13 @@ $("renameBtn").addEventListener("click", async () => {
});
$("removeBtn").addEventListener("click", async () => {
const s = sel(); if (!s || s.isLegacy) return;
- if (!confirm(`Remove the wallet "${s.label}"?\n\nThe on-chain address stays; the wallet is unlinked from Aegis. You can add it back later by creating a new wallet on the same coin + network.`)) return;
+ const ok = await aegisConfirm({
+ title: `Remove "${s.label}"?`,
+ danger: true,
+ confirmLabel: "Remove wallet",
+ body: "The on-chain address stays exactly where it is; the wallet is only unlinked from Aegis.
You can add it back later by creating a new wallet on the same coin + network.",
+ });
+ if (!ok) return;
try { state = await S.invoke("removeWallet", { id: state.selectedWalletId }); settingsFilled = false; render(); }
catch (e) { $("settingsMsg").textContent = cleanErr(e); $("settingsMsg").hidden = false; }
});