import type { CombatantId } from "@initiative/domain"; import { type FormEvent, useCallback, useRef, useState } from "react"; import { useEncounter } from "./hooks/use-encounter"; function formatEvent(e: ReturnType["events"][number]) { switch (e.type) { case "TurnAdvanced": return `Turn: ${e.previousCombatantId} → ${e.newCombatantId} (round ${e.roundNumber})`; case "RoundAdvanced": return `Round advanced to ${e.newRoundNumber}`; case "CombatantAdded": return `Added combatant: ${e.name}`; case "CombatantRemoved": return `Removed combatant: ${e.name}`; case "CombatantUpdated": return `Renamed combatant: ${e.oldName} → ${e.newName}`; } } function EditableName({ name, combatantId, isActive, onRename, }: { name: string; combatantId: CombatantId; isActive: boolean; onRename: (id: CombatantId, newName: string) => void; }) { const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(name); const inputRef = useRef(null); const commit = useCallback(() => { const trimmed = draft.trim(); if (trimmed !== "" && trimmed !== name) { onRename(combatantId, trimmed); } setEditing(false); }, [draft, name, combatantId, onRename]); const startEditing = useCallback(() => { setDraft(name); setEditing(true); requestAnimationFrame(() => inputRef.current?.select()); }, [name]); if (editing) { return ( setDraft(e.target.value)} onBlur={commit} onKeyDown={(e) => { if (e.key === "Enter") commit(); if (e.key === "Escape") setEditing(false); }} /> ); } return ( ); } export function App() { const { encounter, events, advanceTurn, addCombatant, removeCombatant, editCombatant, } = useEncounter(); const activeCombatant = encounter.combatants[encounter.activeIndex]; const [nameInput, setNameInput] = useState(""); const handleAdd = (e: FormEvent) => { e.preventDefault(); if (nameInput.trim() === "") return; addCombatant(nameInput); setNameInput(""); }; return (

Initiative Tracker

{activeCombatant && (

Round {encounter.roundNumber} — Current: {activeCombatant.name}

)}
    {encounter.combatants.map((c, i) => (
  • {" "}
  • ))}
setNameInput(e.target.value)} placeholder="Combatant name" />
{events.length > 0 && (

Events

    {events.map((e, i) => (
  • {formatEvent(e)}
  • ))}
)}
); }