W-050: DND-F02 turn UI¶
Type: feature · Size: S · Epic: E-dnd-m1 · Target: apps/demo-dnd · Breaking: no Depends: W-019, W-020 Refs: brief DND-F02; demo-dnd spec DND-036..039; W-020 (ECS turn loop); W-019 (game area and key rule)
Goal¶
Add the turn UI of apps/demo-dnd on the W-020 ECS: the initiative panel with "Round N", one item per combatant in initiative order (name, initiative value, side as text, hp/max, and aria-current with "Active" on the active one), a "Fallen" list, and "(new)" next to combatants in Cues.defeated. Also the "End turn" button in an "Actions" group (key E, disabled with aria-disabled when endTurn would return false), and the victory or defeat banner (role="status") with a "Restart" button and key R. Restart calls restartEncounter and moves focus to the game area, and focus moves there too when a focused action button becomes disabled (DND-037). The Attack button is W-051, and the Poison Strike button is W-023.
Steps¶
- S1 (DND-036, DND-037):
Initiativeview insrc/ui/initiative/: region "Initiative" with "Round N" and one item per entry ofinitiativeOrder(name, initiative value, side as texthero/monster,hp/max), the active item witharia-current="true"and the text "Active", a separate "Fallen" list forDefeatedcombatants, and "(new)" next to names inCues.defeated. Re-render throughkoota/reacthooks (useQuery,useTraitonEncounter,Health,Cues), not by readinginitiativeOrderonce; mount it inApp.tsxbeside the game area.Outcomebanner (role="status", text "Victory" or "Defeat", shown only when status is not'ongoing') with a "Restart" button; the button and keyRon the game area (only when status is not'ongoing', under the DND-016 key rule) callrestartEncounterand move focus to the game area. Component tests with a real world inWorldProvider, state set throughsetupEncounterand a scriptedRng: order, round, active item, hp, Fallen list, "(new)" present after the defeating hero action and the monster turns that follow, gone after the next accepted hero action; banner text for victory and defeat, none while'ongoing'; Restart andRcallrestartEncounter, focus is on the game area, and the panel shows round 1, the new party, no old entity and an empty log (OQ13 canary);Ron a button inside the game area does nothing; files:apps/demo-dnd/src/ui/initiative/Initiative.tsx,apps/demo-dnd/src/ui/initiative/Outcome.tsx,apps/demo-dnd/src/ui/initiative/Initiative.test.tsx,apps/demo-dnd/src/ui/initiative/Outcome.test.tsx,apps/demo-dnd/src/ui/input/GameArea.tsx,apps/demo-dnd/src/App.tsx - S2 (DND-037, DND-038): "Actions" group in
src/ui/actions/(inside the game area) with the "End turn" button, which callsendTurn, and keyEon the game area under the DND-016 key rule; the button is disabled witharia-disabledexactly whenendTurnwould returnfalse(status not'ongoing'or the active combatant is not a hero). When status becomes not'ongoing'while an action button has focus, focus moves to the game area (DND-037). Component tests with a real world: click andEeach callendTurnonce and advance the round or the active item; disabled andaria-disabledduring a monster turn or after victory or defeat; a focused "End turn" that ends the encounter hands focus to the game area andRthen restarts;Enteron the focused button callsendTurnonce and nothing else (DND-107 pattern, nopreventDefaultby the game);Ewith focus on a button does nothing; files:apps/demo-dnd/src/ui/actions/ActionsGroup.tsx,apps/demo-dnd/src/ui/actions/ActionsGroup.test.tsx,apps/demo-dnd/src/ui/input/GameArea.tsx - S3 (acceptance): run
pnpm turbo run typecheck lint test --filter=demo-dnd, then the spec's acceptance checklist item A2 (DND-039): monster turns appear in the log and on the grid in initiative order immediately, and the 12×12 / ~49-combatant round of logic takes under 16 ms (the benchmark is temporary and not committed)
Log¶
- 2026-11-01 created from the split of W-020 (Triage). Planner's draft: DND-036..039, S, 3 steps. Held in Later until Depends are done
- 2026-11-01 OQ13 canary (DND-037) moved here from the W-020 Log: if the banner does not show the new encounter after restart, the restart design changes (DND-034). Triage decides, not this item
- 2026-11-01 Depends: added W-019 (S1 edits files W-019 creates) (Triage, user-confirmed after board review)
- 2026-11-01 steps S1..S3 written (Planner). Open:
endTurnhas no "would return false" predicate in the spec's action list, so S2 derives it in the view fromEncounter.statusand the active combatant'sHerotag (andactedis irrelevant toendTurn, DND-029) - 2026-11-01 held: waits on the OQ6 decision after the W-039 S1 spike (decision logged in W-039 first) (Triage)