Files
initiative/specs/021-bestiary-statblock/quickstart.md

3.1 KiB

Quickstart: Bestiary Search & Stat Block Display

Branch: 021-bestiary-statblock | Date: 2026-03-06

What This Feature Does

Adds a searchable creature library (D&D 2024 Monster Manual) to the initiative tracker. Users can search for creatures by name, view their full stat block in a side panel, and add them as combatants with stats pre-filled (name, HP, AC). Multiple instances of the same creature are auto-numbered.

Key Files to Touch

New Files (Domain)

  • packages/domain/src/creature-types.ts — Creature, CreatureId, TraitBlock, etc.
  • packages/domain/src/auto-number.ts — Auto-numbering logic for duplicate creature names

New Files (Web)

  • data/bestiary/xmm.json — Raw 5etools bestiary data (bundled at build time)
  • apps/web/src/adapters/bestiary-adapter.ts — Normalizes raw JSON to Creature[]
  • apps/web/src/adapters/strip-tags.ts — Strips {@tag} markup to plain text
  • apps/web/src/hooks/use-bestiary.ts — Provides search + creature lookup
  • apps/web/src/components/bestiary-search.tsx — Search input with autocomplete dropdown
  • apps/web/src/components/stat-block.tsx — Full creature stat block display
  • apps/web/src/components/stat-block-panel.tsx — Side panel / drawer wrapper

Modified Files

  • packages/domain/src/types.ts — Add optional creatureId to Combatant
  • packages/domain/src/index.ts — Export new creature types
  • apps/web/src/App.tsx — Two-column layout + stat block panel state
  • apps/web/src/components/action-bar.tsx — Add search icon + bestiary search integration
  • apps/web/src/hooks/use-encounter.ts — Handle creatureId on add, auto-numbering
  • apps/web/src/persistence/encounter-storage.ts — Persist/rehydrate creatureId

Architecture Fit

data/bestiary/xmm.json (static asset)
        |
        v
bestiary-adapter.ts (web adapter) --normalizes--> Creature[] (domain types)
        |
        v
use-bestiary.ts (hook) --provides--> search + lookup
        |
        v
bestiary-search.tsx --selects creature--> use-encounter.ts --calls--> addCombatant (domain)
        |                                        |
        v                                        v
stat-block-panel.tsx <--reads creature data-- creatureId on Combatant

The domain layer gets the Creature type definitions and auto-numbering logic (pure functions). The web adapter handles the raw-to-domain transformation and tag stripping. The hook layer orchestrates search and state.

Running & Testing

# Dev server
pnpm --filter web dev

# Run all tests
pnpm test

# Typecheck
pnpm typecheck

# Full merge gate
pnpm check

Key Design Decisions

  1. Bundled JSON — No runtime fetching, no database. JSON imported at build time.
  2. Pre-rendered text — Tags stripped during normalization, not at render time.
  3. Creature type in domain — Type definitions live in domain, normalization adapter lives in web.
  4. creatureId on Combatant — Lightweight reference, not embedded creature data.
  5. Auto-numbering in domain — Pure function that takes existing names and base name, returns resolved name.