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",
|
||||
"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.",
|
||||
"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",
|
||||
"capabilities": ["sidebar-panel", "vault-derive", "page-inject", "approval-modal"],
|
||||
"absorbs": ["bchwallet", "siawallet"],
|
||||
|
|
|
|||
13
chrome.html
13
chrome.html
|
|
@ -576,7 +576,18 @@
|
|||
const dataAttr = b.kind === "panel"
|
||||
? `data-panel="${b.panelId.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("");
|
||||
extButtons.querySelectorAll(".extbtn").forEach((btn) => {
|
||||
btn.onclick = () => {
|
||||
|
|
|
|||
20
main.js
20
main.js
|
|
@ -1355,13 +1355,21 @@ function migrateAegisRename() {
|
|||
const aegisDir = path.join(addonsRoot, "aegis");
|
||||
const bchJson = path.join(dataRoot, "bchwallet.json");
|
||||
const aegisJson = path.join(dataRoot, "aegis.json");
|
||||
if (fs.existsSync(bchDir) && !fs.existsSync(aegisDir)) {
|
||||
if (fs.existsSync(bchJson) && !fs.existsSync(aegisJson)) {
|
||||
try { fs.copyFileSync(bchJson, aegisJson); console.log("[addons] migrated bchwallet.json -> aegis.json"); }
|
||||
catch (err) { console.warn("[addons] migrate storage failed:", err?.message); }
|
||||
}
|
||||
// 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)) {
|
||||
try { fs.copyFileSync(bchJson, aegisJson); console.log("[addons] migrated bchwallet.json -> aegis.json"); }
|
||||
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}`);
|
||||
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); }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1219,7 +1219,13 @@
|
|||
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>'
|
||||
: "";
|
||||
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) =>
|
||||
'<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