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.
1280 lines
84 KiB
HTML
1280 lines
84 KiB
HTML
<!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; }
|
||
/* 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; }
|
||
#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; }
|
||
.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); }
|
||
/* 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; }
|
||
/* 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; }
|
||
.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; }
|
||
|
||
/* ---- 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; }
|
||
.qrwrap { display: grid; place-items: center; padding: 12px; background: #fff; border-radius: 10px; margin-top: 12px; }
|
||
.qrwrap[hidden] { display: none; }
|
||
/* 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; }
|
||
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; }
|
||
.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. */
|
||
/* 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
|
||
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;
|
||
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; }
|
||
/* 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;
|
||
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); }
|
||
/* 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;
|
||
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;
|
||
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); }
|
||
/* 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; }
|
||
/* 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],
|
||
#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. -->
|
||
<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">
|
||
<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">
|
||
<!-- 0.9.2: address first, assets next, QR last and collapsed.
|
||
The QR was 200px of always-on vertical space above the thing
|
||
people actually came for, and it pushed the token list below
|
||
the fold. Explorer / Faucet moved to the header. -->
|
||
<div class="card">
|
||
<div class="lbl">Receiving address <span id="addrMeta"></span></div>
|
||
<!-- 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>
|
||
<!-- 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>
|
||
<div class="card" id="tokensCard" hidden style="margin-top:12px">
|
||
<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><!-- /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>
|
||
<!-- 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>
|
||
|
||
<div class="card" style="margin-top:16px" id="wcCard">
|
||
<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.
|
||
</div>
|
||
<div id="wcInputs">
|
||
<div class="field" style="margin-top:8px">
|
||
<input type="text" id="wcUri" spellcheck="false" placeholder="wiz://?p=…&s=…">
|
||
</div>
|
||
<div class="actions">
|
||
<button class="btn primary" id="wcConnectBtn">Connect</button>
|
||
</div>
|
||
</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">
|
||
<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.
|
||
</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>
|
||
</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 -->
|
||
|
||
<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>
|
||
<script src="qr.js"></script>
|
||
<script src="panel.js"></script>
|
||
</body>
|
||
</html>
|