When any variant rule is enabled, the encounter modal switches from
the game's regional dex to an all-Pokemon search (same debounced
API pattern as EggEncounterModal). A new "Run Variant" section in
rules configuration groups these rules, and badges render in amber.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove firstEncounterOnly, permadeath, nicknameRequired, and
postGameCompletion from the rules system — they are either implicit
(it's a nuzlocke tracker) or not enforced. Move levelCaps to core
(it's displayed in the sticky bar). Create a new "playstyle" category
for hardcoreMode and setModeOnly — informational rules useful for
stats but not enforced by the tracker. Remove the completion category
entirely. Add sub-task beans for the rules overhaul epic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace incorrect perceived-brightness formula in Stats progress bars
with proper WCAG relative luminance calculation, and convert type bar
colors to hex values for reliable contrast detection. Add light: variant
classes to status badges, yellow/purple text, and admin nav links across
17 files. Darken light-mode status-active token and text-tertiary/muted
tokens. Add aria-labels to admin filter selects and flex-wrap for mobile
overflow on AdminEvolutions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Set up end-to-end test infrastructure with Docker Compose test
environment, Playwright config, and automated global setup/teardown
that seeds a test database and creates fixtures via the API.
Tests cover 11 pages across both dark/light themes for WCAG 2.0 AA
accessibility (axe-core), and across 3 viewports (mobile, tablet,
desktop) for horizontal overflow and touch target validation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement theme switching via sun/moon toggle in nav bar. Dark
remains the default; light mode overrides surface, text, border,
accent, and status color tokens. Preference persists in localStorage
and falls back to prefers-color-scheme. An inline script in
index.html prevents flash of wrong theme on load.
Define a Tailwind v4 @custom-variant for light mode and update all
badge components (encounter method, rule, condition) to use
light:bg-{color}-100 / light:text-{color}-700 for readable contrast
on light surfaces.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the Python-based pre-commit framework with prek (Rust) for
faster hook execution. Convert .pre-commit-config.yaml to prek.toml,
remove pre-commit from dev dependencies, and apply ruff auto-fixes
(UP037: remove unnecessary string quotes in type annotations).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show colored pill badges (Mega, G-Max, D-Max, Tera) on boss Pokemon
in BossDefeatModal and BossTeamPreview. Starter-dependent condition
labels are ignored. Follows EncounterMethodBadge pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Pokemon teams (pokeapi_id, level, order) to all 327 boss entries
across 21 seed files, sourced from Bulbapedia. Includes starter-dependent
teams via condition_label, Mega Evolution, Dynamax/Gigantamax,
Terastallize, and single-Pokemon entries for Totems/Nobles/Titans.
Also fix _export_bosses to include condition_label in exported data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add missing tsconfig strictness flags (noUncheckedIndexedAccess,
exactOptionalPropertyTypes, noImplicitOverride,
noPropertyAccessFromIndexSignature) and fix all resulting type errors
- Replace ESLint/Prettier with oxlint 1.48.0 and oxfmt 0.33.0
- Pin all frontend and backend dependencies to exact versions
- Pin GitHub Actions to SHA hashes with persist-credentials: false
- Fix CI Python version mismatch (3.12 -> 3.14) and ruff target-version
- Add vitest 4.0.18 with jsdom environment for frontend testing
- Add ty 0.0.17 for Python type checking (non-blocking in CI)
- Add actionlint and zizmor CI job for workflow linting and security audit
- Add Dependabot config for npm, pip, and github-actions
- Update CLAUDE.md and pre-commit hooks to reflect new tooling
- Ignore Claude Code sandbox artifacts in gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wild Pokemon on 6 locations were incorrectly duplicated as gift encounters
by the PokeDB export. Removed 39 from Sword and 40 from Shield, preserving
legitimate gifts (starters, fossils, Type Null, Kubfu, etc.).
Resolves beans-cdmx.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Standardise 426 inconsistent route names across 33 seed data files
(game data + boss files). Converts 'Region Route X' prefix style and
'Route X - Region' dash style to the consistent 'Route X (Region)' format.
Resolves beans-r48e.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Set up pre-commit framework with ruff (backend) and ESLint/Prettier/tsc
(frontend) hooks to catch issues locally before CI. Auto-format all
frontend files with Prettier to comply with the new check.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Routes are shared per version group, so game-exclusive locations (e.g.,
Black City, White Forest) appeared for both games. Now the /games/{id}/routes
endpoint excludes routes that have no encounters for the requested game,
in both flat and hierarchical modes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Route deletion failed with two integrity errors:
1. route_encounters had no cascade, so SQLAlchemy tried to NULL
the non-nullable route_id instead of deleting the rows
2. boss_battles.after_route_id referenced the route being deleted
Added cascade="all, delete-orphan" to Route.route_encounters and
nulled out boss battle after_route_id references before deletion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Originals and remakes previously shared route orderings via aliases
(e.g. red-blue → firered-leafgreen). This prevented customizing route
progression independently. Each version group now has its own list that
can be fine-tuned for game-specific locations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two migrations (add_game_id_to_boss_battles and add_naming_scheme_to_genlockes)
shared the same revision ID, causing multiple heads. Gave boss_battles migration
a new unique ID (g8b9c0d1e2f3) and chained it after the naming_scheme migration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Genlockes can now select a naming scheme at creation time, which is
automatically applied to every leg's run. When catching a pokemon whose
evolution family appeared in a previous leg, the system suggests the
original nickname with a roman numeral suffix (e.g., "Heracles II").
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reviewed-on: TheFurya/nuzlocke-tracker#20
Co-authored-by: Julian Tabel <juliantabel.jt@gmail.com>
Co-committed-by: Julian Tabel <juliantabel.jt@gmail.com>
Crystal shares the same route progression as Gold, so align its route
order values to match. Only order fields changed; encounters preserved.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Visually indent child routes under their parent with tree connectors,
and make dragging a parent move all its children as a unit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Den child routes (~561 per game) bloated the route list without being
useful for Nuzlocke tracking. Adds filter_den_routes() to strip children
matching "(Den " from the route hierarchy, reducing Sw/Sh from ~1,007
to 446 routes each.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the synthetic "Starter" route with actual in-game locations
(e.g. Professor Oak's Laboratory, Iki Town, Littleroot Town). Starters
now appear at their real locations with method "starter" by remapping
PokeDB's "gift" method during import. Split ruby-sapphire and
black-2-white-2 out of special_encounters aliases since their starter
locations differ from the aliased version groups.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The DB accumulated stale routes from previous imports with different
naming (e.g. "Route 1" alongside "Alola Route 1"). These were exported
back to JSON, causing duplicate entries with conflicting order values.
Removed 597 stale routes across 17 game files and re-sorted all 22
game files to match the canonical progression in route_order.json.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add clickable suggestion chips below the nickname input in the encounter
modal. Chips are fetched from GET /runs/{id}/name-suggestions via React
Query, shown only when a naming scheme is set. Clicking a chip fills in
the nickname; a regenerate button fetches a fresh random batch. Completes
the Name Generation epic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>