# AGENTS.md Guidance for AI coding agents working in this repo. Humans: see [CONTRIBUTING.md](CONTRIBUTING.md) — this file is the short, machine-facing version of the same rules. ## What this is Pocketdex — an offline-first Pokédex / team builder / save-file tracker. **Vanilla JS, no UI framework.** Vite 6 + `vite-plugin-pwa` (Workbox, injectManifest). All data comes from [PokéAPI](https://pokeapi.co/). ## Setup & commands ```bash npm install npm run snapshot # build src/data/snapshot.json from PokéAPI (git-ignored; required before first build/dev) npm run dev # dev server, http://localhost:5173 npm run build # runs `snapshot` (prebuild) then builds to dist/ npm run preview # serve the production build — REQUIRED to test the service worker / offline / install npm test # Vitest (pure-logic unit tests in test/) npm run format # Prettier, write ``` - Node **20+**. - CI runs three checks, all must pass: `npm run format:check` (Prettier), `npm test` (Vitest — pure-logic unit tests in `test/`), and `npm run build` (esbuild transforms every module, catching syntax and import errors). Run `npm run format` before finishing. No ESLint. - If you change type-chart / damage-calc / savedex / selection logic, update or add a test in `test/`. - `npm run snapshot -- --force` rebuilds the snapshot even if it's fresh. ## Project structure | Path | What | | ---------------------------- | --------------------------------------------------------------------------- | | `src/main.js` | boot: theme, nav, router, SW registration, install prompt | | `src/router.js` | hash router; `routes` array; each view is `() => Promise` | | `src/views/*.js` | one file per route (`DexGrid`, `PokemonDetail`, `TeamView`, …) | | `src/components/*.js` | reusable DOM pieces (`Card`, `Sprite`, `TypeChip`, `CompareTable`, …) | | `src/store/*.js` | `createStore()` state, persisted to `localStorage` under `pdx.*` | | `src/data/*.js` | snapshot loader, pokedex resolver, lazy PokéAPI client, type chart, natures | | `src/lib/*.js` | app-agnostic helpers (`dom`, `anim`, `damage-calc`, `savedex`, `swipe`, …) | | `scripts/build-snapshot.mjs` | the only thing that calls PokéAPI at build time | | `test/*.test.js` | Vitest unit tests (pure logic only) | | `src/styles/tokens.css` | palette, type colours, the five themes | | `src/styles/layout.css` | everything else | ## Conventions - **Build DOM with `el()`** from `src/lib/dom.js` — `el(tag, props, ...children)`. No template-string HTML, no `innerHTML`, except the deliberate `html:` prop for trusted inline SVG. - **Stores**: `createStore(key, initial)` → `{ get, set, subscribe, replace }`. `set(fn)` **replaces** state (it does not merge) — spread `...s` yourself. In views, `subscribe()` and call the returned unsub in `onTeardown(viewNode, off)`. - **Everything is game-aware.** The selected game lives in `settings.get().versionGroup`. If you touch typings, effectiveness, learnsets or evolution, honour that game's generation: `typesForGen()`, `multiplier(atk, def, gen)`, PokéAPI `past_values`. - **Tracking is per game.** `src/store/selection.js`: `entry(id)` / `toggle(id, field)` / `stats(ids)` default to the selected game; `favorite` and `note` are global. Don't reintroduce a single global caught set. - Respect `prefersReducedMotion()` (from `src/store/settings.js`) for any animation. - Formatting is Prettier (`npm run format`). Otherwise match the surrounding file; keep comments about _why_. - No new runtime dependencies. Prefer adding to the snapshot over a new runtime fetch. ## Adding things - **Route** — new `src/views/Foo.js` exporting `async function Foo()` returning a node; add one entry to `routes` in `src/router.js`. Add a `skeleton` if it does async work before first paint. - **Setting** — key + default in `src/store/settings.js` (and `applyTheme()` if it affects the document), a field in `src/views/SettingsView.js`, and add it to the export payload in `exportData()` if it should survive a backup. - **Snapshot field** — add it in `scripts/build-snapshot.mjs`, run `npm run snapshot -- --force`, read it via `loadSnapshot()`. Keep the snapshot small; it's precached on every install. ## Do not - Commit `dist/` or `src/data/snapshot.json` (both git-ignored). - Commit Pokémon sprites, artwork, audio, ROMs or save files. All imagery is fetched at runtime from PokéAPI. - Add analytics, telemetry, ads, tracking, or "sign in" flows. - Add a framework or a build step that isn't Vite. - Break offline-first: the grid, search, filters and game switching must work with no network. ## Verifying a change `npm run format`, `npm test`, `npm run build` — all must pass. Then sanity-check by hand: the dex grid (filters, sort, catching from a card), switching games (numbers + typings update), a detail page (all tabs, form switch), and `npm run preview` offline (DevTools → Network → Offline → reload). Check light and dark theme and a mobile-width viewport.