DnD Online
Virtual tabletop de D&D en tiempo real
- lang
- TypeScript 95%
- files
- 107
- since
- 2026.08
Descriptionfuente: README.md
Virtual Tabletop de D&D en tiempo real. Multiplayer, con mapas, tokens, dados, combate, chat, fog of war y line of sight.
Technologyfuente: manifiestos · topics · lenguajes
- TypeScript
- ReactViteTailwind CSSshadcn/uiRadix UIPixiJS
- Supabase
- Vitest
- TypeScript 95.1 %
- CSS 4.6 %
Architecturefuente: README.md
src/
├─ engine/ motor puro TS (sin React, sin DOM). Testeable en Node.
│ ├─ board/ tipos, coords, catálogo criaturas, LOS/raycast
│ ├─ character/ sheet, atributos, modificadores D&D 5e
│ ├─ combat/ combatientes, turnos
│ └─ dice/ parser, formato, breakdown
├─ realtime/ hooks Supabase realtime (canal por sala, hooks por dominio)
├─ services/ thin wrappers tipados sobre Supabase (CRUD + RPCs)
├─ app/
│ ├─ pages/ páginas de la app (Room, CampaignDetail, ...)
│ └─ components/ UI por dominio (board, chat, combat, dice, room, ...)
└─ components/ui/ primitives shadcn (Button, Card)
Regla dura: engine/ no importa React ni el cliente Supabase. Puede
correrse en un Edge Function o en tests sin browser.
Status notesfuente: README.md
Todas las fases del roadmap original operativas end-to-end:
| Fase | Descripción | Estado |
|---|---|---|
| 1 | Authentication (email + Google) | ✅ |
| 2 | Rooms (campañas + salas + join por código) | ✅ |
| 3 | Realtime (presence + postgres_changes) | ✅ |
| 4 | Character Creator (multi-step) | ✅ |
| 5 | Virtual Board (Pixi, drag, spawn, HP, visibility) | ✅ |
| 6 | Dynamic Monster Spawn | ✅ (incluido en 5d) |
| 8 | Dice (server-authoritative) | ✅ |
| 9 | Combat (initiative, turnos, rondas) | ✅ |
| 10 | Advanced Map (mapa fondo + fog + LOS + walls) | ✅ |
| + | Chat de sala | ✅ |
Related
README.md
dndonline
Virtual Tabletop de D&D en tiempo real. Multiplayer, con mapas, tokens, dados, combate, chat, fog of war y line of sight.
Stack: React 19 · Vite · TypeScript · Tailwind · shadcn/ui · PixiJS +
@pixi/react · Supabase (Auth · Postgres · Realtime · Storage).
Estado del proyecto
Todas las fases del roadmap original operativas end-to-end:
| Fase | Descripción | Estado |
|---|---|---|
| 1 | Authentication (email + Google) | ✅ |
| 2 | Rooms (campañas + salas + join por código) | ✅ |
| 3 | Realtime (presence + postgres_changes) | ✅ |
| 4 | Character Creator (multi-step) | ✅ |
| 5 | Virtual Board (Pixi, drag, spawn, HP, visibility) | ✅ |
| 6 | Dynamic Monster Spawn | ✅ (incluido en 5d) |
| 8 | Dice (server-authoritative) | ✅ |
| 9 | Combat (initiative, turnos, rondas) | ✅ |
| 10 | Advanced Map (mapa fondo + fog + LOS + walls) | ✅ |
| + | Chat de sala | ✅ |
Arquitectura
src/
├─ engine/ motor puro TS (sin React, sin DOM). Testeable en Node.
│ ├─ board/ tipos, coords, catálogo criaturas, LOS/raycast
│ ├─ character/ sheet, atributos, modificadores D&D 5e
│ ├─ combat/ combatientes, turnos
│ └─ dice/ parser, formato, breakdown
├─ realtime/ hooks Supabase realtime (canal por sala, hooks por dominio)
├─ services/ thin wrappers tipados sobre Supabase (CRUD + RPCs)
├─ app/
│ ├─ pages/ páginas de la app (Room, CampaignDetail, ...)
│ └─ components/ UI por dominio (board, chat, combat, dice, room, ...)
└─ components/ui/ primitives shadcn (Button, Card)
Regla dura: engine/ no importa React ni el cliente Supabase. Puede
correrse en un Edge Function o en tests sin browser.
Setup local
npm install
cp .env.example .env.local
# Rellena VITE_SUPABASE_URL y VITE_SUPABASE_PUBLISHABLE_KEY
npm run dev
Los detalles de la instancia Supabase están en .env.example. El schema
está en Supabase migrations aplicadas directamente vía el MCP durante el
desarrollo.
Modo preview de UI (sin backend)
Para trabajar el diseño de las pantallas que viven detrás del login sin
levantar Supabase, añade a .env.local:
VITE_UI_PREVIEW=true
Da por autenticado a un usuario ficticio y sustituye el cliente de
Supabase por un stub con una campaña, tres personajes, una sala con
combate en curso, mensajes y tiradas de ejemplo. Todo vive en
src/services/preview.ts; con la variable ausente o distinta de true
no se ejecuta nada de eso.
Es sólo para maquetar: los filtros de igualdad se emulan, pero las escrituras no persisten y el realtime nunca emite.
Scripts
npm run dev # Vite dev server
npm run build # tsc + vite build
npm run preview # servir el build
npm run lint # eslint
npm run test # vitest watch
npm run test:run # vitest single-shot (para CI)
Deploy a Vercel
- Crea el repo en GitHub.
- Sube el código:
git init && git add . && git commit -m "init" && git push. - En Vercel → New Project → importa el repo.
- Framework preset: Vite.
vercel.jsonya está listo — no toques ajustes. - En Environment Variables añade:
VITE_SUPABASE_URL=https://nrwetrddwjcipcsfwfbv.supabase.coVITE_SUPABASE_PUBLISHABLE_KEY= tu keysb_publishable_...
- Deploy.
- En Supabase → Auth → URL Configuration:
- Añade la URL de Vercel como Site URL.
- Añade también en Redirect URLs.
Para el OAuth de Google necesitas configurar Client ID/Secret en Supabase Auth → Providers → Google, apuntando el callback al dominio de Vercel.
CI
.github/workflows/ci.yml corre en cada PR y push a main:
npm run lintnpm run test:runnpm run build(necesita los secretsVITE_SUPABASE_URLyVITE_SUPABASE_PUBLISHABLE_KEYen el repo)
Testing
95 tests unit del engine puro (dice, coords, character, combat, LOS).
npm run test:run
No hay tests de UI ni e2e todavía. Pendiente: Playwright.
Modelo de datos
Tablas en public:
profiles— 1:1 conauth.userscampaigns— DM ownsrooms— join_code, active_map_id, fog jsonb, walls jsonbroom_members— user + role (dm|player) + character_idcharacters— owner + sheet jsonb (versionado)maps— campaign owned, image en Storage bucketmapsentities— tokens en el tablero (row, col, hp, visible, character_id)combats— 1 activo por sala (unique parcial), combatants jsonbdice_rolls— audit inmutable de tiradasroom_messages— chat inmutable
Schema internal (no expuesto por PostgREST):
is_room_member(rid),is_room_dm(rid),owns_entity_character(eid)
RPCs (public):
join_room(code),roll_dice(...),start_combat(...)
Seguridad
- RLS en todas las tablas de public.
- Server-authoritative para dados y iniciativa (RPCs con
random()). - Storage bucket
mapspúblico de lectura; writes solo DM de la campaña.
Pendiente
- Tests e2e (Playwright)
- Sonido / animaciones
- Upload de avatares
- Modo pantalla completa
- Undo
- Persistencia de viewport
- Import de mapas (Foundry, Dungeondraft)
- Spells con áreas de efecto
- Campañas con
sessions/ notas del DM