/* ═══════════════════════════════════════════════════════════════════
logos.jsx — real coin logos with graceful letter-glyph fallback
Load AFTER the page's CoinGlyph is defined: this wraps it. Coins the
icon CDN doesn't know simply keep their colored letter glyph.
═══════════════════════════════════════════════════════════════════ */
/* ── Accent theme (set on the Settings page) — applied on EVERY page since
they all load this file. Overrides the --amber CSS vars site-wide. ── */
(function () {
window.ST_ACCENTS = {
amber: { c: '#F7A600', dim: 'rgba(247,166,0,0.10)', line: 'rgba(247,166,0,0.22)' },
teal: { c: '#2BD4C0', dim: 'rgba(43,212,192,0.10)', line: 'rgba(43,212,192,0.24)' },
violet: { c: '#B68CFF', dim: 'rgba(182,140,255,0.10)',line: 'rgba(182,140,255,0.24)' },
blue: { c: '#6EA8FF', dim: 'rgba(110,168,255,0.10)',line: 'rgba(110,168,255,0.24)' },
green: { c: '#2EE49B', dim: 'rgba(46,228,155,0.10)', line: 'rgba(46,228,155,0.24)' },
};
/* paint the CSS vars only — no persistence (used by the server-fallback path) */
const _paintAccent = (name) => {
const a = window.ST_ACCENTS[name] || window.ST_ACCENTS.amber;
const r = document.documentElement.style;
r.setProperty('--amber', a.c);
r.setProperty('--amber-dim', a.dim);
r.setProperty('--amber-line', a.line);
};
/* the picker: paint AND remember this device's choice (instant, no flash) */
window.ST_applyAccent = (name) => {
_paintAccent(name);
try { localStorage.setItem('st_accent', name); } catch (e) {}
};
/* Accent resolution on load:
1. this device's saved choice wins (instant, survives offline)
2. otherwise fall back to the account-level choice on the server, so the
accent follows Nizam onto a fresh browser / another device. We DON'T
persist the server value — localStorage stays empty so the server
remains the source of truth until a deliberate pick on this device. */
try {
const saved = localStorage.getItem('st_accent');
if (saved) {
if (saved !== 'amber') _paintAccent(saved);
} else {
const api = window.ST_API || ((location.hostname === 'localhost' || location.hostname === '127.0.0.1')
? 'http://127.0.0.1:8000/api' : location.origin + '/api');
fetch(api + '/settings')
.then(r => (r.ok ? r.json() : null))
.then(s => {
const a = s && s.theme_accent;
if (a && a !== 'amber' && window.ST_ACCENTS[a]) _paintAccent(a);
})
.catch(() => {});
}
} catch (e) {}
})();
(function () {
/* coincap uses lowercase symbols; a few of ours need mapping; null = no
icon exists upstream (fallback renders) */
const OVERRIDES = { POL: 'matic', POPCAT: null, HYPE: null, SPX: null };
window.ST_LOGO_URL = (sym) => {
if (!sym) return null;
const key = (sym in OVERRIDES) ? OVERRIDES[sym] : sym.toLowerCase();
return key ? `https://assets.coincap.io/assets/icons/${key}@2x.png` : null;
};
const LetterGlyph = window.CoinGlyph || null;
function CoinLogo({ sym, size = 32 }) {
const [err, setErr] = React.useState(false);
const url = window.ST_LOGO_URL(sym);
if (err || !url) {
return LetterGlyph
?
: {(sym || '?')[0]};
}
return (
setErr(true)}
style={{ borderRadius: '50%', display: 'block', flexShrink: 0,
background: 'rgba(255,255,255,0.04)' }} />
);
}
window.CoinLogo = CoinLogo;
if (LetterGlyph) window.CoinGlyph = CoinLogo; /* drop-in upgrade site-wide */
})();