diff --git a/CLAUDE.md b/CLAUDE.md index 273d084..a22b118 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -68,6 +68,7 @@ The constitution (`.specify/memory/constitution.md`) governs all feature work: - TypeScript 5.8 (strict mode, verbatimModuleSyntax) + jscpd (new dev dependency), Lefthook (existing), Biome 2.0 (existing), Knip (existing) (015-add-jscpd-gate) - N/A (no storage changes) (015-add-jscpd-gate) - Browser localStorage (existing adapter, transparent JSON serialization) (016-combatant-ac) +- TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, Lucide React (icons) (017-combat-conditions) ## Recent Changes - 003-remove-combatant: Added TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 076eb14..b108303 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -15,6 +15,7 @@ export function App() { setHp, adjustHp, setAc, + toggleCondition, } = useEncounter(); return ( @@ -34,7 +35,7 @@ export function App() { /> {/* Combatant List */} -
No combatants yet — add one to get started @@ -51,6 +52,7 @@ export function App() { onSetHp={setHp} onAdjustHp={adjustHp} onSetAc={setAc} + onToggleCondition={toggleCondition} /> )) )} diff --git a/apps/web/src/components/combatant-row.tsx b/apps/web/src/components/combatant-row.tsx index a6a3a0c..31a64f2 100644 --- a/apps/web/src/components/combatant-row.tsx +++ b/apps/web/src/components/combatant-row.tsx @@ -1,7 +1,13 @@ -import { type CombatantId, deriveHpStatus } from "@initiative/domain"; +import { + type CombatantId, + type ConditionId, + deriveHpStatus, +} from "@initiative/domain"; import { Shield, X } from "lucide-react"; import { useCallback, useRef, useState } from "react"; import { cn } from "../lib/utils"; +import { ConditionPicker } from "./condition-picker"; +import { ConditionTags } from "./condition-tags"; import { QuickHpInput } from "./quick-hp-input"; import { Button } from "./ui/button"; import { Input } from "./ui/input"; @@ -13,6 +19,7 @@ interface Combatant { readonly maxHp?: number; readonly currentHp?: number; readonly ac?: number; + readonly conditions?: readonly ConditionId[]; } interface CombatantRowProps { @@ -24,6 +31,7 @@ interface CombatantRowProps { onSetHp: (id: CombatantId, maxHp: number | undefined) => void; onAdjustHp: (id: CombatantId, delta: number) => void; onSetAc: (id: CombatantId, value: number | undefined) => void; + onToggleCondition: (id: CombatantId, conditionId: ConditionId) => void; } function EditableName({ @@ -231,81 +239,103 @@ export function CombatantRow({ onSetHp, onAdjustHp, onSetAc, + onToggleCondition, }: CombatantRowProps) { const { id, name, initiative, maxHp, currentHp } = combatant; const status = deriveHpStatus(currentHp, maxHp); + const [pickerOpen, setPickerOpen] = useState(false); return (