Implement the 021-bestiary-statblock feature that adds a searchable D&D 2024 Monster Manual creature library with inline autocomplete suggestions, full stat block display in a fixed side panel, auto-numbering of duplicate creature names, HP/AC pre-fill from bestiary data, and automatic stat block display on turn change for wide viewports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
96
specs/021-bestiary-statblock/plan.md
Normal file
96
specs/021-bestiary-statblock/plan.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Implementation Plan: Bestiary Search & Stat Block Display
|
||||
|
||||
**Branch**: `021-bestiary-statblock` | **Date**: 2026-03-06 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `/specs/021-bestiary-statblock/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Add a searchable creature library (2024 Monster Manual, 503 creatures) to the initiative tracker. Users search by name, view full stat blocks in a responsive side panel, and add creatures as combatants with HP/AC/name pre-filled. Data is bundled as static JSON, normalized at load time via an adapter. Auto-numbering handles duplicate creature names. Layout adapts between side-by-side (desktop) and drawer (mobile).
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.8 (strict mode, verbatimModuleSyntax)
|
||||
**Primary Dependencies**: React 19, Vite 6, Tailwind CSS v4, Lucide React (icons)
|
||||
**Storage**: Browser localStorage (existing adapter, extended for creatureId)
|
||||
**Testing**: Vitest (unit tests for domain functions, adapter normalization, tag stripping)
|
||||
**Target Platform**: Modern browsers (desktop + mobile)
|
||||
**Project Type**: Web application (local-first, single-user)
|
||||
**Performance Goals**: Search results in <200ms for 500 creatures (trivially met with in-memory filter)
|
||||
**Constraints**: Offline-capable, no backend, bundle size increase ~300-500KB gzipped
|
||||
**Scale/Scope**: 503 creatures (2024 MM), extensible to multiple bestiary sources
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| I. Deterministic Domain Core | PASS | Creature types and auto-numbering are pure. No I/O in domain. Normalization adapter lives in web layer. |
|
||||
| II. Layered Architecture | PASS | Domain: types + auto-numbering. Application: N/A (no new use case needed — addCombatant reused). Web: adapter, hooks, components. |
|
||||
| III. Agent Boundary | N/A | No agent layer involvement. |
|
||||
| IV. Clarification-First | PASS | All ambiguities resolved in spec discussion and research phase. |
|
||||
| V. Escalation Gates | PASS | Feature fully specified before planning. |
|
||||
| VI. MVP Baseline Language | PASS | Custom creature editor, fuzzy search, multiple bestiary sources described as future iterations. |
|
||||
| VII. No Gameplay Rules | PASS | Stat block display is data rendering, not gameplay mechanics. |
|
||||
|
||||
**Post-Phase 1 Re-check**: All gates still pass. The `Creature` type in domain contains no I/O. The bestiary adapter in the web layer handles all data transformation. The `creatureId` extension to `Combatant` is a minimal, optional field.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/021-bestiary-statblock/
|
||||
├── spec.md
|
||||
├── plan.md # This file
|
||||
├── research.md
|
||||
├── data-model.md
|
||||
├── quickstart.md
|
||||
├── contracts/
|
||||
│ └── ui-contracts.md
|
||||
├── checklists/
|
||||
│ └── requirements.md
|
||||
└── tasks.md # Generated by /speckit.tasks
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
data/
|
||||
└── bestiary/
|
||||
└── xmm.json # Raw 5etools bestiary (2024 MM)
|
||||
|
||||
packages/domain/src/
|
||||
├── types.ts # MODIFIED: add creatureId to Combatant
|
||||
├── creature-types.ts # NEW: Creature, CreatureId, TraitBlock, etc.
|
||||
├── auto-number.ts # NEW: resolveCreatureName()
|
||||
├── index.ts # MODIFIED: export new types
|
||||
└── __tests__/
|
||||
├── auto-number.test.ts # NEW
|
||||
└── layer-boundaries.test.ts # EXISTING (unchanged)
|
||||
|
||||
packages/application/src/
|
||||
└── (no changes — reuses existing addCombatant use case)
|
||||
|
||||
apps/web/src/
|
||||
├── App.tsx # MODIFIED: two-column layout, stat block state
|
||||
├── adapters/
|
||||
│ ├── bestiary-adapter.ts # NEW: raw JSON → Creature[]
|
||||
│ ├── strip-tags.ts # NEW: {@tag} → plain text
|
||||
│ └── __tests__/
|
||||
│ ├── bestiary-adapter.test.ts # NEW
|
||||
│ └── strip-tags.test.ts # NEW
|
||||
├── hooks/
|
||||
│ ├── use-encounter.ts # MODIFIED: addFromBestiary, auto-numbering
|
||||
│ └── use-bestiary.ts # NEW: search + lookup
|
||||
├── components/
|
||||
│ ├── action-bar.tsx # MODIFIED: search icon, suggestions
|
||||
│ ├── bestiary-search.tsx # NEW: search input + dropdown
|
||||
│ ├── stat-block.tsx # NEW: creature stat block renderer
|
||||
│ └── stat-block-panel.tsx # NEW: responsive panel/drawer wrapper
|
||||
├── persistence/
|
||||
│ └── encounter-storage.ts # MODIFIED: persist/rehydrate creatureId
|
||||
└── index.css # MODIFIED: stat block styling
|
||||
```
|
||||
|
||||
**Structure Decision**: Follows existing monorepo layout. New domain types in `packages/domain`. Bestiary adapter and UI components in `apps/web`. Raw data in top-level `data/` directory (not inside any package, as it's a static asset consumed by the web app's build).
|
||||
Reference in New Issue
Block a user