49 lines
2.5 KiB
Markdown
49 lines
2.5 KiB
Markdown
# Quickstart: Combatant Concentration
|
|
|
|
**Feature**: 018-combatant-concentration | **Date**: 2026-03-06
|
|
|
|
## Overview
|
|
|
|
This feature adds a per-combatant boolean `isConcentrating` state with a Brain icon toggle, a colored left border accent for visual identification, and a pulse animation when a concentrating combatant takes damage.
|
|
|
|
## Key Files to Modify
|
|
|
|
### Domain Layer
|
|
1. **`packages/domain/src/types.ts`** — Add `isConcentrating?: boolean` to `Combatant` interface.
|
|
2. **`packages/domain/src/events.ts`** — Add `ConcentrationStarted` and `ConcentrationEnded` event types to the `DomainEvent` union.
|
|
3. **`packages/domain/src/toggle-concentration.ts`** (new) — Pure function mirroring `toggle-condition.ts` pattern.
|
|
4. **`packages/domain/src/index.ts`** — Re-export new function and event types.
|
|
|
|
### Application Layer
|
|
5. **`packages/application/src/toggle-concentration-use-case.ts`** (new) — Thin orchestration following `toggle-condition-use-case.ts` pattern.
|
|
6. **`packages/application/src/index.ts`** — Re-export new use case.
|
|
|
|
### Web Adapter
|
|
7. **`apps/web/src/persistence/encounter-storage.ts`** — Add `isConcentrating` to combatant rehydration.
|
|
8. **`apps/web/src/hooks/use-encounter.ts`** — Add `toggleConcentration` callback.
|
|
9. **`apps/web/src/components/combatant-row.tsx`** — Add Brain icon toggle, left border accent, and pulse animation.
|
|
10. **`apps/web/src/App.tsx`** — Wire `onToggleConcentration` prop through to `CombatantRow`.
|
|
|
|
## Implementation Pattern
|
|
|
|
Follow the existing `toggleCondition` pattern end-to-end:
|
|
|
|
```
|
|
Domain: toggleConcentration(encounter, combatantId) → { encounter, events } | DomainError
|
|
App: toggleConcentrationUseCase(store, combatantId) → events | DomainError
|
|
Hook: toggleConcentration = useCallback((id) => { ... toggleConcentrationUseCase(makeStore(), id) ... })
|
|
Component: <Brain onClick={() => onToggleConcentration(id)} />
|
|
```
|
|
|
|
## Testing Strategy
|
|
|
|
- **Domain tests**: `toggle-concentration.test.ts` — toggle on/off, combatant not found, immutability, correct events emitted.
|
|
- **UI behavior**: Manual verification of hover show/hide, tooltip, left border accent, pulse animation on damage.
|
|
|
|
## Key Decisions
|
|
|
|
- Concentration is **not** a condition — it has its own boolean field and separate UI treatment.
|
|
- Pulse animation uses **CSS keyframes** triggered by transient React state, not a domain event.
|
|
- Damage detection for pulse uses **HP comparison** in the component (prevHp vs currentHp), not domain events.
|
|
- No localStorage migration needed — optional boolean field is backward-compatible.
|