Commit Graph

45 Commits

Author SHA1 Message Date
Lukas
9437272fe0 Batch bestiary add produces a single undo entry
All checks were successful
CI / check (push) Successful in 1m10s
CI / build-image (push) Successful in 15s
Extract addOneFromBestiary (no undo) and build addMultipleFromBestiary
on top so confirming N creatures from the bestiary panel creates one
undo entry that restores the entire batch, not N individual entries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 00:07:25 +01:00
Lukas
541e04b732 Wrap initiative rolls with undo so they produce undo entries
Initiative rolls (single and bulk) called makeStore() directly from
useInitiativeRolls, bypassing the withUndo wrapper. Expose withUndo
from the encounter context and wrap both roll paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 00:06:50 +01:00
Lukas
29cdd19cab Roll back renames on failed compound add operations
addFromBestiary and addFromPlayerCharacter rename existing combatants
before adding the new one. If the add fails, the renames were applied
without an undo entry. Restore the pre-operation snapshot on failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:31:11 +01:00
Lukas
17cc6ed72c Add undo/redo for all encounter actions
Memento-based undo/redo with full encounter snapshots. Undo stack
capped at 50 entries, persisted to localStorage. Triggered via
buttons in the top bar (inboard of turn navigation) and keyboard
shortcuts (Ctrl+Z / Ctrl+Shift+Z, Cmd on Mac, case-insensitive key
matching). Clear encounter resets both stacks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:30:33 +01:00
Lukas
9d81c8ad27 Atomic addCombatant with optional CombatantInit bag
addCombatant now accepts an optional init parameter for pre-filled stats
(HP, AC, initiative, creatureId, color, icon, playerCharacterId), making
combatant creation a single atomic operation with domain validation.

This eliminates the multi-step store.save() bypass in addFromBestiary and
addFromPlayerCharacter, and removes the CombatantOpts/applyCombatantOpts
helpers. Also extracts shared initiative sort logic into initiative-sort.ts
used by both addCombatant and setInitiative.

Closes #15

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:13:20 +01:00
Lukas
fab9301b20 Decompose ActionBar into hook and focused sub-components
All checks were successful
CI / check (push) Successful in 1m7s
CI / build-image (push) Has been skipped
Extract useActionBarState hook with all search/queue/mode state and
handlers. Extract RollAllButton (context-consuming, zero props),
BrowseSuggestions, CustomStatFields, and refactor AddModeSuggestions
to use grouped SuggestionActions interface (11 props → 6).

ActionBar is now a ~120-line layout shell.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:41:35 +01:00
Lukas
5e5812bcaa Fix stat block panel showing wrong creature on first open
useAutoStatBlock was overriding the user's creature selection when
the panel transitioned from closed to open. Now only auto-updates
when the active turn index changes (advance/retreat), not when the
panel mode changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:18:49 +01:00
Lukas
9e09c8ae2a Sync theme-color meta tag with active light/dark theme
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:59:13 +01:00
Lukas
4043612ccf Add rules edition setting for condition tooltips (5e/5.5e)
All checks were successful
CI / check (push) Successful in 1m8s
CI / build-image (push) Successful in 16s
Introduce a settings modal (opened from the kebab menu) with a rules
edition selector for condition tooltip descriptions and a theme picker
replacing the inline cycle button. About half the conditions have
meaningful mechanical differences between editions.

- Add description5e field to ConditionDefinition with 5e (2014) text
- Add RulesEditionProvider context with localStorage persistence
- Create SettingsModal with Conditions and Theme sections
- Wire condition tooltips to edition-aware descriptions
- Fix 6 inaccurate 5.5e condition descriptions
- Update spec 003 with stories CC-3, CC-8 and FR-095–FR-102

Closes #12

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:08:41 +01:00
Lukas
8bf69fd47d Add temporary hit points as a separate damage buffer
Temp HP absorbs damage before current HP, cannot be healed, and
does not stack (higher value wins). Displayed as cyan +N after
current HP with a Shield button in the HP adjustment popover.
Column space is reserved across all rows only when any combatant
has temp HP. Concentration pulse fires on any damage, including
damage fully absorbed by temp HP.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 11:39:47 +01:00
Lukas
86768842ff Refactor App.tsx from god component to context-based architecture
All checks were successful
CI / check (push) Successful in 1m18s
CI / build-image (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:33:33 +01:00
Lukas
6584d8d064 Add advantage/disadvantage rolling for initiative
All checks were successful
CI / check (push) Successful in 1m17s
CI / build-image (push) Successful in 19s
Right-click or long-press the d20 button (per-combatant or Roll All)
to open a context menu with Advantage and Disadvantage options.
Normal left-click behavior is unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:16:04 +01:00
Lukas
7f38cbab73 Preserve stat block panel collapsed state on turn advance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:23:52 +01:00
Lukas
2971898f0c Add dark and light theme with OS preference support
All checks were successful
CI / check (push) Successful in 1m22s
CI / build-image (push) Successful in 36s
Follow OS color scheme by default, with a three-way toggle
(System / Light / Dark) in the kebab menu. Light theme uses warm,
neutral tones with soft card-to-background contrast. Semantic colors
(damage, healing, conditions) keep their hue across themes.

Closes #10

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 13:24:18 +01:00
Lukas
f024562a7d Auto-open stat block panel when adding first bestiary creature
When the side panel is in its initial closed state (not user-collapsed),
adding a combatant from the bestiary now opens the panel to show its
stat block. This makes the panel discoverable without overriding a
deliberate collapse.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 12:03:34 +01:00
Lukas
4d1a7c6420 Remove noAwaitInLoops biome-ignore by chaining batches with reduce
Replace the for-loop with await-in-loop with a .reduce() chain that
sequences Promise.allSettled batches without triggering the lint rule.
Ratchet source ignore threshold from 4 to 3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 11:24:31 +01:00
Lukas
e531d82d1b Add test coverage for 3 hooks: useEncounter, usePlayerCharacters, useSidePanelState
29 tests covering state transitions, persistence sync, domain error
propagation, bestiary/PC add flows, and panel state machine logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 15:38:51 +01:00
Lukas
c94c30e459 Add oxlint for type-aware linting that Biome cannot cover
Install oxlint with tsgolint for TypeScript type information. Enable
rules for unnecessary type assertions, deprecated API usage, preferring
replaceAll over replace with global regex, and String.raw for escaped
backslashes. Fix all violations: remove redundant as-casts, replace
deprecated FormEvent with SubmitEvent, convert replace(/g) to
replaceAll, and use String.raw in escapeRegExp. Add oxlint to the
pnpm check gate alongside Biome.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 14:41:30 +01:00
Lukas
36768d3aa1 Upgrade Biome to 2.4.7 and enable 54 additional lint rules
Add rules covering bug prevention (noLeakedRender, noFloatingPromises,
noImportCycles, noReactForwardRef), security (noScriptUrl, noAlert),
performance (noAwaitInLoops, useTopLevelRegex), and code style
(noNestedTernary, useGlobalThis, useNullishCoalescing, useSortedClasses,
plus ~40 more). Fix all violations: extract top-level regex constants,
guard React && renders with boolean coercion, refactor nested ternaries,
replace window with globalThis, sort Tailwind classes, and introduce
expectDomainError test helper to eliminate conditional expects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 14:25:09 +01:00
Lukas
971e0ded49 Replace ref+tick workaround with proper state in useBestiary
Store creature map in useState instead of useRef with a dummy
tick counter. React now re-renders naturally when the map changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 13:02:24 +01:00
Lukas
01f2bb3ff1 Move derived encounter flags into useEncounter() hook
Relocate isEmpty, hasCreatureCombatants, and canRollAllInitiative
from App.tsx into useEncounter(), reducing inline derivations in
the component (Phase 5 of App decomposition plan).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 13:02:23 +01:00
Lukas
930301de71 Rename fold/unfold to collapse/expand across panel code
Aligns terminology with standard UI conventions. Renames props,
state, handlers, aria-labels, test descriptions, and the test file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 13:02:23 +01:00
Lukas
cce87318fb Extract useSidePanelState hook from App.tsx
Move panel view state, fold/pin state, isWideDesktop media query,
and all related handlers into a dedicated hook, reducing App.tsx
by ~80 lines of state management boilerplate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 13:02:03 +01:00
Lukas
b7406c4b54 Make player character color and icon optional
Clicking an already-selected color or icon in the create/edit form now
deselects it. PCs without a color use the default combatant styling;
PCs without an icon show no icon. Domain, application, persistence,
and display layers all updated to handle the optional fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:01:20 +01:00
Lukas
91703ddebc Add player character management feature
All checks were successful
CI / check (push) Successful in 45s
CI / build-image (push) Successful in 18s
Persistent player character templates (name, AC, HP, color, icon) with
full CRUD, bestiary-style search to add PCs to encounters with pre-filled
stats, and color/icon visual distinction in combatant rows. Also stops
the stat block panel from auto-opening when adding a creature.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 18:11:08 +01:00
Lukas
b39e4923e1 Remove demo combatants and allow empty encounters
All checks were successful
CI / check (push) Successful in 45s
CI / build-image (push) Successful in 28s
Empty encounters are now valid (INV-1 updated). New sessions start
with zero combatants instead of pre-populated Aria/Brak/Cael.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 10:24:26 +01:00
Lukas
1cf30b3622 Add swipe-to-dismiss gesture for mobile stat block drawer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 22:48:48 +01:00
Lukas
b6e052f198 Overhaul bottom bar: batch add, custom fields, stat block viewer
Unify the action bar into a single search input with inline bestiary
dropdown. Clicking a dropdown entry queues it with +/- count controls
and a confirm button; Enter or confirm adds N copies to combat.

When no bestiary match exists, optional Init/AC/MaxHP fields appear
for custom creatures. The eye icon opens a separate search dropdown
to preview stat blocks without leaving the add flow.

Fix batch-add bug where only the last creature got a creatureId by
using store.save() instead of setEncounter() in addFromBestiary.
Prevent dropdown buttons from stealing input focus so Enter confirms
the queued batch.

Remove the now-redundant BestiarySearch component.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:27:06 +01:00
Lukas
0747d044f3 Implement the 032-inline-confirm-buttons feature that replaces single-click destructive actions with a reusable ConfirmButton component providing inline two-step confirmation (click to arm, click to execute), applied to the remove combatant and clear encounter buttons, with CSS scale pulse animation, 5-second auto-revert, click-outside/Escape/blur dismissal, full keyboard accessibility, and 13 unit tests via @testing-library/react
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 09:51:21 +01:00
Lukas
94d125d9c4 Implement the 030-bulk-import-sources feature that adds a one-click bulk import button to load all bestiary sources at once, with real-time progress feedback in the side panel and a toast notification when the panel is closed, plus completion/failure reporting with auto-dismiss on success and persistent display on partial failure, while also hardening the bestiary normalizer to handle variable stat blocks (spell summons with special AC/HP/CR) and skip malformed monster entries gracefully
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:29:34 +01:00
Lukas
91120d7c82 Implement the 029-on-demand-bestiary feature that replaces the bundled XMM bestiary JSON with a compact search index (~350KB) and on-demand source loading, where users explicitly provide a URL or upload a JSON file to fetch full stat block data per source, which is then normalized and cached in IndexedDB (with in-memory fallback) so creature stat blocks load instantly on subsequent visits while keeping the app bundle small and never auto-fetching copyrighted content
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 22:46:13 +01:00
Lukas
d5f7b6ee36 Implement the 026-roll-initiative feature that adds d20 roll buttons for bestiary combatants' initiative using a click-to-edit pattern (d20 icon when empty, plain text when set), plus a Roll All button in the top bar that batch-rolls for all unrolled bestiary combatants, with randomness confined to the adapter layer and the domain receiving pre-resolved dice values
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:29:09 +01:00
Lukas
24198c25f1 Implement the 023-clear-encounter feature that adds a clear encounter button with confirmation dialog to remove all combatants and reset round/turn counters, with the cleared state persisting across page refreshes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:43:42 +01:00
Lukas
fa078be2f9 Implement the 021-bestiary-statblock feature that adds a searchable D&D 2024 Monster Manual creature library with inline autocomplete suggestions, full stat block display in a fixed side panel, auto-numbering of duplicate creature names, HP/AC pre-fill from bestiary data, and automatic stat block display on turn change for wide viewports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 11:01:07 +01:00
Lukas
e59fd83292 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>
2026-03-06 14:41:59 +01:00
Lukas
febe892e15 Implement the 017-combat-conditions feature that adds D&D 5e status conditions to combatants with icon tags, color coding, and a compact toggle picker in the encounter tracker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:29:39 +01:00
Lukas
78c6591973 Implement the 016-combatant-ac feature that adds an optional Armor Class field to combatants with shield icon display and inline editing in the encounter tracker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:41:56 +01:00
Lukas
7d440677be Implement the 012-turn-navigation feature that adds a RetreatTurn domain operation and relocates turn controls to a navigation bar at the top of the encounter tracker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:11:11 +01:00
Lukas
8185fde0e8 Implement the 009-combatant-hp feature that adds optional max HP and current HP tracking per combatant with +/- controls, direct entry, and persistence
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:18:03 +01:00
Lukas
a9c280a6d6 Implement the 008-persist-encounter feature that saves encounter state to localStorage so it survives page reloads
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:24:00 +01:00
Lukas
fea2bfe39d Implement the 005-set-initiative feature that adds initiative values to combatants with automatic descending sort and active turn preservation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:26:41 +01:00
Lukas
a9df826fef Implement the 004-edit-combatant feature that adds the possibility to change a combatants name 2026-03-04 10:05:13 +01:00
Lukas
aed234de7b Implement the 003-remove-combatant feature that adds the possibility to remove a combatant from an encounter 2026-03-03 23:46:47 +01:00
Lukas
0de68100c8 Implement the 002-add-combatant feature that adds the possibility to add new combatants to an encounter 2026-03-03 23:12:20 +01:00
Lukas
4c2e0a47e6 T012–T016: Phase 3 application + web shell (use case, ports, React hook, UI, README)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:11:33 +01:00