3 integration tests render the full <App /> and exercise multi-component
flows: add/remove combatant, turn tracking across two combatants, and
HP adjustment with unconscious state. Add aria-label to the clickable HP
button so tests query accessible names instead of CSS classes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds threshold entries for application, hooks, components, and components/ui
directories. Ratchets existing thresholds to match actual coverage. Excludes
**/dist/** from coverage to remove build output noise.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds aria-label attributes to HP placeholder and source delete buttons
for both accessibility and testability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Tests verify the get→call→save wiring and error propagation for each
use case. The 15 formulaic use cases share a test file; rollInitiative
and rollAllInitiative have dedicated suites covering their multi-step
logic (creature lookup, modifier calculation, iteration, early return).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mark component props as Readonly<> across 15 component files and
simplify edit-player-character field access with optional chaining
and nullish coalescing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace escaped backslash in template literal with String.raw in
auto-number.ts. Use RegExp#exec() instead of String#match() in
bestiary-adapter.ts. Enable typescript/prefer-regexp-exec in oxlint
for automated enforcement.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
Source rows disappear immediately when cleared instead of waiting
for the IndexedDB operation to complete. Real state syncs after.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Defer rendering of bestiary suggestions and player character matches
in ActionBar so the input stays responsive as the bestiary grows.
Keyboard navigation and selection logic still use the latest values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
Previously the toast only showed when the panel was not in bulk-import
mode. Now it also shows when the panel is folded, since the user can't
see the in-panel progress indicator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move player character modal state (createPlayerOpen, managementOpen,
editingPlayer) into a self-contained component with an imperative ref
handle. Closing the create/edit modal now returns to management.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Three mutually exclusive state variables (selectedCreatureId,
bulkImportMode, sourceManagerMode) replaced with a single PanelView
union type, eliminating impossible states and boolean-clearing
boilerplate in handlers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Override undici to >=7.24.0 to resolve GHSA-v9p9-hfj2-hcw8
(WebSocket 64-bit length overflow). The vulnerable version was
pulled in transitively via jsdom@28.1.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously the import/sources view would stay open when navigating to
the next combatant. Now advancing turns clears those modes so the active
creature's stat block is shown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously the button silently did nothing for creatures whose bestiary
source wasn't loaded. Now it reports how many were skipped and why. Also
keeps the roll-all button visible (but disabled) when there's nothing
left to roll, and moves toasts to the bottom-left corner.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Source management now opens in the right side panel (like bulk import
and stat blocks) instead of rendering inline above the combatant list.
All three panel modes properly clear each other on activation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
The "Add as custom" button and Escape key were clearing the name input
along with the suggestions, preventing the custom fields (Init, AC,
MaxHP) from ever appearing. Now only the suggestions are dismissed,
keeping the typed name intact so the custom combatant form renders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace raw <button> elements with Button variant="ghost" in stat-block
panel, toast, player modals. Add icon-sm size variant (h-6 w-6) for
compact contexts. Consolidate text button sizes into a single default
(h-8 px-3), removing the redundant sm variant. Add size prop to
ConfirmButton for consistent sizing.
Button now has three sizes: default (text), icon, icon-sm.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace one-off hover colors with hover-neutral/hover-destructive tokens
so all interactive elements respond consistently to theme changes. Fix
hover-neutral-bg token value (was identical to card surface, making hover
invisible on card backgrounds) to a semi-transparent primary tint. Switch
turn nav buttons to outline variant for visible hover feedback. Convert HP
popover damage/heal to plain buttons to avoid ghost variant hover conflict
with tailwind-merge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Top bar stripped to turn navigation only (Prev, round badge, Clear, Next).
Roll All Initiative, Manage Sources, and Bulk Import moved to a new
overflow menu in the bottom bar. Player Characters also moved there.
Browse stat blocks is now an Eye/EyeOff toggle inside the search input
that switches between add mode and browse mode. Add button only appears
when entering a custom creature name. Roll All Initiative button shows
conditionally — only when bestiary creatures lack initiative values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The turn navigation bar is now hidden when no combatants exist, keeping
the empty state clean. It slides down from above when the first
combatant is added, synchronized with the action bar settling animation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the abstract + icon with the actual input field centered at the
optical center when no combatants exist. Animate the transition in both
directions: settling down when the first combatant is added, rising up
when all combatants are removed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Player characters now show their chosen color on their name text
rather than as a left border glow. Left border is reserved for
active row (accent) and concentration (purple).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both player management and create/edit modals now close on Escape.
Delete player character button uses Trash2 icon instead of X to
distinguish permanent deletion from dismissal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ConfirmButton now shows hover:text-hover-destructive in its default
state. Source manager delete buttons and Clear All get matching
destructive hover styling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Standardize icon button sizing (size="icon") and color (text-muted-foreground) across top and bottom bars
- Group bottom bar icon buttons with gap-0 to match top bar style
- Add missing tooltips/aria-labels for stat block viewer, bulk import buttons
- Replace Settings icon with Library for source manager
- Make step forward/back buttons use primary (solid) variant
- Move round badge next to combatant name in center of top bar
- Close player create/edit and management modals on backdrop click
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Replace the static "No combatants yet" text with a centered, breathing
"+" icon that focuses the action bar input on click, guiding users to
add their first combatant.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When typing a name that partially matches bestiary entries, users
couldn't access the custom creature fields (Init/AC/MaxHP). Now a
prominent option at the top of the dropdown lets users dismiss
suggestions and add a custom creature instead, with an Esc hint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
The node:22 container doesn't have Docker installed. Running
on the host label executes directly on the VPS where Docker
is available.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The CI runner's pnpm store lands inside the workspace, causing
Biome to lint/format hundreds of store index JSON files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Multi-stage Docker build produces an Nginx container serving the
static SPA. The CI workflow runs pnpm check on every push and
builds/pushes a Docker image on semver tags.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>