Frontend Design โ Glyphoxa Web Management Service
1. Overview
This document defines the frontend architecture, page designs, component hierarchy, and interaction patterns for the Glyphoxa Web Management Service โ a self-service portal for Dungeon Masters to manage campaigns, NPCs, sessions, billing, and more.
Key constraints:
- Self-service for non-technical DMs โ no YAML, no CLI, no API keys
- Separate service โ own Docker container, own domain (
app.glyphoxa.com) - Mobile-friendly โ DMs manage NPCs from their phone during sessions
- Scale to >1000 users โ multi-tenant, tenant-isolated, performant
- English MVP โ i18n-ready for German (and beyond) in Phase 2
Deployment model: The frontend is a standalone SPA served by its own container (nginx or Node SSR), communicating with the Glyphoxa Gateway API over HTTPS. This decouples frontend release cycles from backend deployments and allows independent scaling, CDN caching, and A/B testing.
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ app.glyphoxa.com โ HTTPS โ api.glyphoxa.com โ
โ โโโโโโโโโโโโโโโโโโ โ โโโโโโโ โ โโโโโโโโโโโโโโโโโโ โ
โ โ SPA (Next.js) โ โ โ โ Gateway API โ โ
โ โ Docker: nginx โ โ โ โ (Go) โ โ
โ โโโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ CDN (Cloudflare) โ PostgreSQL + Vault
โ Static assets cached โ Per-tenant schemas
2. Tech Stack
Framework: Next.js 15 (App Router)
| Choice | Rationale |
|---|---|
| Next.js 15 | SSR for landing/marketing pages (SEO), SPA behavior for dashboard. App Router enables React Server Components for fast initial loads. Standalone output mode for Docker. |
| React 19 | Concurrent features, Suspense boundaries, use() hook for data loading. |
| TypeScript 5.5+ | End-to-end type safety with generated API client. |
Why Next.js over Vite+React (from original plan): The original plan recommended Vite+React embedded in the gateway binary. Since weโre now a separate service with its own domain, Next.js provides critical advantages:
- SSR for the landing page (SEO, social sharing, fast FCP)
- Built-in API routes for OAuth callback handling
- Image optimization for NPC avatars
- Middleware for auth guards (edge runtime)
output: 'standalone'produces a minimal Docker image (~50MB)
Vite remains an excellent choice if we revert to gateway-embedded. The architecture below is framework-agnostic at the component level.
UI & Styling
| Choice | Rationale |
|---|---|
| shadcn/ui | Copy-paste Radix primitives. No npm lock-in. Accessible by default (ARIA). Customizable with Tailwind. |
| Tailwind CSS 4 | Utility-first, design tokens via CSS variables, responsive out of the box. |
| Lucide icons | Tree-shakeable, consistent with shadcn/ui. |
| Geist font | Clean, modern, excellent readability at small sizes. |
Data & State
| Choice | Rationale |
|---|---|
| TanStack Query v5 | Server-state cache, background refetch, optimistic mutations, infinite scroll. Replaces Redux for API data. |
| Zustand | Minimal client state (sidebar open, theme, modal stack). No boilerplate. |
| React Hook Form + Zod | Performant forms with schema validation. Zod schemas shared with API types. |
| nuqs | Type-safe URL search params for filters, pagination, search. Shareable URLs. |
Visualization & Real-time
| Choice | Rationale |
|---|---|
| Recharts | Lightweight, composable, responsive charts for usage/billing. Built on D3. |
| Native WebSocket | Live transcript streaming. TanStack Query subscription pattern for reconnect. |
| Web Audio API | Voice preview playback with waveform visualization. |
| @dnd-kit | Accessible drag-and-drop for NPC ordering, behavior rules. |
i18n
| Choice | Rationale |
|---|---|
| next-intl | Next.js-native i18n with App Router support. ICU message format. |
| English MVP | All strings extracted to messages/en.json from day one. German locale added in Phase 2. |
API Client
| Choice | Rationale |
|---|---|
| openapi-typescript + openapi-fetch | Generate types from OpenAPI 3.1 spec. Zero-runtime type safety. Works with TanStack Query. |
3. Routing Structure
/ Landing page (public, SSR)
/pricing Pricing page (public, SSR)
/login Login/Register (public)
/auth/callback/discord OAuth2 callback handler
/auth/callback/google OAuth2 callback handler
/dashboard DM dashboard (authenticated)
/campaigns Campaign list
/campaigns/new Create campaign
/campaigns/[id] Campaign detail/edit
/campaigns/[id]/npcs NPC list for campaign
/campaigns/[id]/npcs/new Create NPC
/campaigns/[id]/npcs/[npcId] NPC editor
/campaigns/[id]/sessions Session history for campaign
/campaigns/[id]/lore Lore editor (markdown)
/sessions All sessions (across campaigns)
/sessions/[id] Session detail + transcript
/sessions/[id]/live Live session monitor (WebSocket)
/sessions/[id]/replay Session replay (future)
/billing Billing & usage
/billing/plans Plan comparison & upgrade
/settings Account settings
/settings/api-keys API key management (BYOK)
/settings/notifications Notification preferences
/admin Super admin dashboard
/admin/tenants Tenant management
/admin/tenants/[id] Tenant detail
/admin/system System health & metrics
/admin/billing All-tenant billing overview
/admin/users User management
/support Help & support
/support/tickets Ticket list
/support/tickets/new Create ticket
/support/docs Documentation links
Route Groups & Layouts
app/
โโโ (public)/ # No auth required
โ โโโ layout.tsx # Marketing header/footer
โ โโโ page.tsx # Landing page
โ โโโ pricing/page.tsx
โ โโโ login/page.tsx
โ โโโ auth/callback/[provider]/route.ts # API route for OAuth
โ
โโโ (app)/ # Authenticated, sidebar layout
โ โโโ layout.tsx # Sidebar + topbar + auth guard
โ โโโ dashboard/page.tsx
โ โโโ campaigns/
โ โ โโโ page.tsx # List
โ โ โโโ new/page.tsx
โ โ โโโ [id]/
โ โ โโโ page.tsx # Detail/edit
โ โ โโโ npcs/
โ โ โ โโโ page.tsx
โ โ โ โโโ new/page.tsx
โ โ โ โโโ [npcId]/page.tsx
โ โ โโโ sessions/page.tsx
โ โ โโโ lore/page.tsx
โ โโโ sessions/
โ โ โโโ page.tsx
โ โ โโโ [id]/
โ โ โโโ page.tsx # Detail + transcript
โ โ โโโ live/page.tsx
โ โ โโโ replay/page.tsx
โ โโโ billing/
โ โ โโโ page.tsx
โ โ โโโ plans/page.tsx
โ โโโ settings/
โ โ โโโ page.tsx
โ โ โโโ api-keys/page.tsx
โ โ โโโ notifications/page.tsx
โ โโโ support/
โ โโโ page.tsx
โ โโโ tickets/
โ โ โโโ page.tsx
โ โ โโโ new/page.tsx
โ โโโ docs/page.tsx
โ
โโโ (admin)/ # super_admin only
โโโ layout.tsx # Admin layout with system nav
โโโ admin/
โโโ page.tsx # Admin dashboard
โโโ tenants/
โ โโโ page.tsx
โ โโโ [id]/page.tsx
โโโ system/page.tsx
โโโ billing/page.tsx
โโโ users/page.tsx
4. Authentication Flow
Login Options
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ Welcome to Glyphoxa โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ๐ฎ Continue with Discord โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ G Continue with Google โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโ or โโโโโโโโโโโโโโโโ โ
โ โ
โ Email: [ ] โ
โ Password: [ ] โ
โ โ
โ [ Sign in ] โ
โ โ
โ Don't have an account? Sign up โ
โ Forgot password? โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
OAuth2 Flow (Discord primary)
User clicks "Continue with Discord"
โ Next.js redirects to Discord OAuth2 authorize URL
(scopes: identify, email, guilds)
โ User authorizes on Discord
โ Discord redirects to /auth/callback/discord?code=...
โ Next.js API route exchanges code for tokens
โ API route calls POST /api/v1/auth/discord with Discord access token
โ Backend validates, creates/finds user, returns JWT
โ Next.js sets JWT in HttpOnly cookie (Secure, SameSite=Lax)
โ Redirect to /dashboard
Token Management
- Access token: JWT, 15min expiry, stored in HttpOnly cookie
- Refresh token: Opaque, 30-day expiry, stored in HttpOnly cookie
- Silent refresh: TanStack Query interceptor calls
/api/v1/auth/refreshwhen a 401 is received. On failure, redirect to /login. - CSRF protection: Double-submit cookie pattern. The API requires a
X-CSRF-Tokenheader matching a non-HttpOnly cookie value.
Role-Based UI
The JWT payload contains { user_id, tenant_id, role }. The frontend uses role to conditionally render navigation items and redirect unauthorized access:
| Role | Visible Sections |
|---|---|
super_admin | Everything + /admin/* |
tenant_admin | Dashboard, campaigns, NPCs, sessions, billing, settings, users |
dm | Dashboard, campaigns (own), NPCs, sessions (own), settings |
viewer | Dashboard (read-only), sessions (read-only), transcripts |
Middleware in (app)/layout.tsx reads the JWT cookie server-side and redirects to /login if expired or missing. Role checks happen both client-side (hide UI) and server-side (API returns 403).
5. Page Designs
5.1 Landing Page (/)
Purpose: Marketing, feature overview, pricing CTA, trust signals.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ [Logo] Glyphoxa Features Pricing Docs [Sign In] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Bring Your NPCs to Life โ
โ โ
โ AI-powered voice NPCs for tabletop RPGs. โ
โ Distinct voices. Real personalities. โ
โ Persistent memory across sessions. โ
โ โ
โ [ Get Started Free ] [ Watch Demo โถ ] โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ ๐ญ Unique โ โ ๐ Real โ โ ๐ง Memory โ โ
โ โ Personalitiesโ โ Voices โ โ That Lasts โ โ
โ โ โ โ โ โ โ โ
โ โ Each NPC hasโ โ ElevenLabs, โ โ NPCs recall โ โ
โ โ their own โ โ Azure, or โ โ past events,โ โ
โ โ personality โ โ bring your โ โ build โ โ
โ โ and behaviorโ โ own voice โ โ relationshipsโ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ How It Works โ
โ โ
โ 1. Create your campaign and define NPCs โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ [Screenshot: NPC editor with voice config] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ 2. Start a session โ NPCs join your voice channel โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ [Screenshot: Discord voice with NPC] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ 3. Players talk naturally โ NPCs respond in character โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ [Screenshot: Live transcript view] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Pricing โ see /pricing for detail โ
โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โ Free โ โ Adventurerโ โ Dungeon Masterโ โ Guild โ โ
โ โ $0/mo โ โ $9/mo โ โ $19/mo โ โ $29/mo โ โ
โ โ 2 sess. โ โ 8 sess. โ โ Unlimited โ โ + 5 seats โ โ
โ โ 2 NPCs โ โ 10 NPCs โ โ Premium voice โ โ Custom โ โ
โ โ โ โ โ โ โ โ voices โ โ
โ โ [Start] โ โ [Start] โ โ [Start] โ โ [Contact] โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Trusted by DMs running campaigns in D&D 5e, Pathfinder 2e, โ
โ and more. Works with Discord and WebRTC. โ
โ โ
โ [Logo] [Logo] [Logo] (game system logos, Discord logo) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ยฉ 2026 Glyphoxa Privacy Terms Discord GitHub โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Implementation notes:
- Server-rendered (RSC) for SEO and fast FCP
- Hero section uses subtle CSS animation (floating NPC silhouettes)
- โWatch Demoโ opens an inline video player (lazy-loaded)
- Pricing cards link to
/pricingwith anchor to selected tier - Mobile: single-column stack, pricing cards horizontally scrollable
5.2 Login / Register (/login)
See wireframe in Section 4 above. Additional details:
- Social auth buttons prominent at top (Discord primary โ most DMs have it)
- Email/password form below the separator
- Register toggle: โDonโt have an account? Sign upโ swaps the form to registration (name + email + password + confirm), or a separate
/registerpage - Password requirements: 8+ chars, shown inline as user types
- Error states: Inline validation, red border + error text below field
- Loading state: Button shows spinner, disabled during request
- Mobile: Full-width, large touch targets (min 44px)
5.3 Dashboard (/dashboard)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โฐ Glyphoxa [๐] [Avatar โผ] โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ โ
โ Dashboard โ Good evening, Luk โ
โ โโโโโโโโโโ โ
โ Campaigns โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ Sessions โ โ Campaigns โ โ Active โ โ This Month โ โ
โ Billing โ โ 3 โ โ Sessions โ โ 47 / 100h โ โ
โ โโโโโโโโโโ โ โ โ 2 โ โ โโโโโโโโโ โ โ
โ Settings โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ Support โ โ
โ โ Active Sessions โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ ๐ข Die Chroniken von Rabenheim โ โ
โ โ โ Guild: Pen & Paper DE โข Duration: 1:42 โ โ
โ โ โ NPCs: Heinrich, Elara, Erzรคhler โ โ
โ โ โ [View Live] [Stop] โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ โ ๐ข Tutorial Campaign โ โ
โ โ โ Guild: Demo Server โข Duration: 0:05 โ โ
โ โ โ NPCs: Guide โ โ
โ โ โ [View Live] [Stop] โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โ Recent Activity โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ Session ended: Rabenheim (1h 23m) โ โ
โ โ โ Today, 19:42 โ โ
โ โ โ + NPC created: "Erzรคhler" in Rabenheim โ โ
โ โ โ Today, 14:15 โ โ
โ โ โ โ Session ended: Tutorial (0h 12m) โ โ
โ โ โ Yesterday, 21:30 โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โ Quick Actions โ
โ โ [+ New Campaign] [+ New NPC] [๐ View Transcripts] โ
โ โ โ
โโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key interactions:
- Metric cards are clickable โ link to respective detail pages
- Active sessions auto-refresh every 10s via TanStack Query
refetchInterval - โView Liveโ navigates to
/sessions/[id]/live - โStopโ shows a confirmation dialog before calling
DELETE /api/v1/sessions/{id} - Activity feed is a reverse-chronological list (last 20 items, โShow moreโ link)
- Greeting uses time-of-day logic (morning/afternoon/evening)
- Mobile: Metric cards in a 2x2 grid, sidebar collapses to hamburger menu
5.4 Campaign Management (/campaigns, /campaigns/[id])
Campaign List:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Campaigns [+ New Campaign] โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ๐ฐ Die Chroniken von โ โ ๐ Tutorial Campaign โ โ
โ โ Rabenheim โ โ โ โ
โ โ โ โ System: D&D 5e โ โ
โ โ System: Das Schwarze Augeโ โ NPCs: 1 โ โ
โ โ NPCs: 5 โ โ Last session: 2 days ago โ โ
โ โ Last session: 2 hours agoโ โ โ โ
โ โ โ โ [Open] โ โ
โ โ ๐ข Active session โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ [Open] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ + Create New Campaign โ โ
โ โ โ โ
โ โ Click to get started โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Campaign Detail/Edit (/campaigns/[id]):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Campaigns / Die Chroniken von Rabenheim [Save] [โฏ] โ
โ โ
โ โโ Details โโฌโ NPCs โโฌโ Lore โโฌโ Sessions โโ โ
โ โ โ โ โ โ โ
โ โโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโ โ
โ โ
โ Campaign Name: โ
โ [ Die Chroniken von Rabenheim ] โ
โ โ
โ Game System: โ
โ [ Das Schwarze Auge โผ ] โ
โ โ
โ Description: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ **B** _I_ ~~S~~ `<>` H1 H2 โข โโ ๐ ๐ท โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Die Stadt Rabenheim liegt am Rand des Dรผsterwalds. โ โ
โ โ Seit dem Verschwinden des alten Bรผrgermeisters โ โ
โ โ herrscht Unruhe in der Bevรถlkerung... โ โ
โ โ โ โ
โ โ ## Wichtige Orte โ โ
โ โ - Das Rathaus โ โ
โ โ - Der Dรผsterwald โ โ
โ โ - Die Taverne "Zum Goldenen Raben" โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Campaign Settings (JSONB): โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Language: [ Deutsch โผ ] โ โ
โ โ Max Players: [ 6 ] โ โ
โ โ Auto-recap: [โ] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโ Danger Zone โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ [ Delete Campaign ] (requires typing campaign name) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Tab navigation: Details | NPCs | Lore | Sessions โ each tab is a sub-route that preserves campaign context without full page reload.
Lore editor: Uses a split-pane markdown editor (edit left, preview right) built with @uiw/react-md-editor or a custom Tiptap setup. Supports image upload via drag-and-drop (stored as presigned S3 URLs or inline base64 for MVP).
5.5 NPC Editor (/campaigns/[id]/npcs/[npcId])
The most complex page. Organized into collapsible sections with a sticky save bar at the top.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Rabenheim / NPCs / Heinrich der Wรคchter โ
โ [Discard] [Save Changes]โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ โโ
โ โ โโ Identity โโ โ โโ Preview โโ โโ
โ โ โ โโ
โ โ Name: โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ [ Heinrich der Wรคchter ] โ โ โโโโโโโโ โ โโ
โ โ โ โ โAvatarโ Heinrich der โ โโ
โ โ Avatar: โ โ โ ๐ก๏ธ โ Wรคchter โ โโ
โ โ [Choose file] or drag here โ โ โโโโโโโโ โ โโ
โ โ โ โ โ โโ
โ โ โ โ "Halt! Wer geht da?" โ โโ
โ โ โ โ [โถ Play Voice] โ โโ
โ โ โ โ โ โโ
โ โ โ โ Engine: Cascaded โ โโ
โ โ โ โ Tier: Standard โ โโ
โ โ โ โ Voice: ElevenLabs/Helmut โ โโ
โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โ โโ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ
โ โ โโ
โ โ โโ Personality โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [โผ collapse] โโ
โ โ โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โ Ein strenger aber gerechter Stadtwรคchter, der seit โ โโ
โ โ โ รผber 20 Jahren die Tore von Rabenheim bewacht. Er kennt โ โโ
โ โ โ jeden Bewohner beim Namen und misstraut Fremden โ โโ
โ โ โ zunรคchst, kann aber durch Ehrlichkeit รผberzeugt werden. โ โโ
โ โ โ โ โโ
โ โ โ Heinrich hat eine tiefe, raue Stimme und spricht in โ โโ
โ โ โ kurzen, prรคgnanten Sรคtzen. Er nennt Respektspersonen โ โโ
โ โ โ beim Titel. โ โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ Characters: 423 / 2000 โโ
โ โ โโ
โ โ โโ Voice โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [โผ] โโ
โ โ โโ
โ โ Provider: [ ElevenLabs โผ ] โโ
โ โ โโ
โ โ Voice: [ Helmut โผ ] [โถ Preview] โโ
โ โ โโ
โ โ Sample text: [ Halt! Wer geht da? Niemand passiert ] โโ
โ โ [ dieses Tor ohne gรผltigen Passierschein. ] โโ
โ โ โโ
โ โ โโ Waveform โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โ โโโโ
โโ
โโโโโโ
โโโโโโ
โโโโโโโ
โโ
โโโโโโ
โโโโโโ
โ โ โโ
โ โ โ 0:00 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 0:04 โ โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โโ
โ โ Pitch: -10 โโโโโโโโโโโโโโโโโ +10 (-2.0 semitones) โโ
โ โ Speed: 0.5 โโโโโโโโโโโโโโโโโ 2.0 (1.0x) โโ
โ โ โโ
โ โ Custom Voice Sample: โโ
โ โ [ Upload .wav / .mp3 (max 10MB) ] โโ
โ โ Used for voice cloning (ElevenLabs Instant Voice Clone) โโ
โ โ โโ
โ โ โโ Engine & Tier โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [โผ] โโ
โ โ โโ
โ โ Engine: โโ
โ โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโ
โ โ โ โ Cascaded โ โ โ S2S โ โ โ Sentence โ โโ
โ โ โ STTโLLMโ โ โ Direct โ โ Cascade โ โโ
โ โ โ TTS โ โ speech-to โ โ Hybrid โ โโ
โ โ โ โ โ -speech โ โ โ โโ
โ โ โ Best qualityโ โ Lowest โ โ Good balance โ โโ
โ โ โ โ โ latency โ โ โ โโ
โ โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโ
โ โ โโ
โ โ Budget Tier: โโ
โ โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโ โโ
โ โ โ โ Fast โ โ โ Standard โ โ โ Deep โ โโ
โ โ โ Quick โ โ Balanced โ โ Thorough โ โโ
โ โ โ responsesโ โ quality & โ โ reasoningโ โโ
โ โ โ โ โ speed โ โ + tools โ โโ
โ โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโ โโ
โ โ โโ
โ โ โโ Knowledge โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [โผ] โโ
โ โ โโ
โ โ Knowledge Scope (topics this NPC knows about): โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โ [Rabenheim history โ] [guard duties โ] โ โโ
โ โ โ [city layout โ] [+ type to add...] โ โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โโ
โ โ Secret Knowledge (facts NPC knows but won't volunteer): โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โ [The mayor's corruption โ] [+ type to add...] โ โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โโ
โ โ โโ Behavior Rules โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [โผ] โโ
โ โ โโ
โ โ โก Spricht immer Deutsch [โ] โโ
โ โ โก Misstraut Fremden zunรคchst [โ] โโ
โ โ โก Nennt Respektspersonen beim Titel [โ] โโ
โ โ [+ Add Rule] โโ
โ โ โโ
โ โ (โก = drag handle for reordering via @dnd-kit) โโ
โ โ โโ
โ โ โโ Advanced โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [โผ] โโ
โ โ โโ
โ โ MCP Tools: โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โ [patrol_route โ] [check_papers โ] [+ add...] โ โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โโ
โ โ [โ] Address Only โ only responds when directly addressed โโ
โ โ [ ] GM Helper โ acts as narrator/GM assistant (1 per campaign) โโ
โ โ โโ
โ โ Custom Attributes (JSON): โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โ { โ โโ
โ โ โ "alignment": "lawful neutral", โ โโ
โ โ โ "age": 52, โ โโ
โ โ โ "faction": "Stadtwache" โ โโ
โ โ โ } โ โโ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ
โ โ โโ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key interactions:
- Voice preview flow:
- User selects provider + voice ID + adjusts pitch/speed
- User types or accepts default sample text
- Clicks โPreviewโ โ
POST /api/v1/npcs/{id}/voice-previewwith{ text, voice_config }body - Returns audio blob (opus/mp3)
- Web Audio API plays audio, waveform visualizer shows amplitude
- Rate-limited: 5 previews/minute client-side, server enforces per-tenant
- Custom voice upload:
- User uploads .wav/.mp3 file (max 10MB)
- Client validates format and duration (5-30 seconds)
POST /api/v1/voices/uploadwith multipart form data- Backend sends to ElevenLabs Instant Voice Clone API
- Returns a new voice_id that can be selected in the voice dropdown
- Show progress bar during upload + cloning
-
Drag-and-drop behavior rules:
@dnd-kit/core+@dnd-kit/sortablefor reordering. Keyboard accessible (Enter to grab, arrows to move, Enter to drop). Persists order index. -
Tag inputs (knowledge scope, secrets, tools): Combobox with free-text entry. Type to filter existing tags, Enter to add. Click โ to remove. Tags are stored as
string[]in the NPC definition. -
Unsaved changes warning:
useBeforeUnloadhook + React Router blocker. Sticky save bar turns yellow when form is dirty. โDiscardโ resets to server state. - Mobile layout: Single column. Preview panel moves above the form. Collapsible sections start collapsed except Identity and Personality. Voice controls use full-width sliders.
5.6 Session Monitoring (/sessions, /sessions/[id]/live)
Session List:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Sessions [Active โผ] [All time โผ] โ
โ โ
โ โโโโโโโโโฌโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโ โ
โ โStatus โ Campaign โ Guild โ Duration โ Actions โ โ
โ โโโโโโโโโผโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโโโค โ
โ โ ๐ข โ Rabenheim โ PP DE โ 1:42:15 โ [Live] [โฏ]โ โ
โ โ ๐ข โ Tutorial โ Demo โ 0:05:30 โ [Live] [โฏ]โ โ
โ โ โช โ Rabenheim โ PP DE โ 1:23:00 โ [View] [โฏ]โ โ
โ โ โช โ Tutorial โ Demo โ 0:12:45 โ [View] [โฏ]โ โ
โ โ ๐ด โ Rabenheim โ PP DE โ 0:03:12 โ [View] [โฏ]โ โ
โ โโโโโโโโโดโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโ โ
โ โ
โ Showing 5 of 47 sessions [โ 1 2 3 4 5 โบ] โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Status indicators: ๐ข active, โช ended, ๐ด ended with error. Filter bar: status dropdown, campaign filter, date range picker. Search: full-text across campaign name, guild name, error messages.
Live Session Monitor (/sessions/[id]/live):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Sessions / Live: Rabenheim ๐ข Connected โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ โ
โ Live Transcript โ Session Info โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ Campaign: Rabenheim โ
โ โ โ โ Guild: Pen & Paper DE โ
โ โ [19:42:15] Player (Luk): โ โ Channel: #taverne โ
โ โ "Guten Abend, Wรคchter. โ โ Duration: 1:42:15 โ
โ โ Wir suchen den Bรผrger- โ โ Worker: worker-2 โ
โ โ meister." โ โ โ
โ โ โ โ Active NPCs โ
โ โ [19:42:18] Heinrich: โ โ โโโโโโโโโโโโโโโโโโโโโโ โ
โ โ "Halt! Der Bรผrgermeister โ โ โ Heinrich โ 3 resp โ โ
โ โ empfรคngt keine Besucher โ โ โ Elara โ 1 resp โ โ
โ โ nach Sonnenuntergang." โ โ โ Erzรคhler โ 5 resp โ โ
โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโ โ
โ โ [19:42:22] Player (Sara): โ โ โ
โ โ "Aber es ist dringend!" โ โ Audio Stats โ
โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โผ auto-scroll โ โ โ VAD: โโโโ
โโ
โโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ STT: 142ms avg โ โ
โ โ โ LLM: 380ms avg โ โ
โ โ โ TTS: 210ms avg โ โ
โ โ โ E2E: 890ms avg โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โ [Force Stop Session] โ
โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Connection: WebSocket โข Latency: 23ms โข Messages: 47 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
WebSocket connection:
- Connect to
wss://api.glyphoxa.com/api/v1/sessions/{id}/live - JWT sent as query param or first message (WebSocket doesnโt support cookies)
- Server pushes:
transcript_entry,audio_stats,session_state_change - Client auto-reconnects with exponential backoff (1s, 2s, 4s, max 30s)
- Connection status indicator in header (๐ข Connected / ๐ก Reconnecting / ๐ด Disconnected)
- Auto-scroll toggle: locks to bottom by default, unlocks if user scrolls up
Mobile: Transcript full-width, info panel in a collapsible bottom sheet.
5.7 Transcript Viewer (/sessions/[id])
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Sessions / Session 2026-03-24 19:00 โ
โ โ
โ โโ Transcript โโฌโ Details โโฌโ Stats โโ โ
โ โ โ โ โ โ
โ โโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโ โ
โ โ
โ Search: [ Search transcript... ๐ ] [Raw โ โ Corrected] โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ 19:00:12 โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ ๐ค Luk (Player) โ โ โ
โ โ โ "Wir betreten die Taverne und schauen uns um." โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ 19:00:15 โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ ๐ญ Erzรคhler (NPC) โ โ โ
โ โ โ "Die Taverne 'Zum Goldenen Raben' ist an diesem โ โ โ
โ โ โ Abend gut besucht. Am Tresen steht ein breit- โ โ โ
โ โ โ schultriger Mann und poliert Glรคser." โ โ โ
โ โ โ 1.1s โก โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ 19:00:22 โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ ๐ค Sara (Player) โ โ โ
โ โ โ "Ich gehe zum Wirt und bestelle ein Bier." โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Export: [๐ Text] [๐ JSON] [๐ CSV] โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key features:
- Raw vs Corrected toggle: Shows original STT output vs LLM-corrected text
- Search: Client-side filter with highlighted matches (for loaded entries). Server-side full-text search via
GET /api/v1/sessions/{id}/transcript?q=... - Infinite scroll: Load 50 entries at a time, scroll to load more (TanStack Query
useInfiniteQuery) - Response time badges: NPC responses show end-to-end latency (โก < 1.2s, โ ๏ธ > 2s)
- Export: Client-side generation for text/JSON, server-side for CSV (large transcripts)
- Color coding: Player messages left-aligned (neutral), NPC messages right-aligned (tinted by NPC color)
5.8 Session Replay (Future โ /sessions/[id]/replay)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Session / Replay: Rabenheim 2026-03-24 โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ (Transcript entries appear synchronized with audio) โ โ
โ โ โ โ
โ โ [Erzรคhler's message highlighted as audio plays] โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โโโโ
โโ
โโโโโโ
โโโโโโ
โโโโโโโ
โโ
โโโโโโ
โโโโโโ
โโโโโโ
โโโโโโ
โโโ โ โ
โ โ 0:00 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 1:42:15 โ โ
โ โ โ โ
โ โ [โฎ] [โโ] [ โถ Play ] [โถโถ] [โญ] 1x โผ ๐ โโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Design notes (future implementation):
- Audio stored as opus/webm per-session (requires backend audio recording feature)
- Transcript entries have timestamps that sync with audio playback position
- Currently-playing entry highlighted and auto-scrolled
- Playback speed: 0.5x, 1x, 1.5x, 2x
- Skip between entries with forward/back buttons
- This page is a placeholder until audio recording is implemented
5.9 Billing & Usage (/billing)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Billing & Usage โ
โ โ
โ Current Plan โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ๐ Dungeon Master Plan โ $19/month โ โ
โ โ โ โ
โ โ Unlimited sessions โข Premium voices โข Unlimited NPCs โ โ
โ โ Next billing date: April 1, 2026 โ โ
โ โ โ โ
โ โ [Change Plan] [Manage Payment Method] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ This Month's Usage (March 2026) โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ Session hrs โ โ LLM Tokens โ โ STT Seconds โ โ TTS Chars โ โ
โ โ 47.2h โ โ 1.2M โ โ 4,230s โ โ 892K โ โ
โ โ โโโโโโโโ โ โ โ โ โ โ โ โ
โ โ 47/100h โ โ (no limit) โ โ (no limit) โ โ (no limit) โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ
โ Usage Over Time โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Session Hours by Day [This month โผ] โ โ
โ โ โ โ
โ โ 5h โค โ โ
โ โ 4h โค โโ โ โ
โ โ 3h โค โโ โโ โโ โ โ
โ โ 2h โค โโ โโ โโ โโ โโ โ โ
โ โ 1h โค โโ โโ โโ โโ โโ โโ โโ โโ โโ โ โ
โ โ 0h โผโโโฌโโโฌโโโฌโโโฌโโโฌโโโฌโโโฌโโโฌโโโฌโโ โ โ
โ โ 1 3 5 7 9 11 13 15 17 19 ... โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Session Breakdown โ
โ โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโ โ
โ โ Date โ Duration โ Tokens โ STT โ TTS โ โ
โ โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโโค โ
โ โ Mar 24, 19:00 โ 1h 42m โ 45,230 โ 156s โ 23,400 โ โ
โ โ Mar 23, 20:15 โ 2h 10m โ 62,100 โ 210s โ 31,200 โ โ
โ โ Mar 22, 19:30 โ 1h 23m โ 38,900 โ 134s โ 19,800 โ โ
โ โโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโ โ
โ โ
โ [Download CSV] โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โChange Planโ flow: Navigates to /billing/plans which shows the pricing comparison (same tiers as landing page) with the current plan highlighted. Upgrade is immediate; downgrade takes effect at end of billing period. Payment via Stripe Checkout or Stripe Elements embedded form.
5.10 Settings (/settings)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Settings โ
โ โ
โ โโ Account โโฌโ API Keys โโฌโ Notifications โโ โ
โ โ โ โ โ โ
โ โโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Profile โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Name: [ Luk ] โ โ
โ โ Email: [ luk@example.com ] (verified)โ โ
โ โ Discord: Connected as LukTTRPG#1234 [Unlink] โ โ
โ โ Google: Not connected [Link] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Appearance โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Theme: ( ) Light (โ) Dark ( ) System โ โ
โ โ Language: [ English โผ ] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโ Danger Zone โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ [ Delete Account ] (requires confirmation) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
API Keys tab (/settings/api-keys):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ API Keys โ Bring Your Own โ
โ โ
โ Use your own API keys to avoid usage limits on your plan. โ
โ Keys are encrypted and stored securely via Vault Transit. โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ OpenAI (LLM) โ โ
โ โ Key: sk-โขโขโขโขโขโขโขโขโขโขโขโขโขโขโขโข3kF7 [Change] [Remove] โ โ
โ โ Status: โ Valid (tested 2h ago) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ ElevenLabs (TTS) โ โ
โ โ Key: Not configured [Add Key] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Deepgram (STT) โ โ
โ โ Key: Not configured [Add Key] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ [Test All Keys] โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
5.11 Admin Dashboard (/admin โ super_admin only)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Admin Dashboard โ
โ โ
โ System Overview โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โ Tenants โ โ Active โ โ Total โ โ System โ โ
โ โ 12 โ โ Sessions โ โ Session hrsโ โ Health โ โ
โ โ โ โ 5 โ โ 342h/mo โ โ โ All OK โ โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โ
โ Provider Health โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ LLM โ โ โ STT โ โ โ TTS โ โ โ VAD โ โ โ EMB โ โ โ
โ โ OpenAI โ โ Deepgram โ โ Eleven โ โ Silero โ โ Gemini โ โ
โ โ P50: 380 โ โ P50: 142 โ โ P50: 210 โ โ P50: 12 โ โ P50: 95 โ โ
โ โ P99: 920 โ โ P99: 310 โ โ P99: 540 โ โ P99: 28 โ โ P99: 220 โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ
โ Active Sessions Across Tenants โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโ โ
โ โ Tenant โ Campaign โ Guild โ Duration โ Worker โ โ
โ โโโโโโโโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโโโค โ
โ โ luk โ Rabenheim โ PP DE โ 1:42:15 โ worker-2 โ โ
โ โ demo โ Tutorial โ Demo โ 0:05:30 โ worker-1 โ โ
โ โ team_a โ Ravenloft โ D&D Club โ 3:10:42 โ worker-3 โ โ
โ โโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโ โ
โ โ
โ Revenue This Month โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ MRR: $156 โข New: 3 โข Churned: 0 โข Upgraded: 1 โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Quick Links โ
โ [Grafana Dashboard] [OTel Traces] [Prometheus Metrics] โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
5.12 Support (/support)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Help & Support โ
โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ ๐ Docs โ โ ๐ฌ Discord โ โ ๐ซ Tickets โ โ
โ โ Browse the โ โ Join our โ โ Submit a โ โ
โ โ documenta- โ โ community โ โ support โ โ
โ โ tion โ โ server โ โ ticket โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ
โ Frequently Asked Questions โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โถ How do I add my Discord bot to a server? โ โ
โ โ โถ Why is my NPC's voice sounding different? โ โ
โ โ โถ How do I use my own API keys? โ โ
โ โ โถ What game systems are supported? โ โ
โ โ โถ How does the knowledge graph work? โ โ
โ โ โถ Can players interact with NPCs in text chat? โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Your Tickets โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโ โ
โ โ # โ Subject โ Status โ Date โ โ
โ โโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโค โ
โ โ 42 โ NPC not responding in voice โ ๐ก Open โ Mar 22 โ โ
โ โ 38 โ Billing question โ โ Closed โ Mar 15 โ โ
โ โโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโ โ
โ โ
โ [+ New Ticket] โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
6. Component Hierarchy
Shared Layout Components
<RootLayout> # HTML shell, font loading, providers
โโโ <ThemeProvider> # Dark/light/system theme
โโโ <QueryClientProvider> # TanStack Query
โโโ <IntlProvider> # next-intl translations
โ
โโโ (public) <MarketingLayout> # Landing, pricing, login
โ โโโ <MarketingHeader /> # Logo, nav links, sign-in button
โ โโโ {children}
โ โโโ <MarketingFooter /> # Links, legal, social
โ
โโโ (app) <AppLayout> # Authenticated pages
โ โโโ <AuthGuard /> # Redirect to /login if unauthenticated
โ โโโ <Sidebar> # Collapsible navigation
โ โ โโโ <SidebarLogo />
โ โ โโโ <SidebarNav> # Role-filtered nav items
โ โ โ โโโ <NavItem /> # Dashboard, Campaigns, Sessions, etc.
โ โ โ โโโ <NavGroup /> # Collapsible groups (Settings, Admin)
โ โ โโโ <SidebarFooter /> # User avatar, logout
โ โโโ <Topbar>
โ โ โโโ <Breadcrumb /> # Auto-generated from route segments
โ โ โโโ <SearchCommand /> # Cmd+K global search (cmdk)
โ โ โโโ <NotificationBell /> # Unread count badge
โ โ โโโ <UserMenu /> # Avatar dropdown (profile, settings, logout)
โ โโโ <MainContent>
โ โโโ <Suspense fallback={<PageSkeleton />}>
โ โโโ {children}
โ
โโโ (admin) <AdminLayout> # Super admin pages
โโโ <AuthGuard requiredRole="super_admin" />
โโโ <AdminSidebar /> # Admin-specific navigation
โโโ {children}
Page-Level Components
<DashboardPage>
โโโ <MetricCardGrid>
โ โโโ <MetricCard /> # Campaigns count
โ โโโ <MetricCard /> # Active sessions
โ โโโ <MetricCard variant="progress" /> # Usage quota bar
โโโ <ActiveSessionsList>
โ โโโ <ActiveSessionCard /> # Per-session row with actions
โโโ <RecentActivityFeed>
โ โโโ <ActivityItem /> # Timestamped event entry
โโโ <QuickActions /> # CTA buttons
<CampaignListPage>
โโโ <PageHeader title actions={<CreateButton />} />
โโโ <CampaignCardGrid>
โ โโโ <CampaignCard /> # Name, system, NPC count, status
โโโ <EmptyState /> # Shown when no campaigns exist
<CampaignDetailPage>
โโโ <PageHeader title breadcrumb actions={<SaveButton />} />
โโโ <Tabs>
โ โโโ <Tab label="Details">
โ โ โโโ <CampaignForm /> # React Hook Form
โ โโโ <Tab label="NPCs">
โ โ โโโ <NPCListForCampaign />
โ โโโ <Tab label="Lore">
โ โ โโโ <MarkdownEditor /> # Split-pane edit/preview
โ โโโ <Tab label="Sessions">
โ โโโ <SessionHistoryTable />
โโโ <DangerZone />
<NPCEditorPage> # Most complex page
โโโ <StickyHeader>
โ โโโ <Breadcrumb />
โ โโโ <UnsavedIndicator />
โ โโโ <DiscardButton />
โ โโโ <SaveButton />
โโโ <TwoColumnLayout>
โ โโโ <LeftColumn> # Form (scrollable)
โ โ โโโ <IdentitySection>
โ โ โ โโโ <TextInput name="name" />
โ โ โ โโโ <AvatarUpload />
โ โ โโโ <CollapsibleSection title="Personality">
โ โ โ โโโ <Textarea />
โ โ โโโ <CollapsibleSection title="Voice">
โ โ โ โโโ <VoiceProviderSelect />
โ โ โ โโโ <VoiceIdSelect />
โ โ โ โโโ <VoicePreviewButton />
โ โ โ โโโ <AudioWaveform />
โ โ โ โโโ <PitchSlider />
โ โ โ โโโ <SpeedSlider />
โ โ โ โโโ <VoiceSampleUpload />
โ โ โโโ <CollapsibleSection title="Engine & Tier">
โ โ โ โโโ <EngineRadioCards />
โ โ โ โโโ <BudgetTierRadioCards />
โ โ โโโ <CollapsibleSection title="Knowledge">
โ โ โ โโโ <TagInput name="knowledgeScope" />
โ โ โ โโโ <TagInput name="secretKnowledge" />
โ โ โโโ <CollapsibleSection title="Behavior Rules">
โ โ โ โโโ <SortableRulesList /> # @dnd-kit sortable
โ โ โโโ <CollapsibleSection title="Advanced">
โ โ โโโ <TagInput name="tools" />
โ โ โโโ <Checkbox name="addressOnly" />
โ โ โโโ <Checkbox name="gmHelper" />
โ โ โโโ <JsonEditor name="attributes" />
โ โโโ <RightColumn> # Preview (sticky on desktop)
โ โโโ <NPCPreviewCard />
โโโ <UnsavedChangesDialog />
<LiveSessionPage>
โโโ <ConnectionStatusBar /> # WebSocket status
โโโ <TwoColumnLayout>
โ โโโ <TranscriptStream> # Virtualized scrolling list
โ โ โโโ <TranscriptEntry variant="player" />
โ โ โโโ <TranscriptEntry variant="npc" />
โ โโโ <SessionInfoPanel>
โ โโโ <SessionMetadata />
โ โโโ <ActiveNPCsList />
โ โโโ <AudioStatsWidget /> # Real-time latency metrics
โ โโโ <ForceStopButton />
โโโ <StatusFooter /> # Connection, latency, message count
<BillingPage>
โโโ <CurrentPlanCard />
โโโ <UsageMetricGrid>
โ โโโ <UsageMetricCard /> # Session hours, tokens, etc.
โโโ <UsageChart /> # Recharts line/bar chart
โโโ <SessionBreakdownTable /> # Paginated table
โโโ <ExportButton />
Reusable UI Components (shadcn/ui + custom)
Primitives (shadcn/ui): Custom composites:
โโโ Button โโโ MetricCard
โโโ Input โโโ TagInput (combobox + chips)
โโโ Textarea โโโ CollapsibleSection
โโโ Select โโโ SortableList (@dnd-kit)
โโโ Checkbox โโโ AudioWaveform (Web Audio)
โโโ RadioGroup โโโ VoicePreviewButton
โโโ Slider โโโ MarkdownEditor
โโโ Dialog โโโ JsonEditor (Monaco or simple)
โโโ DropdownMenu โโโ DataTable (TanStack Table)
โโโ Tabs โโโ EmptyState
โโโ Badge โโโ StatusBadge (๐ข๐ก๐ด)
โโโ Tooltip โโโ PageHeader (title + breadcrumb + actions)
โโโ Skeleton โโโ ConfirmDialog
โโโ Toast (sonner) โโโ FileUpload (drag & drop zone)
โโโ Command (cmdk) โโโ InfiniteScrollList
โโโ Sheet (mobile sidebar)
โโโ Card
โโโ Avatar
โโโ Separator
7. Key Interactions
7.1 Voice Preview
User action System behavior
โโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
Select voice provider โ Load available voices for provider
Select voice ID โ Enable preview button
Click "Preview" โ 1. Disable button, show spinner
2. POST /api/v1/npcs/{id}/voice-preview
body: { text, voice: { provider, voice_id, pitch, speed } }
3. Receive audio blob (opus/mp3)
4. Decode with Web Audio API (AudioContext)
5. Display waveform (AnalyserNode โ canvas)
6. Auto-play audio
7. Re-enable button
Adjust pitch/speed slider โ Debounce 300ms, then auto-preview
Click waveform โ Seek to position in audio
Rate limit exceeded โ Toast: "Too many previews. Wait 30s."
Button disabled with countdown
Web Audio API setup:
// Simplified โ actual implementation in src/hooks/useVoicePreview.ts
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
async function playPreview(audioBlob: Blob) {
const buffer = await audioContext.decodeAudioData(await audioBlob.arrayBuffer());
const source = audioContext.createBufferSource();
source.buffer = buffer;
source.connect(analyser);
analyser.connect(audioContext.destination);
source.start();
drawWaveform(analyser); // requestAnimationFrame loop
}
7.2 Drag-and-Drop NPC Ordering
Within a campaignโs NPC list, NPCs can be reordered via drag-and-drop to control their priority (first NPC is the default responder).
Drag start (mouse/touch) โ 1. Show drag overlay with NPC card
2. Other items animate to make space
3. Keyboard: Enter to pick up, arrows to move
Drop โ 1. Animate into new position
2. Optimistic update (TanStack Query)
3. PATCH /api/v1/campaigns/{id}/npcs/order
body: { npc_ids: ["id1", "id3", "id2"] }
4. On error: revert optimistic update + toast
7.3 Live Transcript WebSocket
Component mount โ 1. Connect to wss://api/v1/sessions/{id}/live
2. Send auth: { type: "auth", token: jwt }
3. Server sends session snapshot (last 50 entries)
4. Render initial transcript
Server push โ Message types:
- transcript_entry: { speaker, text, npc_id, ts }
- audio_stats: { vad_active, stt_ms, llm_ms, tts_ms, e2e_ms }
- session_state: { state: "ended", error? }
- heartbeat: { ts } (every 30s)
Client receives entry โ 1. Append to virtualized list
2. Auto-scroll if at bottom
3. If scrolled up: show "N new messages โ" pill
Connection lost โ 1. Show ๐ก Reconnecting
2. Exponential backoff: 1s, 2s, 4s, ..., 30s max
3. On reconnect: request entries since last ts
4. Show ๐ข Connected
Session ends โ 1. Show "Session ended" banner
2. Close WebSocket
3. Show link to transcript viewer
7.4 Global Search (Cmd+K)
Powered by cmdk (shadcn/ui Command component):
Cmd+K โ Open command palette
Type query โ Search across:
- Campaigns (by name)
- NPCs (by name, campaign)
- Sessions (by campaign, date)
- Pages (Dashboard, Settings, etc.)
Debounced 200ms, client-side for pages,
server-side for entities
Select result โ Navigate to entity page
Escape โ Close palette
8. Accessibility
WCAG 2.1 AA Compliance
- Color contrast: All text meets 4.5:1 ratio (Tailwind defaults + verified). Status indicators use shapes + text alongside color (not color alone).
- Keyboard navigation: All interactive elements focusable with Tab. Custom components (sliders, drag-and-drop, tag inputs) have keyboard handlers.
- Screen readers: shadcn/ui components use Radix ARIA patterns. Custom components have
aria-label,aria-describedby,roleattributes. - Focus management: Dialog opens โ focus trapped. Dialog closes โ focus returns. Page navigation โ focus moves to main heading (via
useEffect+ref.focus()). - Motion:
prefers-reduced-motionrespected. Drag-and-drop falls back to button-based reorder. Waveform animation paused. - Forms: Every input has a visible label. Error messages linked via
aria-describedby. Required fields marked witharia-required. Form-level error summary at top. - Skip links: โSkip to main contentโ link visible on focus.
- Language:
<html lang="en">set, updated for i18n. NPC content thatโs in a different language can be wrapped in<span lang="de">.
Touch Targets
- Minimum 44x44px for all interactive elements (buttons, links, inputs)
- Sidebar nav items: 48px height
- Mobile: 12px minimum gap between adjacent targets
9. Performance Strategy
Code Splitting & Lazy Loading
Route-based splitting (automatic with Next.js App Router):
/ โ ~50KB (landing, SSR, minimal JS)
/dashboard โ ~80KB (charts, metric cards)
/campaigns/[id]/npcs/[npcId] โ ~120KB (heaviest: editor, audio, dnd-kit)
/sessions/[id]/live โ ~60KB (WebSocket, virtualized list)
/billing โ ~70KB (Recharts)
Component-level lazy loading:
- MarkdownEditor โ dynamic(() => import('./MarkdownEditor'))
- JsonEditor โ dynamic(() => import('./JsonEditor'))
- AudioWaveform โ dynamic(() => import('./AudioWaveform'))
- UsageChart โ dynamic(() => import('./UsageChart'))
- KnowledgeGraph โ dynamic(() => import('./KnowledgeGraph')) // Phase 3
Data Loading
| Pattern | Used for |
|---|---|
| Server Components | Landing page, initial page shells, SEO content |
| TanStack Query | All API data. staleTime: 30s for lists, 60s for detail. |
| Optimistic updates | NPC save, campaign edit, rule reorder, session stop |
| Infinite scroll | Transcript entries (50/page), session history, activity feed |
| Polling | Active sessions (10s), dashboard metrics (30s) |
| WebSocket | Live transcript only (dedicated connection per active session) |
| Prefetch | Hover on campaign card โ prefetch campaign detail + NPCs |
Bundle Optimization
- Tree shaking: ES modules only. No barrel exports in component library.
- Font subsetting: Geist loaded via
next/fontwith Latin subset. - Image optimization:
next/imagewith WebP/AVIF, lazy loading, blur placeholders. - CSS: Tailwind purge removes unused classes. Final CSS ~15-25KB gzipped.
- Icons: Lucide tree-shakes to only imported icons.
Target Metrics
| Metric | Target | Measurement |
|---|---|---|
| FCP (landing) | < 1.0s | Lighthouse, real user monitoring |
| LCP (landing) | < 2.0s | Lighthouse |
| TTI (dashboard) | < 2.5s | Lighthouse |
| CLS | < 0.1 | Lighthouse |
| JS bundle (initial) | < 100KB gzipped | Build analysis |
| API response (p95) | < 200ms | Server-side metrics |
10. Responsive Design
Breakpoints (Tailwind defaults)
| Breakpoint | Width | Layout |
|---|---|---|
sm | 640px | Single column, hamburger nav |
md | 768px | Two columns where needed |
lg | 1024px | Full sidebar + content |
xl | 1280px | Wider content area |
2xl | 1536px | Max-width container |
Mobile Adaptations
| Desktop | Mobile |
|---|---|
| Sidebar always visible | Sheet overlay (hamburger toggle) |
| Two-column NPC editor | Single column, preview at top |
| Data tables | Card-based list or horizontal scroll |
| Inline form sections | Collapsed accordions (expand on tap) |
| Hover tooltips | Long-press or info icons |
| Cmd+K search | Search icon in topbar |
| Side-by-side markdown editor | Tabs: Edit / Preview |
| Live monitor split view | Transcript full width, info in bottom sheet |
PWA Considerations (Future)
The app is structured to support Progressive Web App features later:
manifest.jsonfor home screen installation- Service worker for offline NPC editing (drafts saved to IndexedDB)
- Push notifications for session events
11. Error Handling & Loading States
Loading Patterns
Initial page load โ Full-page skeleton (shadcn Skeleton components)
Data refetch โ Subtle shimmer on stale data (no layout shift)
Mutation in progress โ Button spinner + disabled state
Long operation โ Progress bar or step indicator
WebSocket reconnect โ Banner: "Reconnecting..." with spinner
Error Patterns
API 400 (validation) โ Inline field errors (red border + message below)
API 401 (auth) โ Silent refresh attempt โ if fails, redirect to /login
API 403 (forbidden) โ Toast: "You don't have permission for this action"
API 404 (not found) โ Full-page "Not Found" with link to parent
API 429 (rate limit) โ Toast: "Too many requests. Please wait."
API 500 (server) โ Toast: "Something went wrong. Please try again."
+ Retry button for idempotent operations
Network error โ Banner: "Connection lost. Retrying..."
+ TanStack Query auto-retry (3 attempts)
Form submission fail โ Error summary at top of form + scroll to first error
WebSocket disconnect โ Connection status indicator + auto-reconnect
Empty States
Every list view has a designed empty state:
- No campaigns: Illustration + โCreate your first campaignโ CTA
- No NPCs: โAdd an NPC to bring your campaign to lifeโ + example templates
- No sessions: โStart a session in Discord to see it hereโ
- No transcripts: โRun a session to generate transcriptsโ
12. i18n Strategy
Phase 1 (English MVP)
- All user-facing strings extracted to
messages/en.jsonusingnext-intl - ICU message format for plurals, dates, numbers:
{ "dashboard.activeSessions": "{count, plural, =0 {No active sessions} one {# active session} other {# active sessions}}", "billing.usage": "Used {used} of {total} hours" } - Date/time formatting via
Intl.DateTimeFormat(locale-aware) - Number formatting via
Intl.NumberFormat(locale-aware) - No hardcoded strings in components
Phase 2 (German)
- Add
messages/de.json - URL prefix routing:
/de/dashboard,/en/dashboard - Language picker in settings + browser
Accept-Languagedetection - NPC content (personalities, rules) is user-authored and not translated โ the UI chrome is translated, content stays in whatever language the DM writes
RTL / Other Languages (Future)
- Tailwindโs logical properties (
ms-4instead ofml-4) from day one dirattribute responsive to locale
13. Directory Structure
web/
โโโ public/
โ โโโ favicon.ico
โ โโโ og-image.png # Social sharing image
โ โโโ fonts/ # Geist font files
โ
โโโ src/
โ โโโ app/ # Next.js App Router (see Section 3)
โ โ โโโ layout.tsx # Root layout
โ โ โโโ (public)/
โ โ โโโ (app)/
โ โ โโโ (admin)/
โ โ
โ โโโ components/
โ โ โโโ ui/ # shadcn/ui primitives (generated)
โ โ โ โโโ button.tsx
โ โ โ โโโ input.tsx
โ โ โ โโโ ...
โ โ โโโ layout/ # Layout shells
โ โ โ โโโ sidebar.tsx
โ โ โ โโโ topbar.tsx
โ โ โ โโโ breadcrumb.tsx
โ โ โ โโโ page-header.tsx
โ โ โโโ campaign/ # Campaign-specific composites
โ โ โ โโโ campaign-card.tsx
โ โ โ โโโ campaign-form.tsx
โ โ โ โโโ campaign-settings.tsx
โ โ โโโ npc/ # NPC-specific composites
โ โ โ โโโ npc-card.tsx
โ โ โ โโโ npc-form.tsx
โ โ โ โโโ voice-preview.tsx
โ โ โ โโโ voice-sample-upload.tsx
โ โ โ โโโ engine-selector.tsx
โ โ โ โโโ knowledge-tags.tsx
โ โ โ โโโ behavior-rules.tsx
โ โ โโโ session/ # Session-specific composites
โ โ โ โโโ session-table.tsx
โ โ โ โโโ live-transcript.tsx
โ โ โ โโโ transcript-entry.tsx
โ โ โ โโโ audio-stats.tsx
โ โ โ โโโ connection-status.tsx
โ โ โโโ billing/ # Billing composites
โ โ โ โโโ plan-card.tsx
โ โ โ โโโ usage-chart.tsx
โ โ โ โโโ usage-metric.tsx
โ โ โโโ shared/ # Cross-cutting composites
โ โ โโโ metric-card.tsx
โ โ โโโ tag-input.tsx
โ โ โโโ collapsible-section.tsx
โ โ โโโ sortable-list.tsx
โ โ โโโ audio-waveform.tsx
โ โ โโโ markdown-editor.tsx
โ โ โโโ json-editor.tsx
โ โ โโโ data-table.tsx
โ โ โโโ empty-state.tsx
โ โ โโโ confirm-dialog.tsx
โ โ โโโ file-upload.tsx
โ โ โโโ infinite-scroll.tsx
โ โ
โ โโโ hooks/ # Custom React hooks
โ โ โโโ use-voice-preview.ts # Web Audio playback + waveform
โ โ โโโ use-websocket.ts # WebSocket with reconnect
โ โ โโโ use-auth.ts # JWT token management
โ โ โโโ use-role.ts # Role-based access checks
โ โ โโโ use-unsaved-changes.ts # Form dirty tracking
โ โ โโโ use-debounce.ts
โ โ
โ โโโ api/ # API client layer
โ โ โโโ client.ts # openapi-fetch configured instance
โ โ โโโ types.ts # Generated from OpenAPI spec
โ โ โโโ queries/ # TanStack Query hooks (per domain)
โ โ โ โโโ campaigns.ts
โ โ โ โโโ npcs.ts
โ โ โ โโโ sessions.ts
โ โ โ โโโ usage.ts
โ โ โ โโโ users.ts
โ โ โ โโโ auth.ts
โ โ โโโ mutations/ # TanStack mutation hooks
โ โ โโโ campaigns.ts
โ โ โโโ npcs.ts
โ โ โโโ sessions.ts
โ โ โโโ auth.ts
โ โ
โ โโโ lib/ # Utilities
โ โ โโโ auth.ts # Token parsing, role checks
โ โ โโโ format.ts # Date, number, duration formatting
โ โ โโโ cn.ts # Tailwind class merge utility
โ โ โโโ constants.ts # Route paths, config values
โ โ
โ โโโ messages/ # i18n translation files
โ โ โโโ en.json
โ โ โโโ de.json # Phase 2
โ โ
โ โโโ styles/
โ โโโ globals.css # Tailwind base + custom tokens
โ
โโโ next.config.ts
โโโ tailwind.config.ts
โโโ tsconfig.json
โโโ package.json
โโโ Dockerfile
โโโ .env.example # NEXT_PUBLIC_API_URL, etc.
14. Deployment
Docker Image
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
Image size: ~50MB (standalone output, Alpine base)
Environment Variables
NEXT_PUBLIC_API_URL=https://api.glyphoxa.com # Gateway API base URL
NEXT_PUBLIC_WS_URL=wss://api.glyphoxa.com # WebSocket base URL
NEXT_PUBLIC_DISCORD_CLIENT_ID=... # For OAuth redirect
NEXT_PUBLIC_GOOGLE_CLIENT_ID=... # For OAuth redirect
K3s Deployment
# Abbreviated โ full manifest in infra/
apiVersion: apps/v1
kind: Deployment
metadata:
name: glyphoxa-web
spec:
replicas: 2 # Stateless, horizontally scalable
template:
spec:
containers:
- name: web
image: ghcr.io/glyphoxa/web:latest
ports:
- containerPort: 3000
resources:
requests: { cpu: 50m, memory: 64Mi }
limits: { cpu: 200m, memory: 256Mi }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: glyphoxa-web
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
rules:
- host: app.glyphoxa.com
http:
paths:
- path: /
backend:
service:
name: glyphoxa-web
port: { number: 3000 }
tls:
- hosts: [app.glyphoxa.com]
secretName: glyphoxa-web-tls
CORS Configuration (Gateway)
Since the frontend is on a separate domain, the Gateway API needs CORS headers:
Access-Control-Allow-Origin: https://app.glyphoxa.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-CSRF-Token
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
15. Phase Breakdown (Frontend)
Phase 1: MVP
Scope: Core CRUD pages, API key auth, basic monitoring.
- Next.js scaffolding (App Router, Tailwind, shadcn/ui, TanStack Query)
- API client generation from OpenAPI spec
- Marketing layout (landing page, pricing)
- API key login page
- App layout (sidebar, topbar, breadcrumbs)
- Dashboard (metrics, active sessions, activity feed)
- Campaign list + create/edit
- NPC list + full editor (all sections)
- Voice preview with Web Audio API
- Session list + transcript viewer
- Basic usage display
- Settings page (profile, appearance)
- Support page (FAQ, docs links)
- Responsive design pass (mobile)
- Docker image + K3s deployment
- i18n setup (English strings extracted)
Estimated: ~50-60 components, ~30 route pages
Phase 2: Auth + Live + i18n
- Discord OAuth2 login flow
- Google OAuth2 login flow
- JWT-based auth with silent refresh
- Role-based navigation and access guards
- User management page (admin)
- Live session monitoring (WebSocket)
- Audio stats real-time visualization
- German locale (
messages/de.json) - Notification preferences
- Billing integration (Stripe)
- Plan upgrade/downgrade flow
- Voice sample upload for custom voices
Phase 3: Advanced
- Session replay with synced audio
- Knowledge graph visualization
- Admin dashboard (all tenants, system health)
- Audit log viewer
- Usage CSV export
- PWA support (offline NPC editing)
- A/B testing infrastructure
16. Open Questions
-
Separate domain vs subdomain:
app.glyphoxa.com(subdomain) vsglyphoxa.app(separate domain)? Subdomain is simpler for shared cookies withapi.glyphoxa.comif both are under*.glyphoxa.com. -
SSR vs static export: Should the dashboard pages be SSR (for server-side auth checks) or static with client-side auth? Recommendation: SSR for auth pages, static for marketing pages.
-
Stripe integration timeline: Should billing/payments be in Phase 1 (if we want to charge from launch) or Phase 2 (launch free, add payments later)?
-
NPC templates: Should we ship pre-built NPC templates (tavern keeper, guard, merchant) that DMs can clone and customize? This would significantly improve onboarding for new users.
-
Collaborative editing: Multiple DMs editing the same campaign simultaneously? This adds significant complexity (CRDTs or OT). Defer to Phase 3+ unless thereโs strong demand.
-
Analytics: Do we need user analytics (page views, feature usage) for product decisions? If yes, recommend PostHog (self-hosted, privacy-friendly) or Plausible.