/** * Public-demo helpers. * * The demo build (VITE_DEMO=1) replays the intro/onboarding on each visit, but * only once per calendar day per browser — tracked in localStorage so it * survives the short-lived backend session. Also exposes the shared demo * credentials shown on the login screen. */ export const IS_DEMO = import.meta.env.VITE_DEMO === '1' || import.meta.env.VITE_DEMO === 'true' /** Memorable shared password for the public demo (must match the mock backend). */ export const DEMO_PASSWORD = 'entertoexit' const INTRO_DATE_KEY = 'demo_intro_date' function todayKey(): string { // Local calendar day, e.g. "2026-06-22". const d = new Date() return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}` } /** True if this browser already watched the intro earlier today. */ export function demoIntroSeenToday(): boolean { try { return localStorage.getItem(INTRO_DATE_KEY) === todayKey() } catch { return false } } /** Record that the intro has been seen today, so it won't replay until tomorrow. */ export function markDemoIntroSeen(): void { try { localStorage.setItem(INTRO_DATE_KEY, todayKey()) } catch { /* ignore (private mode / storage disabled) */ } } /** Forget today's "seen" marker so the intro plays again (e.g. "Replay Intro"). */ export function clearDemoIntroSeen(): void { try { localStorage.removeItem(INTRO_DATE_KEY) } catch { /* ignore */ } } // ── Demoable apps ─────────────────────────────────────────────────────────── // Only these apps actually do something in the demo (a mock UI or a real // external site). Everything else shows "No demo" on a disabled install button // and is not launchable. const DEMO_EXTERNAL_URLS: Record = { // Real, public sites that we open instead of mocking. indeedhub: 'https://indee.tx1138.com/', mempool: 'https://mempool.space/testnet', 'mempool-web': 'https://mempool.space/testnet', } // Apps with a same-origin mock UI served by the demo backend. const DEMO_MOCK_UI: Record = { 'bitcoin-knots': '/app/bitcoin-ui/', 'bitcoin-core': '/app/bitcoin-ui/', bitcoin: '/app/bitcoin-ui/', 'bitcoin-ui': '/app/bitcoin-ui/', electrs: '/app/electrumx/', electrumx: '/app/electrumx/', 'archy-electrs-ui': '/app/electrumx/', fedimint: '/app/fedimint/', fedimintd: '/app/fedimint/', filebrowser: '/app/filebrowser/', } /** Apps that open in a new tab (external real sites) rather than an iframe. */ export function isDemoExternal(appId: string): boolean { return appId === 'mempool' || appId === 'mempool-web' } /** Can this app be launched/installed in the demo? */ export function isDemoApp(appId: string): boolean { return appId in DEMO_EXTERNAL_URLS || appId in DEMO_MOCK_UI } /** Resolve the demo launch URL for an app, or null if it isn't demoable. */ export function demoAppUrl(appId: string): string | null { return DEMO_EXTERNAL_URLS[appId] ?? DEMO_MOCK_UI[appId] ?? null }