fix(theseus/aegis): retire bchwallet on every launch + branded dock/list icon
Two follow-ups from the on-device test. Two Aegis addons showing up (bchwallet + aegis): - migrateAegisRename previously only ran when addons/aegis/ didn't exist, which meant any bchwallet copy the signed OTA update endpoint reinstalls after the first migration stays there forever, and AddonHost loads both as separate wallets. Rewritten to always retire addons/bchwallet/ when it's present, regardless of whether aegis/ is already installed. The storage-copy (bchwallet.json → aegis.json) still only runs the first time so a downgrade doesn't clobber fresh 0.4+ state. - Also flushes any stray addons/siawallet/ that comes back the same way. Sidebar dock and Extensions list icons showed the raw 🛡 emoji: - chrome.html's dock-button renderer and settings.html's extensions-list renderer now accept `data:image/svg+xml…` values for manifest.icon and render them as <img> instead of text. Emoji strings still render as before. - aegis addon.json's icon is now the exact hex-aspis mark from aegis.x/brand/favicon.svg (URL-encoded inline). Version bumped to 0.4.1 so seedBundledAddons reseeds the new addon.json on next launch.
This commit is contained in:
parent
7d580be598
commit
22be28ed17
4 changed files with 35 additions and 10 deletions
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"id": "aegis",
|
"id": "aegis",
|
||||||
"name": "Aegis Wallet",
|
"name": "Aegis Wallet",
|
||||||
"version": "0.4.0",
|
"version": "0.4.1",
|
||||||
"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.",
|
"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",
|
"author": "Silent Mode",
|
||||||
"icon": "🛡",
|
"icon": "data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none'%3E%3Cpolygon points='16,2 28,9 28,23 16,30 4,23 4,9' fill='%230a0a0d' stroke='%23D6FF3D' stroke-width='1.6' stroke-linejoin='round'/%3E%3Ccircle cx='16' cy='16' r='4.5' fill='none' stroke='%23D6FF3D' stroke-width='1.4'/%3E%3Ccircle cx='16' cy='16' r='1.6' fill='%23D6FF3D'/%3E%3C/svg%3E",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"capabilities": ["sidebar-panel", "vault-derive", "page-inject", "approval-modal"],
|
"capabilities": ["sidebar-panel", "vault-derive", "page-inject", "approval-modal"],
|
||||||
"absorbs": ["bchwallet", "siawallet"],
|
"absorbs": ["bchwallet", "siawallet"],
|
||||||
|
|
|
||||||
13
chrome.html
13
chrome.html
|
|
@ -576,7 +576,18 @@
|
||||||
const dataAttr = b.kind === "panel"
|
const dataAttr = b.kind === "panel"
|
||||||
? `data-panel="${b.panelId.replace(/"/g,""")}"`
|
? `data-panel="${b.panelId.replace(/"/g,""")}"`
|
||||||
: `data-menu="${b.addonId.replace(/"/g,""")}"`;
|
: `data-menu="${b.addonId.replace(/"/g,""")}"`;
|
||||||
return `<button class="extbtn${cls}" ${dataAttr} title="${b.title.replace(/"/g,""")}">${b.icon.replace(/</g,"<")}</button>`;
|
// Icon is text by default (an emoji like 🛡, or a "•" fallback). An
|
||||||
|
// add-on that wants a branded mark can set manifest.icon to a data:
|
||||||
|
// URI (svg or png) and we render it as an <img> instead — same size
|
||||||
|
// slot, no layout change. Anything else is HTML-escaped and stays
|
||||||
|
// text so the emoji case keeps working.
|
||||||
|
let iconHtml;
|
||||||
|
if (/^data:image\//i.test(b.icon)) {
|
||||||
|
iconHtml = `<img src="${b.icon.replace(/"/g,""")}" alt="" style="width:18px;height:18px;display:block;margin:0 auto">`;
|
||||||
|
} else {
|
||||||
|
iconHtml = b.icon.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||||
|
}
|
||||||
|
return `<button class="extbtn${cls}" ${dataAttr} title="${b.title.replace(/"/g,""")}">${iconHtml}</button>`;
|
||||||
}).join("");
|
}).join("");
|
||||||
extButtons.querySelectorAll(".extbtn").forEach((btn) => {
|
extButtons.querySelectorAll(".extbtn").forEach((btn) => {
|
||||||
btn.onclick = () => {
|
btn.onclick = () => {
|
||||||
|
|
|
||||||
12
main.js
12
main.js
|
|
@ -1355,13 +1355,21 @@ function migrateAegisRename() {
|
||||||
const aegisDir = path.join(addonsRoot, "aegis");
|
const aegisDir = path.join(addonsRoot, "aegis");
|
||||||
const bchJson = path.join(dataRoot, "bchwallet.json");
|
const bchJson = path.join(dataRoot, "bchwallet.json");
|
||||||
const aegisJson = path.join(dataRoot, "aegis.json");
|
const aegisJson = path.join(dataRoot, "aegis.json");
|
||||||
if (fs.existsSync(bchDir) && !fs.existsSync(aegisDir)) {
|
// Copy legacy storage into the new file exactly once, only when the new
|
||||||
|
// file doesn't exist yet (a downgrade to 0.3.x would otherwise clobber
|
||||||
|
// fresh state written by 0.4+).
|
||||||
if (fs.existsSync(bchJson) && !fs.existsSync(aegisJson)) {
|
if (fs.existsSync(bchJson) && !fs.existsSync(aegisJson)) {
|
||||||
try { fs.copyFileSync(bchJson, aegisJson); console.log("[addons] migrated bchwallet.json -> aegis.json"); }
|
try { fs.copyFileSync(bchJson, aegisJson); console.log("[addons] migrated bchwallet.json -> aegis.json"); }
|
||||||
catch (err) { console.warn("[addons] migrate storage failed:", err?.message); }
|
catch (err) { console.warn("[addons] migrate storage failed:", err?.message); }
|
||||||
}
|
}
|
||||||
|
// Retire the bchwallet folder EVERY LAUNCH it exists. The signed OTA
|
||||||
|
// update endpoint may still advertise `id=bchwallet` updates, and
|
||||||
|
// promoteStagedUpdates (which runs before us) could reinstall it. Left
|
||||||
|
// active it would load as a second Aegis under a stale id, doubling
|
||||||
|
// every wallet in the sidebar.
|
||||||
|
if (fs.existsSync(bchDir)) {
|
||||||
const backup = path.join(backups, `bchwallet-migrated-${stamp}`);
|
const backup = path.join(backups, `bchwallet-migrated-${stamp}`);
|
||||||
try { fs.renameSync(bchDir, backup); console.log(`[addons] retired addons/bchwallet -> addons-backups/${path.basename(backup)} (folder renamed to aegis/)`); }
|
try { fs.renameSync(bchDir, backup); console.log(`[addons] retired addons/bchwallet -> addons-backups/${path.basename(backup)}${fs.existsSync(aegisDir) ? " (aegis already present)" : " (folder renamed to aegis/)"}`); }
|
||||||
catch (err) { console.warn("[addons] retire bchwallet failed:", err?.message); }
|
catch (err) { console.warn("[addons] retire bchwallet failed:", err?.message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1219,7 +1219,13 @@
|
||||||
const builtInBadge = isBundled
|
const builtInBadge = isBundled
|
||||||
? '<span style="display:inline-block;font-size:10px;letter-spacing:.05em;padding:1px 6px;border-radius:3px;background:rgba(214,255,61,.14);color:var(--acid);font-weight:700;margin-left:6px;vertical-align:middle">BUILT-IN</span>'
|
? '<span style="display:inline-block;font-size:10px;letter-spacing:.05em;padding:1px 6px;border-radius:3px;background:rgba(214,255,61,.14);color:var(--acid);font-weight:700;margin-left:6px;vertical-align:middle">BUILT-IN</span>'
|
||||||
: "";
|
: "";
|
||||||
return '<div class="row"><div class="txt"><div class="t">' + a.icon + ' ' + escapeHtml(a.name) + builtInBadge + ' <span style="color:var(--dim);font-weight:400">v' + escapeHtml(a.version) + '</span>' + caps + '</div><div class="d">' + escapeHtml(a.description || "") + (a.author ? ' <span style="color:var(--dim)">— ' + escapeHtml(a.author) + '</span>' : '') + '</div></div><div style="display:flex;gap:8px;align-items:center"><button class="btn" data-reveal="' + escapeAttr(a.folder) + '">Show folder</button><label class="sw"><input type="checkbox" data-toggle="' + escapeAttr(a.id) + '" ' + (a.enabled ? "checked" : "") + '><span class="track"><span class="knob"></span></span></label></div></div>';
|
// Manifest icons may be an emoji ("🛡"), a "•" fallback, or a
|
||||||
|
// data: URI to a branded SVG/PNG — render as <img> for URIs,
|
||||||
|
// as escaped text otherwise so the emoji case keeps working.
|
||||||
|
const iconHtml = /^data:image\//i.test(a.icon || "")
|
||||||
|
? '<img src="' + escapeAttr(a.icon) + '" alt="" style="width:16px;height:16px;vertical-align:middle;margin-right:4px">'
|
||||||
|
: escapeHtml(a.icon || "•");
|
||||||
|
return '<div class="row"><div class="txt"><div class="t">' + iconHtml + ' ' + escapeHtml(a.name) + builtInBadge + ' <span style="color:var(--dim);font-weight:400">v' + escapeHtml(a.version) + '</span>' + caps + '</div><div class="d">' + escapeHtml(a.description || "") + (a.author ? ' <span style="color:var(--dim)">— ' + escapeHtml(a.author) + '</span>' : '') + '</div></div><div style="display:flex;gap:8px;align-items:center"><button class="btn" data-reveal="' + escapeAttr(a.folder) + '">Show folder</button><label class="sw"><input type="checkbox" data-toggle="' + escapeAttr(a.id) + '" ' + (a.enabled ? "checked" : "") + '><span class="track"><span class="knob"></span></span></label></div></div>';
|
||||||
};
|
};
|
||||||
const sectionHeader = (label, hint) =>
|
const sectionHeader = (label, hint) =>
|
||||||
'<div class="d" style="color:var(--dim);margin:12px 0 6px;font-size:12px;text-transform:uppercase;letter-spacing:.04em">' + escapeHtml(label) + '</div>'
|
'<div class="d" style="color:var(--dim);margin:12px 0 6px;font-size:12px;text-transform:uppercase;letter-spacing:.04em">' + escapeHtml(label) + '</div>'
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue