# Initiative A local-first initiative tracker and encounter manager for tabletop RPGs (D&D 5e / 2024). Runs entirely in the browser — no server, no account, no data leaves your machine. ## What it does - **Initiative tracking** — add combatants (batch-add from bestiary, custom creatures with optional stats), roll initiative (manual or d20), cycle turns and rounds - **Encounter state** — HP, AC, conditions, concentration tracking with visual status indicators - **Bestiary integration** — import bestiary JSON sources, search creatures, and view full stat blocks - **Player characters** — create reusable player character templates with name, AC, HP, level, color, and icon; search and add them to encounters with pre-filled stats; manage (edit/delete) from a dedicated panel - **Encounter difficulty** — live 3-bar indicator in the top bar showing encounter difficulty (Trivial/Low/Moderate/High) based on the 2024 5.5e XP budget system; automatically derived from PC levels and bestiary creature CRs - **Undo/redo** — reverse any encounter action with Undo/Redo buttons or keyboard shortcuts (Ctrl+Z / Ctrl+Shift+Z, Cmd on Mac); history persists across page reloads - **Import/export** — export the full encounter state (combatants, undo/redo history, player characters) as a JSON file or copy to clipboard; import from file upload or pasted JSON with validation and confirmation - **Persistent** — encounters survive page reloads via localStorage; bestiary data cached in IndexedDB; player characters stored independently ## Prerequisites - Node.js 22+ - pnpm 10.6+ ## Getting Started ```sh pnpm install pnpm --filter web dev ``` Open `http://localhost:5173`. ## Scripts | Command | Description | |---------|-------------| | `pnpm --filter web dev` | Start the dev server | | `pnpm --filter web build` | Production build | | `pnpm test` | Run all tests (Vitest) | | `pnpm test:watch` | Tests in watch mode | | `pnpm vitest run path/to/test.ts` | Run a single test file | | `pnpm typecheck` | TypeScript type checking | | `pnpm lint` | Biome lint | | `pnpm format` | Biome format (writes changes) | | `pnpm check` | Full merge gate (see below) | ### Merge gate (`pnpm check`) All of these run at pre-commit via Lefthook (in parallel where possible): - `pnpm audit` — security audit - `knip` — unused code detection - `biome check` — formatting + linting - `oxlint` — type-aware linting (complements Biome) - Custom scripts — lint-ignore caps, className enforcement, component prop limits - `tsc --build` — TypeScript strict mode - `vitest run` — tests with per-path coverage thresholds - `jscpd` + `jsinspect` — copy-paste and structural duplication detection ## Tech Stack - TypeScript 5.8 (strict mode), React 19, Vite 6 - Tailwind CSS v4 (dark/light theme) - Biome 2.4 (formatting + linting), oxlint (type-aware linting) - Vitest (testing, v8 coverage), Lefthook (pre-commit hooks) - Knip (unused code), jscpd + jsinspect (duplication detection) ## Project Structure ``` apps/web/ React 19 + Vite — UI components, hooks, adapters packages/domain/ Pure functions — state transitions, types, validation packages/application/ Use cases — orchestrates domain via port interfaces data/bestiary/ Pre-built bestiary search index (~10k creatures) scripts/ Build tooling (layer checks, index generation) specs/ Feature specifications (spec → plan → tasks) ``` ## Architecture Strict layered architecture with enforced dependency direction: ``` apps/web (adapters) → packages/application (use cases) → packages/domain (pure logic) ``` - **Domain** — pure functions, no I/O, no randomness, no framework imports. Errors returned as values (`DomainError`), never thrown. - **Application** — orchestrates domain calls via port interfaces (`EncounterStore`, `PlayerCharacterStore`, etc.). No business logic. - **Web** — React adapter. Implements ports using hooks/state. All UI components, persistence, and external data access live here. Layer boundaries are enforced by automated import checks that run as part of the test suite. ## Contributing ### Workflow Development is spec-driven. Feature specs live in `specs/NNN-feature-name/` and are managed through Claude Code skills (see [CLAUDE.md](./CLAUDE.md) for full details). | Scope | What to do | |-------|-----------| | Bug fix / CSS tweak | Fix it, run `pnpm check`, commit. Optionally use `/browser-interactive-testing` for visual verification. | | Change to existing feature | Update the feature spec, then implement | | Larger change to existing feature | Update the spec → `/rpi-research` → `/rpi-plan` → `/rpi-implement` | | New feature | `/speckit.specify` → `/speckit.clarify` → `/speckit.plan` → `/speckit.tasks` → `/speckit.implement` | Use `/write-issue` to create well-structured Gitea issues, and `/integrate-issue` to pull an existing issue's requirements into the relevant feature spec. ### Before committing Run `pnpm check` — Lefthook runs this automatically at pre-commit, but running it manually first saves time. All checks must pass. ### Conventions - **Biome** for formatting and linting — tab indentation, 80-char lines - **TypeScript strict mode** with `verbatimModuleSyntax` (type-only imports must use `import type`) - **Max 8 props** per component interface — use React context for shared state - **Tests** in `__tests__/` directories — test pure functions directly, use `renderHook` for hooks See [CLAUDE.md](./CLAUDE.md) for the full conventions and project constitution. ## Bestiary Index The bestiary search index (`data/bestiary/index.json`) is pre-built and checked into the repo. To regenerate it (e.g., after a new source book release): 1. Clone [5etools-mirror-3/5etools-src](https://github.com/5etools-mirror-3/5etools-src) locally 2. Run `node scripts/generate-bestiary-index.mjs /path/to/5etools-src` The script extracts creature names, stats, and source info into a compact search index.