Files
initiative/CLAUDE.md
Lukas b6e052f198 Overhaul bottom bar: batch add, custom fields, stat block viewer
Unify the action bar into a single search input with inline bestiary
dropdown. Clicking a dropdown entry queues it with +/- count controls
and a confirm button; Enter or confirm adds N copies to combat.

When no bestiary match exists, optional Init/AC/MaxHP fields appear
for custom creatures. The eye icon opens a separate search dropdown
to preview stat blocks without leaving the add flow.

Fix batch-add bug where only the last creature got a creatureId by
using store.save() instead of setEncounter() in addFromBestiary.
Prevent dropdown buttons from stealing input focus so Enter confirms
the queued batch.

Remove the now-redundant BestiarySearch component.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:27:06 +01:00

5.4 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

pnpm check              # Merge gate — must pass before every commit (audit + knip + biome + typecheck + test/coverage + jscpd)
pnpm knip               # Unused code detection (Knip)
pnpm test               # Run all tests (Vitest)
pnpm test:watch         # Tests in watch mode
pnpm typecheck          # tsc --build (project references)
pnpm lint               # Biome lint
pnpm format             # Biome format (writes)
pnpm --filter web dev   # Vite dev server (localhost:5173)
pnpm --filter web build # Production build

Run a single test file: pnpm vitest run packages/domain/src/__tests__/advance-turn.test.ts

Architecture

Strict layered architecture with ports/adapters and enforced dependency direction:

apps/web (React 19 + Vite)  →  packages/application (use cases)  →  packages/domain (pure logic)
  • Domain — Pure functions, no I/O, no framework imports. All state transitions are deterministic. Errors returned as values (DomainError), never thrown. Adapters may throw only for programmer errors.
  • Application — Orchestrates domain calls via port interfaces (EncounterStore, BestiarySourceCache). No business logic here.
  • Web — React adapter. Implements ports using hooks/state. All UI components, routing, and user interaction live here.

Layer boundaries are enforced by scripts/check-layer-boundaries.mjs, which runs as a Vitest test. Domain and application must never import from React, Vite, or upper layers.

Data & Storage

  • localStorage — encounter persistence (adapter layer, JSON serialization)
  • IndexedDB — bestiary source cache (apps/web/src/adapters/bestiary-cache.ts, via idb wrapper)
  • data/bestiary/index.json — pre-built search index for creature lookup, generated by scripts/generate-bestiary-index.mjs

Project Structure

apps/web/                  React app — components, hooks, adapters
packages/domain/src/       Pure state transitions, types, validation
packages/application/src/  Use cases, port interfaces
data/bestiary/             Bestiary search index
scripts/                   Build tooling (layer checks, index generation)
specs/<feature>/           Feature specs (spec.md, plan.md, tasks.md)
.specify/memory/           Project constitution

Tech Stack

  • TypeScript 5.8 (strict mode, verbatimModuleSyntax)
  • React 19, Vite 6, Tailwind CSS v4
  • Lucide React (icons)
  • idb (IndexedDB wrapper for bestiary cache)
  • Biome 2.0 (formatting + linting), Knip (unused code), jscpd (copy-paste detection)
  • Vitest (testing, v8 coverage), Lefthook (pre-commit hooks)

Conventions

  • Biome 2.0 for formatting and linting (no Prettier, no ESLint). Tab indentation, 80-char lines. Imports are auto-organized alphabetically.
  • TypeScript strict mode with verbatimModuleSyntax. Use .js extensions in relative imports when required by the repo's ESM settings (e.g., ./types.js).
  • Branded types for identity values (e.g., CombatantId). Prefer immutability/readonly where practical.
  • Domain events are plain data objects with a type discriminant — no classes.
  • Tests live in packages/*/src/__tests__/*.test.ts. Test pure functions directly; map acceptance scenarios and invariants from specs to individual it() blocks.
  • Feature specs live in specs/<feature>/ with spec.md, plan.md, tasks.md. The project constitution is at .specify/memory/constitution.md.
  • Quality gates are enforced at pre-commit via Lefthook's pnpm check — the project's single earliest enforcement point. No gate may exist only as a CI step or manual process.

Constitution (key principles)

The constitution (.specify/memory/constitution.md) governs all feature work:

  1. Deterministic Domain Core — Pure state transitions only; no I/O, randomness, or clocks in domain.
  2. Layered Architecture — Domain → Application → Adapters. Never skip layers or reverse dependencies.
  3. Clarification-First — Ask before making non-trivial assumptions.
  4. MVP Baseline — Say "MVP baseline does not include X", never permanent bans.
  5. Every feature begins with a spec — Spec → Plan → Tasks → Implementation.

Active Technologies

  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Lucide React, class-variance-authority (cva) (032-inline-confirm-buttons)
  • N/A (no persistence changes — confirm state is ephemeral) (032-inline-confirm-buttons)
  • TypeScript 5.8, CSS (Tailwind CSS v4) + React 19, Tailwind CSS v4 (033-fix-concentration-glow-clip)
  • N/A (no persistence changes) (033-fix-concentration-glow-clip)
  • N/A (no persistence changes — display-only refactor) (034-topbar-redesign)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Lucide React (icons), class-variance-authority (035-statblock-fold-pin)
  • N/A (no persistence changes — all new state is ephemeral) (035-statblock-fold-pin)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Lucide React (icons) (036-bottombar-overhaul)
  • N/A (no persistence changes — queue state and custom fields are ephemeral) (036-bottombar-overhaul)

Recent Changes

  • 032-inline-confirm-buttons: Added TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Lucide React, class-variance-authority (cva)