feat(theseus/aegis): official brand favicon + discoverable Add-wallet UX

Two fixes off the first-launch feedback: users didn't see how to add a
wallet, and the branded shield from aegis.x/brand hadn't landed in the
panel.

- panel.html + panel.js: swap the ad-hoc shield SVG for the exact mark
  from aegis.x/brand/favicon.svg — hexagonal aspis with dark fill +
  acid stroke + boss ring + centre point. The panel's tab favicon
  (<link rel="icon">) and the "Aegis" fallback badge in the header
  now render byte-close to what a user downloads from the brand kit.
- Add-wallet discoverability: an always-visible "+" chip lives in the
  header next to the picker caret; clicking it opens the picker with
  the coin list pre-expanded. When the panel is genuinely empty (a
  vault Aegis hasn't seen before), the gate now shows a big primary
  "+ Add your first wallet" button plus copy that spells out the
  seed source — Aegis derives every wallet from the Theseus password
  vault, no separate seed to import.
- addon.json bumped to 0.3.1 so seedBundledAddons() picks up the fresh
  panel files on the next Theseus launch (bundleVer === userVer would
  otherwise skip the reseed and users would keep loading the old
  panel from their addons/ dir).
This commit is contained in:
Local Dev 2026-09-08 13:19:29 +02:00
parent 1886b76fb2
commit ce53db3063
3 changed files with 41 additions and 7 deletions

View file

@ -1,8 +1,8 @@
{ {
"id": "bchwallet", "id": "bchwallet",
"name": "Aegis Wallet", "name": "Aegis Wallet",
"version": "0.3.0", "version": "0.3.1",
"description": "Multi-chain wallet (BCH mainnet/chipnet, Tron mainnet/Nile, Siacoin, DigiByte) derived from your Theseus vault. Dapps get window.bitcoincash on .x sites and window.tronWeb / window.tronLink 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": "🛡",
"main": "index.js", "main": "index.js",

View file

@ -3,7 +3,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Aegis Wallet</title> <title>Aegis Wallet</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,%3Csvg%20xmlns%3D'http%3A//www.w3.org/2000/svg'%20viewBox%3D'0%200%2032%2032'%3E%3Cpolygon%20points%3D'16%2C2%2029%2C9%2029%2C23%2016%2C30%203%2C23%203%2C9'%20fill%3D'none'%20stroke%3D'%23d6ff3d'%20stroke-width%3D'2.5'%20stroke-linejoin%3D'round'/%3E%3Ccircle%20cx%3D'16'%20cy%3D'16'%20r%3D'4.3'%20fill%3D'none'%20stroke%3D'%23d6ff3d'%20stroke-width%3D'1.6'/%3E%3Ccircle%20cx%3D'16'%20cy%3D'16'%20r%3D'1.6'%20fill%3D'%23d6ff3d'/%3E%3Cpath%20d%3D'M16%2010.5%20v-2.5%20M16%2021.5%20v2.5%20M10.5%2016%20h-2.5%20M21.5%2016%20h2.5'%20stroke%3D'%23d6ff3d'%20stroke-width%3D'1.6'%20stroke-linecap%3D'round'/%3E%3C/svg%3E"> <!-- Aegis brand mark from aegis.x/brand/favicon.svg — kept byte-close so
the sidebar-tab icon reads as the identity a user just downloaded from. -->
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,%3Csvg%20xmlns%3D'http%3A//www.w3.org/2000/svg'%20viewBox%3D'0%200%2032%2032'%20fill%3D'none'%3E%3Cpolygon%20points%3D'16%2C2%2028%2C9%2028%2C23%2016%2C30%204%2C23%204%2C9'%20fill%3D'%230a0a0d'%20stroke%3D'%23D6FF3D'%20stroke-width%3D'1.6'%20stroke-linejoin%3D'round'/%3E%3Ccircle%20cx%3D'16'%20cy%3D'16'%20r%3D'4.5'%20fill%3D'none'%20stroke%3D'%23D6FF3D'%20stroke-width%3D'1.4'/%3E%3Ccircle%20cx%3D'16'%20cy%3D'16'%20r%3D'1.6'%20fill%3D'%23D6FF3D'/%3E%3C/svg%3E">
<style> <style>
:root { color-scheme: light dark; :root { color-scheme: light dark;
--bg:#0e131c; --panel:#141a24; --card:#0f1621; --line:rgba(255,255,255,.09); --bg:#0e131c; --panel:#141a24; --card:#0f1621; --line:rgba(255,255,255,.09);
@ -55,6 +57,11 @@
.ttag { display: inline-block; font-size: 9.5px; letter-spacing: .06em; padding: 1px 5px; border-radius: 3px; .ttag { display: inline-block; font-size: 9.5px; letter-spacing: .06em; padding: 1px 5px; border-radius: 3px;
background: rgba(224,179,65,.18); color: #e0b341; font-weight: 700; vertical-align: middle; margin-left: 2px; } background: rgba(224,179,65,.18); color: #e0b341; font-weight: 700; vertical-align: middle; margin-left: 2px; }
#hNet { color: var(--dim); font-size: 11px; margin-left: 4px; font-weight: 500; } #hNet { color: var(--dim); font-size: 11px; margin-left: 4px; font-weight: 500; }
.picker-actions { display: flex; align-items: center; gap: 6px; }
.chip { background: transparent; border: 1px solid var(--line); color: var(--mut); border-radius: 6px;
padding: 0 8px; height: 22px; cursor: pointer; font: inherit; font-size: 13.5px; line-height: 1;
display: inline-flex; align-items: center; justify-content: center; }
.chip:hover { border-color: var(--acid); color: var(--acid); }
nav { display: flex; border-bottom: 1px solid var(--line); background: var(--panel); } nav { display: flex; border-bottom: 1px solid var(--line); background: var(--panel); }
nav button { flex: 1; padding: 9px 0 8px; border: 0; background: transparent; color: var(--mut); cursor: pointer; nav button { flex: 1; padding: 9px 0 8px; border: 0; background: transparent; color: var(--mut); cursor: pointer;
font: inherit; font-size: 12.5px; border-bottom: 2px solid transparent; } font: inherit; font-size: 12.5px; border-bottom: 2px solid transparent; }
@ -122,7 +129,10 @@
<span class="lbl" id="hLabel">Aegis Wallet</span> <span class="lbl" id="hLabel">Aegis Wallet</span>
<span id="hNet"></span> <span id="hNet"></span>
</div> </div>
<div class="caret"></div> <div class="picker-actions">
<button type="button" id="hAddWallet" class="chip" title="Add a new wallet"></button>
<span class="caret"></span>
</div>
</div> </div>
<div id="drop" hidden></div> <div id="drop" hidden></div>
<div class="bal"> <div class="bal">

View file

@ -155,8 +155,19 @@ function showTab(name) {
// ---- wallet picker (two-step add) ------------------------------------------ // ---- wallet picker (two-step add) ------------------------------------------
$("pickerBtn").addEventListener("click", () => { $("pickerBtn").addEventListener("click", (e) => {
// The "" chip inside the picker header opens the dropdown with the
// Add-wallet section pre-expanded — same UX as the empty-state gate
// button but always available.
const isAddChip = e.target && (e.target.id === "hAddWallet" || e.target.closest("#hAddWallet"));
const d = $("drop"); const d = $("drop");
if (isAddChip) {
d.hidden = false;
fillPicker();
setTimeout(() => { const first = d.querySelector(".coinrow"); if (first) first.click(); }, 0);
e.stopPropagation();
return;
}
d.hidden = !d.hidden; d.hidden = !d.hidden;
if (!d.hidden) fillPicker(); if (!d.hidden) fillPicker();
}); });
@ -250,9 +261,22 @@ function render() {
locked: ["🔒", "Unlock your password vault to open the wallet.", "Settings Passwords. Aegis derives its keys from the vault seed, so there is nothing separate to unlock."], locked: ["🔒", "Unlock your password vault to open the wallet.", "Settings Passwords. Aegis derives its keys from the vault seed, so there is nothing separate to unlock."],
nosetup: ["🗝", "Set up a password vault to create your wallet.", "Settings Passwords Set up. Use a recovery phrase there and every wallet in Aegis can be recreated from it on any machine."], nosetup: ["🗝", "Set up a password vault to create your wallet.", "Settings Passwords Set up. Use a recovery phrase there and every wallet in Aegis can be recreated from it on any machine."],
error: ["⚠", "This wallet could not start.", s?.error || ""], error: ["⚠", "This wallet could not start.", s?.error || ""],
empty: ["🧩", "No wallets yet.", "Open the wallet picker at the top and pick a coin, then a network to create one."], empty: ["🛡", "No wallets yet.", "Aegis derives every wallet from your Theseus password vault — there's no separate seed to import. Pick a coin below to create your first one."],
}[s?.phase || "locked"] || ["…", "Starting…", ""]; }[s?.phase || "locked"] || ["…", "Starting…", ""];
gate.innerHTML = `<div class="big">${copy[0]}</div><div><b>${esc(copy[1])}</b></div><div class="hint" style="margin-top:8px">${esc(copy[2])}</div>`; const showAddButton = s?.phase === "empty";
gate.innerHTML = `<div class="big">${copy[0]}</div><div><b>${esc(copy[1])}</b></div><div class="hint" style="margin-top:8px">${esc(copy[2])}</div>`
+ (showAddButton ? `<div class="actions" style="justify-content:center;margin-top:16px"><button class="btn primary" id="gateAddWallet"> Add your first wallet</button></div>` : "");
// Wire the empty-state button — opens the header picker with the Add
// section pre-expanded so the user can pick a coin in one click.
if (showAddButton) {
const btn = $("gateAddWallet");
if (btn) btn.addEventListener("click", () => {
const d = $("drop");
d.hidden = false;
fillPicker();
setTimeout(() => { const first = d.querySelector(".coinrow"); if (first) first.click(); }, 0);
});
}
} }
const dot = $("dot"); const dot = $("dot");
dot.className = "dot " + (s?.server ? (s?.scanning ? "busy" : "on") : ""); dot.className = "dot " + (s?.server ? (s?.scanning ? "busy" : "on") : "");