Implement the 018-combatant-concentration feature that adds a per-combatant concentration toggle with Brain icon, purple border accent, and damage pulse animation in the encounter tracker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
48
specs/018-combatant-concentration/quickstart.md
Normal file
48
specs/018-combatant-concentration/quickstart.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user