Env-driven dev ports + splash as the front door

vite.config.ts now reads WEB_PORT (Vite) and PORT (API) from .env via
loadEnv, with the /api proxy following PORT and strictPort on — so two
branch checkouts can each run `npm run dev` on their own ports.

Play now always lands on the splash at the bare root instead of silently
resuming an active playthrough; a Resume action continues one when present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 15:43:08 +02:00
co-authored by Claude Opus 4.8
parent 34aa23237e
commit cd4b8bf4fa
3 changed files with 32 additions and 10 deletions
+7 -3
View File
@@ -7,6 +7,7 @@ import { CutsceneHost, DialoguePlayer, SplashScreen, type PlaythroughState } fro
// board route (/level/<clone id>), which App renders.
export function Play() {
const [state, setState] = useState<PlaythroughState | null>(null)
const [resumable, setResumable] = useState<PlaythroughState | null>(null)
const [splash, setSplash] = useState(false)
const [busy, setBusy] = useState(false)
const [status, setStatus] = useState('')
@@ -37,10 +38,13 @@ export function Play() {
if (!cancelled) res.ok ? apply(await res.json()) : setStatus('NODE NOT FOUND')
return
}
// The bare root is always the front door: show the splash, and offer Resume
// when a playthrough is already in progress rather than auto-resuming into it.
const res = await fetch('/api/playthroughs/current')
if (cancelled) return
if (res.status === 204) { setSplash(true); setStatus('AWAITING PRINCIPAL INVESTIGATOR'); return }
res.ok ? apply(await res.json()) : setSplash(true)
if (res.ok && res.status !== 204) setResumable(await res.json())
setSplash(true)
setStatus('AWAITING PRINCIPAL INVESTIGATOR')
})()
return () => { cancelled = true }
}, [apply])
@@ -62,7 +66,7 @@ export function Play() {
if (res.ok) apply(await res.json())
}
if (splash) return <SplashScreen hasResume={false} busy={busy} status={status} onNewGame={newGame} onResume={() => {}} />
if (splash) return <SplashScreen hasResume={!!resumable} busy={busy} status={status} onNewGame={newGame} onResume={() => { if (resumable) apply(resumable) }} />
const node = state?.node
if (!node) return <div className="boot"><div className="seal">GU</div><p>GLITCH UNIVERSITY NETWORK TERMINAL</p><small>{status || 'OPENING CASE FILE…'}</small></div>