import { createContext, useContext, useEffect, useState, type ReactNode } from "react"; import { api } from "../api/client"; import type { DemoManifest } from "../api/types"; interface DemoManifestState { manifest: DemoManifest | null; loading: boolean; error: boolean; refresh: () => void; } const DemoManifestContext = createContext(undefined); export function DemoManifestProvider({ children }: { children: ReactNode }) { const [manifest, setManifest] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); const [version, setVersion] = useState(0); useEffect(() => { const controller = new AbortController(); setLoading(true); setError(false); // Public endpoint by design: the demo-entry screen needs this before any session // exists, so it is never gated behind auth. api .get("/api/v1/demo/manifest", { signal: controller.signal }) .then((result) => { if (!controller.signal.aborted) setManifest(result); }) .catch(() => { if (controller.signal.aborted) return; setManifest(null); setError(true); }) .finally(() => { if (!controller.signal.aborted) setLoading(false); }); return () => controller.abort(); }, [version]); return ( setVersion((v) => v + 1) }} > {children} ); } export function useDemoManifest(): DemoManifestState { const ctx = useContext(DemoManifestContext); if (!ctx) throw new Error("useDemoManifest must be used within DemoManifestProvider"); return ctx; }