diff --git a/bundled-addons/aegis/panel.html b/bundled-addons/aegis/panel.html
index bdfda373..dba704de 100644
--- a/bundled-addons/aegis/panel.html
+++ b/bundled-addons/aegis/panel.html
@@ -41,6 +41,11 @@
.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; }
diff --git a/bundled-addons/aegis/panel.js b/bundled-addons/aegis/panel.js
index 3d0b6e07..0e8d21ff 100644
--- a/bundled-addons/aegis/panel.js
+++ b/bundled-addons/aegis/panel.js
@@ -140,22 +140,79 @@ function fmtBig(units, dec) {
const s = (Number(units || 0) / Math.pow(10, d)).toFixed(d);
return s.replace(/(\.\d*?[1-9])0+$|\.0+$/, "$1");
}
-// Paint the header balance. Groups the integer part with thousands
-// separators and wraps the fraction in .dec so CSS can dim it — the
-// "$98,230.02" treatment the reference wallets lead with. Anything that is
-// not a bare number (an em dash, a skeleton, an error) falls through to
-// plain text. The regex guarantees the interpolated pieces are digits,
-// sign and dot only, so building this with innerHTML cannot inject markup.
+// Header balance. Groups the integer part and dims the fraction — the
+// "98,230.02" treatment the reference wallets lead with — and by default
+// shortens a long tail, because eight decimals of noise sit exactly where
+// the eye lands.
+//
+// The shortening NEVER rounds and never collapses a non-zero balance to
+// zeros, which is the way this idea normally goes wrong: a flat "2 decimal
+// places" rule renders 0.00042 BCH as "0.00" and the wallet reads as empty.
+// Instead it keeps two decimals minimum and then extends past any leading
+// zeros so roughly four significant digits always survive, capped at what
+// the amount actually has:
+//
+// 1204.23234145 -> 1,204.23 0.23234145 -> 0.2323
+// 0.00012345 -> 0.0001234 0.00000001 -> 0.00000001 (1 sat, intact)
+//
+// It truncates rather than rounds, so the figure shown is never more than
+// the wallet holds and sending the displayed amount always clears.
+// An ellipsis marks the cut, hover shows the exact figure, and clicking
+// pins full precision (remembered). Only this hero is abbreviated: Send,
+// MAX, history and the wallet strip keep calling fmtBig directly, so every
+// number you act on is exact.
+const BAL_MIN_DP = 2, BAL_SIG_DP = 4;
+let balFullText = "—";
+let balFullPrecision = false;
+try { balFullPrecision = localStorage.getItem("aegis/fullPrecision") === "1"; } catch (_e) {}
+
function setBalMain(text) {
+ balFullText = String(text == null ? "—" : text);
+ paintBalMain();
+}
+
+function paintBalMain() {
const el = $("balMain");
if (!el) return;
- const s = String(text == null ? "—" : text);
- const m = /^(-?)(d+)(.d+)?$/.exec(s);
- if (!m) { el.textContent = s; return; }
- const grouped = m[2].replace(/B(?=(d{3})+(?!d))/g, ",");
- el.innerHTML = m[1] + grouped + (m[3] ? '' + m[3] + "" : "");
+ const m = /^(-?)(\d+)(\.\d+)?$/.exec(balFullText);
+ if (!m) {
+ el.textContent = balFullText;
+ el.removeAttribute("title");
+ el.classList.remove("balx");
+ return;
+ }
+ const group = (d) => d.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+ const head = m[1] + group(m[2]);
+ const fullFrac = m[3] ? m[3].slice(1) : "";
+ let frac = fullFrac, cut = false;
+ if (!balFullPrecision && fullFrac) {
+ // Whole units already carry the significant digits, so anything with a
+ // non-zero integer part only needs the two decimals. It is the amounts
+ // under 1 that have to dig past their leading zeros to keep any meaning.
+ const whole = m[2] !== "0";
+ const zeros = (/^0*/.exec(fullFrac) || [""])[0].length;
+ const want = whole ? BAL_MIN_DP : zeros + BAL_SIG_DP;
+ const keep = Math.max(BAL_MIN_DP, Math.min(fullFrac.length, want));
+ if (keep < fullFrac.length) { frac = fullFrac.slice(0, keep); cut = true; }
+ }
+ // Every interpolated piece is digits, a sign, a dot or a comma the regex
+ // vouched for, so innerHTML here cannot inject markup.
+ el.innerHTML = head + (frac
+ ? '.' + frac + (cut ? '' + String.fromCharCode(0x2026) + "" : "") + ""
+ : "");
+ const exact = head + (fullFrac ? "." + fullFrac : "");
+ const toggleable = cut || balFullPrecision;
+ el.title = cut ? exact + " — click for every decimal"
+ : (balFullPrecision ? exact + " — click to shorten" : exact);
+ el.classList.toggle("balx", toggleable);
}
+$("balMain") && $("balMain").addEventListener("click", () => {
+ balFullPrecision = !balFullPrecision;
+ try { localStorage.setItem("aegis/fullPrecision", balFullPrecision ? "1" : "0"); } catch (_e) {}
+ paintBalMain();
+});
+
function fmtSmall(units) {
if (typeof units === "string" && /^-?\d+$/.test(units)) return units.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return Number(units || 0).toLocaleString("en-US");