React/TypeScript game client for the SideQuest AI Narrator. Connects to the Rust API via WebSocket for real-time game sessions.
npm install # Install dependencies
npm run dev # Dev server at localhost:5173
npm test # Run tests (Vitest)
npm run build # Type-check + production build
npm run lint # ESLintThe dev server proxies four paths to the Rust API at localhost:8765:
| Path | Target |
|---|---|
/ws |
ws://localhost:8765 |
/api |
http://localhost:8765 |
/genre |
http://localhost:8765 |
/renders |
http://localhost:8765 |
- React 19 + TypeScript 5.9
- Vite 8
- Tailwind CSS 4 + shadcn/ui (base-nova style)
- Vitest 4.1.1 + React Testing Library + JSDOM
- lucide-react for icons
A game session moves through three phases:
- ConnectScreen (
src/screens/ConnectScreen.tsx) — Server connection, genre/world selection via dropdowns, player name entry. Persists selections in localStorage. - CharacterCreation (
src/components/CharacterCreation/) — AI-driven multi-turn dialogue. The server offers choices and accepts freeform input to build a character collaboratively. - GameBoard (
src/components/GameBoard/GameBoard.tsx) — Active gameplay. A widget-based layout (seewidgetRegistry.ts) that composes the narration, party, inventory, map, and overlay panels listed below.
All paths are relative to src/components/ unless noted.
| Component | Purpose |
|---|---|
GameBoard/GameBoard.tsx |
Root gameplay layout with widget registry |
NarrationCards.tsx + NarrationFocus.tsx + NarrationScroll.tsx |
Narration rendering (current-turn focus + scrollback) |
NarrativeView.tsx (in src/screens/) |
Markdown narration (DOMPurify), streaming chunks, images |
CharacterPanel.tsx |
Persistent themed sidebar showing active character |
PartyPanel.tsx |
Party portraits, HP bars, status effects |
CharacterSheet.tsx |
Stats grid, abilities, backstory |
InventoryPanel.tsx |
Items grouped by type, equipped state, currency |
MapOverlay.tsx + Automapper.tsx + DungeonMapRenderer.tsx + TacticalGridRenderer.tsx |
SVG / grid map rendering |
JournalView.tsx + KnowledgeJournal.tsx |
Handouts and lore journal |
ConfrontationOverlay.tsx |
Encounter / combat overlay — enemy HP, turn order, status |
TurnStatusPanel.tsx |
Current turn + phase indicator |
AudioStatus.tsx |
2-channel mixer UI (music/SFX), mute toggles |
InputBar.tsx |
Text input with aside toggle |
Dashboard/DashboardApp.tsx |
Watcher/GM telemetry app (tabs: Timeline, State, Subsystems, Timing, Console) |
GenericResourceBar.tsx |
Reusable resource bar (HP, stamina, etc.) |
This table is a guided tour, not an exhaustive index. Treat
src/components/as authoritative.
| Key | Panel |
|---|---|
P |
Party panel |
C |
Character sheet |
I |
Inventory |
M |
Map overlay |
J |
Journal |
Custom hooks under src/hooks/:
| Hook | Responsibility |
|---|---|
useWebSocket |
Low-level WebSocket transport with reconnect |
useGameSocket |
Game-message dispatch built on useWebSocket |
useStateMirror |
Sync local game state from server messages |
useWatcherSocket |
Telemetry WebSocket for GM mode |
useSlashCommands |
Parse /inventory, /character, /quests, etc. |
useAudio |
Core audio context management |
useAudioCue |
Play one-shot audio cues (SFX) from server events |
useGenreTheme |
Inject genre pack CSS variables |
useChromeArchetype |
Archetype-driven UI chrome styling |
useLayoutMode |
Desktop/mobile layout selection |
useBreakpoint |
Responsive breakpoint detection |
useLocalPrefs |
Persisted user preferences (volume, panel layout, etc.) |
useRunningHeader |
Scroll-aware running header state |
useGameBoardLayout |
Game board panel arrangement |
useGameBoardHotkeys |
Keyboard shortcut bindings for game board panels |
The full list is authoritative in
src/hooks/. Former voice hooks (useVoiceChat,useVoicePlayback,usePushToTalk,useWhisper) were removed along with the TTS / WebRTC voice pipeline (2026-04).
The audio subsystem uses the Web Audio API with two independent channels:
- AudioEngine.ts — 2-channel mixer (music + SFX) with per-channel gain
- AudioCache.ts — URL-to-AudioBuffer cache to avoid redundant fetches
- Crossfader.ts — Smooth gain-curve transitions between music tracks
The voice channel, LocalTranscriber.ts, Ducker.ts, and the Kokoro TTS
playback path were all removed in 2026-04. Music-ducking was only ever wired
to duck under TTS voice playback; with voice gone, the entire duck/restore
chain is gone too — the Rust server no longer emits AudioAction::Duck
constructions. See orc-quest/docs/adr/076-narration-protocol-collapse-post-tts.md.
Client-handled message types include NARRATION, NARRATION_END, PARTY_STATUS,
CHARACTER_SHEET, INVENTORY, MAP_UPDATE, IMAGE, AUDIO_CUE, CHAPTER_MARKER,
SESSION_EVENT, TURN_STATUS, CHARACTER_CREATION, THINKING, ERROR,
COMBAT_EVENT, ACTION_QUEUE, and the dice protocol triplet (DICE_REQUEST,
DICE_THROW, DICE_RESULT).
See src/types/ for the authoritative TypeScript payload definitions and
orc-quest/docs/api-contract.md for the cross-repo protocol reference.
Vitest + React Testing Library + JSDOM. Test files cover integration, component, hook, and audio behavior.
npm test # Watch mode
npx vitest run # Single run
npx vitest run --ui # Browser UI- orc-quest — Orchestrator (sprint tracking, ADRs, genre packs)
- sidequest-api — Rust backend
- sidequest-daemon — Python media services