feat(aegis): 0.10.0 — shorten the balance by default, full precision on demand
Eight decimals of tail sat exactly where the eye lands. The hero now shows two decimals once there is a whole part, because the whole units already carry the significant digits, and digs past leading zeros otherwise so amounts under 1 keep roughly four significant figures: 1204.23234145 -> 1,204.23 0.23234145 -> 0.2323 0.00012345 -> 0.0001234 0.00000001 -> 0.00000001 A flat "two decimal places" rule is the obvious version of this and it is wrong: it renders 0.00042 BCH as 0.00 and the wallet reads as empty. The rule here never collapses a non-zero balance to zeros, and 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 gives the exact figure, clicking pins full precision and is remembered. Only this hero abbreviates — Send, MAX, history and the wallet strip still call fmtBig, so every number acted on is exact. Also repairs the regexes in the same function, which lost their backslashes when the earlier commit was scripted: /^(-?)(\d+)(\.\d+)?$/ had become /^(-?)(d+)(.d+)?$/, matching the letter "d". Still valid JS, so it parsed cleanly and every balance quietly fell through to unformatted text — the grouping and the dimmed decimals introduced in bb639a9 had never once run. Caught by testing the function body extracted from the file rather than a transcription of it.
This commit is contained in:
parent
570e9e6213
commit
18f2839bd4
2 changed files with 73 additions and 11 deletions
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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] ? '<span class="dec">' + m[3] + "</span>" : "");
|
||||
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
|
||||
? '<span class="dec">.' + frac + (cut ? '<span class="more">' + String.fromCharCode(0x2026) + "</span>" : "") + "</span>"
|
||||
: "");
|
||||
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");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue