feat: complete Quantumancy web app — full frontend + docs

Frontend (React 18 + TS + Vite):
- Landing: glitching hero, live /api/stats veil ticker, mode cards, featured spirits
- Séance: three.js shader ghost (hue/form per entity, mood + audio-reactive),
  Ouija planchette board spelling utterances, transcript with TTS replay,
  entity dossier, direct contact streaming, passive/active listening
- Modes: Wire Ghost telemetry panel, EVP mic anomaly detection, WebUSB
  RTL-SDR sweep + waterfall (hardware pass pending), Ouija/Direct Contact
- Codex: public registry + entity dossiers, rarity tiers, i18n EN/ES complete
- State: VeilSocket (reconnect/backoff), seance reducer, auth context
- 72 vitest tests green; served by FastAPI at :7777

Docs: README + as-built plans 3-7
This commit is contained in:
Indiana
2026-07-20 21:11:49 +00:00
parent 1ec9ea5863
commit 6edbbbbc2a
48 changed files with 10494 additions and 168 deletions

View File

@@ -0,0 +1,61 @@
// Auth context: who is seated at the table. Wraps the /auth REST endpoints
// and exposes { user, checking, login, register, logout } to the app.
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
import type { ReactNode } from 'react'
import * as api from '../api'
import type { User } from '../api'
export type AuthApi = {
user: User | null
checking: boolean
login: (username: string, password: string) => Promise<User>
register: (username: string, password: string) => Promise<User>
logout: () => Promise<void>
}
const AuthContext = createContext<AuthApi | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [checking, setChecking] = useState(true)
useEffect(() => {
api
.me()
.then(setUser)
.catch(() => setUser(null))
.finally(() => setChecking(false))
}, [])
const login = useCallback(async (username: string, password: string) => {
const u = await api.login(username, password)
setUser(u)
return u
}, [])
const register = useCallback(
async (username: string, password: string) => {
await api.register(username, password)
return login(username, password)
},
[login],
)
const logout = useCallback(async () => {
await api.logout()
setUser(null)
}, [])
const value = useMemo<AuthApi>(
() => ({ user, checking, login, register, logout }),
[user, checking, login, register, logout],
)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
export function useAuth(): AuthApi {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>')
return ctx
}