Merge pull request 'fix(intro): splash reliability on fresh visits + 3x lighter video' (#90) from intro-reliability-video-perf into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m33s

This commit is contained in:
ai 2026-07-15 10:54:21 +00:00
commit 2c82b498f0
5 changed files with 23 additions and 21 deletions

View File

@ -187,8 +187,9 @@ http {
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
} }
# Cache static assets # Cache static assets (media too — the intro video/audio are versioned
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { # with ?v=N query busters, so immutable is safe)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|mp4|webm|mp3|woff2?)$ {
expires 1y; expires 1y;
add_header Cache-Control "public, immutable"; add_header Cache-Control "public, immutable";
} }

View File

@ -433,6 +433,17 @@ onMounted(async () => {
onboardingComplete, onboardingComplete,
replayRequested, replayRequested,
})) { })) {
// Kick off the intro video download NOW: the splash's <video> element only
// mounts ~20s in (after the typing sequence), and on a cold cache the file
// would otherwise start fetching mid-sequence and stutter.
try {
const link = document.createElement('link')
link.rel = 'preload'
link.as = 'video'
link.type = 'video/mp4'
link.href = '/assets/video/video-intro.mp4?v=8'
document.head.appendChild(link)
} catch { /* ignore */ }
// Coming from boot screen show the full splash intro (Enter to Exit typing logo) // Coming from boot screen show the full splash intro (Enter to Exit typing logo)
showSplash.value = true showSplash.value = true
} else { } else {

View File

@ -14,7 +14,7 @@
preload="auto" preload="auto"
poster="/assets/img/bg-intro.jpg" poster="/assets/img/bg-intro.jpg"
> >
<source src="/assets/video/video-intro.mp4?v=7" type="video/mp4"> <source src="/assets/video/video-intro.mp4?v=8" type="video/mp4">
<!-- Fallback to image if video fails --> <!-- Fallback to image if video fails -->
<div <div
class="absolute inset-0" class="absolute inset-0"
@ -266,16 +266,12 @@ watch([showWelcome, showLogo], ([welcome, logo]) => {
} }
}) })
// Check if user has seen intro // NOTE: whether the intro should play is decided ONCE by App.vue
// Also detect returning users who cleared cache: if we're on a /dashboard route, // (shouldShowIntroSplash) this component is only mounted when the answer was
// the backend session is still active so the user has been here before. // yes, so it must not re-check neode_intro_seen itself. The old internal
const storedSeenIntro = localStorage.getItem('neode_intro_seen') === '1' // re-check silently skipped the whole sequence for any browser that had seen
const isOnDashboard = window.location.pathname.startsWith('/dashboard') // the intro before, even when App.vue explicitly asked for a replay (demo
const seenIntro = storedSeenIntro || isOnDashboard // fresh visits, the Replay Intro link).
// Persist the flag so subsequent loads don't re-check
if (!storedSeenIntro && isOnDashboard) {
localStorage.setItem('neode_intro_seen', '1')
}
function handleEnterKey(e: KeyboardEvent) { function handleEnterKey(e: KeyboardEvent) {
if (e.key === 'Enter' && showTapToStart.value && !tapStartTransitioning.value) { if (e.key === 'Enter' && showTapToStart.value && !tapStartTransitioning.value) {
@ -468,13 +464,7 @@ function startAlienIntro() {
} }
onMounted(() => { onMounted(() => {
if (seenIntro) { window.addEventListener('keydown', handleEnterKey)
showSplash.value = false
document.body.classList.add('splash-complete')
emit('complete')
} else {
window.addEventListener('keydown', handleEnterKey)
}
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {

View File

@ -17,7 +17,7 @@
@pause.prevent="handleVideoPause" @pause.prevent="handleVideoPause"
@ended="handleVideoEnded" @ended="handleVideoEnded"
> >
<source src="/assets/video/video-intro.mp4?v=7" type="video/mp4"> <source src="/assets/video/video-intro.mp4?v=8" type="video/mp4">
</video> </video>
<!-- Login: static background + archipelago-style glitch (no zoom) --> <!-- Login: static background + archipelago-style glitch (no zoom) -->