diff --git a/apps/web/src/components/quick-hp-input.tsx b/apps/web/src/components/quick-hp-input.tsx index 3c80ff5..27dbeab 100644 --- a/apps/web/src/components/quick-hp-input.tsx +++ b/apps/web/src/components/quick-hp-input.tsx @@ -5,8 +5,6 @@ import { cn } from "../lib/utils"; import { Button } from "./ui/button"; import { Input } from "./ui/input"; -type Mode = "damage" | "heal"; - interface QuickHpInputProps { readonly combatantId: CombatantId; readonly disabled?: boolean; @@ -18,75 +16,46 @@ export function QuickHpInput({ disabled, onAdjustHp, }: QuickHpInputProps) { - const [mode, setMode] = useState("damage"); const [inputValue, setInputValue] = useState(""); const inputRef = useRef(null); - const apply = useCallback(() => { - if (inputValue === "") return; - const n = Number.parseInt(inputValue, 10); - if (Number.isNaN(n) || n <= 0) return; - const delta = mode === "damage" ? -n : n; - onAdjustHp(combatantId, delta); - setInputValue(""); - }, [inputValue, mode, combatantId, onAdjustHp]); + const parsedValue = + inputValue === "" ? null : Number.parseInt(inputValue, 10); + const isValid = + parsedValue !== null && !Number.isNaN(parsedValue) && parsedValue > 0; - const toggleMode = useCallback(() => { - setMode((m) => (m === "damage" ? "heal" : "damage")); - }, []); + const applyDelta = useCallback( + (sign: -1 | 1) => { + if (inputValue === "") return; + const n = Number.parseInt(inputValue, 10); + if (Number.isNaN(n) || n <= 0) return; + onAdjustHp(combatantId, sign * n); + setInputValue(""); + }, + [inputValue, combatantId, onAdjustHp], + ); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === "Enter") { - apply(); + applyDelta(-1); } else if (e.key === "Escape") { setInputValue(""); - } else if (e.key === "Tab") { - e.preventDefault(); - toggleMode(); } }, - [apply, toggleMode], + [applyDelta], ); - const isDamage = mode === "damage"; - return (
- { const v = e.target.value; if (v === "" || /^\d+$/.test(v)) { @@ -95,6 +64,36 @@ export function QuickHpInput({ }} onKeyDown={handleKeyDown} /> + +
); } diff --git a/specs/014-inline-hp-delta/checklists/requirements.md b/specs/014-inline-hp-delta/checklists/requirements.md new file mode 100644 index 0000000..62191b9 --- /dev/null +++ b/specs/014-inline-hp-delta/checklists/requirements.md @@ -0,0 +1,34 @@ +# Specification Quality Checklist: Inline HP Delta Input + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-03-05 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- All items pass. Spec is ready for `/speckit.clarify` or `/speckit.plan`. diff --git a/specs/014-inline-hp-delta/data-model.md b/specs/014-inline-hp-delta/data-model.md new file mode 100644 index 0000000..7dac5ef --- /dev/null +++ b/specs/014-inline-hp-delta/data-model.md @@ -0,0 +1,42 @@ +# Data Model: Inline HP Delta Input + +## No data model changes + +This feature does not modify any domain entities, events, or state transitions. The existing data model fully supports the new UI: + +### Existing Entities (unchanged) + +**Combatant** +- `id`: CombatantId (branded string) +- `name`: string +- `initiative?`: number +- `maxHp?`: number +- `currentHp?`: number + +**Encounter** +- `combatants`: readonly Combatant[] +- `activeIndex`: number +- `roundNumber`: number + +### Existing Domain Function (unchanged) + +**adjustHp(encounter, combatantId, delta) -> AdjustHpSuccess | DomainError** +- Negative delta = damage, positive delta = heal +- Clamps result to [0, maxHp] +- Emits `CurrentHpAdjusted` event + +### Existing Domain Event (unchanged) + +**CurrentHpAdjusted** +- `combatantId`: CombatantId +- `previousHp`: number +- `newHp`: number +- `delta`: number + +### UI State Changes + +The only state change is in the QuickHpInput component: +- **Removed**: `mode: "damage" | "heal"` state variable +- **Kept**: `value: string` state variable for the input field + +The action (damage vs heal) is now determined at invocation time by which button/key triggered it, not by a stored mode. diff --git a/specs/014-inline-hp-delta/plan.md b/specs/014-inline-hp-delta/plan.md new file mode 100644 index 0000000..074bf7d --- /dev/null +++ b/specs/014-inline-hp-delta/plan.md @@ -0,0 +1,70 @@ +# Implementation Plan: Inline HP Delta Input + +**Branch**: `014-inline-hp-delta` | **Date**: 2026-03-05 | **Spec**: [spec.md](./spec.md) +**Input**: Feature specification from `/specs/014-inline-hp-delta/spec.md` + +## Summary + +Replace the existing QuickHpInput component (which uses a damage/heal mode toggle) with a simpler inline HP delta input. Users type a number and press Enter to apply damage by default, or click explicit damage/heal buttons. No domain changes needed -- the existing `adjustHp` pure function already accepts positive and negative deltas. This is a UI-only refactor of the quick HP input component. + +## Technical Context + +**Language/Version**: TypeScript 5.8 (strict mode, verbatimModuleSyntax) +**Primary Dependencies**: React 19, Vite 6, Tailwind CSS v4, shadcn/ui-style components, Lucide React (icons) +**Storage**: N/A (no storage changes -- existing localStorage persistence unchanged) +**Testing**: Vitest +**Target Platform**: Web browser (local-first, single-user) +**Project Type**: Web application (monorepo: domain + application + web adapter) +**Performance Goals**: Instant feedback on HP adjustment (<100ms perceived) +**Constraints**: Local-first, single-user MVP +**Scale/Scope**: Single encounter screen, ~1-20 combatants + +## Constitution Check + +*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* + +| Principle | Status | Notes | +|-----------|--------|-------| +| I. Deterministic Domain Core | PASS | No domain changes. Existing `adjustHp` is a pure function. | +| II. Layered Architecture | PASS | Change is entirely in the adapter layer (web UI). Domain and application layers unchanged. | +| III. Agent Boundary | N/A | No agent features involved. | +| IV. Clarification-First | PASS | Spec is clear; no ambiguities remain. | +| V. Escalation Gates | PASS | Feature is within spec scope -- replacing existing UI with simplified version. | +| VI. MVP Baseline Language | PASS | Assumptions use "MVP baseline does not include" language. | +| VII. No Gameplay Rules | PASS | No gameplay mechanics in this feature. | + +**Gate result**: PASS -- all principles satisfied. + +## Project Structure + +### Documentation (this feature) + +```text +specs/014-inline-hp-delta/ +├── plan.md # This file +├── research.md # Phase 0 output +├── data-model.md # Phase 1 output +├── quickstart.md # Phase 1 output +└── tasks.md # Phase 2 output (/speckit.tasks command) +``` + +### Source Code (repository root) + +```text +packages/domain/src/ +├── adjust-hp.ts # Existing — no changes needed +├── types.ts # Existing — no changes needed +└── __tests__/adjust-hp.test.ts # Existing — no changes needed + +packages/application/src/ +└── adjust-hp-use-case.ts # Existing — no changes needed + +apps/web/src/ +├── components/ +│ ├── quick-hp-input.tsx # MODIFY — replace mode toggle with damage/heal buttons +│ └── combatant-row.tsx # Existing — integration point (no changes expected) +└── hooks/ + └── use-encounter.ts # Existing — no changes needed +``` + +**Structure Decision**: Existing monorepo structure (domain → application → web adapter) is used. This feature only modifies the web adapter layer, specifically the `quick-hp-input.tsx` component. No new files or packages needed. diff --git a/specs/014-inline-hp-delta/quickstart.md b/specs/014-inline-hp-delta/quickstart.md new file mode 100644 index 0000000..2c16223 --- /dev/null +++ b/specs/014-inline-hp-delta/quickstart.md @@ -0,0 +1,43 @@ +# Quickstart: Inline HP Delta Input + +## What changes + +One file: `apps/web/src/components/quick-hp-input.tsx` + +The QuickHpInput component is refactored from a mode-toggle design to an explicit-button design: + +**Before** (011-quick-hp-input): +- Mode toggle button (Sword/Heart icon) + numeric input +- User switches mode, then types number, then presses Enter +- Mode state determines whether delta is positive or negative + +**After** (014-inline-hp-delta): +- Numeric input + damage button (Sword) + heal button (Heart) +- Enter key always applies damage (negative delta) +- Damage button applies damage (negative delta) +- Heal button applies healing (positive delta) +- No mode state + +## How to implement + +1. Remove the `mode` state variable and the toggle button +2. Replace with two action buttons (damage + heal) alongside the input +3. Enter key handler calls `onAdjustHp(combatantId, -n)` (always damage) +4. Damage button calls `onAdjustHp(combatantId, -n)` +5. Heal button calls `onAdjustHp(combatantId, +n)` +6. Remove Tab key override (restore default browser behavior) +7. Clear input after any successful apply + +## How to verify + +```bash +pnpm check # Must pass (knip + format + lint + typecheck + test) +``` + +Manual verification: +1. Set a combatant's max HP to 20 (currentHp initializes to 20) +2. Type 7 in the delta input, press Enter → HP should show 13 +3. Type 5 in the delta input, click heal button → HP should show 18 +4. Type 3 in the delta input, click damage button → HP should show 15 +5. Verify input clears after each action +6. Verify no mode toggle exists in the UI diff --git a/specs/014-inline-hp-delta/research.md b/specs/014-inline-hp-delta/research.md new file mode 100644 index 0000000..7e77d99 --- /dev/null +++ b/specs/014-inline-hp-delta/research.md @@ -0,0 +1,37 @@ +# Research: Inline HP Delta Input + +## R-001: Can the existing domain function support the new UI? + +**Decision**: Yes -- the existing `adjustHp(encounter, combatantId, delta)` function already accepts any integer delta. Negative deltas deal damage, positive deltas heal. No domain changes required. + +**Rationale**: The domain function is already delta-based and mode-agnostic. The current QuickHpInput component translates its damage/heal mode into a signed delta before calling `onAdjustHp`. The new UI simply changes how the sign is determined (Enter = negative, heal button = positive, damage button = negative). + +**Alternatives considered**: None -- the domain API is already ideal for this use case. + +## R-002: UI pattern for inline delta with explicit action buttons + +**Decision**: Replace the mode toggle button + input with a single numeric input flanked by two small icon buttons (damage on left, heal on right). Enter key applies damage by default. + +**Rationale**: This eliminates the mode toggle state entirely. Users always see both options. The most common action (damage) is the keyboard default. Less common action (heal) requires a button click -- still only 2-3 interactions. + +**Alternatives considered**: +- Separate damage and heal inputs per combatant: rejected as too much UI clutter for the compact row layout. +- Negative number = damage, positive = heal (user types sign): rejected as slower (extra keystroke) and error-prone. + +## R-003: What happens to the Tab key behavior? + +**Decision**: Remove the Tab key override. Tab returns to its default browser behavior (move focus to next element). + +**Rationale**: The Tab key was previously overridden to toggle between damage/heal modes. Since modes no longer exist, Tab should not be intercepted. This improves accessibility and standard keyboard navigation. + +**Alternatives considered**: None -- restoring default Tab behavior is the obvious choice. + +## R-004: Button icons and visual distinction + +**Decision**: Use Sword icon (red) for damage button and Heart icon (green) for heal button, consistent with the existing color scheme from the mode toggle. + +**Rationale**: These icons are already imported (Lucide React) and used in the current mode toggle. Users familiar with the current UI will recognize them. Red = damage, green = heal is a universal convention. + +**Alternatives considered**: +- Minus/Plus icons: too generic, less evocative of the D&D context. +- Text labels ("Dmg"/"Heal"): take more space than icons in the compact row. diff --git a/specs/014-inline-hp-delta/spec.md b/specs/014-inline-hp-delta/spec.md new file mode 100644 index 0000000..16f59da --- /dev/null +++ b/specs/014-inline-hp-delta/spec.md @@ -0,0 +1,104 @@ +# Feature Specification: Inline HP Delta Input + +**Feature Branch**: `014-inline-hp-delta` +**Created**: 2026-03-05 +**Status**: Draft +**Input**: User description: "change the quick hp input to be an inline HP delta input per combatant. Users type a number and press Enter to apply damage by default, or click small damage/heal buttons to apply the value explicitly. Clear the input after applying. No toggle for damage and heal anymore." + +## User Scenarios & Testing + +### User Story 1 - Apply Damage via Enter Key (Priority: P1) + +As a game master in the heat of combat, I want to type a damage number and press Enter to immediately subtract it from a combatant's HP, so that damage application is as fast as possible with minimal interaction. + +**Why this priority**: Damage is the most frequent HP change during combat. Enter-to-damage is the fastest possible workflow for the most common action. + +**Independent Test**: Can be fully tested by having a combatant with HP, typing a number, pressing Enter, and verifying HP decreases. + +**Acceptance Scenarios**: + +1. **Given** a combatant has 20/20 HP, **When** the user types 7 into the delta input and presses Enter, **Then** current HP decreases to 13. +2. **Given** a combatant has 10/20 HP, **When** the user types 15 and presses Enter, **Then** current HP is clamped to 0 (not negative). +3. **Given** a combatant has 20/20 HP, **When** the user types a number and presses Enter, **Then** the input field clears and is ready for the next entry. +4. **Given** a combatant has 5/20 HP, **When** the user types 0 and presses Enter, **Then** the input is rejected and HP remains unchanged. + +--- + +### User Story 2 - Apply Damage or Heal via Explicit Buttons (Priority: P1) + +As a game master, I want small damage and heal buttons next to the input so that I can explicitly choose whether to apply the entered value as damage or healing. + +**Why this priority**: While Enter defaults to damage, users still need a way to heal. Explicit buttons remove ambiguity and eliminate the need for a mode toggle. + +**Independent Test**: Can be fully tested by typing a number and clicking the heal button, verifying HP increases. + +**Acceptance Scenarios**: + +1. **Given** a combatant has 10/20 HP and the user has typed 5, **When** the user clicks the heal button, **Then** current HP increases to 15 and the input clears. +2. **Given** a combatant has 18/20 HP and the user has typed 10, **When** the user clicks the heal button, **Then** current HP is clamped to 20 (max HP) and the input clears. +3. **Given** a combatant has 20/20 HP and the user has typed 7, **When** the user clicks the damage button, **Then** current HP decreases to 13 and the input clears. +4. **Given** a combatant has 0/20 HP and the user has typed 8, **When** the user clicks the heal button, **Then** current HP increases to 8 and the input clears. + +--- + +### User Story 3 - Keyboard-Driven Workflow (Priority: P2) + +As a game master, I want to use only the keyboard to apply damage quickly so that I can keep up with fast-paced combat without reaching for the mouse. + +**Why this priority**: Speed is critical during combat encounters. The keyboard workflow should cover the most common action (damage) end-to-end. + +**Independent Test**: Can be fully tested by using only keyboard interactions to apply damage values. + +**Acceptance Scenarios**: + +1. **Given** the delta input is focused, **When** the user types a number and presses Enter, **Then** damage is applied immediately and the input clears. +2. **Given** the delta input is focused, **When** the user presses Escape, **Then** the input value is cleared without applying any change. + +--- + +### Edge Cases + +- What happens when the user enters a non-numeric value? The input only accepts digits; non-numeric characters are rejected. +- What happens when the user enters a very large number (e.g., 99999)? The value is applied normally; clamping to 0 or max HP ensures no invalid state. +- What happens when the combatant has no HP tracking (no max HP set)? The delta input is not shown for that combatant. +- What happens when the user submits an empty input? No change is applied; the input remains ready for the next entry. +- What happens when the user clicks a button with an empty input? No change is applied. +- What happens when the user rapidly enters multiple values? Each entry is applied sequentially; no values are lost or merged. + +## Requirements + +### Functional Requirements + +- **FR-001**: The system MUST provide an inline numeric input per combatant for entering HP delta amounts. +- **FR-002**: When the user types a number and presses Enter, the system MUST apply the value as damage (subtract from current HP), clamping to 0. +- **FR-003**: The system MUST provide a small damage button that applies the entered value as damage (subtract from current HP), clamping to 0. +- **FR-004**: The system MUST provide a small heal button that applies the entered value as healing (add to current HP), clamping to max HP. +- **FR-005**: After any value is applied (via Enter or button click), the input MUST clear automatically. +- **FR-006**: The input MUST only accept positive integers. Zero, negative numbers, and non-numeric input MUST be rejected. +- **FR-007**: Pressing Escape while the input is focused MUST clear the input value without applying any change. +- **FR-008**: The delta input MUST only be available for combatants that have HP tracking active (max HP is set). +- **FR-009**: There MUST NOT be a toggle between damage and heal modes. The input is mode-free; the action is determined by which button is clicked or by pressing Enter (which defaults to damage). +- **FR-010**: Direct editing of the current HP absolute value MUST remain available alongside the delta input. +- **FR-011**: The damage and heal buttons MUST be visually distinct from each other (e.g., different colors or icons). + +### Key Entities + +- **HP Delta**: A numeric value entered by the user, applied as either damage or healing to a combatant's current HP. + +## Success Criteria + +### Measurable Outcomes + +- **SC-001**: A user can apply damage to a combatant in 2 interactions or fewer (type number + press Enter). +- **SC-002**: A user can apply healing to a combatant in 3 interactions or fewer (type number + click heal button). +- **SC-003**: Current HP never exceeds max HP or drops below 0 after any delta input operation. +- **SC-004**: No mode toggle exists in the interface -- damage and heal are always available as distinct actions. + +## Assumptions + +- This feature replaces the existing quick HP input (011-quick-hp-input) which used a damage/heal mode toggle. The toggle is removed in favor of explicit actions. +- Enter key defaults to damage because damage is the most frequent HP change during combat. +- Direct HP entry (absolute value) remains available for overrides and corrections. +- There is no undo/redo for HP changes in the MVP baseline. +- There is no damage type tracking (fire, slashing, etc.) in the MVP baseline. +- There is no hit log or damage history in the MVP baseline. diff --git a/specs/014-inline-hp-delta/tasks.md b/specs/014-inline-hp-delta/tasks.md new file mode 100644 index 0000000..498da9b --- /dev/null +++ b/specs/014-inline-hp-delta/tasks.md @@ -0,0 +1,143 @@ +# Tasks: Inline HP Delta Input + +**Input**: Design documents from `/specs/014-inline-hp-delta/` +**Prerequisites**: plan.md, spec.md, research.md, data-model.md, quickstart.md + +**Tests**: Tests are included because domain tests already exist and the spec has testable acceptance scenarios. + +**Organization**: Tasks are grouped by user story. US1 and US2 share the same component refactor but have distinct acceptance criteria. + +## Format: `[ID] [P?] [Story] Description` + +- **[P]**: Can run in parallel (different files, no dependencies) +- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3) +- Include exact file paths in descriptions + +--- + +## Phase 1: Setup + +**Purpose**: No new project setup needed. This feature modifies a single existing component. + +- [x] T001 Verify existing `pnpm check` passes before starting work (run from repo root) + +--- + +## Phase 2: Foundational + +**Purpose**: No foundational changes needed. The existing `adjustHp` domain function, `adjustHpUseCase`, and `useEncounter` hook all support the new UI without modification. + +**Checkpoint**: No blocking prerequisites -- proceed directly to user story implementation. + +--- + +## Phase 3: User Story 1 - Apply Damage via Enter Key (Priority: P1) MVP + +**Goal**: Users type a number and press Enter to apply damage. Input clears after applying. + +**Independent Test**: Set a combatant to 20/20 HP, type 7, press Enter, verify HP shows 13 and input is cleared. + +### Tests for User Story 1 + +- [x] T002 [US1] Write acceptance tests for Enter-to-damage behavior in `packages/domain/src/__tests__/adjust-hp.test.ts` -- verify existing domain tests cover: damage subtraction, clamp to 0, zero rejection. Add any missing scenarios from spec (US1 scenarios 1-4). + +### Implementation for User Story 1 + +- [x] T003 [US1] Refactor `apps/web/src/components/quick-hp-input.tsx`: remove `Mode` type, remove `mode` state variable, remove mode toggle button, remove Tab key override for mode switching. Keep the numeric input and the `value` state variable. +- [x] T004 [US1] Update Enter key handler in `apps/web/src/components/quick-hp-input.tsx`: when user presses Enter with a valid positive integer, call `onAdjustHp(combatantId, -n)` (always damage), then clear input. Reject zero, empty, and non-numeric values. +**Checkpoint**: At this point, Enter-to-damage works. No heal capability yet (added in US2). (Escape key verification merged into T009.) + +--- + +## Phase 4: User Story 2 - Apply Damage or Heal via Explicit Buttons (Priority: P1) + +**Goal**: Small damage and heal buttons next to the input allow explicit action choice. + +**Independent Test**: Set a combatant to 10/20 HP, type 5, click heal button, verify HP shows 15 and input clears. + +### Implementation for User Story 2 + +- [x] T006 [US2] Add damage button (Sword icon, red styling) to `apps/web/src/components/quick-hp-input.tsx` that reads the current input value and calls `onAdjustHp(combatantId, -n)`, then clears input. Button is disabled when input is empty or invalid. +- [x] T007 [US2] Add heal button (Heart icon, green styling) to `apps/web/src/components/quick-hp-input.tsx` that reads the current input value and calls `onAdjustHp(combatantId, +n)`, then clears input. Button is disabled when input is empty or invalid. +- [x] T008 [US2] Ensure damage and heal buttons are visually distinct (red vs green, Sword vs Heart icons) and match existing Tailwind/shadcn styling conventions in `apps/web/src/components/quick-hp-input.tsx`. + +**Checkpoint**: Both Enter-to-damage and explicit damage/heal buttons work. Full feature is functional. + +--- + +## Phase 5: User Story 3 - Keyboard-Driven Workflow (Priority: P2) + +**Goal**: Keyboard-only workflow for damage is seamless (type + Enter). Escape clears without applying. + +**Independent Test**: Focus input, type number, press Enter -- damage applied. Focus input, type number, press Escape -- input cleared, no HP change. + +### Implementation for User Story 3 + +- [x] T009 [US3] Verify that the refactored component in `apps/web/src/components/quick-hp-input.tsx` supports full keyboard workflow: focus input, type digits, Enter applies damage, Escape clears. Confirm Tab key is NOT overridden (default browser focus behavior). Also verify FR-008: delta input is not rendered for combatants without HP tracking (no max HP set). Fix any issues found. + +**Checkpoint**: All user stories functional and keyboard workflow verified. + +--- + +## Phase 6: Polish & Cross-Cutting Concerns + +**Purpose**: Final validation across all stories. + +- [x] T010 Run `pnpm check` (knip + format + lint + typecheck + test) and fix any issues +- [ ] T011 Run quickstart.md manual verification steps against the dev server (`pnpm --filter web dev`). Also verify FR-010: direct editing of the current HP absolute value still works alongside the delta input. + +--- + +## Dependencies & Execution Order + +### Phase Dependencies + +- **Setup (Phase 1)**: No dependencies +- **Foundational (Phase 2)**: N/A (no foundational tasks) +- **US1 (Phase 3)**: Depends on Phase 1 passing +- **US2 (Phase 4)**: Depends on T003 (component refactor from US1) +- **US3 (Phase 5)**: Depends on T004 (Enter key handler from US1) +- **Polish (Phase 6)**: Depends on all user stories complete + +### User Story Dependencies + +- **User Story 1 (P1)**: No dependencies on other stories. Core refactor. +- **User Story 2 (P1)**: Depends on US1 component refactor (T003) since buttons are added to the refactored component. +- **User Story 3 (P2)**: Depends on US1 (T004) since it validates the keyboard workflow established there. + +### Within Each User Story + +- Tests/verification before or alongside implementation +- Core logic before visual polish +- Commit after each task or logical group + +### Parallel Opportunities + +- T006 and T007 (damage button and heal button) can be implemented in parallel since they are independent click handlers in the same file -- but given they're both small additions to one component, sequential is equally efficient. + +--- + +## Implementation Strategy + +### MVP First (User Story 1 Only) + +1. T001: Verify clean state +2. T002: Verify/add domain tests +3. T003-T005: Refactor component, Enter-to-damage, verify Escape +4. **STOP and VALIDATE**: Type number + Enter applies damage, input clears + +### Incremental Delivery + +1. US1: Enter-to-damage works (MVP) +2. US2: Add damage/heal buttons (full feature) +3. US3: Verify keyboard workflow (polish) +4. T010-T011: Final quality gate + +--- + +## Notes + +- This is a single-component refactor. All implementation tasks modify `apps/web/src/components/quick-hp-input.tsx`. +- No domain or application layer changes needed. +- The existing `adjustHp` domain function already handles positive (heal) and negative (damage) deltas with clamping. +- Total: 10 tasks across 6 phases (T005 merged into T009).