// ─── App principal: theme, navegación, tweaks ────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"accent": "#00FFB2",
"mode": "dark",
"intensity": "media"
}/*EDITMODE-END*/;
// Para light mode usamos versiones más saturadas/oscuras que leen bien sobre blanco
const LIGHT_TONE = {
'#00FFB2': '#00A574',
'#00D4FF': '#0090C2',
'#BD55FF': '#8B22D0',
'#FFB800': '#B07700',
};
function App() {
const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
const [screen, setScreen] = React.useState('detail'); // 'portfolio' | 'detail'
const [symbol, setSymbol] = React.useState('NVRA');
const [holdings, setHoldings] = React.useState(window.HOLDINGS);
const [watchlist, setWatchlist] = React.useState(window.WATCHLIST);
const [query, setQuery] = React.useState('');
const [toast, setToast] = React.useState(null);
const [splashOut, setSplashOut] = React.useState(false);
// Splash inicial
React.useEffect(() => {
const t = setTimeout(() => setSplashOut(true), 850);
return () => clearTimeout(t);
}, []);
// Aplicar tema al :root
React.useEffect(() => {
const root = document.documentElement;
const baseHex = tweaks.accent || '#00FFB2';
const c = tweaks.mode === 'dark' ? baseHex : (LIGHT_TONE[baseHex] || baseHex);
root.style.setProperty('--accent', c);
root.style.setProperty('--accent-glow', hexToRgba(c, tweaks.intensity === 'atrevida' ? 0.55 : tweaks.intensity === 'media' ? 0.38 : 0.18));
root.style.setProperty('--accent-dim', hexToRgba(c, 0.15));
root.style.setProperty('--accent-text', tweaks.mode === 'dark' ? '#07070F' : '#FFFFFF');
root.style.setProperty('--glow-on', tweaks.intensity === 'conservadora' ? '0' : '1');
root.dataset.mode = tweaks.mode;
root.dataset.intensity = tweaks.intensity;
}, [tweaks]);
// Navegación
const navigate = (s, sym) => {
setScreen(s);
if (sym) setSymbol(sym);
window.scrollTo({ top: 0, behavior: 'auto' });
};
const openTicker = (sym) => navigate('detail', sym);
const toggleWatch = (sym) => {
setWatchlist((w) => {
if (w.includes(sym)) {
flash(`${sym} eliminado del seguimiento`);
return w.filter((x) => x !== sym);
}
flash(`${sym} añadido al seguimiento`);
return [sym, ...w];
});
};
const flash = (msg) => {
setToast(msg);
setTimeout(() => setToast(null), 2200);
};
return (
<>
{!splashOut && }
{screen === 'portfolio' && (
)}
{screen === 'detail' && (
navigate('portfolio')}
onOpenTicker={openTicker}
holdings={holdings} watchlist={watchlist}
isWatching={watchlist.includes(symbol)}
onToggleWatch={toggleWatch}
accent="var(--accent)" intensity={tweaks.intensity}/>
)}
{toast && {toast}
}
setTweak('mode', v)}/>
setTweak('accent', v)}/>
setTweak('intensity', v)}/>
Ir a pantalla
>
);
}
// Splash
function Splash() {
return (
);
}
// ─── Helpers ──────────────────────────────────────────────────────────────
function hexToRgba(hex, alpha) {
const h = hex.replace('#', '');
const full = h.length === 3 ? h.replace(/./g, (c) => c + c) : h;
const n = parseInt(full, 16);
const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;
return `rgba(${r},${g},${b},${alpha})`;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render();