diff --git a/ui/src/api.ts b/ui/src/api.ts index 785829f..c0263fd 100644 --- a/ui/src/api.ts +++ b/ui/src/api.ts @@ -80,8 +80,16 @@ const DEMO = (() => { if (typeof window === 'undefined') return ENV.VITE_DEMO === '1' try { const q = new URLSearchParams(window.location.search) - if (q.get('demo') === '1') { localStorage.setItem('konclave.demo', '1'); return true } - if (q.get('demo') === '0') { localStorage.removeItem('konclave.demo'); return false } + const flag = q.get('demo') + if (flag === '1' || flag === '0') { + if (flag === '1') localStorage.setItem('konclave.demo', '1') + else localStorage.removeItem('konclave.demo') + // Strip `?demo` from the address bar so the path stays clean; the mode persists via localStorage. + q.delete('demo') + const qs = q.toString() + window.history.replaceState(null, '', window.location.pathname + (qs ? `?${qs}` : '') + window.location.hash) + return flag === '1' + } if (localStorage.getItem('konclave.demo') === '1') return true } catch { /* storage unavailable — fall through to the build flag */ } return ENV.VITE_DEMO === '1' diff --git a/ui/src/components.tsx b/ui/src/components.tsx index 520a70c..1b4c5c4 100644 --- a/ui/src/components.tsx +++ b/ui/src/components.tsx @@ -3,12 +3,11 @@ import { Link } from 'react-router-dom' import { useReveal } from './reveal' import { useI18n, useT } from './i18n' import { IS_DEMO } from './api' -import { helperConfigured } from './helper' /** A thin, always-visible strip in demo mode (runtime `?demo=1` or the VITE_DEMO fallback) so a - * visitor never mistakes the sample data for a real vault. Offers "Exit demo" (`?demo=0`) ONLY when - * a real mode exists to exit to (a helper is configured) — a pure demo deploy has nowhere to go, so - * the exit would land on an empty backend. Renders nothing outside demo mode. */ + * visitor never mistakes the sample data for a real vault. Always offers a way back to the landing + * (which leaves demo mode via `?demo=0`), from where a visitor re-enters the demo or the real vault. + * Renders nothing outside demo mode. */ export function DemoBanner() { const t = useT() if (!IS_DEMO) return null @@ -16,7 +15,7 @@ export function DemoBanner() {
DEMO {t('demo.bannerNote')} - {helperConfigured() && {t('demo.bannerExit')}} + {t('demo.bannerExit')}
) } diff --git a/ui/src/i18n/en.ts b/ui/src/i18n/en.ts index 55967b0..471af9b 100644 --- a/ui/src/i18n/en.ts +++ b/ui/src/i18n/en.ts @@ -234,7 +234,7 @@ export const en: Record = { 'demo.live': 'Live demo', 'demo.note': 'This is the cryptography itself, running live, not the everyday vault screens.', 'demo.bannerNote': 'Demonstration with sample data. No real funds, no real transactions.', - 'demo.bannerExit': 'Exit demo', + 'demo.bannerExit': '← Back to landing', 'settings.eyebrow': 'SETTINGS', 'settings.title': 'Settings', 'settings.vault': 'Vault', diff --git a/ui/src/i18n/pt-BR.ts b/ui/src/i18n/pt-BR.ts index f0100cb..61a62d2 100644 --- a/ui/src/i18n/pt-BR.ts +++ b/ui/src/i18n/pt-BR.ts @@ -234,7 +234,7 @@ export const ptBR: Record = { 'demo.live': 'Demonstração ao vivo', 'demo.note': 'Isto é a própria criptografia, rodando ao vivo, não as telas do dia a dia do cofre.', 'demo.bannerNote': 'Demonstração com dados de exemplo. Nenhum fundo real, nenhuma transação de verdade.', - 'demo.bannerExit': 'Sair da demo', + 'demo.bannerExit': '← Voltar à landing', 'settings.eyebrow': 'AJUSTES', 'settings.title': 'Ajustes', 'settings.vault': 'Cofre',