// Generated, do not edit. sha256:98f4651e5cb7036965f92ae8206b5dd6cbf62ce4e42388c68825bc8d9de6ca9d import { configureSharedUi } from './ui/shared-setup.js'; import './edition.js'; import { assertConfig, ROUTE_KEYS, hashFromLocation } from '../shared/config.js'; import { listAssets } from './data/assets.js'; import { refreshNotifications } from './data/notifications.js'; import { initCommands } from './commands.js'; import { loadAppSettings, appSetting } from './app-settings.js'; import { loadLicense } from './license.js'; import { t, setLocale } from './i18n/index.js'; import { initStatusBar } from './ui/status-bar.js'; import { renderErrorPanel } from './ui/error-panel.js'; import { createRenderTurns } from './ui/render-turn.js'; import { finishSplash, initSplashReplay } from './ui/splash.js'; import { refreshActivePortfolio, watchPortfolioChanges, cacheSleutels } from './portfolio.js'; import { applyAurora } from './ui/aurora.js'; import { initScheduler } from './ui/scheduler.js'; import { refreshAttention, attentionForRoute, attentionDotHtml } from './ui/attention.js'; import { registerAttentionSources } from './ui/attention-sources.js'; import { clearPageHint } from './ui/page-hint.js'; import { refreshRecentCloses } from './price/providers.js'; import * as home from './pages/home.js'; import * as conversionBitcoin from './pages/conversion-bitcoin.js'; import * as priceHistory from './pages/price-history.js'; import * as wealth from './pages/wealth.js'; import * as events from './pages/events.js'; import * as tracking from './pages/tracking.js'; import * as system from './pages/system.js'; import * as actions from './pages/actions.js'; import * as guide from './pages/guide.js'; import { makeStub } from './pages/stub.js'; import { ADJUST } from './ui/icons.js'; import { esc } from './ui/escape.js'; import * as dialog from './ui/dialog.js'; import { maybeStartSetup } from './onboarding/setup.js'; import { registerRoute, routePage } from './routes.js'; registerRoute(ROUTE_KEYS.HOME, home); registerRoute(ROUTE_KEYS.WEALTH, wealth); registerRoute(ROUTE_KEYS.EVENTS, events); registerRoute(ROUTE_KEYS.VALIDATION, makeStub('validation')); registerRoute(ROUTE_KEYS.TRACKING, tracking); registerRoute(ROUTE_KEYS.CONVERSION_BITCOIN, conversionBitcoin); registerRoute(ROUTE_KEYS.PRICE_HISTORY, priceHistory); registerRoute(ROUTE_KEYS.SYSTEM, system); registerRoute(ROUTE_KEYS.ACTIONS, actions); registerRoute(ROUTE_KEYS.GUIDE, guide); const NAV = [ { items: ['home', 'actions', 'events'] }, { section: 'onchain', items: ['tracking'] }, { section: 'tax', items: ['wealth', 'taxes'] }, { section: 'observe', items: ['conversion/bitcoin', 'price-history', 'charts'] }, { items: ['guide', 'system'], bottom: true }, ]; function currentRoute() { const key = hashFromLocation(location.hash).slice(2); if (routePage(key)) return key; const head = key.split('/')[0]; return routePage(head) ? head : 'home'; } const LOOK_ATTRS = Object.freeze({ profile: 'profile', glow: 'glow', glowshape: 'glowshape', motion: 'motion', rowsel: 'rowsel', glass_render: 'render' }); const LOOK_TOKENS = Object.freeze({ glass_transparency: ['--glass-transparency', '%'], glass_frost: ['--glass-frost', '%'], glass_blur: ['--dialog-transparency', ''], }); function applyTheme(s) { const { theme, accent, depth } = s; document.documentElement.dataset.theme = theme; document.documentElement.dataset.accent = accent; document.documentElement.dataset.depth = depth; const look = {}; for (const [key, attr] of Object.entries(LOOK_ATTRS)) { document.documentElement.dataset[attr] = s[key]; look[attr] = s[key]; } for (const [key, [token, unit]] of Object.entries(LOOK_TOKENS)) { document.documentElement.style.setProperty(token, `${s[key]}${unit}`); look[token] = `${s[key]}${unit}`; } applyAurora(s); try { const per = cacheSleutels(); localStorage.setItem(per.theme, theme); localStorage.setItem(per.accent, accent); localStorage.setItem(per.depth, depth); localStorage.setItem(per.look, JSON.stringify(look)); } catch { } } function applyPageTitle(show) { document.documentElement.dataset.pagetitle = show ? 'on' : 'off'; } function applyPageHint(show) { document.documentElement.dataset.pagehint = show ? 'on' : 'off'; } const hasBitcoin = async () => (await listAssets()).some(a => a.symbol === 'BTC'); const NAV_WHEN = { [ROUTE_KEYS.WEALTH]: async () => (await listAssets()).length > 0, [ROUTE_KEYS.CHARTS]: async () => appSetting('persona') !== 'broad', [ROUTE_KEYS.CONVERSION_BITCOIN]: hasBitcoin, [ROUTE_KEYS.TRACKING]: hasBitcoin, }; const NAV_CUSTOMIZABLE = new Set(['home']); const editModeRoutes = new Set(); function toggleEdit(key) { if (editModeRoutes.has(key)) editModeRoutes.delete(key); else editModeRoutes.add(key); if (currentRoute() === key) renderPage(); else location.hash = `#/${key}`; } function setEditMode(key, on) { if (editModeRoutes.has(key) === !!on) return; toggleEdit(key); } let kanSluiten = false; async function onNavCloseClick(e) { if (!e.target.closest('[data-app-close]')) return; e.preventDefault(); if (!await dialog.confirm({ title: t('appWindow.confirmTitle'), message: t('appWindow.confirmClose'), confirmLabel: t('appWindow.close') })) return; const r = await fetch('/api/app/close', { method: 'POST' }).catch(() => null); if (r && !r.ok) dialog.alert({ title: t('appWindow.confirmTitle'), message: t('appWindow.closeUnavailable') }); } function onNavEditClick(e) { const tog = e.target.closest('[data-edit]'); if (!tog) return; e.preventDefault(); e.stopPropagation(); toggleEdit(tog.dataset.edit); } async function renderSidebar() { const nav = document.getElementById('nav'); const active = currentRoute(); const navToolsSlot = !appSetting('show_page_title'); const zichtbaar = new Set(); for (const [route, voorwaarde] of Object.entries(NAV_WHEN)) { if (await voorwaarde()) zichtbaar.add(route); } const keepTools = nav.querySelector('#nav-tools'); nav.innerHTML = NAV.map(group => { const title = group.section ? `` : ''; const links = group.items.filter(k => routePage(k) && (!NAV_WHEN[k] || zichtbaar.has(k))).map(k => { const dot = attentionDotHtml(attentionForRoute(k)); const editTog = NAV_CUSTOMIZABLE.has(k) ? `${ADJUST}` : ''; const tools = navToolsSlot && k === active && editModeRoutes.has(k) ? '' : ''; const link = `${t(`nav.${k}`)}${dot}${editTog}${tools}`; return tools ? `` : link; }).join(''); const sluiten = group.bottom && kanSluiten ? `${esc(t('appWindow.close'))}` : ''; return links ? (group.bottom ? '' : '') + title + links + sluiten : ''; }).join(''); const slot = nav.querySelector('#nav-tools'); if (slot && keepTools && keepTools.firstChild) slot.replaceWith(keepTools); } function renderPageHeader(key) { const host = document.getElementById('page-header'); if (!host) return; const mod = routePage(key); const naam = mod?.headerInColumn || typeof mod?.title !== 'function' ? '' : mod.title(); if (!naam) { host.innerHTML = ''; return; } const icoon = typeof mod.titleIcon === 'function' ? mod.titleIcon() : ''; host.innerHTML = ``; } const renderTurns = createRenderTurns(); async function renderPage() { const turn = renderTurns.take(); const key = currentRoute(); for (const k of [...editModeRoutes]) if (k !== key) editModeRoutes.delete(k); document.getElementById('sidebar').classList.remove('open'); const view = document.getElementById('view'); renderPageHeader(key); clearPageHint(); try { await renderSidebar(); await routePage(key).render(view, { edit: editModeRoutes.has(key), setEdit: on => setEditMode(key, on) }); } catch (err) { if (!renderTurns.isCurrent(turn)) { console.warn(`[render] ${key} overtaken:`, err); return; } renderErrorPanel(view, err, key); } } async function boot() { try { assertConfig(); const s = await loadAppSettings(); setLocale(s.locale); configureSharedUi(); await loadLicense(); await refreshNotifications(); applyTheme(s); applyPageTitle(s.show_page_title); applyPageHint(s.show_page_hint); document.getElementById('logo-text').textContent = t('app.name'); document.getElementById('mobile-logo-text').textContent = t('app.name'); initSplashReplay(); refreshActivePortfolio(); watchPortfolioChanges(); document.getElementById('menu-btn').onclick = () => document.getElementById('sidebar').classList.toggle('open'); document.addEventListener('settings-changed', async () => { const s2 = await loadAppSettings(); setLocale(s2.locale); configureSharedUi(); applyTheme(s2); applyPageTitle(s2.show_page_title); applyPageHint(s2.show_page_hint); renderPage(); }); window.addEventListener('hashchange', renderPage); document.getElementById('nav').addEventListener('click', onNavEditClick); document.getElementById('nav').addEventListener('click', onNavCloseClick); kanSluiten = await fetch('/api/app/window').then(r => (r.ok ? r.json() : {})) .then(j => j.canClose === true).catch(() => false); registerAttentionSources(); document.addEventListener('attention-changed', () => renderSidebar().catch(e => console.error('[app] refreshing the sidebar failed:', e))); for (const signaal of ['providers-changed', 'connections-changed', 'ai-activity-changed', 'ai-proposals-changed', 'price-gaps-changed']) { document.addEventListener(signaal, () => refreshAttention()); } window.addEventListener('hashchange', () => refreshAttention()); document.addEventListener('app-refresh', () => { refreshAttention(); renderPage(); }); initStatusBar(); initScheduler(); initCommands(); await renderPage(); document.documentElement.dataset.app = 'ready'; finishSplash().then(maybeStartSetup); refreshAttention(); listAssets() .then(assets => refreshRecentCloses(assets)) .then(({ written }) => { if (written) document.dispatchEvent(new CustomEvent('price-gaps-changed')); }) .catch(e => console.error('[app] refreshing the recent closes failed:', e)); } catch (err) { document.body.innerHTML = `

Start afgebroken

${esc(err.message)}
`; throw err; } } boot();