Rotom Table is a long-running hobby/passion project: a local-first Nuxt 3 tabletop companion for Pokémon Tabletop United campaigns. It brings an isometric map table, editable Pokémon and trainer sheets, encounter-table tooling, a searchable Pokédex, and PTU reference pages into one browser app backed by inspectable JSON files.
The project is intentionally product-shaped rather than tutorial-sized. It demonstrates TypeScript/Nuxt application structure, complex Vue UI, Three.js scene management, domain modelling for tabletop rules, filesystem-backed persistence, data-management workflows, and long-term ownership of a feature-rich tool.
Rotom Table is a fan-made tabletop utility, not an official or commercial Pokémon product. It uses a trust-based GM / Player role picker for local campaign use; it is not hardened public authentication and should not be exposed as a public multi-user service without replacing those assumptions.
npm install
npm run devNuxt will print the local URL, usually http://localhost:3000. Open it in a browser and choose GM Login for editing/encounter tools or Player Login for the shared player-facing view.
Recommended verification commands:
npm run typecheck
npm test
npm run build- docs/review-guide.md — fastest path through the project for recruiters and reviewers.
- docs/architecture.md — high-level Nuxt, Nitro, local data, and Three.js architecture.
- docs/data-model.md — maps, sheets, trainers, encounter tables, app-owned PTU reference data, and generated sheets.
- docs/local-development.md — local setup, scripts, and filesystem persistence notes.
- docs/fan-project-notice.md — fan project and ownership boundaries.
- Isometric map table — create map folders, edit maps, build voxel terrain, place hazards, manage field effects, spawn Pokémon and trainer tokens, move/turn tokens, track initiative, and use move/ability automation.
- Sheet library — create, organise, rename, edit, and autosave Pokémon and trainer sheets from the browser.
- Pokédex browser — search and filter Pokémon entries, view sprites and detail panes, and jump directly to Pokémon-specific pages.
- Reference library — browse moves, maneuvers, abilities, capabilities, conditions, rules, items, features, and edges.
- Encounter tools — manage JSON encounter tables, roll previews, and generate wild Pokémon sheets into the sheet library.
- GM/player access modes — GM-only routes and controls are hidden from player sessions and checked on server routes.
- Filesystem-backed data — maps, sheets, trainers, and encounter tables are stored as JSON in the repository tree for easy inspection, backup, and diffing.
- Read the project positioning above and the fan/auth caveats.
- Skim docs/review-guide.md.
- Inspect the route list in this README, then open
/maps,/sheets,/pokedex, and/generatelocally as GM. - Look at
src/pages/,src/components/map/,src/utils/isometric/,server/useCases/, andtests/to see the product surface area.
- Run
npm run typecheck,npm test, andnpm run build. - Review docs/architecture.md and docs/data-model.md.
- Trace one local-first workflow end-to-end: edit a sheet, place it on a map, save the map JSON, and inspect the corresponding
data/file. - Trace one encounter workflow: inspect
encounter_tables/, open/encounter-tables, then use/generateorjust encounter ... preview. - Browse a few tests under
tests/server,tests/composables, andtests/utilsto see behaviour coverage around persistence, routing, and domain helpers.
- Product thinking around a real tabletop workflow instead of a thin demo.
- Frontend complexity: searchable libraries, editors, autosave, role-aware navigation, and dense control panels.
- Domain modelling for maps, sheets, trainers, move data, encounter tables, and app-owned PTU reference content.
- Local-first persistence with human-readable JSON and
.gitignoreboundaries for personal campaign data. - Long-term ownership: broad test coverage, refactoring-oriented structure, and supporting documentation.
- Hardened public authentication, accounts, permissions, or multi-tenant hosting.
- Cloud persistence or collaborative database infrastructure.
- Claiming official status, commercial distribution, or ownership of Pokémon/PTU names, images, or concepts.
- A generic virtual tabletop; this is specifically shaped around one PTU campaign workflow.
No screenshot files are committed in this presentation pass. See docs/screenshots.md for the capture checklist to add later without inventing or linking missing images.
- Nuxt 3 and Vue 3
- TypeScript
- Vitest
- Three.js
- npm
- Optional Python/
justhelper scripts for PTU reference lookup and encounter generation
src/contains the Nuxt app: pages, components, composables, assets, and browser-side utilities.server/contains Nitro API routes, use cases, and filesystem persistence helpers.shared/contains auth, path, realtime, sheet, and encounter helpers shared by app and server code.data/andencounter_tables/hold app-owned JSON/TypeScript data consumed at runtime;ptu-data/is documentary upstream/source material and parser tooling.tests/contains Vitest coverage across server use cases, composables, shared helpers, and domain utilities.
See docs/architecture.md for more detail.
| Route | Purpose |
|---|---|
/ |
Redirects to the map library. |
/login |
Choose GM or Player session mode. |
/maps |
Map library and folders. |
/maps/:slug |
Map editor/table view. |
/sheets |
Pokémon and trainer sheet library. |
/sheets/:slug |
Pokémon sheet editor. |
/sheets/trainers/:slug |
Trainer sheet editor. |
/pokedex |
Searchable Pokédex browser. |
/pokedex/:pokemon_name |
Pokédex detail view for one Pokémon. |
/generate |
GM encounter generation page. |
/encounter-tables |
GM encounter-table library/editor. |
/moves, /maneuvers, /abilities, /capabilities, /conditions, /rules, /items, /features, /edges |
PTU reference pages. |
| Path | What it contains |
|---|---|
data/maps/ |
Saved map JSON and map-adjacent local files. |
data/sheets/ |
Pokémon character-sheet JSON, including generated wild sheets. |
data/trainers/ |
Trainer sheet JSON. |
encounter_tables/ |
Encounter-table JSON, grouped by folder/region. |
data/reference/ |
App-owned PTU reference JSON used by runtime pages, sheets, lookup helpers, and automation. |
books/markdown/ |
Markdown source/reference content. |
ptu-data/ |
Documentary upstream PTU parsing/source helpers; not the runtime source of truth. |
public/ |
Public static assets. |
trainer_sizes/sprites/ |
Trainer sprite assets served by Nitro at /trainer-sprites. |
src/ |
Nuxt app source: pages, components, composables, assets, and utilities. |
server/ |
Nitro API routes and filesystem persistence helpers. |
shared/ |
Shared auth/path/sheet helpers used by both app and server. |
tests/ |
Vitest coverage for shared logic, utilities, composables, and server helpers. |
Saved sheets and maps are edited by the app itself. In development, Nuxt/Vite ignores changes under data/sheets, data/trainers, and data/maps so autosaves do not force a full page reload. .gitignore is configured to keep personal campaign data out of the repository while allowing curated examples to remain inspectable.
| Command | Description |
|---|---|
npm run dev |
Start the Nuxt development server. |
npm run build |
Build the Nuxt app. |
npm run preview |
Preview the built app. |
npm run typecheck |
Run Nuxt/Vue TypeScript checks. |
npm test |
Run the Vitest test suite once. |
npm run test:watch |
Run Vitest in watch mode. |
npm run check:move-automation |
Check move automation coverage. |
npm run sync:item-sprites |
Sync item sprite assets. |
npm run refactor:loop |
Run the refactor loop helper script. |
The justfile includes convenience commands for PTU reference lookups, encounter rolls, and generated sheets.
just # show available commands
just pokemon "Pikachu" # lookup a Pokémon
just move "Thunderbolt" # lookup a move
just ability "Static" # lookup an ability
just encounter # list available encounter regions
just encounter <region> # list tables in a region
just encounter <region> <table> <count>Generated encounters are written under data/sheets/wild/<table>_<count>/ by default, which makes them appear automatically in the /sheets page.
Preview without writing files:
just encounter <region> <table> <count> previewClear generated encounter output:
just encounter --clearEncounter tables live in encounter_tables/ and are exposed through the GM-only /encounter-tables route. A table has a name, level range, and weighted entries with species/level data. The app can create, rename, move, delete, and save encounter tables during local development.
The /generate page rolls from those tables and can either preview generated sheets or write them into the sheet data tree.
Rotom Table currently uses a trust-based role picker, not password authentication:
- GM — full map, sheet, encounter, and control-panel access.
- Player — shared player view with player-visible maps and sheets.
- Guest — redirected to
/login.
Server routes also check the session role for protected actions. Treat this as a local/campaign-table workflow, not a hardened public authentication system.
This project is strongest as a local development/table tool because many workflows persist JSON directly into the repository tree. Several mutating API routes are guarded against production mode, so use npm run dev when you need browser-based editing, autosave, encounter generation, or filesystem-backed management.
For a hosted deployment, decide which data should be static, which data should be persisted elsewhere, and whether to replace the trust-based role picker with real authentication.
Rotom Table complements backend/platform repositories by showing a different set of engineering strengths: frontend/product complexity, UI state management, interactive graphics, typed domain modelling, data stewardship, and the maintenance habits required for a long-lived personal tool.
I am redirected to login.
Choose GM or Player on /login. The chosen role is stored in the rotom-role cookie.
Browser edits are not visible immediately.
Most app pages update through local state and realtime events. If you generated or edited files outside the browser, refresh the page or restart the dev server.
Write actions fail in production.
Run the app with npm run dev. Production mode intentionally disables several filesystem-mutating endpoints.
Generated wild sheets do not show up.
Check that generated JSON files landed under data/sheets/, usually data/sheets/wild/..., and refresh the /sheets page.
- See CONTRIBUTING.md for local setup, checks, data hygiene, and fan-project boundaries.
- See SECURITY.md for local-first/trust-based security expectations and reporting guidance.
- See NOTICE.md and docs/fan-project-notice.md for fan project and reuse boundaries.
Original Rotom Table application code, project-specific documentation, and original tooling are available under the MIT License. See LICENSE.
That license does not grant rights to Pokémon-related or PTU-related names, images, rules terms, concepts, sprites, reference text, or other third-party materials.
Rotom Table is a fan-made tabletop utility. Pokémon-related and PTU-related names, images, and concepts belong to their respective owners. This repository does not claim official affiliation, endorsement, or ownership of those materials.