diff --git a/neode-ui/src/App.vue b/neode-ui/src/App.vue index acee52b1..01002a24 100644 --- a/neode-ui/src/App.vue +++ b/neode-ui/src/App.vue @@ -398,8 +398,13 @@ onMounted(async () => { // One-shot "Replay intro" request from the login screen — must survive the // auto-re-mark below (which otherwise instantly suppresses the replay on any // onboarded node). - const replayRequested = sessionStorage.getItem('archipelago_replay_intro') === '1' + let replayRequested = sessionStorage.getItem('archipelago_replay_intro') === '1' if (replayRequested) sessionStorage.removeItem('archipelago_replay_intro') + // Public demo: every fresh boot at the root (first visit or a browser + // refresh) starts with the typing splash for the full effect. In-session SPA + // navigation never remounts App, and deep-route refreshes keep their place. + const { IS_DEMO } = await import('@/composables/useDemoIntro') + if (IS_DEMO && route.path === '/') replayRequested = true let onboardingComplete: boolean | null = localStorage.getItem('neode_onboarding_complete') === '1' ? true : null const splashCandidate = !seenIntro && (fromBoot || (route.path === '/' && import.meta.env.VITE_DEV_MODE !== 'boot')) diff --git a/neode-ui/src/composables/useDemoIntro.ts b/neode-ui/src/composables/useDemoIntro.ts index 8b683868..70f5314f 100644 --- a/neode-ui/src/composables/useDemoIntro.ts +++ b/neode-ui/src/composables/useDemoIntro.ts @@ -1,10 +1,10 @@ /** * 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. + * 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 = @@ -13,36 +13,10 @@ export const IS_DEMO = /** 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"). */ +/** Forget any legacy per-day gate marker (pre-2026-07 builds stored one). */ export function clearDemoIntroSeen(): void { try { - localStorage.removeItem(INTRO_DATE_KEY) + localStorage.removeItem('demo_intro_date') } catch { /* ignore */ } diff --git a/neode-ui/src/views/Cloud.vue b/neode-ui/src/views/Cloud.vue index 4bfee8fa..d9be21a7 100644 --- a/neode-ui/src/views/Cloud.vue +++ b/neode-ui/src/views/Cloud.vue @@ -6,12 +6,12 @@
-