2026-06-22 09:28:05 -04:00
|
|
|
/**
|
|
|
|
|
* 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 */
|
|
|
|
|
}
|
|
|
|
|
}
|
feat(demo): app launch UIs, "No demo" gating, onboarding skip, 12 nodes
App launching (DEMO):
- resolveAppUrl routes every app to its demo target: mock UIs for Bitcoin Core,
ElectrumX, Fedimint (served by the backend), IndeeHub → iframe indee.tx1138.com,
Mempool → mempool.space/testnet (new tab); all others → a generic "Demo preview"
notice page.
- Non-demoable apps show a disabled "No demo" install button (marketplace details,
app grid, featured apps).
Onboarding:
- Demo treats the visitor as fully set up so the onboarding WIZARD (seed/identity)
is never forced; the welcome intro still replays per day. Intro CTA goes straight
to login; wizard entry points + login restart-onboarding link hidden in demo.
Network:
- federation.list-nodes now returns 12 trusted/federated nodes (9 trusted, 3
observer); transport.peers already at 5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:26:35 -04:00
|
|
|
|
|
|
|
|
// ── 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.
|
2026-06-22 16:34:12 -04:00
|
|
|
const DEMO_EXTERNAL_URLS: Record<string, string> = {}
|
feat(demo): app launch UIs, "No demo" gating, onboarding skip, 12 nodes
App launching (DEMO):
- resolveAppUrl routes every app to its demo target: mock UIs for Bitcoin Core,
ElectrumX, Fedimint (served by the backend), IndeeHub → iframe indee.tx1138.com,
Mempool → mempool.space/testnet (new tab); all others → a generic "Demo preview"
notice page.
- Non-demoable apps show a disabled "No demo" install button (marketplace details,
app grid, featured apps).
Onboarding:
- Demo treats the visitor as fully set up so the onboarding WIZARD (seed/identity)
is never forced; the welcome intro still replays per day. Intro CTA goes straight
to login; wizard entry points + login restart-onboarding link hidden in demo.
Network:
- federation.list-nodes now returns 12 trusted/federated nodes (9 trusted, 3
observer); transport.peers already at 5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:26:35 -04:00
|
|
|
|
2026-06-22 16:34:12 -04:00
|
|
|
// 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.
|
feat(demo): app launch UIs, "No demo" gating, onboarding skip, 12 nodes
App launching (DEMO):
- resolveAppUrl routes every app to its demo target: mock UIs for Bitcoin Core,
ElectrumX, Fedimint (served by the backend), IndeeHub → iframe indee.tx1138.com,
Mempool → mempool.space/testnet (new tab); all others → a generic "Demo preview"
notice page.
- Non-demoable apps show a disabled "No demo" install button (marketplace details,
app grid, featured apps).
Onboarding:
- Demo treats the visitor as fully set up so the onboarding WIZARD (seed/identity)
is never forced; the welcome intro still replays per day. Intro CTA goes straight
to login; wizard entry points + login restart-onboarding link hidden in demo.
Network:
- federation.list-nodes now returns 12 trusted/federated nodes (9 trusted, 3
observer); transport.peers already at 5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:26:35 -04:00
|
|
|
const DEMO_MOCK_UI: Record<string, string> = {
|
2026-06-22 16:34:12 -04:00
|
|
|
indeedhub: '/app/indeedhub/',
|
|
|
|
|
mempool: '/app/mempool/',
|
|
|
|
|
'mempool-web': '/app/mempool/',
|
2026-06-22 14:19:38 -04:00
|
|
|
'bitcoin-knots': '/app/bitcoin-knots/',
|
2026-06-22 13:55:50 -04:00
|
|
|
'bitcoin-core': '/app/bitcoin-core/',
|
|
|
|
|
bitcoin: '/app/bitcoin-core/',
|
feat(demo): app launch UIs, "No demo" gating, onboarding skip, 12 nodes
App launching (DEMO):
- resolveAppUrl routes every app to its demo target: mock UIs for Bitcoin Core,
ElectrumX, Fedimint (served by the backend), IndeeHub → iframe indee.tx1138.com,
Mempool → mempool.space/testnet (new tab); all others → a generic "Demo preview"
notice page.
- Non-demoable apps show a disabled "No demo" install button (marketplace details,
app grid, featured apps).
Onboarding:
- Demo treats the visitor as fully set up so the onboarding WIZARD (seed/identity)
is never forced; the welcome intro still replays per day. Intro CTA goes straight
to login; wizard entry points + login restart-onboarding link hidden in demo.
Network:
- federation.list-nodes now returns 12 trusted/federated nodes (9 trusted, 3
observer); transport.peers already at 5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:26:35 -04:00
|
|
|
'bitcoin-ui': '/app/bitcoin-ui/',
|
|
|
|
|
electrs: '/app/electrumx/',
|
|
|
|
|
electrumx: '/app/electrumx/',
|
|
|
|
|
'archy-electrs-ui': '/app/electrumx/',
|
2026-06-22 12:39:33 -04:00
|
|
|
lnd: '/app/lnd/',
|
|
|
|
|
'lnd-ui': '/app/lnd/',
|
|
|
|
|
'archy-lnd-ui': '/app/lnd/',
|
|
|
|
|
thunderhub: '/app/lnd/',
|
feat(demo): app launch UIs, "No demo" gating, onboarding skip, 12 nodes
App launching (DEMO):
- resolveAppUrl routes every app to its demo target: mock UIs for Bitcoin Core,
ElectrumX, Fedimint (served by the backend), IndeeHub → iframe indee.tx1138.com,
Mempool → mempool.space/testnet (new tab); all others → a generic "Demo preview"
notice page.
- Non-demoable apps show a disabled "No demo" install button (marketplace details,
app grid, featured apps).
Onboarding:
- Demo treats the visitor as fully set up so the onboarding WIZARD (seed/identity)
is never forced; the welcome intro still replays per day. Intro CTA goes straight
to login; wizard entry points + login restart-onboarding link hidden in demo.
Network:
- federation.list-nodes now returns 12 trusted/federated nodes (9 trusted, 3
observer); transport.peers already at 5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:26:35 -04:00
|
|
|
fedimint: '/app/fedimint/',
|
|
|
|
|
fedimintd: '/app/fedimint/',
|
|
|
|
|
filebrowser: '/app/filebrowser/',
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 12:39:33 -04:00
|
|
|
/**
|
2026-06-22 14:45:04 -04:00
|
|
|
* 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.
|
2026-06-22 12:39:33 -04:00
|
|
|
*/
|
2026-06-22 14:45:04 -04:00
|
|
|
export function isDemoExternal(_appId: string): boolean {
|
|
|
|
|
return false
|
feat(demo): app launch UIs, "No demo" gating, onboarding skip, 12 nodes
App launching (DEMO):
- resolveAppUrl routes every app to its demo target: mock UIs for Bitcoin Core,
ElectrumX, Fedimint (served by the backend), IndeeHub → iframe indee.tx1138.com,
Mempool → mempool.space/testnet (new tab); all others → a generic "Demo preview"
notice page.
- Non-demoable apps show a disabled "No demo" install button (marketplace details,
app grid, featured apps).
Onboarding:
- Demo treats the visitor as fully set up so the onboarding WIZARD (seed/identity)
is never forced; the welcome intro still replays per day. Intro CTA goes straight
to login; wizard entry points + login restart-onboarding link hidden in demo.
Network:
- federation.list-nodes now returns 12 trusted/federated nodes (9 trusted, 3
observer); transport.peers already at 5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:26:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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
|
|
|
|
|
}
|