theseus/bundled-addons/aegis/panel.html

1294 lines
84 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Aegis Wallet</title>
<!-- 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>
:root { color-scheme: light dark;
--bg:#0e131c; --panel:#141a24; --card:#0f1621; --line:rgba(255,255,255,.09);
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d; --danger:#f6768a; --ok:#5ad38a;
--bch:#0ac18e; --trx:#ff060a; --trxNile:#4d9dff; }
@media (prefers-color-scheme: light) {
:root { --bg:#f8faff; --panel:#ffffff; --card:#f1f4fa; --line:rgba(0,0,0,.10);
--ink:#1a1f2b; --mut:#5c6577; --dim:#8a93a5;
/* Darker acid on light backgrounds — ~7:1 contrast on white. */
--acid: #0AC18E; }
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body { background: var(--bg); color: var(--ink);
font: 13.5px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
display: flex; flex-direction: column; position: relative; }
header { padding: 10px 14px 10px; border-bottom: 1px solid var(--line); background: var(--panel); position: relative; }
.picker { display: flex; align-items: center; justify-content: space-between; gap: 8px; cursor: pointer; user-select: none; }
.picker .t { display: flex; align-items: center; gap: 7px; font-weight: 600; min-width: 0; }
.picker .t .badge { font-size: 15px; line-height: 1; }
.picker .t .lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker .caret { color: var(--mut); font-size: 10px; }
.picker:hover { color: var(--acid); }
.net { display: flex; align-items: center; gap: 6px; color: var(--dim); font-size: 11.5px; white-space: nowrap; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dim); }
.dot.on { background: var(--ok); } .dot.busy { background: #e0b341; }
/* Balance hero. Every modern wallet leads with one large numeral and pushes
the fractional part back to ~40% opacity, so the eye reads magnitude
before precision; the ticker and the fiat conversion are annotations on
that number rather than peers of it. Baseline-aligned flex keeps them on
the numeral's baseline however long the amount grows. */
.bal { margin-top: 10px; font-variant-numeric: tabular-nums; }
.bal .big { font-size: 32px; font-weight: 680; letter-spacing: -.6px; line-height: 1.1;
display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap; }
.bal .big .dec { color: var(--dim); font-weight: 620; }
2026-09-27 10:17:15 +02:00
/* Marks a shortened tail, and only appears when one was actually cut. */
.bal .big .more { opacity: .5; margin-left: 1px; }
/* Pointer only when there is something to toggle — a balance that already
fits shows no affordance it cannot honour. */
.bal .big #balMain.balx { cursor: pointer; }
.bal .big small { font-size: 13px; color: var(--mut); font-weight: 500; margin-left: 0; }
/* Scoped to the hero — .fiat is also the Send form's conversion line. */
.bal .big .fiat { font-size: 14px; margin-left: 6px; }
/* .sub is gone — its contents moved up into .netrow, see below. */
/* Full-panel sheet — fills most of the sidebar but stops above the
footer so the aegis.x brand + version chip stay visible. Users
opening + should still know which build they're on and be able
to hit the update button. */
#drop { position: fixed; left: 0; right: 0; top: 60px; bottom: 30px;
background: var(--panel); border-top: 1px solid var(--line);
box-shadow: 0 -6px 26px rgba(0,0,0,.35); z-index: 20;
display: flex; flex-direction: column; }
#drop[hidden] { display: none; }
#drop .droptabs { display: flex; border-bottom: 1px solid var(--line); flex: none; }
#drop .droptabs button { flex: 1; background: transparent; border: 0; color: var(--dim);
padding: 10px 8px; font: inherit; font-size: 13px; cursor: pointer; border-bottom: 2px solid transparent; }
#drop .droptabs button.on { color: var(--ink); border-bottom-color: var(--acid, #d6ff3d); font-weight: 600; }
#drop .droptabs .closex { flex: none; width: 40px; font-size: 16px; color: var(--dim); border-left: 1px solid var(--line); }
#drop .droppane { flex: 1; overflow-y: auto; padding: 6px; }
#drop .droppane[hidden] { display: none; }
#drop .row, #drop .coinrow { display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: 7px; cursor: pointer; }
#drop .row:hover, #drop .coinrow:hover { background: rgba(255,255,255,.05); }
#drop .row .m, #drop .coinrow .m { flex: 1; min-width: 0; }
#drop .row .m .l, #drop .coinrow .m .l { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#drop .row .m .s, #drop .coinrow .m .s { color: var(--dim); font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#drop .row .v { color: var(--mut); font-size: 12px; font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
#drop .row .v .fs { color: var(--dim); font-size: 11px; margin-top: 2px; }
#drop .row.on { background: rgb(from var(--acid) r g b / .10); }
#drop .coinrow .caret { color: var(--dim); font-size: 11px; }
#drop hr { border: 0; border-top: 1px solid var(--line); margin: 6px 0; }
#drop .addhdr { padding: 5px 8px 3px; color: var(--dim); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
#drop .netgroup { padding: 2px 4px 6px 40px; }
#drop .netgroup[hidden] { display: none; }
#drop .netchoice { padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: 12.5px; color: var(--mut); }
#drop .netchoice:hover { background: rgba(255,255,255,.06); color: var(--ink); }
/* Currency-browse network chip row (0.8.0). Sits above the wallet list
under a coin; clicking a chip switches which subnetwork's wallets
are visible AND persists the choice per chain. */
#drop .brnetrow { display: flex; gap: 6px; padding: 8px 8px 4px; flex-wrap: wrap; border-bottom: 1px solid var(--line); }
#drop .brnet { background: transparent; border: 1px solid var(--line); color: var(--mut);
border-radius: 999px; padding: 3px 10px; font: inherit; font-size: 12px; cursor: pointer;
display: inline-flex; align-items: center; gap: 4px; }
#drop .brnet:hover { border-color: var(--acid, #d6ff3d); color: var(--acid, #d6ff3d); }
#drop .brnet.on { background: rgb(from var(--acid, #d6ff3d) r g b / .14); border-color: rgb(from var(--acid, #d6ff3d) r g b / .55);
color: var(--acid, #d6ff3d); font-weight: 600; }
#drop .brnet .hint { color: currentColor; opacity: .65; font-size: 11px; }
chore(aegis): 0.9.2 — Receive tab reorder, one balance, token history Receive was ordered QR → address → tokens, so 200px of always-on QR sat above the thing people came for and pushed the asset list off screen. - Address first, with Copy and a QR button; the QR expands inline and the choice sticks, because someone who receives by QR wants it every time and someone who copies never does. - Explorer and Faucet moved up to the header status row. They act on the selected wallet, not on the act of receiving, and down there they competed with Copy for the one row that gets used. - Assets card renamed from Tokens and now leads with the native coin, so "what does this wallet hold" is one list rather than two places. - "+ Add another <TICKER>" moved below the address list. The balance appeared three times — header, drilldown subtitle, address row. Now once in the header; the subtitle keeps only the per-unit price, and the per-address amount returns when a coin actually has more than one address to compare. The address row drops its truncated address (the full one is at the top of Receive) and keeps the wallet name. The per-address asset list added in 0.8.8 duplicated the Assets card and is removed — assets live in one place. Token amounts were unreadable: an 18-decimal balance rendered as 60000000.000000005435817984. Capped to 8 decimals with thousands separators, exact value on hover. A symbol claimed by more than one contract now carries a LOOK-ALIKE tag. The test wallet holds four different contracts all calling themselves "Test USDT" — spam mints borrowing a trusted ticker so a careless send lands on the wrong one. We can't tell which is genuine, so we mark every member of the clash rather than guessing. History showed native transfers only, so a wallet that had only ever moved USDT looked empty. TRC20 transfers are merged in newest-first, each carrying its own decimals and ticker (rendering a token against the chain's scale would be off by orders of magnitude). Both feeds are on by default; the checkboxes narrow rather than opt in, and the last one checked can't be unchecked into an empty list.
2026-09-23 01:17:33 +02:00
.histfilter { display: flex; align-items: center; gap: 14px; padding: 0 2px 10px;
border-bottom: 1px solid var(--line); margin-bottom: 8px; font-size: 12px; }
.histfilter[hidden] { display: none; }
/* Assets card rows (0.9.2). The native coin leads the list; a symbol
claimed by several contracts gets a LOOK-ALIKE tag so a borrowed
ticker can't pass for the real token. */
.tx.asset { padding: 7px 8px; border-radius: 6px; }
.tx.asset:hover { background: rgba(255,255,255,.04); }
.tx.asset.native { background: rgb(from var(--acid, #d6ff3d) r g b / .06); }
.ttag.tdupe { background: rgba(224,90,90,.18); color: #f6768a; cursor: help; }
.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; }
/* 0.8.4: hNet became a proper chip on its own row (.netrow) so the
network the wallet is on is easy to spot AND easy to switch.
0.10.0: that row carried one small chip and ~300px of dead space, while
a second row under the balance carried the connection state and the
per-wallet actions. Two thin rows sandwiching the balance pushed the
number people open the panel for into the middle of the header. They are
one row now: [network] [state] ........ [Explorer] [Faucet], above the
balance. The whole row hides together when there is no wallet context,
which is the only case the old .netrow hid in. */
.netrow { display: flex; align-items: center; gap: 7px; margin-top: 8px;
padding: 0 2px; min-width: 0; }
/* Only the hostname may shrink. Without this the flexbox took width off
the network chip first, clipping "Chipnet testnet" to "Chipnet" — the
one label on the row that must never be ambiguous, since it is what
tells you whether you are about to move real money. */
.netrow .netchip, .netrow .net, .netrow .hdracts { flex: none; }
.netrow .netlbl { flex: 1 1 auto; color: var(--dim); font-size: 11.5px; min-width: 0;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.netchip { background: rgba(255,255,255,.05); border: 1px solid var(--line);
color: var(--mut); border-radius: 999px; padding: 3px 10px 3px 8px;
font: inherit; font-size: 11.5px; cursor: pointer; display: inline-flex;
align-items: center; gap: 6px; line-height: 1.2; }
.netchip:hover { border-color: var(--acid, #d6ff3d); color: var(--acid, #d6ff3d); }
.netchip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .7; }
/* Currency-picker search + all-coins catalogue (0.8.4). */
#drop .pickersearch { padding: 8px; border-bottom: 1px solid var(--line); }
2026-09-23 02:49:05 +02:00
/* Search + close share the top row now that the title row is gone. */
#drop .pickersearch-top { display: flex; align-items: center; gap: 6px; }
#drop .pickersearch-top input { flex: 1; min-width: 0; }
#drop .pickersearch input { width: 100%; box-sizing: border-box; background: rgba(255,255,255,.04);
border: 1px solid var(--line); color: var(--ink); border-radius: 7px;
padding: 7px 10px; font: inherit; font-size: 12.5px; outline: none; }
#drop .pickersearch input:focus { border-color: rgb(from var(--acid, #d6ff3d) r g b / .55); }
#drop .catgroup { padding: 4px 6px 2px 10px; color: var(--dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; }
#drop .row.unowned { opacity: .8; }
#drop .row.unowned .v { color: var(--acid, #d6ff3d); font-weight: 600; font-size: 11px; }
chore(aegis): 0.9.2 — Receive tab reorder, one balance, token history Receive was ordered QR → address → tokens, so 200px of always-on QR sat above the thing people came for and pushed the asset list off screen. - Address first, with Copy and a QR button; the QR expands inline and the choice sticks, because someone who receives by QR wants it every time and someone who copies never does. - Explorer and Faucet moved up to the header status row. They act on the selected wallet, not on the act of receiving, and down there they competed with Copy for the one row that gets used. - Assets card renamed from Tokens and now leads with the native coin, so "what does this wallet hold" is one list rather than two places. - "+ Add another <TICKER>" moved below the address list. The balance appeared three times — header, drilldown subtitle, address row. Now once in the header; the subtitle keeps only the per-unit price, and the per-address amount returns when a coin actually has more than one address to compare. The address row drops its truncated address (the full one is at the top of Receive) and keeps the wallet name. The per-address asset list added in 0.8.8 duplicated the Assets card and is removed — assets live in one place. Token amounts were unreadable: an 18-decimal balance rendered as 60000000.000000005435817984. Capped to 8 decimals with thousands separators, exact value on hover. A symbol claimed by more than one contract now carries a LOOK-ALIKE tag. The test wallet holds four different contracts all calling themselves "Test USDT" — spam mints borrowing a trusted ticker so a careless send lands on the wrong one. We can't tell which is genuine, so we mark every member of the clash rather than guessing. History showed native transfers only, so a wallet that had only ever moved USDT looked empty. TRC20 transfers are merged in newest-first, each carrying its own decimals and ticker (rendering a token against the chain's scale would be off by orders of magnitude). Both feeds are on by default; the checkboxes narrow rather than opt in, and the last one checked can't be unchecked into an empty list.
2026-09-23 01:17:33 +02:00
/* Per-wallet actions promoted into the header's status row (0.9.2). */
.hdracts { margin-left: auto; display: inline-flex; gap: 4px; }
.hdract { background: transparent; border: 1px solid var(--line); color: var(--dim);
border-radius: 999px; padding: 1px 9px; font: inherit; font-size: 10.5px;
cursor: pointer; line-height: 1.5; }
.hdract:hover { border-color: var(--acid, #d6ff3d); color: var(--acid, #d6ff3d); }
.hdract[hidden] { display: none; }
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
.fiat { color: var(--dim); font-size: 12.5px; margin-left: 10px; font-weight: 500; letter-spacing: .2px; }
.portfolio { margin-top: 6px; color: var(--mut); font-size: 11.5px; }
.portfolio b { color: var(--ink); font-weight: 600; }
.portfolio[hidden] { display: none; }
.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); }
/* Edit-this-wallet button living inside the label group. Compact and
borderless so it reads as an inline affordance, not a separate
action chip. Fades in on hover of the label row so the header
itself stays visually quiet when the user isn't targeting it. */
.editchip { background: transparent; border: 0; color: var(--dim); cursor: pointer;
padding: 2px 4px; border-radius: 4px; font: inherit; font-size: 12px; line-height: 1;
opacity: .5; transition: opacity .12s, color .12s; margin-left: 2px; }
.picker:hover .editchip { opacity: 1; }
.editchip:hover { color: var(--acid, #d6ff3d); background: rgba(255,255,255,.06); }
/* Send/Receive mode toggle (0.8.0). Segmented control at the top of a
tab; picks whether the body shows the normal flow or the consolidate
inline picker. */
.modetoggle { display: flex; gap: 0; border: 1px solid var(--line); border-radius: 8px; padding: 3px;
margin-bottom: 12px; background: rgba(255,255,255,.03); }
.modetoggle[hidden] { display: none; }
.modetoggle button { flex: 1; background: transparent; border: 0; color: var(--dim);
padding: 6px 8px; font: inherit; font-size: 12.5px; border-radius: 6px;
cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
.modetoggle button.on { background: rgb(from var(--acid, #d6ff3d) r g b / .16); color: var(--acid, #d6ff3d); font-weight: 600; }
.modetoggle button:hover:not(.on) { color: var(--ink); }
.modetoggle .hint { color: currentColor; opacity: .65; font-size: 11px; }
/* Settings section chip nav (0.8.2). Segmented row that pages between
Security / Session / Wallet / Prices / Sites / About cards without
scrolling through the whole tab. */
.setsecnav { display: flex; flex-wrap: wrap; gap: 4px; border: 1px solid var(--line);
border-radius: 8px; padding: 3px; margin-bottom: 14px;
background: rgba(255,255,255,.03); }
.setsecnav button { flex: 1 0 auto; min-width: 62px; background: transparent; border: 0;
color: var(--dim); padding: 6px 10px; font: inherit; font-size: 12px;
border-radius: 6px; cursor: pointer; }
.setsecnav button.on { background: rgb(from var(--acid, #d6ff3d) r g b / .16);
color: var(--acid, #d6ff3d); font-weight: 600; }
.setsecnav button:hover:not(.on) { color: var(--ink); }
/* Segmented pill bar. The underline-tab treatment read as browser chrome,
and a 2px underline under a short label is nearly invisible once the
sidebar is narrow. A filled pill for the active section is what the
wallet apps use and it holds up at any width. The hover tint derives
from --ink so it also shows in the light theme, where a white overlay
would vanish. */
nav { display: flex; gap: 4px; padding: 6px 8px; border-bottom: 1px solid var(--line); background: var(--panel); }
nav button { flex: 1; padding: 7px 0; border: 0; border-radius: 999px; background: transparent;
color: var(--mut); cursor: pointer; font: inherit; font-size: 12.5px;
transition: background .12s, color .12s; }
nav button.on { background: var(--acid); color: #0b0f08; font-weight: 650; }
nav button:hover:not(.on) { background: rgb(from var(--ink) r g b / .08); color: var(--ink); }
main { flex: 1; overflow: auto; padding: 14px; }
feat(aegis): 0.12.0 — open the wallet full screen, sidebar becomes the rail The 380px panel is the wrong shape for anything that needs room. This opens the wallet as a full Theseus tab, with the sidebar's own furniture — identity, network, balance, section nav, wallet list — laid out as a left rail and the tab body given to the selected section. It is the SAME panel.html, loaded with ?surface=web. No second wallet, no second copy of 4,600 lines to drift apart. That works because an add-on's own tab is handed a window.silentmode with the same invoke/on surface as the sidebar, and addon-msg dispatches it as from:"panel" with the add-on identity derived from the file:// sender — so every existing handler, including the panel-only ones, works there untouched. The whole change is a CSS grid behind one attribute plus a chip to open it. Details that needed care: - The QR is drag-sized against a 380px panel and the size is remembered. Given a 720px column it filled the page, so it is capped on this surface only; the stored sidebar preference is left exactly as the user set it. - #drop (the coin picker sheet) is fixed-position and sized for the panel; it is pinned to the rail instead of covering the window. - Content columns are capped at 720px so forms and lists keep the measure the sidebar already tuned, rather than stretching across a monitor. - Under 900px the grid falls back to the stacked layout, so a narrow window degrades to what the sidebar already does. - The "open full screen" chip hides itself on the full-screen surface, so it cannot open a second copy of itself. Everything is scoped to [data-surface="web"], so the sidebar is byte-for-byte unchanged. Verified both surfaces, the narrow fallback (by exercising the real media rule) and zero horizontal overflow. The aegis.x/app URL still needs a main.js route in Theseus; this ships the destination over the add-on channel first.
2026-09-27 20:17:08 +02:00
/* ---- full-screen surface (?surface=web) -------------------------------
The same panel.html, opened as a Theseus tab through the "open-tab"
capability. Nothing is duplicated: an add-on's own tab gets a
window.silentmode with the SAME invoke/on surface as the sidebar, and
main dispatches it as from:"panel", so panel.js runs here unchanged and
this is purely a re-layout.
The sidebar becomes a left rail — identity, network, balance, section
nav and the wallet list, in the order they already appear — and the
tab body gets the rest of the window. Everything below only applies
when the surface flag is set, so the 380px panel is untouched. */
:root[data-surface="web"] body {
display: grid;
grid-template-columns: 340px minmax(0, 1fr);
grid-template-rows: auto auto minmax(0, 1fr) auto;
grid-template-areas: "head main" "nav main" "strip main" "foot main";
height: 100vh; overflow: hidden;
}
:root[data-surface="web"] header { grid-area: head; }
:root[data-surface="web"] nav { grid-area: nav; }
:root[data-surface="web"] .wstrip{ grid-area: strip; max-height: none; }
:root[data-surface="web"] .brandfoot { grid-area: foot; position: static; }
:root[data-surface="web"] main {
grid-area: main; padding: 22px 26px 40px;
border-left: 1px solid var(--line); background: var(--bg);
}
/* Forms and lists stop stretching into unreadable line lengths on a wide
monitor; the column keeps the measure the sidebar already tuned for. */
:root[data-surface="web"] main > #tabs,
:root[data-surface="web"] main > .gate { max-width: 720px; }
/* The picker sheet is fixed-position and sized for a 380px panel. Pin it
to the rail instead of letting it cover the whole window. */
:root[data-surface="web"] #drop { right: auto; width: 340px; bottom: 34px; }
/* The lock screen still owns the whole window — a locked vault should not
look like a half-usable app. */
:root[data-surface="web"] #lockScreen { inset: 0; }
@media (max-width: 900px) {
/* Narrow window: fall back to the stacked sidebar layout. */
:root[data-surface="web"] body {
display: flex; flex-direction: column; height: auto; overflow: visible;
}
:root[data-surface="web"] main { border-left: 0; padding: 14px; }
:root[data-surface="web"] #drop { right: 0; width: auto; }
}
/* The QR is drag-sized against the 380px sidebar and its size is
remembered. Given a 720px column it stretches to fill and dominates the
page, so cap it here rather than touching the stored size — the user's
sidebar preference stays exactly as they left it. */
:root[data-surface="web"] .qrwrap { max-width: 320px; }
/* "Open full screen" chip — hidden once we ARE the full-screen surface. */
:root[data-surface="web"] #hFull { display: none; }
/* Persistent aegis.x brand strip. Sticks to the panel's bottom edge so the
wallet always advertises its own front-door site — helps users bookmark
it, and doubles as a version marker for support triage. */
.brandfoot { flex: none; display: flex; align-items: center; justify-content: space-between;
padding: 6px 12px; border-top: 1px solid var(--line); background: var(--panel);
font-size: 11px; color: var(--dim); }
.brandlink { display: inline-flex; align-items: center; gap: 5px; color: var(--dim); text-decoration: none;
padding: 2px 4px; border-radius: 4px; }
.brandlink:hover { color: var(--acid, #d6ff3d); }
.brandlink svg { color: currentColor; }
.brandver { font-variant-numeric: tabular-nums; letter-spacing: .3px; }
.brandfoot-right { display: inline-flex; align-items: center; gap: 8px; }
.brandcheck { background: transparent; border: 0; color: var(--dim); cursor: pointer; padding: 0 2px;
font: inherit; font-size: 12px; line-height: 1; opacity: .7; transition: opacity .12s, color .12s; }
.brandcheck:hover { color: var(--acid, #d6ff3d); opacity: 1; }
.brandcheck.spin { animation: brandspin 1s linear infinite; }
@keyframes brandspin { to { transform: rotate(360deg); } }
.brandupd { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; padding: 2px 8px;
border-radius: 999px; background: rgb(from var(--acid, #d6ff3d) r g b / .14); color: var(--acid, #d6ff3d);
cursor: pointer; text-decoration: none; font-weight: 600;
transition: opacity .2s; }
.brandupd[hidden] { display: none; }
.brandupd:hover { background: rgb(from var(--acid, #d6ff3d) r g b / .22); }
/* Transient states painted by paintFooterUpdate: "✓ Up to date" after a
manual check when no newer version is available, and "⚠ Check failed"
when the OTA fetch errors. Both auto-hide after ~2s; the styling reads
as ephemeral confirmation rather than a persistent chip. */
.brandupd.brandok { background: rgba(255,255,255,.06); color: var(--dim); font-weight: 500;
cursor: default; }
.brandupd.brandok:hover { background: rgba(255,255,255,.06); }
.brandupd.branderr { background: rgba(224,90,90,.18); color: #e05a5a; font-weight: 500;
cursor: default; }
.brandupd.branderr:hover { background: rgba(224,90,90,.18); }
/* In-progress state: painted while the panel is fetching / staging /
restarting. Not clickable, dimmer than the acid CTA chip so users
don't try to smash it. */
.brandupd.brandwait { background: rgba(255,255,255,.06); color: var(--mut);
font-weight: 500; cursor: default; }
.brandupd.brandwait:hover { background: rgba(255,255,255,.06); }
section[hidden] { display: none; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 12px; }
.mono { font: 12.5px/1.45 ui-monospace, "Cascadia Code", Consolas, monospace; word-break: break-all; }
.lbl { color: var(--dim); font-size: 11.5px; margin-bottom: 4px; }
.btn { padding: 7px 12px; border-radius: 7px; border: 1px solid var(--line); background: var(--panel); color: var(--ink);
cursor: pointer; font: inherit; font-size: 12.5px; }
.btn:hover { border-color: rgb(from var(--acid) r g b / .4); }
.btn.primary { background: var(--acid); color: #0b0e14; border-color: transparent; font-weight: 650; }
.btn.primary:disabled { opacity: .45; cursor: default; }
.btn.danger { color: var(--danger); }
.btn.sm { padding: 4px 9px; font-size: 12px; }
.actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
chore(aegis): 0.9.2 — Receive tab reorder, one balance, token history Receive was ordered QR → address → tokens, so 200px of always-on QR sat above the thing people came for and pushed the asset list off screen. - Address first, with Copy and a QR button; the QR expands inline and the choice sticks, because someone who receives by QR wants it every time and someone who copies never does. - Explorer and Faucet moved up to the header status row. They act on the selected wallet, not on the act of receiving, and down there they competed with Copy for the one row that gets used. - Assets card renamed from Tokens and now leads with the native coin, so "what does this wallet hold" is one list rather than two places. - "+ Add another <TICKER>" moved below the address list. The balance appeared three times — header, drilldown subtitle, address row. Now once in the header; the subtitle keeps only the per-unit price, and the per-address amount returns when a coin actually has more than one address to compare. The address row drops its truncated address (the full one is at the top of Receive) and keeps the wallet name. The per-address asset list added in 0.8.8 duplicated the Assets card and is removed — assets live in one place. Token amounts were unreadable: an 18-decimal balance rendered as 60000000.000000005435817984. Capped to 8 decimals with thousands separators, exact value on hover. A symbol claimed by more than one contract now carries a LOOK-ALIKE tag. The test wallet holds four different contracts all calling themselves "Test USDT" — spam mints borrowing a trusted ticker so a careless send lands on the wrong one. We can't tell which is genuine, so we mark every member of the clash rather than guessing. History showed native transfers only, so a wallet that had only ever moved USDT looked empty. TRC20 transfers are merged in newest-first, each carrying its own decimals and ticker (rendering a token against the chain's scale would be off by orders of magnitude). Both feeds are on by default; the checkboxes narrow rather than opt in, and the last one checked can't be unchecked into an empty list.
2026-09-23 01:17:33 +02:00
.qrwrap { display: grid; place-items: center; padding: 12px; background: #fff; border-radius: 10px; margin-top: 12px; }
.qrwrap[hidden] { display: none; }
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
/* The canvas scales to whatever height the user dragged the panel to,
and never wider than the sidebar. aspect-ratio keeps it square. */
.qrwrap canvas { width: auto; height: auto; max-width: 100%;
aspect-ratio: 1 / 1; image-rendering: pixelated; }
/* Drag handle under the QR. A thin strip rather than a corner grip, so
it stays reachable at any sidebar width. */
.qrgrip { height: 14px; margin: 2px 0 -4px; display: grid; place-items: center;
cursor: ns-resize; touch-action: none; }
.qrgrip span { display: block; width: 34px; height: 3px; border-radius: 999px;
background: var(--line); transition: background .12s; }
.qrgrip:hover span, .qrgrip.dragging span { background: var(--acid, #d6ff3d); }
/* Address + inline copy. The value clamps to two lines; anything longer
is cut rather than growing the card. */
.addrrow { display: grid; grid-template-columns: minmax(0,1fr) 30px; gap: 6px;
align-items: start; }
.addrval { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
overflow: hidden; word-break: break-all; line-height: 1.35; }
.addrcopy { background: transparent; border: 1px solid var(--line); color: var(--dim);
cursor: pointer; width: 30px; height: 30px; border-radius: 7px;
display: inline-flex; align-items: center; justify-content: center;
line-height: 0; transition: color .12s, border-color .12s, background .12s; }
.addrcopy:hover { color: var(--acid, #d6ff3d); border-color: rgb(from var(--acid, #d6ff3d) r g b / .5); }
.addrcopy.copied { color: var(--acid, #d6ff3d); border-color: rgb(from var(--acid, #d6ff3d) r g b / .6); }
.addrcopy svg { width: 15px; height: 15px; display: block; }
/* Hide the action row entirely when its only child is hidden, so it
doesn't leave a gap under the address. */
#addrActions:not(:has(> :not([hidden]))) { display: none; }
canvas { image-rendering: pixelated; }
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
input[type=text], input[type=number], input[type=password], textarea { width: 100%; padding: 7px 9px; border-radius: 7px; background: var(--panel);
border: 1px solid var(--line); color: var(--ink); font: inherit; font-size: 13px; outline: none; }
input:focus, textarea:focus { border-color: rgb(from var(--acid) r g b / .5); }
textarea { resize: vertical; min-height: 96px; font: 12px/1.45 ui-monospace, Consolas, monospace; }
.field { margin-bottom: 12px; }
.hint { color: var(--dim); font-size: 11.5px; margin-top: 4px; }
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
.switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; font-size: 12.5px; color: var(--ink); }
.switch input { accent-color: var(--brand, #d6ff3d); width: 15px; height: 15px; margin: 0; }
.amt { display: flex; gap: 6px; }
.amt input { flex: 1; }
.unit { display: flex; border: 1px solid var(--line); border-radius: 7px; overflow: hidden; }
.unit button { border: 0; background: var(--panel); color: var(--mut); padding: 0 10px; cursor: pointer; font: inherit; font-size: 12px; }
.unit button.on { background: rgb(from var(--acid) r g b / .14); color: var(--acid); }
.fee { display: flex; align-items: center; gap: 10px; }
.fee input[type=range] { flex: 1; accent-color: var(--acid); }
.fee .v { color: var(--mut); font-size: 12px; min-width: 70px; text-align: right; font-variant-numeric: tabular-nums; }
.summary { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; font-size: 12.5px; margin-top: 10px; }
.summary .k { color: var(--dim); } .summary .v { text-align: right; font-variant-numeric: tabular-nums; }
.msg { margin-top: 10px; font-size: 12.5px; padding: 8px 10px; border-radius: 7px; }
.msg.err { background: rgba(246,118,138,.12); color: var(--danger); }
.msg.ok { background: rgba(90,211,138,.12); color: var(--ok); }
.msg a { color: inherit; }
.tx { display: grid; grid-template-columns: 22px 1fr auto; gap: 2px 10px; padding: 9px 4px; border-bottom: 1px solid var(--line); cursor: pointer; }
.tx:hover { background: rgba(255,255,255,.03); }
.tx .ic { font-size: 15px; line-height: 1.3; }
.tx .ic.in { color: var(--ok); } .tx .ic.out { color: var(--danger); }
.tx .what { font-size: 12.5px; }
.tx .when { color: var(--dim); font-size: 11.5px; grid-column: 2; }
.tx .amt2 { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; font-size: 12.5px; }
.tx .amt2.in { color: var(--ok); }
.tx .conf { grid-column: 3; text-align: right; color: var(--dim); font-size: 11px; }
.tx .conf.pending { color: #e0b341; }
.empty { color: var(--dim); text-align: center; padding: 30px 10px; font-size: 12.5px; }
.gate { padding: 40px 18px; text-align: center; color: var(--mut); }
.gate .big { font-size: 28px; margin-bottom: 8px; }
.gate b { color: var(--ink); }
.kv { margin-top: 10px; }
.kv .lbl { margin-top: 8px; }
a.link { color: var(--acid); text-decoration: none; cursor: pointer; }
/* Preset-server checkbox list — one row per known Electrum endpoint, plus
any URL the user added via the "Add a custom server" reveal. Rows show
the host + a small hover-to-remove for user-added entries. */
.serverlist { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }
.serverlist label { display: flex; align-items: center; gap: 8px; padding: 5px 6px;
border-radius: 6px; cursor: pointer; font-size: 12.5px; color: var(--mut); }
.serverlist label:hover { background: rgba(255,255,255,.04); color: var(--ink); }
.serverlist input[type=checkbox] { accent-color: var(--acid, #d6ff3d); width: 14px; height: 14px; margin: 0; }
.serverlist .surl { flex: 1; font: 11.5px/1.4 ui-monospace, Consolas, monospace; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.serverlist .sremove { flex: none; background: transparent; border: 0; color: var(--dim); cursor: pointer; padding: 0 4px; opacity: 0; }
.serverlist label:hover .sremove { opacity: 1; }
.serverlist .sremove:hover { color: var(--danger, #f6768a); }
/* Always-visible wallet list — vertical column of rows so users can scan
names + balances without dragging chips sideways. Capped at ~40vh so a
giant list still leaves the main content readable; scrolls inside. */
.wstrip { flex: none; display: flex; flex-direction: column; gap: 1px; padding: 4px 6px 5px 6px;
max-height: 44vh; overflow-y: auto; scrollbar-width: thin;
background: var(--panel); border-bottom: 1px solid var(--line); }
.wstrip::-webkit-scrollbar { width: 4px; }
.wstrip::-webkit-scrollbar-thumb { background: var(--line); border-radius: 2px; }
.wstrip .wchip { display: flex; align-items: center; gap: 4px; padding: 4px 6px 4px 4px;
background: transparent; border: 1px solid transparent; border-radius: 7px;
font: inherit; font-size: 12.5px; color: var(--mut); min-width: 0; }
.wstrip .wchip:hover { background: rgba(255,255,255,.04); color: var(--ink); }
.wstrip .wchip.on { background: rgb(from var(--acid, #d6ff3d) r g b / .10);
border-color: rgb(from var(--acid, #d6ff3d) r g b / .35);
color: var(--ink); }
.wstrip .wchip .wclick { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;
cursor: pointer; padding: 2px 4px; border-radius: 5px; }
.wstrip .wchip .wtext { flex: 1; min-width: 0; overflow: hidden; display: inline-flex;
align-items: center; gap: 6px; flex-wrap: nowrap; }
.wstrip .wchip .wname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wstrip .wchip .wsub { color: var(--dim); font-size: 10.5px; font-weight: 400; margin-left: 6px;
font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Price chip sitting right next to the wallet label. Acid tint with a
subtle glow lifts it off the grey row so users spot movement without
hunting; the small background gives it structural presence too. */
.wstrip .wchip .wprice { color: var(--acid, #d6ff3d); font-size: 11px; font-weight: 600;
font-variant-numeric: tabular-nums; white-space: nowrap;
padding: 1px 6px; border-radius: 999px;
background: rgb(from var(--acid, #d6ff3d) r g b / .10);
text-shadow: 0 0 6px rgb(from var(--acid, #d6ff3d) r g b / .35); }
.wstrip .wchip .wact { flex: none; background: transparent; border: 0; color: var(--dim);
cursor: pointer; padding: 3px 6px; border-radius: 5px; font-size: 12px; line-height: 1; }
.wstrip .wchip .wact:hover { color: var(--acid, #d6ff3d); background: rgba(255,255,255,.06); }
/* Four-column layout: [logo][ticker+price stack][amount+fiat stack][actions].
The old separate Price and Chain columns collapsed into the ticker cell —
per-unit price sits directly under the ticker so the eye can read
"BCH · $430" as one unit before jumping to the balance on the right.
A non-mainnet row also carries a small network pill inline with the
ticker (Chipnet / Sepolia / …); mainnet rows omit it entirely. */
.wstrip { padding: 4px 6px 6px 6px; }
.wstrip .wrow { display: grid; grid-template-columns: 18px minmax(72px,auto) minmax(0,1fr) auto auto;
gap: 8px; align-items: center; padding: 5px 4px;
border-radius: 6px; border: 1px solid transparent; cursor: pointer;
min-height: 30px; }
.wstrip .wrow:hover { background: rgba(255,255,255,.04); }
.wstrip .wrow.on { background: rgb(from var(--acid, #d6ff3d) r g b / .10);
border-color: rgb(from var(--acid, #d6ff3d) r g b / .35); }
.wstrip .wrow.dragging { opacity: .45; cursor: grabbing; }
.wstrip .wrow.drop-before { box-shadow: 0 -2px 0 0 var(--acid, #d6ff3d); }
.wstrip .wrow.drop-after { box-shadow: 0 2px 0 0 var(--acid, #d6ff3d); }
.wstrip .wgrip { display: inline-block; color: var(--dim); font-size: 10px; padding: 0 2px;
cursor: grab; user-select: none; opacity: 0; transition: opacity .12s; }
.wstrip .wrow:hover .wgrip { opacity: 1; }
.wstrip .wrow[draggable="true"] { cursor: grab; }
.wstrip .wcell { min-width: 0; display: inline-flex; align-items: center; gap: 3px; }
.wstrip .wclogo { justify-self: start; align-self: start; padding-top: 2px; }
/* Ticker cell: two-line stack. Line 1 has the ticker, an optional ▾
chevron (only when the chain has more than one network to switch
between), an optional Chipnet/Testnet pill, and the wallet count.
Line 2 is the per-unit price in acid-green. The cell as a whole is
the click target for the network dropdown. */
.wstrip .wcname { flex-direction: column; align-items: flex-start; line-height: 1.15; gap: 1px; }
.wstrip .wcname .wtline { display: inline-flex; align-items: center; gap: 5px; }
.wstrip .wcname .wtck { font-weight: 700; font-size: 13px; color: var(--ink); }
.wstrip .wcname .wchev { color: var(--dim); font-size: 9px; line-height: 1; padding: 0 1px;
transition: color .12s, transform .12s; }
.wstrip .wcname.wswitchable { cursor: pointer; }
.wstrip .wcname.wswitchable:hover .wchev { color: var(--acid, #d6ff3d); }
.wstrip .wcname .wnetpill { font-size: 10px; padding: 1px 6px; border-radius: 999px;
background: rgba(255,255,255,.06); color: var(--mut);
white-space: nowrap; line-height: 1.35; }
.wstrip .wcname .wnetpill.wchipnet { background: rgb(from var(--acid, #d6ff3d) r g b / .18);
color: var(--acid, #d6ff3d); font-weight: 600; }
.wstrip .wcname .wnetpill.wtestnet { background: rgba(224,179,65,.18); color: #e0b341; font-weight: 600; }
/* Multi-wallet count pill inside the ticker cell. When the group has
more than one wallet it doubles as the "swap active wallet" trigger
(▾ chevron), independent of the row's own click target which now
selects the current active wallet directly. */
.wstrip .wcname .wgcount.wgpick { cursor: pointer; }
.wstrip .wcname .wgcount.wgpick:hover { background: rgba(255,255,255,.12); color: var(--acid, #d6ff3d); }
.wstrip .wcname .wgcount .wgchev { color: var(--dim); font-size: 9px; margin-left: 1px; }
.wstrip .wcname .wgcount.wgpick:hover .wgchev { color: var(--acid, #d6ff3d); }
.wstrip .wcname .wgcount { color: var(--dim); font-size: 10.5px; padding: 0 6px; border-radius: 999px;
background: rgba(255,255,255,.06); font-variant-numeric: tabular-nums; line-height: 1.4; }
.wstrip .wcname .wcprice { color: var(--acid, #d6ff3d); font-size: 11px; font-weight: 600;
font-variant-numeric: tabular-nums; white-space: nowrap;
text-shadow: 0 0 5px rgb(from var(--acid, #d6ff3d) r g b / .30); }
.wstrip .wcamt { flex-direction: column; align-items: flex-end; text-align: right;
font-variant-numeric: tabular-nums; line-height: 1.2; }
.wstrip .wcamt .wnative { font-size: 12.5px; color: var(--ink); font-weight: 500; }
.wstrip .wcamt .wfiat { font-size: 11px; color: var(--dim); }
.wstrip .wcact { display: inline-flex; gap: 2px; }
.wstrip .wcact .wact { background: transparent; border: 0; color: var(--dim); cursor: pointer;
padding: 2px 5px; border-radius: 4px; font-size: 12.5px; line-height: 1; }
.wstrip .wcact .wact:hover { color: var(--acid, #d6ff3d); background: rgba(255,255,255,.06); }
/* Network dropdown menu — anchored below the ticker cell on click. Shows
every network under the chain (mainnet + testnets) with its own totals,
so switching networks feels like flipping a segment on the same coin
row rather than jumping to a separate wallet. */
.netmenu { position: absolute; z-index: 60; min-width: 180px; background: var(--panel, #1a1a1a);
border: 1px solid var(--line); border-radius: 8px; padding: 4px;
box-shadow: 0 6px 20px rgba(0,0,0,.35); }
.netmenu .nmitem { display: flex; align-items: center; justify-content: space-between;
gap: 10px; padding: 6px 8px; border-radius: 5px; cursor: pointer;
color: var(--ink); font-size: 12.5px; }
.netmenu .nmitem:hover { background: rgba(255,255,255,.06); }
.netmenu .nmitem.on { background: rgb(from var(--acid, #d6ff3d) r g b / .12);
color: var(--acid, #d6ff3d); }
.netmenu .nmitem .nmname { display: inline-flex; align-items: center; gap: 6px; }
.netmenu .nmitem .nmnet { font-weight: 600; }
.netmenu .nmitem .nmcount { color: var(--dim); font-size: 10.5px; padding: 0 5px; border-radius: 999px;
background: rgba(255,255,255,.06); }
.netmenu .nmitem .nmamt { font-variant-numeric: tabular-nums; font-size: 11.5px; color: var(--dim); }
.netmenu .nmitem.on .nmamt { color: var(--acid, #d6ff3d); }
/* Acid-green testnet tag for the Chipnet group. Overrides the warm-amber
default so chipnet reads as "the friendly BCH testnet" instead of
borrowing the caution palette. */
.ttag.acid { background: rgb(from var(--acid, #d6ff3d) r g b / .18); color: var(--acid, #d6ff3d); }
.wstrip .waddwrap { display: flex; gap: 5px; padding: 0 0 4px 0; margin-bottom: 4px; border-bottom: 1px solid var(--line); }
.wstrip .waddwrap button { flex: 1; background: transparent; border: 1px dashed var(--line); border-radius: 6px;
padding: 5px 8px; color: var(--dim); cursor: pointer; font: inherit; font-size: 12px; line-height: 1; }
.wstrip .waddwrap button:hover { color: var(--acid, #d6ff3d); border-color: var(--acid, #d6ff3d); }
/* Inline coin-address view — takes the place of the six-column grid when
a user opens a multi-wallet group. Back-arrow row + one row per wallet
under the coin. Same container so the visual context stays put. */
.wstrip .wcoinhead { display: flex; align-items: center; gap: 6px; padding: 3px 4px 4px 4px;
border-bottom: 1px solid var(--line); }
.wstrip .wcoinhead .wback { background: transparent; border: 0; color: var(--dim); cursor: pointer;
padding: 2px 4px; border-radius: 4px; font: inherit; font-size: 12px; line-height: 1; }
.wstrip .wcoinhead .wback:hover { color: var(--acid, #d6ff3d); background: rgba(255,255,255,.06); }
.wstrip .wcoinhead .wctitle { flex: 1; min-width: 0; display: inline-flex; align-items: center; gap: 6px;
font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wstrip .wcoinhead .wcount { color: var(--dim); font-size: 11.5px; font-weight: 500; }
/* Inline address-list row (per-coin drilldown). 0.6.35 layout: strictly
columnar so every row's fields line up in the same x positions no
matter how long each label happens to be. The address column is the
only flex one (minmax 0/1fr) — it fills whatever's left after the
fixed cells and truncates with an ellipsis, so a wider sidebar shows
more of the address without the label/balance jumping around. Every
other column has an explicit width — label pill is fixed to 68px so
"a" and "anthem…" occupy the same slot, amount is fixed to 90px so
"0" and "0.000123" right-align identically, actions are fixed to
46px. Net result: columns look like a table, not a flex mess. */
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
/* 0.9.8: five columns for the five cells the row actually renders —
icon · label · copy · amount · menu. The template used to describe
the pre-0.9.2 layout (six columns, including an address cell that no
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
longer exists), so every cell sat one column left of where it
belonged, which is what made copy and amount collide.
0.9.9: the columns are declared ONCE on the list and every row
inherits them via subgrid. Previously each row was its own grid, so
the `auto` amount column resolved per row — a row holding
0.3066756 got a narrower column than one holding 0.43789841, and the
copy button landed at a different x on every line. Column widths
must be shared to line up, and subgrid is what shares them. */
/* Column gap lives on the parent: a subgrid takes its gutters in the
subgridded axis from the grid it inherits, so setting it on the row
would be ignored. */
.wstrip .walist { display: grid;
grid-template-columns: 16px minmax(0,1fr) 24px auto 26px;
column-gap: 8px; row-gap: 2px; }
.wstrip .warow { display: grid;
grid-template-columns: subgrid; grid-column: 1 / -1;
align-items: center; padding: 5px 6px;
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
border-radius: 6px; border: 1px solid transparent; cursor: pointer; min-height: 30px; }
/* Fallback for a host without subgrid: pin the amount column to a fixed
width so the copy column still lines up. Wide enough for eight
decimals of BCH plus its ticker. */
@supports not (grid-template-columns: subgrid) {
.wstrip .walist { display: block; }
.wstrip .warow { grid-template-columns: 16px minmax(0,1fr) 24px 104px 26px;
grid-column: auto; gap: 8px; }
}
.wstrip .warow:hover { background: rgba(255,255,255,.04); }
.wstrip .warow.on { background: rgb(from var(--acid, #d6ff3d) r g b / .10);
border-color: rgb(from var(--acid, #d6ff3d) r g b / .35); }
.wstrip .warow .waaddr { font: 12px/1.15 ui-monospace, Consolas, monospace; color: var(--ink);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
/* Copy chip: its own 24px column between label and amount, so it can
never crowd the number. SVG rather than the 📋 emoji, which rendered
as a tofu box here and read as a glyph stuck to the balance. */
.wstrip .warow .wacopy { background: transparent; border: 0; color: var(--dim); cursor: pointer;
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
padding: 0; border-radius: 5px; line-height: 0;
width: 24px; height: 22px; display: inline-flex;
align-items: center; justify-content: center;
transition: color .12s, background .12s; justify-self: center; }
.wstrip .warow .wacopy svg { width: 13px; height: 13px; display: block; }
.wstrip .warow .wacopy:hover { color: var(--acid, #d6ff3d); background: rgba(255,255,255,.06); }
.wstrip .warow .wacopy.copied { color: var(--acid, #d6ff3d); }
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
/* Label pill, left-aligned in the flexible column so a name reads as
the row's identity rather than floating mid-row. Truncates with an
ellipsis instead of pushing the number around. */
.wstrip .warow .walabel { font-size: 11.5px; color: var(--ink);
padding: 2px 8px; border-radius: 999px;
background: rgba(255,255,255,.07);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
max-width: 100%; box-sizing: border-box;
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
justify-self: start; }
.wstrip .warow .walabel:empty { visibility: hidden; }
/* Balance shares a single line with everything else — no vertical
amount/fiat stack. Ticker follows the number in a dim tone so the
row reads "0 BCH" at a glance. Right-aligned within the fixed
amount column so short and long numbers line up. */
.wstrip .warow .waamt { text-align: right; font-variant-numeric: tabular-nums;
font-size: 12.5px; color: var(--ink); white-space: nowrap;
display: inline-flex; align-items: baseline; gap: 4px;
justify-self: end; overflow: hidden; }
.wstrip .warow .waamt .watkr { color: var(--dim); font-size: 11px; font-weight: 500; }
.wstrip .warow .wact { background: transparent; border: 0; color: var(--dim); cursor: pointer;
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
padding: 2px 4px; border-radius: 4px; font-size: 13px; line-height: 1;
justify-self: end; }
.wstrip .warow .wact:hover { color: var(--acid, #d6ff3d); background: rgba(255,255,255,.06); }
chore(aegis): 0.8.8 — coin drilldown, per-address assets, WizardConnect from the page Wallet strip: - Clicking a coin opens that coin's page (addresses, price, totals, back and close) instead of only flipping the selection and leaving the list sitting there. The page already existed but was reachable only via the small count chip. - The per-coin second action was a gear that selected the wallet and opened the global Settings tab — the same destination for every coin, so it read as a per-coin control that wasn't one. It is now Remove, behind a confirm, with the default/legacy wallet showing a lock instead since it gates legacy funds. - Each address in the drilldown can expand to show what THAT address holds: TRC20/SPL via the adapter's tokens, BCH CashTokens via tokenBalances. walletSummary now carries both per wallet, so the view no longer has to borrow the selected wallet's assets. WizardConnect — the Connect pane was effectively unusable: - The locked-vault branch told the user to unlock and gave them nothing to click. It is reachable without the lock screen ever appearing, because a mounted imported wallet makes overallPhase read "ready". It now carries the same unlock form the lock screen uses. - Imported BCH wallets were never registered with the WC manager — startForWallet ran only in the vault-derived mount branch. They mount as ready, so they appeared in the "Sign with" picker and then failed on pair. They now register from their stored seed. WC derives a child key tree, so single-key (WIF) imports genuinely cannot pair; those are disabled in the picker with the reason, rather than failing on click. - Adds window.wizardconnect so a dapp can hand over the wiz:// URI it already generated instead of making the user copy it between tabs. The protocol is Nostr-relay pairing designed for phone-scans-QR, and the SDK has no in-page discovery at all, so this is our own surface: connect() + isReady(), plus a wizardconnect:announceProvider event shaped like EIP-6963 so several WC wallets can coexist. Pairing always goes through the approval modal; the URI is validated before any UI shows, and the wallet never reads the page to find one.
2026-09-22 23:08:16 +02:00
/* Per-address asset list (0.8.8). The count chip expands the row into a
nested list of what that ONE address holds beyond the native coin. */
.wstrip .waassets { background: rgba(255,255,255,.06); border: 0; color: var(--dim); cursor: pointer;
font: inherit; font-size: 10px; padding: 1px 6px; border-radius: 999px; line-height: 1.5; }
.wstrip .waassets:hover, .wstrip .waassets.on { color: var(--acid, #d6ff3d); background: rgb(from var(--acid, #d6ff3d) r g b / .14); }
.wstrip .waassetlist { padding: 2px 6px 6px 26px; display: flex; flex-direction: column; gap: 2px; }
.wstrip .waasset { display: grid; grid-template-columns: minmax(0,1fr) auto auto; gap: 8px; align-items: baseline;
font-size: 11px; color: var(--mut); padding: 2px 4px; border-radius: 4px; }
.wstrip .waasset:hover { background: rgba(255,255,255,.04); }
.wstrip .waasset .waaname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wstrip .waasset .waaid { color: var(--dim); font-size: 10px; }
.wstrip .waasset .waaamt { font-variant-numeric: tabular-nums; color: var(--ink); }
/* Coin drilldown header: shows the coin's per-unit price + running
total of the addresses below, so the aggregate context isn't lost
when the user is deep in the per-address view. */
.wstrip .wcoinsub { display: flex; align-items: baseline; gap: 8px; padding: 2px 4px 5px 4px;
font-size: 11.5px; color: var(--mut); border-bottom: 1px solid var(--line);
margin-bottom: 3px; }
.wstrip .wcoinsub .wprice { color: var(--acid, #d6ff3d); font-weight: 600; font-variant-numeric: tabular-nums;
text-shadow: 0 0 5px rgb(from var(--acid, #d6ff3d) r g b / .30); }
.wstrip .wcoinsub .wsep { color: var(--dim); }
.wstrip .wcoinsub .wtot { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 500; }
.wstrip .wcoinsub .wtotfiat { color: var(--dim); font-variant-numeric: tabular-nums; }
/* Adapter-error line above the address rows. Shown only when at least
one wallet has a non-null .error — makes silent RPC failures visible
instead of the display quietly rendering 0. */
.wstrip .wcoinerr { color: #e05a5a; font-size: 11px; padding: 4px 6px;
background: rgba(224,90,90,.08); border-radius: 4px;
margin-bottom: 4px; overflow-wrap: anywhere; }
/* Full-panel lock screen — takes over the entire panel below the aegis
footer when the vault is locked or awaiting first-time setup. Rest of
the app (header picker, tabs, wallet strip) is hidden until unlocked
so nothing sensitive shows and there is no accidental interaction
surface. */
#lockScreen[hidden] { display: none; }
#lockScreen { position: absolute; inset: 0 0 30px 0; z-index: 50; background: var(--bg);
display: flex; flex-direction: column; align-items: center; justify-content: center;
padding: 24px 18px; color: var(--ink); text-align: center; }
#lockScreen .aegisMark { width: 64px; height: 64px; margin-bottom: 14px; }
#lockScreen .aegisMark svg { width: 100%; height: 100%; color: var(--acid, #d6ff3d);
filter: drop-shadow(0 0 12px rgb(from var(--acid, #d6ff3d) r g b / .35)); }
#lockScreen h1 { font: 600 16px/1.3 inherit; margin: 0 0 4px 0; letter-spacing: .2px; }
#lockScreen .subhint { color: var(--mut); font-size: 12px; max-width: 320px; margin: 0 0 20px 0; }
chore(aegis): 0.8.9 — ETH/SOL imported history + tokens, centred setup screen Completes the parity work 0.8.7 started for Tron. Imported ETH and SOL wallets showed a native balance and nothing else, because the JSON-RPC endpoints they poll have no history or token concept at all. - ETH history + ERC-20 balances via Blockscout, which needs no API key (Etherscan V2 does). Mainnet RPC moves off eth.llamarpc.com, which was answering 525 with an HTML error page — that parsed as a JSON error and showed as a 0 balance. - SOL history via getSignaturesForAddress and SPL balances via getTokenAccountsByOwner, both keyless on the public RPC. Three things the live testing turned up: - A Blockscout mempool entry is {result:"pending", status:null}. Reading that as "not ok, therefore failed" showed pending sends as failures. Now carries a distinct pending state through to the row. - History `delta` is now a number, a decimal string, or null. ETH wei needs the string (18 decimals overflows a JS number, and Math.abs was silently rounding it); Solana's signature feed carries no amount at all, and null >= 0 is true, so unknown amounts were rendering as a "+" that claimed a receive we cannot verify. Unknown now renders as a neutral row instead. - A real address came back with 855 ERC-20s and 3078 SPL mints, nearly all airdrop spam, some with blank, zero-width or bidi-override symbols that render as an empty row borrowing trust from its neighbours. Token text is sanitised and lists are capped at 50, sorted so named tokens survive the cap. Also: the first-run setup screen forced text-align:left on the form, so its helper copy ran ragged under a centred mark, title and description. The form now inherits the centred alignment; the mnemonic box stays left-aligned on purpose, since centring wrapped seed words makes them harder to check.
2026-09-23 00:05:14 +02:00
/* The form inherits the lock screen's centred alignment — it used to
force text-align:left, which left the setup screen's helper copy
running ragged against a centred title, mark and description. */
#lockScreen .lockform { width: min(320px, 100%); display: flex; flex-direction: column; gap: 10px; text-align: center; }
#lockScreen .lockform input[type=password],
chore(aegis): 0.8.9 — ETH/SOL imported history + tokens, centred setup screen Completes the parity work 0.8.7 started for Tron. Imported ETH and SOL wallets showed a native balance and nothing else, because the JSON-RPC endpoints they poll have no history or token concept at all. - ETH history + ERC-20 balances via Blockscout, which needs no API key (Etherscan V2 does). Mainnet RPC moves off eth.llamarpc.com, which was answering 525 with an HTML error page — that parsed as a JSON error and showed as a 0 balance. - SOL history via getSignaturesForAddress and SPL balances via getTokenAccountsByOwner, both keyless on the public RPC. Three things the live testing turned up: - A Blockscout mempool entry is {result:"pending", status:null}. Reading that as "not ok, therefore failed" showed pending sends as failures. Now carries a distinct pending state through to the row. - History `delta` is now a number, a decimal string, or null. ETH wei needs the string (18 decimals overflows a JS number, and Math.abs was silently rounding it); Solana's signature feed carries no amount at all, and null >= 0 is true, so unknown amounts were rendering as a "+" that claimed a receive we cannot verify. Unknown now renders as a neutral row instead. - A real address came back with 855 ERC-20s and 3078 SPL mints, nearly all airdrop spam, some with blank, zero-width or bidi-override symbols that render as an empty row borrowing trust from its neighbours. Token text is sanitised and lists are capped at 50, sorted so named tokens survive the cap. Also: the first-run setup screen forced text-align:left on the form, so its helper copy ran ragged under a centred mark, title and description. The form now inherits the centred alignment; the mnemonic box stays left-aligned on purpose, since centring wrapped seed words makes them harder to check.
2026-09-23 00:05:14 +02:00
#lockScreen .lockform input[type=text] { text-align: center; }
/* The mnemonic stays left-aligned on purpose: 12/24 words wrap across
several lines, and centring makes them ragged on both edges, which is
exactly the wrong thing when someone is checking a seed word by word. */
#lockScreen .lockform textarea { text-align: left; }
#lockScreen .lockform .hint { text-align: center; }
#lockScreen .altline { color: var(--dim); font-size: 11.5px; margin-top: 12px; text-align: center; }
#lockScreen .altline a { color: var(--acid, #d6ff3d); cursor: pointer; text-decoration: none; }
#lockScreen .altline a:hover { text-decoration: underline; }
/* PIN pad — six dots + a 3x4 keypad. Used both in lock screen and in the
PIN approval modal. */
.pinpad { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.pinpad .pindots { display: flex; gap: 12px; }
.pinpad .pindot { width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid var(--dim);
background: transparent; transition: background .12s, border-color .12s; }
.pinpad .pindot.on { background: var(--acid, #d6ff3d); border-color: var(--acid, #d6ff3d);
box-shadow: 0 0 6px rgb(from var(--acid, #d6ff3d) r g b / .5); }
.pinpad .pinkeys { display: grid; grid-template-columns: repeat(3, 62px); gap: 8px; }
.pinpad .pinkeys button { height: 46px; border-radius: 10px; border: 1px solid var(--line);
background: var(--card); color: var(--ink); font: 500 18px inherit;
cursor: pointer; }
.pinpad .pinkeys button:hover { border-color: var(--acid, #d6ff3d); color: var(--acid, #d6ff3d); }
.pinpad .pinkeys button.util { background: transparent; font-size: 13px; color: var(--dim); }
.pinpad .pinerr { color: var(--danger); font-size: 12px; text-align: center; min-height: 16px; }
/* PIN modal overlay — used for set-PIN / change-PIN / verify-PIN flows. */
.pinmodal { position: fixed; inset: 0; background: rgba(0,0,0,.55); display: flex;
align-items: center; justify-content: center; z-index: 9999; padding: 20px; }
.pinmodal .pincard { width: min(94vw, 340px); background: var(--panel); border: 1px solid var(--line);
border-radius: 12px; padding: 18px 16px 14px; box-shadow: 0 10px 40px rgba(0,0,0,.4); }
.pinmodal h2 { margin: 0 0 6px 0; font: 600 15px inherit; }
.pinmodal .pinsub { color: var(--mut); font-size: 12px; margin-bottom: 12px; text-align: center; }
.pinmodal .pinactions { display: flex; gap: 6px; justify-content: center; margin-top: 12px; }
/* General settings cards — pinned at top of Settings tab so cross-cutting
security/policy controls stay in one place, ahead of the per-wallet
stuff below. */
.gsec { margin-bottom: 14px; }
.gsec .gtitle { font: 600 13px inherit; color: var(--ink); margin-bottom: 6px; }
.gsec .gline { display: flex; align-items: center; justify-content: space-between; gap: 10px;
padding: 6px 0; border-top: 1px solid var(--line); }
.gsec .gline:first-of-type { border-top: 0; }
.gsec .gline .glabel { min-width: 0; font-size: 12.5px; color: var(--ink); }
.gsec .gline .ghint { display: block; color: var(--dim); font-size: 11px; margin-top: 2px; }
.gsec .gline .gactions { flex: none; display: inline-flex; gap: 6px; }
.gsec .gsoon { font-size: 10px; padding: 1px 6px; border-radius: 999px; letter-spacing: .04em;
background: rgba(224,179,65,.14); color: #e0b341; text-transform: uppercase; }
</style>
</head>
<body>
<header>
<div class="picker" id="pickerBtn">
<div class="t">
<span class="badge" id="hBadge"></span>
<span class="lbl" id="hLabel">Aegis Wallet</span>
<!-- Edit chip sits inline with the wallet name so it reads as
"edit THIS wallet". Was in picker-actions on the far right
until 0.7.2, which mixed poorly with the global + button. -->
<button type="button" id="hManage" class="editchip" title="Edit this wallet — rename, derivation path, remove">✎</button>
</div>
<div class="picker-actions">
<!-- Single right-corner chip. Opens a compact popover menu with
Create new / Import / Connect / About — replacing the old
three-chip cluster (+ ⋯ ✎). Method chosen there routes into
the coin picker inside #drop or an appropriate modal. -->
feat(aegis): 0.12.0 — open the wallet full screen, sidebar becomes the rail The 380px panel is the wrong shape for anything that needs room. This opens the wallet as a full Theseus tab, with the sidebar's own furniture — identity, network, balance, section nav, wallet list — laid out as a left rail and the tab body given to the selected section. It is the SAME panel.html, loaded with ?surface=web. No second wallet, no second copy of 4,600 lines to drift apart. That works because an add-on's own tab is handed a window.silentmode with the same invoke/on surface as the sidebar, and addon-msg dispatches it as from:"panel" with the add-on identity derived from the file:// sender — so every existing handler, including the panel-only ones, works there untouched. The whole change is a CSS grid behind one attribute plus a chip to open it. Details that needed care: - The QR is drag-sized against a 380px panel and the size is remembered. Given a 720px column it filled the page, so it is capped on this surface only; the stored sidebar preference is left exactly as the user set it. - #drop (the coin picker sheet) is fixed-position and sized for the panel; it is pinned to the rail instead of covering the window. - Content columns are capped at 720px so forms and lists keep the measure the sidebar already tuned, rather than stretching across a monitor. - Under 900px the grid falls back to the stacked layout, so a narrow window degrades to what the sidebar already does. - The "open full screen" chip hides itself on the full-screen surface, so it cannot open a second copy of itself. Everything is scoped to [data-surface="web"], so the sidebar is byte-for-byte unchanged. Verified both surfaces, the narrow fallback (by exercising the real media rule) and zero horizontal overflow. The aegis.x/app URL still needs a main.js route in Theseus; this ships the destination over the add-on channel first.
2026-09-27 20:17:08 +02:00
<button type="button" id="hFull" class="chip" title="Open the wallet full screen">⤢</button>
<button type="button" id="hAdd" class="chip" title="Add or import a wallet">+</button>
</div>
</div>
<!-- 0.8.4: network picker is its own row below the coin label so it
reads as "the network I'm on right now" instead of a tiny suffix
next to the wallet name. Click cycles / opens the network menu. -->
<div class="netrow" id="hNetRow" hidden>
<button type="button" class="netchip" id="hNet" title="Switch network"></button>
<span class="net"><span class="dot" id="dot"></span></span>
<!-- Not just a hostname: this line is also where "connecting…",
"· syncing", "· <n> unconfirmed" and the walletd setup prompt are
written, so it cannot move to Settings without hiding an
unconfirmed balance and a setup instruction. -->
<span class="netlbl" id="netlbl">connecting…</span>
<!-- 0.9.2 moved Explorer/Faucet out of the Receive card because they act
on the selected wallet, not on the act of receiving. They stay a
per-wallet action for the same reason they are not Settings: they
are verbs on the current wallet, not configuration. -->
<span class="hdracts">
<button class="hdract" id="viewAddr" type="button" title="Open this address in a block explorer">Explorer</button>
<button class="hdract" id="openFaucet" type="button" title="Open this testnet's faucet" hidden>Faucet</button>
</span>
</div>
<div id="drop" hidden></div>
<div class="bal">
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
<div class="big">
<span id="balMain">—</span><small id="balTicker">—</small>
<span id="balFiat" class="fiat" hidden></span>
</div>
<div id="portfolio" class="portfolio" hidden></div>
</div>
</header>
<nav>
<button data-tab="receive" class="on">Receive</button>
<button data-tab="send">Send</button>
<button data-tab="history">History</button>
<button data-tab="settings">Settings</button>
</nav>
<!-- Always-visible wallet list. Sits under the Receive/Send/History/Settings
tab bar so the top of the panel stays about the SELECTED wallet, and
the list of siblings/switch-targets reads as a secondary layer below.
[+ Add] opens the Add-only picker; [⋯ More] opens Import/Connect/Manage. -->
<div id="walletStrip" class="wstrip" aria-label="Your wallets"></div>
<main>
<div id="gate" class="gate" hidden></div>
<div id="tabs">
<section id="tab-receive">
<!-- Segmented mode toggle (0.8.0). Switches the tab body between
the normal Receive view (QR + address) and the Consolidate
view (inline picker for sweeping other-wallet balances into
this one). Hidden when there is nothing to consolidate. -->
<div id="rcvModeToggle" class="modetoggle" hidden>
<button data-rcv-mode="receive" class="on" type="button">Receive</button>
<button data-rcv-mode="consolidate" type="button">Consolidate <span id="rcvConsolidateCount" class="hint"></span></button>
</div>
<div id="rcvNormal">
feat(aegis): 0.16.0 — Address & QR and Assets are peer chips, and the coin row loses its dropdown Two changes to the same idea: one way to do each thing, and the same shape for every coin. **Assets is no longer buried under the QR.** The Receive body stacked address → QR → assets in one column, so in a 380px panel the token list sat permanently below the fold behind 200px of QR — and a coin holding no tokens simply lost a section, which made every coin look structurally different. Address & QR and Assets are now peers behind a persistent chip row. The count rides on the chip, and a wallet with nothing says "No assets held by this wallet" instead of the section vanishing. That mattered more than it sounds: a chipnet wallet with 46 CashToken categories was indistinguishable from an empty one until 0.15.0, because the only signal was an absent card. The choice persists across re-renders and wallet switches — comparing token balances between wallets should not throw you back to the QR on every click. Switching back to Address redraws the QR, since a canvas sized while hidden comes out blank and renderReceive only repaints when the address changes. **The coin row's ▾ network dropdown is gone.** The coin view already carries real network chips (data-browse-net: they filter the wallet list and show a per-network count), so the popover was a second control for one job — and the only one that hid its options behind a click. Chips are the single network control now and the ticker is plain text again. Verified in the panel: chips switch panes and track their own state, the count shows, the empty state reads correctly, the QR survives the round trip, and no .wchev / .wcname.wswitchable remain in the DOM.
2026-09-28 23:05:48 +02:00
<!-- 0.16.0: Address-and-QR and Assets are PEERS, switched by a
persistent chip row, instead of one stacked column. Stacking meant
the QR sat between the address and the token list, so assets were
permanently below the fold in a 380px panel; and it made every coin
look different, since a coin with no tokens simply lost a section.
Chips give one shape for every coin and a visible answer either
way. (0.9.2 had already reordered the stack for the same reason —
this replaces that workaround.) -->
<div id="rcvViewToggle" class="modetoggle">
<button data-rcv-view="address" class="on" type="button">Address &amp; QR</button>
<button data-rcv-view="assets" type="button">Assets <span id="rcvAssetsCount" class="hint"></span></button>
</div>
<div id="rcvAddressPane">
<div class="card">
<div class="lbl">Receiving address <span id="addrMeta"></span></div>
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
<!-- 0.9.8: address and its copy button share one row, so the
button sits at the end of the value it copies instead of on a
separate action line. The address clamps to two lines and
truncates beyond that, rather than growing the card in a
narrow sidebar. -->
<div class="addrrow">
<div class="mono addrval" id="addr">—</div>
<button class="addrcopy" id="copyAddr" title="Copy this address"></button>
</div>
<div class="actions" id="addrActions">
<button class="btn sm" id="nextAddr" hidden>Next unused address</button>
</div>
fix(aegis): 0.9.8 — address row columns, resizable QR, inline copy The row's grid template still described the pre-0.9.2 layout — six columns including an address cell that no longer exists — while the row renders five. Every cell therefore sat one column left of where it belonged: copy landed in the label's space, the amount in the old label column, and the ⋯ in the amount column instead of the edge. That is the whole cause of "the copy button collides with the amount" and "the last edit button is not on the edge". Now five columns for five cells: icon · label · copy · amount · menu, 8px gaps. The label is the flexible one, left-aligned, so it takes the slack and truncates rather than squeezing the number. Verified at 520, 400 and 300px: no cell overlap at any width, no row overflow, and the amount never clips — only long labels give way. The copy glyph was the 📋 emoji, which has no glyph in this platform's font stack and rendered as a tofu box that read like a stray character stuck to the balance. Replaced with an inline SVG in both the row and the address card. Both confirmation flashes now swap innerHTML rather than textContent, which would have deleted the SVG and left a blank square. Address card: the address and its copy button share one row, so the button sits at the end of the value it copies. The address clamps to two lines and truncates beyond that instead of growing the card in a narrow sidebar. The QR is always visible and the panel is drag-resizable from a grip under it (pointer events, arrow keys as a non-mouse path, clamped 90–420px, capped against the panel's own width so a size set on a wide sidebar cannot overflow a narrow one, persisted). That replaces the 0.9.2 show/hide toggle — a size set once beats a binary, and it frees the row Copy was sharing with a QR button. drawQr was setting cv.style.width/height, which would have reset the panel to its intrinsic size on every redraw — i.e. every time the address changed. It now sets only the backing store and re-asserts the user's size after drawing. "Next unused address" is correct at the adapter level on both mainnet and chipnet (tested: the index advances and the address changes), so the reported failure is elsewhere. The handler was discarding the error and flashing a bare "Failed", which is why there was nothing to diagnose; it now surfaces the real message.
2026-09-24 22:30:10 +02:00
<!-- Drag the bottom edge to resize the QR. Height persists. -->
<div class="qrwrap" id="qrWrap"><canvas id="qr" width="200" height="200"></canvas></div>
<div class="qrgrip" id="qrGrip" title="Drag to resize the QR code"><span></span></div>
</div>
feat(aegis): 0.16.0 — Address & QR and Assets are peer chips, and the coin row loses its dropdown Two changes to the same idea: one way to do each thing, and the same shape for every coin. **Assets is no longer buried under the QR.** The Receive body stacked address → QR → assets in one column, so in a 380px panel the token list sat permanently below the fold behind 200px of QR — and a coin holding no tokens simply lost a section, which made every coin look structurally different. Address & QR and Assets are now peers behind a persistent chip row. The count rides on the chip, and a wallet with nothing says "No assets held by this wallet" instead of the section vanishing. That mattered more than it sounds: a chipnet wallet with 46 CashToken categories was indistinguishable from an empty one until 0.15.0, because the only signal was an absent card. The choice persists across re-renders and wallet switches — comparing token balances between wallets should not throw you back to the QR on every click. Switching back to Address redraws the QR, since a canvas sized while hidden comes out blank and renderReceive only repaints when the address changes. **The coin row's ▾ network dropdown is gone.** The coin view already carries real network chips (data-browse-net: they filter the wallet list and show a per-network count), so the popover was a second control for one job — and the only one that hid its options behind a click. Chips are the single network control now and the ticker is plain text again. Verified in the panel: chips switch panes and track their own state, the count shows, the empty state reads correctly, the QR survives the round trip, and no .wchev / .wcname.wswitchable remain in the DOM.
2026-09-28 23:05:48 +02:00
</div><!-- /rcvAddressPane -->
<div id="rcvAssetsPane" hidden>
<div class="card" id="tokensCard">
<div class="lbl">Assets <span class="hint" id="tokensCount"></span></div>
<div id="tokensList" class="kv"></div>
<div class="hint" id="tokensHint">Tokens held by this wallet.</div>
</div>
</div>
</div><!-- /rcvNormal -->
<!-- Consolidate view (inline, replaces Receive body when active). -->
<div id="rcvConsolidate" hidden>
<div id="rcvConsolidateInline"></div>
</div>
</section>
<section id="tab-send" hidden>
<!-- Send/Consolidate mode toggle (0.8.0). Same pattern as Receive. -->
<div id="sendModeToggle" class="modetoggle" hidden>
<button data-send-mode="send" class="on" type="button">Send</button>
<button data-send-mode="consolidate" type="button">Consolidate <span id="sendConsolidateCount" class="hint"></span></button>
</div>
<div id="sendNormal">
<!-- Legacy chip retained but hidden — 0.7.7's chip is replaced by
the mode toggle above. Kept in the DOM so existing panel.js
code that reads it doesn't NPE mid-migration. -->
<div id="consolidateChipWrap" hidden style="margin-bottom:12px">
<button id="consolidateChip" class="btn sm" type="button" style="width:100%;text-align:left;display:flex;align-items:center;gap:8px;justify-content:space-between">
<span>⇢ Consolidate balances into <b id="consolidateChipDest">this wallet</b></span>
<span class="hint" id="consolidateChipCount"></span>
</button>
</div>
<div class="field" id="sendAssetField" hidden>
<div class="lbl">Asset</div>
<select id="sendAsset"></select>
<div class="hint">Pick the native coin or an SPL token in this wallet.</div>
</div>
<div class="field">
<div class="lbl">Recipient</div>
<input type="text" id="sendTo" spellcheck="false" autocomplete="off" placeholder="…">
<div class="hint" id="sendToHint"></div>
</div>
<div class="field">
<div class="lbl">Amount <span id="sendAmtFiat" class="fiat" style="margin-left:8px" hidden></span></div>
<div class="amt">
<input type="text" id="sendAmt" inputmode="decimal" autocomplete="off" placeholder="0.00">
<div class="unit" id="unitPicker"></div>
<button class="btn sm" id="sendMax" type="button">Max</button>
</div>
</div>
<div class="field" id="feeField">
<div class="lbl">Fee</div>
<div class="fee"><input type="range" id="feeRate" min="1" max="5" step="1" value="1"><span class="v" id="feeLbl">1 sat/B</span></div>
</div>
<!-- Optional OP_RETURN memo. BCH-only for now (UTXO chains only);
the field hides when a non-BCH wallet is selected. 220 byte cap
keeps the tx under standardness relay policy. Memo is public on
the ledger — the placeholder text warns before someone types. -->
<div class="field" id="memoField" hidden>
<div class="lbl">Memo <span class="hint" style="margin-left:8px;font-weight:normal">optional · public on-chain · ≤220 bytes</span></div>
<input type="text" id="sendMemo" maxlength="220" spellcheck="false" autocomplete="off"
placeholder="e.g. Invoice #1234 — visible to anyone">
</div>
<div class="card">
<div class="summary">
<div class="k">Amount</div><div class="v" id="sumAmt">—</div>
<div class="k">Network fee</div><div class="v" id="sumFee">—</div>
<div class="k">Total</div><div class="v" id="sumTotal">—</div>
</div>
</div>
<div class="actions"><button class="btn primary" id="sendBtn" disabled>Send</button></div>
<div class="msg" id="sendMsg" hidden></div>
</div><!-- /sendNormal -->
<div id="sendConsolidate" hidden>
<div id="sendConsolidateInline"></div>
</div>
</section>
<section id="tab-history" hidden>
chore(aegis): 0.9.2 — Receive tab reorder, one balance, token history Receive was ordered QR → address → tokens, so 200px of always-on QR sat above the thing people came for and pushed the asset list off screen. - Address first, with Copy and a QR button; the QR expands inline and the choice sticks, because someone who receives by QR wants it every time and someone who copies never does. - Explorer and Faucet moved up to the header status row. They act on the selected wallet, not on the act of receiving, and down there they competed with Copy for the one row that gets used. - Assets card renamed from Tokens and now leads with the native coin, so "what does this wallet hold" is one list rather than two places. - "+ Add another <TICKER>" moved below the address list. The balance appeared three times — header, drilldown subtitle, address row. Now once in the header; the subtitle keeps only the per-unit price, and the per-address amount returns when a coin actually has more than one address to compare. The address row drops its truncated address (the full one is at the top of Receive) and keeps the wallet name. The per-address asset list added in 0.8.8 duplicated the Assets card and is removed — assets live in one place. Token amounts were unreadable: an 18-decimal balance rendered as 60000000.000000005435817984. Capped to 8 decimals with thousands separators, exact value on hover. A symbol claimed by more than one contract now carries a LOOK-ALIKE tag. The test wallet holds four different contracts all calling themselves "Test USDT" — spam mints borrowing a trusted ticker so a careless send lands on the wrong one. We can't tell which is genuine, so we mark every member of the clash rather than guessing. History showed native transfers only, so a wallet that had only ever moved USDT looked empty. TRC20 transfers are merged in newest-first, each carrying its own decimals and ticker (rendering a token against the chain's scale would be off by orders of magnitude). Both feeds are on by default; the checkboxes narrow rather than opt in, and the last one checked can't be unchecked into an empty list.
2026-09-23 01:17:33 +02:00
<!-- 0.9.2: token movement used to be invisible here — a wallet that
only ever moved USDT looked like it had no history. Both feeds
are on by default; the filters exist to narrow, not to opt in. -->
<div class="histfilter" id="histFilter" hidden>
<label class="switch"><input type="checkbox" id="histNative" checked><span id="histNativeLbl">Coin</span></label>
<label class="switch"><input type="checkbox" id="histTokens" checked><span>Tokens</span></label>
<span class="hint" id="histCount" style="margin-left:auto"></span>
</div>
<div id="txlist"></div>
</section>
<section id="tab-settings" hidden>
<!-- 0.8.2: Settings sectioned. Chip row at top picks which section
is visible; each section wraps a group of related cards. The
chain-specific cards (bchSettings / trxSettings / …) sit
inside the "Wallet" section and their own hidden-vs-shown
toggle (per selected chain) still applies within it. -->
<div class="setsecnav" id="setsecnav">
<button data-setsec="security" class="on" type="button">Security</button>
<button data-setsec="session" type="button">Session</button>
<button data-setsec="wallet" type="button">Wallet</button>
<button data-setsec="prices" type="button">Prices</button>
<button data-setsec="sites" type="button">Sites</button>
<button data-setsec="about" type="button">About</button>
</div>
<div data-setsec-body="security">
<!-- Cross-cutting security / policy controls, ahead of anything
per-wallet or chain-specific. Present even when the vault is
still locked so users can set up a PIN or read the multi-sig
roadmap without unlocking first (they only do the ACTUAL PIN
enrollment after a fresh unlock, though). -->
<div class="card gsec" id="genSecurity" style="margin-bottom:14px">
<div class="gtitle">Security</div>
<div class="gline">
<div class="glabel">
Quick-access PIN
<span class="ghint" id="pinStatusHint">Off — Aegis asks for the master password every time.</span>
</div>
<div class="gactions">
<button class="btn sm" id="gsPinSet" hidden>Set PIN…</button>
<button class="btn sm" id="gsPinChange" hidden>Change…</button>
<button class="btn sm danger" id="gsPinRemove" hidden>Remove</button>
</div>
</div>
<div class="gline" id="gsRequirePinLine" hidden>
<div class="glabel">
Require PIN for sending
<span class="ghint">Prompts for your PIN on every panel-initiated Send. Dapp-driven approvals still use the standard approval overlay.</span>
</div>
<div class="gactions">
<label class="switch"><input type="checkbox" id="gsRequirePin"><span></span></label>
</div>
</div>
</div>
</div><!-- /security section (multi-sig card lives inside it below) -->
<div data-setsec-body="session" hidden>
<!-- Session: stay-signed-in + idle auto-lock + manual sign-out.
Uses electron.safeStorage under the hood so the master password
is only decryptable under this OS user account. When "Lock on
Navigator close" is on (the default), Aegis stores nothing on
disk and forces a fresh unlock on every launch. -->
<div class="card gsec" id="genSession" style="margin-bottom:14px">
<div class="gtitle">Session</div>
<div class="gline">
<div class="glabel">
Lock on Navigator close
<span class="ghint" id="gsSessionHint">On — Aegis asks for the master password (or PIN) every time Theseus starts. Turn off to stay signed in across restarts via the OS keystore.</span>
</div>
<div class="gactions">
<label class="switch"><input type="checkbox" id="gsLockOnClose" checked><span></span></label>
</div>
</div>
<div class="gline">
<div class="glabel">
Auto-lock when idle
<span class="ghint">Locks the vault after this long with no panel activity. Applies inside the wallet panel; a locked panel elsewhere in the browser doesn't count as idle.</span>
</div>
<div class="gactions">
<select id="gsIdleMinutes" style="padding:5px 8px;border-radius:6px;background:var(--panel);border:1px solid var(--line);color:var(--ink);font-size:12px">
<option value="0">Never</option>
<option value="5">5 min</option>
<option value="15" selected>15 min</option>
<option value="30">30 min</option>
<option value="60">1 hour</option>
<option value="180">3 hours</option>
</select>
</div>
</div>
<div class="gline">
<div class="glabel">
Sign out now
<span class="ghint">Locks the vault immediately. Any dapp connections stay paired, but Aegis will refuse to sign until you unlock again.</span>
</div>
<div class="gactions">
<button class="btn sm danger" id="gsSignOut">Sign out</button>
</div>
</div>
</div>
<div class="card gsec" id="genMasterPw" style="margin-bottom:14px">
<div class="gtitle">Master password</div>
<div class="gline">
<div class="glabel">
Change master password
<span class="ghint">Opens Theseus <span class="mono">Settings › Passwords</span>. The vault re-encrypts under the new password; every derived wallet keeps working.</span>
</div>
<div class="gactions">
<button class="btn sm" id="gsOpenPasswords">Open Passwords…</button>
</div>
</div>
</div>
</div><!-- /session section -->
<div data-setsec-body="security" hidden>
<div class="card gsec" id="genMultiSig" style="margin-bottom:14px">
<div class="gtitle">Second-device approval <span class="gsoon">soon</span></div>
<div class="gline">
<div class="glabel">
Co-sign sends from a second device
<span class="ghint">Aegis will pair with Ariadne on your phone so large transfers need a nod from both places before they broadcast. Not shipped yet.</span>
</div>
<div class="gactions">
<button class="btn sm" disabled title="Not shipped yet">Set up…</button>
</div>
</div>
</div>
</div><!-- /security-multisig -->
<div data-setsec-body="wallet" hidden>
<div class="card" id="walletManage" style="margin-bottom:14px">
<div class="lbl">This wallet</div>
<div class="field" style="margin-top:6px;margin-bottom:8px">
<input type="text" id="renameLabel" placeholder="Wallet name">
</div>
<div class="actions">
<button class="btn sm" id="renameBtn">Rename</button>
<button class="btn sm danger" id="removeBtn">Remove wallet</button>
</div>
<div class="hint" id="removeHint"></div>
</div>
<div id="bchSettings" hidden>
<div class="field">
<div class="lbl">Derivation path (account)</div>
<input type="text" id="setPath" spellcheck="false" placeholder="m/44'/145'/0'">
<div class="hint">Changing this switches to a different set of addresses under the same wallet seed.</div>
</div>
<div class="field" id="bchServersRow">
<div class="lbl">Electrum servers <span class="hint" style="font-weight:normal">(BCH mainnet — shared across BCH wallets)</span></div>
<div id="setServersList" class="serverlist"></div>
<details style="margin-top:6px">
<summary style="cursor:pointer;color:var(--dim);font-size:11.5px">Add a custom server…</summary>
<div style="display:flex;gap:6px;margin-top:6px">
<input type="text" id="setServersCustom" spellcheck="false" placeholder="wss://host:port" style="flex:1">
<button class="btn sm" id="addCustomServer" type="button">Add</button>
</div>
</details>
<div class="hint" id="serverHint"></div>
</div>
<div class="actions">
<button class="btn primary" id="applySettings">Apply</button>
<button class="btn" id="resetServers">Reset servers</button>
</div>
<div class="card" style="margin-top:16px">
<div class="lbl">Recovery info</div>
<div class="hint">Keys come from your Theseus password vault under <span class="mono" id="purpose"></span>. The same vault seed on another machine recreates this wallet.</div>
<div class="kv" id="recovery"></div>
<div class="actions">
<button class="btn" id="showXpub">Show account xpub</button>
<button class="btn danger" id="showXprv">Show account private key</button>
</div>
</div>
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
<div class="card" style="margin-top:16px" id="wcCard">
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
<div class="lbl">WizardConnect</div>
<div class="hint" style="margin-bottom:8px">
Pair this BCH wallet with a dapp that speaks the WizardConnect protocol
(Cauldron, Moria, or any site built on the SDK). Paste the <span class="mono">wiz://</span>
URI the dapp shows in its Connect dialog. Aegis signs every transaction only after you approve it.
</div>
<!-- Warning that appears when the selected wallet is imported —
WC signing needs the derive-based signer path, which imported
wallets don't have yet. Shown in place of the input so users
don't try to paste a URI and hit "wallet not ready". -->
<div class="msg err" id="wcImportedNotice" hidden style="margin-bottom:8px">
WizardConnect isn't available for imported wallets yet — pairing
uses Aegis' vault-derived signer path, and imported wallets are
read-only for now. Coming in a later release.
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
</div>
<div id="wcInputs">
<div class="field" style="margin-top:8px">
<input type="text" id="wcUri" spellcheck="false" placeholder="wiz://?p=…&amp;s=…">
</div>
<div class="actions">
<button class="btn primary" id="wcConnectBtn">Connect</button>
</div>
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
</div>
<div class="msg" id="wcMsg" hidden></div>
<div class="kv" id="wcSites" style="margin-top:8px"></div>
</div>
</div>
<div id="trxSettings" hidden>
<div class="card">
<div class="lbl">Recovery info</div>
<div class="hint">Keys come from your Theseus password vault under <span class="mono" id="trxPurpose"></span>. Import into TronLink with the derivation path <span class="mono">m/44'/195'/0'/0/0</span> from the same vault seed.</div>
</div>
</div>
<div id="scSettings" hidden>
<div class="field">
<div class="lbl">walletd URL</div>
<input type="text" id="setWalletdUrl" spellcheck="false" placeholder="https://your-walletd.example/api">
<div class="hint">Any public or self-hosted <span class="mono">go.sia.tech/walletd</span> in "full" index mode. The URL is per-wallet, so different Sia sub-accounts can point at different nodes. It is not shared with anyone else.</div>
</div>
<div class="actions">
<button class="btn primary" id="applyWalletdUrl">Apply</button>
</div>
<div class="card" style="margin-top:16px">
<div class="lbl">Recovery info</div>
<div class="hint">Keys come from your Theseus password vault under <span class="mono" id="scPurpose"></span>. Address scheme: walletd <span class="mono">KeyFromSeed(seed, index)</span>.</div>
<div class="kv" id="scRecovery"></div>
<div class="actions">
<button class="btn danger" id="showScSeed">Reveal wallet seed</button>
</div>
</div>
</div>
<div id="dgbSettings" hidden>
<div class="field">
<div class="lbl">Address family</div>
<select id="setDgbFamily"></select>
<div class="hint">Picks the BIP purpose that shapes your DGB addresses. Switching rebuilds the wallet against a different set of addresses under the same seed — old funds don't move; they still live under the family they were received on.</div>
</div>
<div class="field">
<div class="lbl">Derivation path (account)</div>
<input type="text" id="setDgbPath" spellcheck="false" placeholder="m/84'/20'/0'">
<div class="hint">Auto-filled from the family above. Edit only if you need a non-default account.</div>
</div>
<div class="actions">
<button class="btn primary" id="applyDgbPath">Apply</button>
</div>
<div class="card" style="margin-top:16px">
<div class="lbl">Recovery info</div>
<div class="hint">Keys come from your Theseus password vault under <span class="mono" id="dgbPurpose"></span>. Any BIP39 tool set to <span class="mono">DGB</span> (coin type 20) at the same purpose can reproduce this wallet.</div>
<div class="kv" id="dgbRecovery"></div>
<div class="actions">
<button class="btn" id="showDgbXpub">Show account xpub</button>
<button class="btn danger" id="showDgbXprv">Show account private key</button>
</div>
</div>
</div>
<div id="btcSettings" hidden>
<div class="field">
<div class="lbl">Address family</div>
<select id="setBtcFamily"></select>
<div class="hint">Picks the BIP purpose that shapes your Bitcoin addresses. Switching rebuilds the wallet against a different set of addresses under the same seed — old funds don't move; they still live under the family they were received on.</div>
</div>
<div class="field">
<div class="lbl">Derivation path (account)</div>
<input type="text" id="setBtcPath" spellcheck="false" placeholder="m/84'/0'/0'">
<div class="hint">Auto-filled from the family above. Edit only if you need a non-default account.</div>
</div>
<div class="actions">
<button class="btn primary" id="applyBtcPath">Apply</button>
</div>
<div class="card" style="margin-top:16px">
<div class="lbl">Recovery info</div>
<div class="hint">Keys come from your Theseus password vault under <span class="mono" id="btcPurpose"></span>. Any BIP39 tool at coin type 0 (mainnet) / 1 (testnet) and the same purpose can reproduce this wallet.</div>
<div class="kv" id="btcRecovery"></div>
<div class="actions">
<button class="btn" id="showBtcXpub">Show account xpub</button>
<button class="btn danger" id="showBtcXprv">Show account private key</button>
</div>
</div>
</div>
<div id="ethSettings" hidden>
<div class="field">
<div class="lbl">RPC URL</div>
<input type="text" id="setEthRpcUrl" spellcheck="false" placeholder="https://cloudflare-eth.com">
<div class="hint">JSON-RPC endpoint Aegis reads balances and broadcasts from. Any provider works — public gateways (Cloudflare, PublicNode), Alchemy, Infura, or your own node. Stored per-wallet; not shared.</div>
</div>
<div class="actions">
<button class="btn primary" id="applyEthRpc">Apply</button>
</div>
<div class="card" style="margin-top:16px">
<div class="lbl">Recovery info</div>
<div class="hint">Keys come from your Theseus password vault under <span class="mono" id="ethPurpose"></span>. Import into MetaMask / any Ethereum wallet using the derivation path <span class="mono">m/44'/60'/0'/0/0</span> from the same vault seed.</div>
<div class="kv" id="ethRecovery"></div>
<div class="actions">
<button class="btn danger" id="showEthKey">Reveal private key</button>
</div>
</div>
</div>
<div id="solSettings" hidden>
<div class="field">
<div class="lbl">RPC URL</div>
<input type="text" id="setSolRpcUrl" spellcheck="false" placeholder="https://api.mainnet-beta.solana.com">
<div class="hint">JSON-RPC endpoint. The public Solana RPCs are heavily rate-limited — for real use point Aegis at Helius / QuickNode / your own node.</div>
</div>
<div class="actions">
<button class="btn primary" id="applySolRpc">Apply</button>
</div>
<div class="card" style="margin-top:16px">
<div class="lbl">Recovery info</div>
<div class="hint">Keys come from your Theseus password vault under <span class="mono" id="solPurpose"></span>. Import into Phantom / Solflare with the derivation path <span class="mono">m/44'/501'/0'/0'</span> from the same vault seed.</div>
<div class="kv" id="solRecovery"></div>
<div class="actions">
<button class="btn danger" id="showSolKey">Reveal wallet seed</button>
</div>
</div>
</div>
</div><!-- /wallet section -->
<div data-setsec-body="prices" hidden>
<div class="card" style="margin-top:0">
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
<div class="lbl">Fiat prices</div>
<div class="hint" style="margin-bottom:10px">
Off by default. When enabled, Aegis polls every supported oracle in
parallel and reconciles the results — two or more sources agreeing
within ±3% form the trusted price, so no single oracle can quietly
make Aegis show a wrong number. Every enabled oracle sees your IP
+ a User-Agent every poll, which is a signal that a wallet is open
on this machine.
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
</div>
<div class="actions" style="align-items:center;gap:12px">
<label class="switch">
<input type="checkbox" id="pricesToggle">
<span>Enable USD prices</span>
</label>
<button class="btn sm" id="refreshPrices" hidden>Refresh now</button>
<span class="hint" id="pricesStatus" style="margin-left:auto"></span>
</div>
<!-- Per-source status: which oracles are up, which are down, and
the last per-chain reconciliation method (majority / median /
single). Populated from the priceFeed snapshot. -->
<div id="pricesSources" class="hint" style="margin-top:10px"></div>
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
</div>
<!-- Hidden legacy select so panel.js code that reads .value doesn't
NPE while we transition; select stays hidden and empty. -->
<select id="pricesSource" hidden></select>
<span id="pricesSourceHint" hidden></span>
</div><!-- /prices section -->
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect Aegis Wallet 0.4.4 → 0.6.1: - Vault lifecycle from the wallet gate. The locked / not-yet-created states now show a master-password form (with optional BIP39 mnemonic on setup) instead of redirecting users to Settings › Passwords. New api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by the existing "vault-derive" capability. api.openSettings(section) also added; settings.html honours a #section hash on open. - Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44 path or a WIF; the cashaddr is derived in the add-on, the signer material goes to a separate wallet-imports.enc via api.vault.imports {list, add, remove, signer}. Argus password-vault gains createImports / unlockImports / saveImports with its own KDF salt so the imports key is disjoint from the passwords key. lib/chain-bch-imported.js is a single-address Electrum adapter; spend support is deferred to M.1b. - Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted in add-on storage. Fiat lines under balances, in the wallet picker, and a portfolio total when 2+ wallets are open. Settings tab is now reachable while the vault is locked so the toggle is always available. - WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js). @wizardconnect/{core,wallet} are loaded dynamically via api.import to stay on the right side of LGPL §4d. Sign requests go through approvalModal and are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS. - DGB adapter load is now soft-fail: when Aegis runs from userData/addons the bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of taking the whole add-on down.
2026-09-09 10:33:21 +02:00
<div data-setsec-body="sites" hidden>
<div class="card" style="margin-top:0">
<div class="lbl">Connected sites</div>
<div class="hint">Sites allowed to see your address, allowances for silent BCH payments, and Tron dapps you've connected. Message signing always asks.</div>
<div id="sites" class="kv"></div>
</div>
</div><!-- /sites section -->
<div data-setsec-body="about" hidden>
<div class="card" style="margin-top:0">
<div class="lbl">About Aegis</div>
<div class="hint" style="margin-bottom:10px">
Aegis is Silent Mode's built-in multi-chain wallet. Keys are derived
from your Theseus vault — same vault, every wallet across every
chain. Aegis lives at <a class="link" id="aboutOpenAegisSite">aegis.x</a> and
this build's version + update chip sit at the bottom-right of the
panel.
</div>
<div class="hint">
Silent Mode — <a class="link" id="aboutOpenSilentmodeSite">silentmode.st</a> ·
docs on the plugin system live at <span class="mono">theseus.x/plug-ins</span>.
</div>
</div>
</div><!-- /about section -->
<div class="msg err" id="settingsMsg" hidden></div>
</section>
</div>
</main>
<!-- Full-panel lock screen. Absolutely positioned so it takes over the
entire content area (everything above the aegis footer) whenever the
vault is locked or awaiting first-time setup — the header picker, nav
tabs, and wallet strip stay in the DOM but sit visually under it. The
Settings tab still hides itself; nothing here interacts with it. -->
<div id="lockScreen" hidden>
<div class="aegisMark" aria-hidden="true">
<svg viewBox="0 0 32 32" fill="none">
<polygon points="16,2 28,9 28,23 16,30 4,23 4,9" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
<circle cx="16" cy="16" r="4.5" fill="none" stroke="currentColor" stroke-width="1.4"/>
<circle cx="16" cy="16" r="1.6" fill="currentColor"/>
</svg>
</div>
<h1 id="lockTitle">Unlock Aegis</h1>
<div class="subhint" id="lockSub">Aegis derives its keys from your Theseus vault. There's nothing separate to unlock — the vault is your wallet.</div>
<div id="lockBody"></div>
</div>
<footer class="brandfoot" id="brandFoot">
<a href="#" id="brandLink" class="brandlink" title="Open aegis.x">
<svg viewBox="0 0 32 32" width="14" height="14" aria-hidden="true"><polygon points="16,2 28,9 28,23 16,30 4,23 4,9" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/><circle cx="16" cy="16" r="4.3" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="16" cy="16" r="1.3" fill="currentColor"/></svg>
<span>aegis.x</span>
</a>
<!-- Update controls (0.8.1): the update chip lives INSIDE the right-side
group, sharing the slot with the version marker. paintFooterUpdate()
hides brandVer when a newer build is available and shows brandUpdate
in its place — clicking the acid-green "↑ v0.8.1" text stages the
download and relaunches Theseus. Idle: brandVer visible, brandUpdate
hidden. Same one-click Update/Install path the user asked for, at
the exact spot they identified. -->
<span class="brandfoot-right">
<button id="brandCheck" class="brandcheck" type="button" title="Check for updates">↻</button>
<span id="brandUpdate" class="brandupd" hidden></span>
<span class="brandver" id="brandVer"></span>
</span>
</footer>
feat(aegis): 0.13.0 — read a seed phrase from a QR image Import accepts a picture of a QR code. The file is decoded in the panel by the vendored jsQR, drawn to a canvas: no upload, no network, no camera. Image, not camera, on purpose. Theseus sets blockCamera + hideMediaDevices by default and hides device labels from fingerprinting; a camera scanner would fail silently for everyone until they turned that off globally, and a wallet should not be the reason a privacy browser gives up the camera. A photo or screenshot of the code needs no permission at all. What comes back is classified, never trusted. A QR is opaque to the person holding it, and "scan this to restore your wallet" is a working phish, so the decode only chooses which field to fill: BIP39-shaped (12/15/18/21/24 lowercase words) -> mnemonic field WIF or 32-byte hex -> private key field anything else -> nothing is filled; the decoded text is shown so the user can see it was a URL, an address, or junk Nothing auto-submits. The user reads what landed in the box and presses Import, and the host handler still does the real validation. jsQR 1.4.0 is vendored at lib/jsqr.js under Apache-2.0 with its LICENSE beside it, unmodified and unminified — code that touches seed phrases should be auditable in the shipped add-on, not an opaque blob. It is 57 KB gzipped. Verified by round-tripping through the shipped path: Aegis's own encoder builds the QR, it is rasterised to a real PNG File, and decodeQrFile() reads it back byte-exact; an image with no code returns null rather than throwing; and driving the actual file input fills the mnemonic for a seed, fills the key field for a WIF, and leaves every field untouched for a phishing URL.
2026-09-28 00:05:04 +02:00
<script src="lib/jsqr.js"></script>
<script src="qr.js"></script>
<script src="panel.js"></script>
</body>
</html>