archy/neode-ui/src/composables/useDemoIntro.ts
Dorian c13ee58edf feat(demo): typing splash + intro on every fresh visit; neutral desktop Cloud tabs
- Demo boots at '/' (first visit or a browser refresh) always start with
  the typing splash into the onboarding intro — the per-calendar-day
  gate is gone. In-session SPA navigation never replays it, and
  deep-route refreshes keep the visitor's place.
- Cloud top-level tabs return to the standard mode-switcher style on
  desktop; the orange full-width switcher stays mobile-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 11:12:49 +01:00

80 lines
2.9 KiB
TypeScript

/**
* Public-demo helpers.
*
* The demo build (VITE_DEMO=1) replays the typing splash + intro/onboarding on
* EVERY fresh boot of the app at '/' (first visit or browser refresh) — see
* App.vue and RootRedirect.demoRoute. 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'
/** Forget any legacy per-day gate marker (pre-2026-07 builds stored one). */
export function clearDemoIntroSeen(): void {
try {
localStorage.removeItem('demo_intro_date')
} 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<string, string> = {}
// Apps loaded in the in-app iframe via a same-origin path. IndeeHub and Mempool
// are reverse-proxied by nginx (X-Frame-Options/CSP stripped + asset paths
// rewritten) so the frame-busting real sites can be embedded.
const DEMO_MOCK_UI: Record<string, string> = {
indeedhub: '/app/indeedhub/',
mempool: '/app/mempool/',
'mempool-web': '/app/mempool/',
'bitcoin-knots': '/app/bitcoin-knots/',
'bitcoin-core': '/app/bitcoin-core/',
bitcoin: '/app/bitcoin-core/',
'bitcoin-ui': '/app/bitcoin-ui/',
electrs: '/app/electrumx/',
electrumx: '/app/electrumx/',
'archy-electrs-ui': '/app/electrumx/',
lnd: '/app/lnd/',
'lnd-ui': '/app/lnd/',
'archy-lnd-ui': '/app/lnd/',
thunderhub: '/app/lnd/',
fedimint: '/app/fedimint/',
fedimintd: '/app/fedimint/',
filebrowser: '/app/filebrowser/',
// Static placeholder dashboards served by the mock backend (DEMO_APP_PAGES).
'btcpay-server': '/app/btcpay-server/',
grafana: '/app/grafana/',
nextcloud: '/app/nextcloud/',
jellyfin: '/app/jellyfin/',
vaultwarden: '/app/vaultwarden/',
'nostr-rs-relay': '/app/nostr-rs-relay/',
searxng: '/app/searxng/',
'uptime-kuma': '/app/uptime-kuma/',
}
/**
* Whether a demo app opens in a new tab. Nothing does — IndeeHub and Mempool
* both load their real site directly in the in-app iframe.
*/
export function isDemoExternal(_appId: string): boolean {
return false
}
/** 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
}