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.
This commit is contained in:
Local Dev 2026-09-27 20:17:08 +02:00
parent e0246f8520
commit b0d70dd942
4 changed files with 100 additions and 4 deletions

View file

@ -1,17 +1,29 @@
{
"id": "aegis",
"name": "Aegis Wallet",
"version": "0.11.0",
"version": "0.12.0",
"category": "plugin",
"description": "Multi-chain wallet (BCH, BTC, TRX, ETH, SOL, SC, DGB) derived from your Theseus vault. Dapps get window.bitcoincash on .x sites; window.tronWeb / window.tronLink / window.ethereum / window.solana on any https page.",
"author": "Silent Mode",
"icon": "data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none'%3E%3Cpolygon points='16,2 28,9 28,23 16,30 4,23 4,9' fill='%230a0a0d' stroke='%23D6FF3D' stroke-width='1.6' stroke-linejoin='round'/%3E%3Ccircle cx='16' cy='16' r='4.5' fill='none' stroke='%23D6FF3D' stroke-width='1.4'/%3E%3Ccircle cx='16' cy='16' r='1.6' fill='%23D6FF3D'/%3E%3C/svg%3E",
"main": "index.js",
"updateURL": "https://navigate.st/bns/theseus.x/extensions/aegis/updates.json",
"capabilities": ["sidebar-panel", "vault-derive", "page-inject", "approval-modal", "scan-page"],
"absorbs": ["bchwallet", "siawallet"],
"capabilities": [
"sidebar-panel",
"vault-derive",
"page-inject",
"approval-modal",
"scan-page",
"open-tab"
],
"absorbs": [
"bchwallet",
"siawallet"
],
"page-inject": {
"preload": "wallet-inject.js",
"origins": ["https://*/*"]
"origins": [
"https://*/*"
]
}
}

View file

@ -1598,6 +1598,20 @@ function registerPanelMessages(api) {
return { entry, rt };
}
// Open the wallet as a full Theseus tab. It loads panel.html — the very
// same file the sidebar uses — with ?surface=web, because an add-on's own
// tab is handed the same window.silentmode surface and main dispatches it
// as from:"panel". So this is a re-layout, not a second wallet: one set of
// handlers, one UI, no drift between the two.
api.onMessage("openFullScreen", (_p, m) => {
fromPanel(m);
if (typeof api.openTab !== "function") {
throw new Error("This Theseus build can't open add-on tabs — update Theseus.");
}
api.openTab("panel.html", { query: { surface: "web" } });
return true;
});
api.onMessage("promotePreview", async (p, m) => {
fromPanel(m);
const { entry, rt } = promotableImport(p && p.walletId);

View file

@ -202,6 +202,58 @@
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. */
@ -659,6 +711,7 @@
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>

View file

@ -3,6 +3,16 @@
// input; it never touches keys or the vault.
const $ = (id) => document.getElementById(id);
const S = window.silentmode;
// Which surface is this? The sidebar panel and the full-screen tab are the
// SAME file — an add-on's own tab is handed a window.silentmode with the
// same invoke/on surface, and main dispatches it as from:"panel", so
// everything below works identically in both. The flag only drives layout.
const SURFACE = (function () {
try { return new URL(location.href).searchParams.get("surface") || "panel"; }
catch (e) { return "panel"; }
})();
if (SURFACE === "web") document.documentElement.dataset.surface = "web";
let state = null; // full state (all wallets + selected)
let tab = "receive";
let unit = null; // "big" | "small" — chain-dependent
@ -454,6 +464,13 @@ $("pickerBtn").addEventListener("click", (e) => {
// 0.8.4: separate netchip row. Click jumps straight to the browse:chain
// view for the current wallet's chain — the network-chip strip at the
// top of that view is what actually switches networks.
// Open the same wallet as a full Theseus tab. CSS hides this chip on the
// full-screen surface, so it never offers to open a second copy of itself.
$("hFull") && $("hFull").addEventListener("click", async (e) => {
e.stopPropagation();
try { await S.invoke("openFullScreen"); }
catch (err) { showErr(cleanErr(err)); }
});
$("hNet").addEventListener("click", (e) => {
e.stopPropagation();
const w = (state?.wallets || []).find((x) => x.id === state?.selectedWalletId);