mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-09-18 19:42:04 +00:00
Compare commits
92 Commits
669d0cdeaa
..
v0.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
| a209ab3b63 | |||
| 10221812a8 | |||
| 33fbf6e3ac | |||
| 1531fbe591 | |||
| c8a82c0bbe | |||
| 8a9185944d | |||
| 9d4ed604a8 | |||
| c97347722a | |||
| 3a195f6176 | |||
| fed7003972 | |||
| 4be21fb66b | |||
| bb3b646587 | |||
| 2fb2b2c06d | |||
| 4dc5d86a74 | |||
| a61b6a3f50 | |||
| f5c580b828 | |||
| 2db9045d3d | |||
| 1a6232f5bd | |||
| f2da6e5b37 | |||
| e871fa7fea | |||
| dbafe342b7 | |||
| e371e4b813 | |||
| 1cdbab0f6c | |||
| c72adff5bf | |||
| 171a202ef6 | |||
| 2608f3dba6 | |||
| e9c483045a | |||
| 955a8c869e | |||
| 5fdc43124e | |||
| 3d42bef3e5 | |||
| 72254fb479 | |||
| a3cfb4e83c | |||
| 2a33dc1781 | |||
| 2404499858 | |||
| 8c5bc48802 | |||
| f5ae56f6a6 | |||
| 8a7e2ea631 | |||
| 228a37a2f6 | |||
| f420b6372e | |||
| f6741f4b1e | |||
| a375489cc6 | |||
| 092ffde8e4 | |||
| 469290662f | |||
| ba06fbee2b | |||
| 76b617b349 | |||
| 8262dbfcf4 | |||
| c666583058 | |||
| 61feede30a | |||
| 9e21f8edbf | |||
| 7dda58ec24 | |||
| f64f8b2bc6 | |||
| 062eff0433 | |||
| e4e133b333 | |||
| 0fe76a9ec6 | |||
| d41b467d43 | |||
| 6712339640 | |||
| 7c0dec0cb4 | |||
| 4c9631e00f | |||
| c6db2a9add | |||
| 4952eda3e0 | |||
| fa3fe31fb6 | |||
| 70c31f6798 | |||
| 999cb8bf3f | |||
| ae4c27ae6e | |||
| 4d4aecf2f6 | |||
| a7838b8e7b | |||
| d581e26234 | |||
| 9c22786dd5 | |||
| 69c80a5e39 | |||
| bc4e69e95e | |||
| 89993f305a | |||
| e78a562725 | |||
| 4c71e92780 | |||
| 60bc76705b | |||
| ab225b661f | |||
| 21f8d35bb7 | |||
| df66370e02 | |||
| 364b593f08 | |||
| eacd695a40 | |||
| 97be847b44 | |||
| 2b220f1437 | |||
| 99655d3864 | |||
| 869be67c54 | |||
| 6cfa2bd6d7 | |||
| b7996e50a6 | |||
| 88b5edb4b9 | |||
| 596bfa3ec1 | |||
| aee56d0fc9 | |||
| 3c1ae70392 | |||
| 5650a6d8d7 | |||
| 2de4e8b54b | |||
| b2b0cf0035 |
+3
-22
@@ -1,22 +1,3 @@
|
||||
PUBLIC_SUPABASE_URL="your-supabase-project-url"
|
||||
PUBLIC_SUPABASE_ANON_KEY="your-supabase-anon-key"
|
||||
SUPABASE_SERVICE_ROLE_KEY="your-supabase-service-role-key"
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER="false"
|
||||
|
||||
GOOGLE_OAUTH_CLIENT_ID="your-google-client-id"
|
||||
GOOGLE_OAUTH_CLIENT_SECRET="your-google-client-secret"
|
||||
DROPBOX_OAUTH_CLIENT_ID="your-dropbox-client-id"
|
||||
DROPBOX_OAUTH_CLIENT_SECRET="your-dropbox-client-secret"
|
||||
|
||||
# Endpoint overrides for deterministic local provider tests. They are ignored unless
|
||||
# ALLOW_PROVIDER_ENDPOINT_OVERRIDES is exactly "true", the local BDD stack variables are present,
|
||||
# and every URL is loopback. These endpoints receive OAuth secrets, so never set this in a deployed
|
||||
# environment. `npm run test:bdd` supplies the complete test context.
|
||||
ALLOW_PROVIDER_ENDPOINT_OVERRIDES=""
|
||||
GOOGLE_OAUTH_AUTHORIZE_URL=""
|
||||
GOOGLE_OAUTH_TOKEN_URL=""
|
||||
GOOGLE_DRIVE_API_URL=""
|
||||
GOOGLE_DRIVE_UPLOAD_URL=""
|
||||
DROPBOX_OAUTH_AUTHORIZE_URL=""
|
||||
DROPBOX_OAUTH_TOKEN_URL=""
|
||||
DROPBOX_UPLOAD_URL=""
|
||||
MONGO_URL=""
|
||||
PUBLIC_SUPABASE_URL=""
|
||||
PUBLIC_SUPABASE_ANON_KEY=""
|
||||
@@ -11,5 +11,3 @@ node_modules
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
.wrangler/
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
concurrency:
|
||||
group: tests-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# `$env/static/public` is resolved at build time, so every variable imported from it must be
|
||||
# present for `vite build` and `svelte-check` to succeed on a clean checkout. These are the
|
||||
# local-stack defaults already published in .env.local.example - never real credentials.
|
||||
env:
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER: 'false'
|
||||
PUBLIC_SUPABASE_URL: http://127.0.0.1:54321
|
||||
PUBLIC_SUPABASE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Check committed whitespace
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
git diff --check "${{ github.event.pull_request.base.sha }}...HEAD"
|
||||
else
|
||||
git diff --check "HEAD^...HEAD"
|
||||
fi
|
||||
- run: npm run check
|
||||
- run: npm run lint
|
||||
- run: npm run test:fast
|
||||
- name: Verify tests leave the checkout clean
|
||||
run: test -z "$(git status --porcelain --untracked-files=all)"
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: always()
|
||||
with:
|
||||
name: coverage
|
||||
path: coverage/
|
||||
if-no-files-found: ignore
|
||||
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
# `supabase start` applies migrations and seeds; no separate reset is needed.
|
||||
- run: npx supabase start
|
||||
- run: npm run test:integration
|
||||
- if: always()
|
||||
run: npx supabase stop
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run test:build
|
||||
|
||||
lighthouse:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
form-factor: [mobile, desktop]
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
# The homepage streams public stats from the database, so audit against a real stack.
|
||||
- run: npx supabase start
|
||||
- run: npm run test:lighthouse
|
||||
# Not LHCI_*: lhci reads any LHCI_ variable as a CLI flag (LHCI_PRESET became `--preset`).
|
||||
env:
|
||||
LIGHTHOUSE_FORM_FACTOR: ${{ matrix.form-factor }}
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: always()
|
||||
with:
|
||||
name: lighthouse-${{ matrix.form-factor }}
|
||||
path: .lighthouseci/
|
||||
# upload-artifact v4+ skips dot-directories unless told otherwise.
|
||||
include-hidden-files: true
|
||||
if-no-files-found: ignore
|
||||
- if: always()
|
||||
run: npx supabase stop
|
||||
|
||||
bdd:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
# Serve the committed sprites so offline sync doesn't download every Living Dex sprite from
|
||||
# GitHub on each run.
|
||||
env:
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER: 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-chromium-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
playwright-chromium-${{ runner.os }}-
|
||||
- run: npx playwright install --with-deps chromium
|
||||
- run: npx supabase start
|
||||
- run: npm run test:bdd
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: failure()
|
||||
with:
|
||||
name: playwright-failures
|
||||
path: |
|
||||
test-results/
|
||||
playwright-report/
|
||||
if-no-files-found: ignore
|
||||
- if: always()
|
||||
run: npx supabase stop
|
||||
|
||||
pokedex-performance:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER: 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npx playwright install --with-deps chromium
|
||||
- run: npx supabase start
|
||||
- run: npm run test:performance
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: always()
|
||||
with:
|
||||
name: pokedex-performance
|
||||
path: test-results/performance/
|
||||
if-no-files-found: ignore
|
||||
- if: always()
|
||||
run: npx supabase stop
|
||||
|
||||
cloudflare-preview-build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
WRANGLER_SEND_METRICS: 'false'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run build:cloudflare
|
||||
- run: npm run check:cloudflare
|
||||
# This is compilation/package coverage, not a production compatibility gate.
|
||||
# Native sharp still blocks the share-preview route in the Worker runtime.
|
||||
@@ -9,12 +9,4 @@ node_modules
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
/static/output.css
|
||||
.lighthouseci
|
||||
.netlify
|
||||
.features-gen
|
||||
coverage
|
||||
playwright-report
|
||||
test-results
|
||||
|
||||
/static/sprites-grid/
|
||||
.wrangler/
|
||||
|
||||
@@ -2,18 +2,3 @@
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# Generated data exports. Nobody hand-edits these, and reformatting them buries real data
|
||||
# changes under tens of thousands of lines of churn.
|
||||
src/lib/helpers/pokeapi-pokemon.json
|
||||
static/sprites/pokemon.json
|
||||
src/lib/helpers/pokedex.json
|
||||
src/lib/helpers/sprites.json
|
||||
static/sprites-small/manifest.json
|
||||
|
||||
# Machine-local editor and tool settings.
|
||||
**/*.local.json
|
||||
|
||||
# Generated grid artwork and local Worker runtime output.
|
||||
static/sprites-grid/
|
||||
.wrangler/
|
||||
|
||||
@@ -4,24 +4,18 @@ A web app to track completion of a living Pokédex.
|
||||
|
||||
## Developing
|
||||
|
||||
1. Clone the repository
|
||||
2. Install the dependencies with `npm install`
|
||||
3. Ensure that Docker is running
|
||||
4. An example `.env` file is provided in the repository. You will need to copy `.env.example` to `.env` and fill in the values with your own credentials. For local development with Supabase running in Docker, you can use the following values:
|
||||
Cloned the repository you can install the dependencies with `npm install`, and start a development server:
|
||||
|
||||
- The `PUBLIC_SUPABASE_URL` will be `"http://127.0.0.1:54321"`
|
||||
- The `PUBLIC_SUPABASE_ANON_KEY` will be the 'Publishable' authentication key displayed when you run Supabase in the terminal
|
||||
- The `SUPABASE_SERVICE_ROLE_KEY` will be the 'Secret' authentication key displayed when you run Supabase in the terminal
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
5. Start local Supabase and a development server with `npm run dev:supabase`
|
||||
6. The Pokédex data is automatically seeded via database migrations when Supabase starts
|
||||
7. Create an account using the sign-up form and access the email it sends in [MailPit](http://127.0.0.1:54324/) to verify your email address.
|
||||
N.B. All local emails are captured by MailPit when running Supabase in Docker.
|
||||
8. You can now use [the app](http://localhost:5173/).
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Reference Data Updates
|
||||
An example `.env` file is provided in the repository. You will need to copy `.env.example` to `.env` and fill in the values with your own credentials.
|
||||
|
||||
The seed data lives in `supabase/migrations/20260118001000_seed_reference_data.sql`. Update that migration directly when new data is added. You can access a local copy of [Supabase](http://localhost:54323/) to check it.
|
||||
Once you have done this, you can seed the Pokédex data using the /api/seed endpoint. You will need to comment out the `return;` at the beginning of the `GET` function in `/src/routes/api/seed.ts` to do this. Make sure you remember to uncomment it afterwards.
|
||||
|
||||
## Building
|
||||
|
||||
@@ -31,110 +25,16 @@ To create a production version:
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
The test suite is split by responsibility so a failure points to the correct layer:
|
||||
|
||||
- `tests/unit` contains fast, isolated tests for utilities, repositories, and services.
|
||||
- `tests/data` validates the tracked Pokémon, game, region, dex, and sprite reference files.
|
||||
- `tests/integration` checks the migrated Supabase schema, views, constraints, RLS, and repositories.
|
||||
- `tests/bdd/features` is the executable Gherkin specification for user-visible behaviour. Step
|
||||
definitions and browser fixtures live beside it under `tests/bdd`.
|
||||
- `tests/build` verifies generated service-worker and manifest artifacts after each supported build,
|
||||
and fails if the gzipped JS or CSS every page loads grows past its budget.
|
||||
- `lighthouserc.cjs` audits the public pages with Lighthouse CI (`npm run test:lighthouse`, which
|
||||
builds and serves the Node output). PRs fail if Performance, Accessibility, Best Practices or SEO
|
||||
drops below 90, or if LCP, TBT, CLS, script, stylesheet or total transfer size exceeds its budget.
|
||||
CI runs it with both the mobile and desktop profiles (`LIGHTHOUSE_FORM_FACTOR=desktop`).
|
||||
- `tests/bdd/features/performance.feature` holds time budgets for signed-in pages Lighthouse can't
|
||||
reach: opening a Pokédex and switching between it and the Pokédex list.
|
||||
|
||||
The budgets sit just above current measurements so regressions fail the PR. If a change genuinely
|
||||
needs more, raise the budget in the same PR so the cost is reviewed.
|
||||
|
||||
Run the offline suites while developing. `test:fast` includes the coverage run, so there is no need
|
||||
to run both:
|
||||
|
||||
```bash
|
||||
npm run test:fast
|
||||
```
|
||||
|
||||
Coverage is measured across all of `src/lib` (excluding type-only models and the browser-only
|
||||
store/action modules) with global thresholds set to the current baseline, so new untested code lowers
|
||||
the number instead of being invisible to the gate. Ratchet the thresholds in `vitest.config.mts` up as
|
||||
coverage grows, never down.
|
||||
|
||||
Database and BDD tests require Docker and the local Supabase stack. The wrappers read the local keys
|
||||
from `supabase status` at run time, so no keys are hard-coded in the test files; the local stack's
|
||||
well-known demo keys do appear in `.env.local.example`, and no real credentials are committed:
|
||||
|
||||
```bash
|
||||
npm run supabase:start
|
||||
npm run supabase:reset
|
||||
npm run test:integration
|
||||
npm run test:bdd
|
||||
```
|
||||
|
||||
`npm test` runs the complete CI-equivalent sequence and fails with setup instructions when Supabase is
|
||||
not available. Individual layers are available as `test:unit`, `test:data`, `test:integration`,
|
||||
`test:build`, and `test:bdd`.
|
||||
|
||||
Gherkin describes outcomes in domain language. Keep selectors, API calls, test-user provisioning, and
|
||||
provider mocks in step definitions or support fixtures. `@product-review` marks a rule that should be
|
||||
reviewed with product stakeholders, but does not skip it. Missing or ambiguous steps fail generation.
|
||||
|
||||
Google Drive and Dropbox scenarios use a local provider server (`scripts/mock-provider-server.mjs`)
|
||||
and never contact real provider accounts. The endpoint overrides that point at it are refused unless
|
||||
`ALLOW_PROVIDER_ENDPOINT_OVERRIDES=true`, the local test-stack/service-role variables are present,
|
||||
and the override is a loopback URL. These endpoints receive the OAuth client secret and refresh
|
||||
token, so they must not be redirectable in a deployed environment. `npm run test:bdd` supplies the
|
||||
complete test context.
|
||||
|
||||
The mock's recorded requests, refresh counter and fail-uploads switch are reset before every scenario
|
||||
by an auto fixture in `tests/bdd/fixtures.ts`. That reset is also why the suite runs with a single
|
||||
worker: the mock is one shared process, so parallel scenarios would reset each other's state. A global
|
||||
teardown deletes the users each run creates, so repeated local runs do not need a database reset.
|
||||
|
||||
Chromium is the only configured browser project. Playwright traces and screenshots are retained on
|
||||
failure under `test-results`.
|
||||
|
||||
Password-reset scenarios follow recovery links generated by the local Supabase stack and verify both
|
||||
the rejected old password and accepted replacement password. The application waits for Supabase to
|
||||
confirm the recovery session before enabling the replacement form.
|
||||
|
||||
After sign-in, the application automatically stores a versioned, per-user read-only copy of every
|
||||
Pokédex and its referenced artwork. Offline navigation opens a static viewer; all mutation and
|
||||
authentication controls remain unavailable until connectivity returns. A successful sign-out removes
|
||||
the user-specific snapshot and artwork caches from the device.
|
||||
|
||||
The current National Dex maximum is deliberately asserted as 1025. When adding a new generation,
|
||||
update that expectation together with Pokémon data, the corresponding game/dex files, database seed,
|
||||
and sprites. Data tests print the exact conflicting identities or broken references.
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
## Sprites
|
||||
|
||||
The app uses WebP sprites from `static/sprites-small`. During builds we generate this folder from the
|
||||
full-resolution PNGs in `static/sprites`:
|
||||
|
||||
```bash
|
||||
npm run sprites:build
|
||||
```
|
||||
|
||||
If you want to serve sprites locally, set `PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER="true"` in `.env`.
|
||||
Otherwise the app defaults to GitHub raw for `static/sprites-small`.
|
||||
|
||||
## Hosting
|
||||
|
||||
The app is hosted on [Netlify](https://www.netlify.com/) at [pokedex.jcreek.co.uk](https://pokedex.jcreek.co.uk/).
|
||||
|
||||
The Pokédex data is stored in a [Supabase](https://supabase.com/) database.
|
||||
The Pokédex data is stored in a [MongoDB](https://www.mongodb.com/) database hosted on [MongoDB Cloud](https://cloud.mongodb.com/).
|
||||
|
||||
User authentication is handled by [Supabase Auth](https://supabase.com/auth).
|
||||
|
||||
## Dependencies
|
||||
|
||||
The living dex tracker's sprite collection is derived from [PokéAPI Sprites](https://github.com/PokeAPI/sprites)
|
||||
and converted to smaller WebP files in `static/sprites-small`. PokéAPI sprites are licensed under
|
||||
[the Creative Commons CC0 1.0 Universal license](https://github.com/PokeAPI/sprites/blob/master/LICENCE.txt).
|
||||
The living dex tracker's sprite collection is taken from [PokéAPI Sprites](https://github.com/PokeAPI/sprites), which is licensed under [the Creative Commons CC0 1.0 Universal license](https://github.com/PokeAPI/sprites/blob/master/LICENCE.txt).
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
/sprites-grid/v1/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
+5
-10
@@ -1,12 +1,7 @@
|
||||
import process from 'node:process';
|
||||
import process from 'node:process'
|
||||
import AdapterNode from '@sveltejs/adapter-node';
|
||||
import AdapterNetlify from '@sveltejs/adapter-netlify';
|
||||
import AdpaterStatic from '@sveltejs/adapter-static';
|
||||
|
||||
export const nodeAdapter =
|
||||
process.env.NODE_ADAPTER === 'true' || process.env.DEPLOY_TARGET === 'node';
|
||||
export const cloudflareAdapter = process.env.DEPLOY_TARGET === 'cloudflare';
|
||||
export const adapter = nodeAdapter
|
||||
? AdapterNode({ precompress: true })
|
||||
: cloudflareAdapter
|
||||
? (await import('@sveltejs/adapter-cloudflare')).default()
|
||||
: AdapterNetlify({ edge: false, split: false });
|
||||
export const nodeAdapter = process.env.NODE_ADAPTER === 'true'
|
||||
|
||||
export const adapter = nodeAdapter ? AdapterNode() : AdpaterStatic()
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
|
||||
test('Test offline and trailing slashes', async ({ browser }) => {
|
||||
// test offline + trailing slashes routes
|
||||
const context = await browser.newContext();
|
||||
const offlinePage = await context.newPage();
|
||||
await offlinePage.goto('/');
|
||||
const offlineSwURL = await offlinePage.evaluate(async () => {
|
||||
const registration = await Promise.race([
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
navigator.serviceWorker.ready,
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Service worker registration failed: time out')), 10000)
|
||||
)
|
||||
]);
|
||||
// @ts-expect-error registration is of type unknown
|
||||
return registration.active?.scriptURL;
|
||||
});
|
||||
const offlineSwName = 'sw.js';
|
||||
expect(offlineSwURL).toBe(`http://localhost:4173/${offlineSwName}`);
|
||||
await context.setOffline(true);
|
||||
const aboutAnchor = offlinePage.getByRole('link', { name: 'About' });
|
||||
expect(await aboutAnchor.getAttribute('href')).toBe('/about');
|
||||
await aboutAnchor.click({ noWaitAfter: false });
|
||||
const url = await offlinePage.evaluate(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||
return location.href;
|
||||
});
|
||||
expect(url).toBe('http://localhost:4173/about');
|
||||
expect(offlinePage.locator('li[aria-current="page"] a').getByText('About')).toBeTruthy();
|
||||
await offlinePage.reload({ waitUntil: 'load' });
|
||||
expect(offlinePage.url()).toBe('http://localhost:4173/about');
|
||||
expect(offlinePage.locator('li[aria-current="page"] a').getByText('About')).toBeTruthy();
|
||||
// Dispose context once it's no longer needed.
|
||||
await context.close();
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
|
||||
test('The service worker is registered and cache storage is present', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
const swURL = await page.evaluate(async () => {
|
||||
const registration = await Promise.race([
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
navigator.serviceWorker.ready,
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Service worker registration failed: time out')), 10000)
|
||||
)
|
||||
]);
|
||||
// @ts-expect-error registration is of type unknown
|
||||
return registration.active?.scriptURL;
|
||||
});
|
||||
const swName = 'sw.js';
|
||||
expect(swURL).toBe(`http://localhost:4173/${swName}`);
|
||||
|
||||
const cacheContents = await page.evaluate(async () => {
|
||||
const cacheState: Record<string, Array<string>> = {};
|
||||
for (const cacheName of await caches.keys()) {
|
||||
const cache = await caches.open(cacheName);
|
||||
cacheState[cacheName] = (await cache.keys()).map((req) => req.url);
|
||||
}
|
||||
return cacheState;
|
||||
});
|
||||
|
||||
expect(Object.keys(cacheContents).length).toEqual(1);
|
||||
|
||||
const key = 'workbox-precache-v2-http://localhost:4173/';
|
||||
|
||||
expect(Object.keys(cacheContents)[0]).toEqual(key);
|
||||
|
||||
const urls = cacheContents[key].map((url) => url.slice('http://localhost:4173/'.length));
|
||||
|
||||
/*
|
||||
'http://localhost:4173/about?__WB_REVISION__=38251751d310c9b683a1426c22c135a2',
|
||||
'http://localhost:4173/?__WB_REVISION__=073370aa3804305a787b01180cd6b8aa',
|
||||
'http://localhost:4173/manifest.webmanifest?__WB_REVISION__=27df2fa4f35d014b42361148a2207da3'
|
||||
*/
|
||||
expect(urls.some((url) => url.startsWith('manifest.webmanifest?__WB_REVISION__='))).toEqual(true);
|
||||
expect(urls.some((url) => url.startsWith('?__WB_REVISION__='))).toEqual(true);
|
||||
expect(urls.some((url) => url.startsWith('about?__WB_REVISION__='))).toEqual(true);
|
||||
// dontCacheBustURLsMatching: any asset in _app/immutable folder shouldn't have a revision (?__WB_REVISION__=)
|
||||
expect(urls.some((url) => url.startsWith('_app/immutable/') && url.endsWith('.css'))).toEqual(
|
||||
true
|
||||
);
|
||||
expect(urls.some((url) => url.startsWith('_app/immutable/') && url.endsWith('.js'))).toEqual(
|
||||
true
|
||||
);
|
||||
expect(urls.some((url) => url.includes('_app/version.json?__WB_REVISION__='))).toEqual(true);
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
dexId,gameDisplayName,dexDisplayName,sortOrder,isDlc,parentDexId,file
|
||||
red-kanto,Red,Kanto,1,false,,native-game-dexes/red-blue-yellow.csv
|
||||
blue-kanto,Blue,Kanto,1,false,,native-game-dexes/red-blue-yellow.csv
|
||||
yellow-kanto,Yellow,Kanto,1,false,,native-game-dexes/red-blue-yellow.csv
|
||||
gold-johto,Gold,Johto,1,false,,native-game-dexes/gold-silver-crystal.csv
|
||||
silver-johto,Silver,Johto,1,false,,native-game-dexes/gold-silver-crystal.csv
|
||||
crystal-johto,Crystal,Johto,1,false,,native-game-dexes/gold-silver-crystal.csv
|
||||
ruby-hoenn,Ruby,Hoenn,1,false,,native-game-dexes/ruby-sapphire-emerald.csv
|
||||
sapphire-hoenn,Sapphire,Hoenn,1,false,,native-game-dexes/ruby-sapphire-emerald.csv
|
||||
emerald-hoenn,Emerald,Hoenn,1,false,,native-game-dexes/ruby-sapphire-emerald.csv
|
||||
firered-kanto,FireRed,Kanto,1,false,,native-game-dexes/fire-red-leaf-green.csv
|
||||
leafgreen-kanto,LeafGreen,Kanto,1,false,,native-game-dexes/fire-red-leaf-green.csv
|
||||
diamond-sinnoh,Diamond,Sinnoh,1,false,,native-game-dexes/diamond-pearl.csv
|
||||
pearl-sinnoh,Pearl,Sinnoh,1,false,,native-game-dexes/diamond-pearl.csv
|
||||
platinum-sinnoh,Platinum,Sinnoh,1,false,,native-game-dexes/platinum.csv
|
||||
heart-gold-johto,Heart Gold,Johto,1,false,,native-game-dexes/heart-gold-soul-silver.csv
|
||||
soul-silver-johto,Soul Silver,Johto,1,false,,native-game-dexes/heart-gold-soul-silver.csv
|
||||
black-unova,Black,Unova,1,false,,native-game-dexes/black-white.csv
|
||||
white-unova,White,Unova,1,false,,native-game-dexes/black-white.csv
|
||||
black-2-unova,Black 2,Unova,1,false,,native-game-dexes/black-2-white-2.csv
|
||||
white-2-unova,White 2,Unova,1,false,,native-game-dexes/black-2-white-2.csv
|
||||
x-central-kalos,X,Central Kalos,1,false,,native-game-dexes/x-y-central-kalos.csv
|
||||
x-coastal-kalos,X,Coastal Kalos,2,false,,native-game-dexes/x-y-coastal-kalos.csv
|
||||
x-mountain-kalos,X,Mountain Kalos,3,false,,native-game-dexes/x-y-mountain-kalos.csv
|
||||
y-central-kalos,Y,Central Kalos,1,false,,native-game-dexes/x-y-central-kalos.csv
|
||||
y-coastal-kalos,Y,Coastal Kalos,2,false,,native-game-dexes/x-y-coastal-kalos.csv
|
||||
y-mountain-kalos,Y,Mountain Kalos,3,false,,native-game-dexes/x-y-mountain-kalos.csv
|
||||
omega-ruby-hoenn,Omega Ruby,Hoenn,1,false,,native-game-dexes/omega-ruby-alpha-sapphire.csv
|
||||
alpha-sapphire-hoenn,Alpha Sapphire,Hoenn,1,false,,native-game-dexes/omega-ruby-alpha-sapphire.csv
|
||||
sun-alola,Sun,Alola,1,false,,native-game-dexes/sun-moon-alola.csv
|
||||
moon-alola,Moon,Alola,1,false,,native-game-dexes/sun-moon-alola.csv
|
||||
ultra-sun-alola,Ultra Sun,Alola,1,false,,native-game-dexes/ultra-sun-ultra-moon-alola.csv
|
||||
ultra-moon-alola,Ultra Moon,Alola,1,false,,native-game-dexes/ultra-sun-ultra-moon-alola.csv
|
||||
lg-pikachu-kanto,LG: Pikachu,Kanto,1,false,,native-game-dexes/lets-go-pikachu-eevee.csv
|
||||
lg-eevee-kanto,LG: Eevee,Kanto,1,false,,native-game-dexes/lets-go-pikachu-eevee.csv
|
||||
sword-galar,Sword,Galar,1,false,,native-game-dexes/sword-shield.csv
|
||||
sword-isle-armor,Sword,Isle of Armor,2,true,sword-galar,native-game-dexes/sword-shield-isle-armor.csv
|
||||
sword-crown-tundra,Sword,Crown Tundra,3,true,sword-galar,native-game-dexes/sword-shield-crown-tundra.csv
|
||||
shield-galar,Shield,Galar,1,false,,native-game-dexes/sword-shield.csv
|
||||
shield-isle-armor,Shield,Isle of Armor,2,true,shield-galar,native-game-dexes/sword-shield-isle-armor.csv
|
||||
shield-crown-tundra,Shield,Crown Tundra,3,true,shield-galar,native-game-dexes/sword-shield-crown-tundra.csv
|
||||
brilliant-diamond-sinnoh,Brilliant Diamond,Sinnoh,1,false,,native-game-dexes/brilliant-diamond-shining-pearl.csv
|
||||
shining-pearl-sinnoh,Shining Pearl,Sinnoh,1,false,,native-game-dexes/brilliant-diamond-shining-pearl.csv
|
||||
legends-arceus-hisui,Legends: Arceus,Hisui,1,false,,native-game-dexes/legends-arceus.csv
|
||||
scarlet-paldea,Scarlet,Paldea,1,false,,native-game-dexes/scarlet-violet.csv
|
||||
scarlet-kitakami,Scarlet,Kitakami,2,true,scarlet-paldea,native-game-dexes/scarlet-violet-kitakami.csv
|
||||
scarlet-blueberry,Scarlet,Blueberry,3,true,scarlet-paldea,native-game-dexes/scarlet-violet-blueberry.csv
|
||||
violet-paldea,Violet,Paldea,1,false,,native-game-dexes/scarlet-violet.csv
|
||||
violet-kitakami,Violet,Kitakami,2,true,violet-paldea,native-game-dexes/scarlet-violet-kitakami.csv
|
||||
violet-blueberry,Violet,Blueberry,3,true,violet-paldea,native-game-dexes/scarlet-violet-blueberry.csv
|
||||
legends-z-a-kalos,Legends: Z-A,Kalos,1,false,,native-game-dexes/legends-z-a.csv
|
||||
legends-z-a-mega-dimension,Legends: Z-A,Mega Dimension,2,true,legends-z-a-kalos,native-game-dexes/legends-z-a-mega-dimension.csv
|
||||
|
@@ -1,46 +0,0 @@
|
||||
game,region,releaseYear
|
||||
Red,Kanto,1996
|
||||
Blue,Kanto,1996
|
||||
Yellow,Kanto,1998
|
||||
Gold,Johto,1999
|
||||
Silver,Johto,1999
|
||||
Crystal,Johto,2000
|
||||
Ruby,Hoenn,2002
|
||||
Sapphire,Hoenn,2002
|
||||
Colosseum,Orre,2003
|
||||
Emerald,Hoenn,2004
|
||||
FireRed,Kanto,2004
|
||||
LeafGreen,Kanto,2004
|
||||
XD: Gale of Darkness,Orre,2005
|
||||
Diamond,Sinnoh,2006
|
||||
Pearl,Sinnoh,2006
|
||||
Platinum,Sinnoh,2008
|
||||
Ranch,Ranch,2008
|
||||
Heart Gold,Johto,2009
|
||||
Soul Silver,Johto,2009
|
||||
Black,Unova,2010
|
||||
White,Unova,2010
|
||||
Black 2,Unova,2012
|
||||
White 2,Unova,2012
|
||||
Dream Radar,Unova,2012
|
||||
X,Kalos,2013
|
||||
Y,Kalos,2013
|
||||
Omega Ruby,Hoenn,2014
|
||||
Alpha Sapphire,Hoenn,2014
|
||||
Sun,Alola,2016
|
||||
Moon,Alola,2016
|
||||
Moon (demo),Alola,2016
|
||||
Go,Go,2016
|
||||
Ultra Sun,Alola,2017
|
||||
Ultra Moon,Alola,2017
|
||||
LG: Pikachu,Kanto,2018
|
||||
LG: Eevee,Kanto,2018
|
||||
Sword,Galar,2019
|
||||
Shield,Galar,2019
|
||||
Home,Home,2020
|
||||
Brilliant Diamond,Sinnoh,2021
|
||||
Shining Pearl,Sinnoh,2021
|
||||
Legends: Arceus,Hisui,2022
|
||||
Scarlet,Paldea,2022
|
||||
Violet,Paldea,2022
|
||||
Legends: Z-A,Kalos,2025
|
||||
|
@@ -1,302 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
0,Victini,,
|
||||
1,Snivy,,
|
||||
2,Servine,,
|
||||
3,Serperior,,
|
||||
4,Tepig,,
|
||||
5,Pignite,,
|
||||
6,Emboar,,
|
||||
7,Oshawott,,
|
||||
8,Dewott,,
|
||||
9,Samurott,,
|
||||
10,Patrat,,
|
||||
11,Watchog,,
|
||||
12,Purrloin,,
|
||||
13,Liepard,,
|
||||
14,Pidove,,
|
||||
15,Tranquill,,
|
||||
16,Unfezant,,
|
||||
17,Sewaddle,,
|
||||
18,Swadloon,,
|
||||
19,Leavanny,,
|
||||
20,Sunkern,,
|
||||
21,Sunflora,,
|
||||
22,Lillipup,,
|
||||
23,Herdier,,
|
||||
24,Stoutland,,
|
||||
25,Mareep,,
|
||||
26,Flaaffy,,
|
||||
27,Ampharos,,
|
||||
28,Psyduck,,
|
||||
29,Golduck,,
|
||||
30,Azurill,,
|
||||
31,Marill,,
|
||||
32,Azumarill,,
|
||||
33,Riolu,,
|
||||
34,Lucario,,
|
||||
35,Dunsparce,,
|
||||
36,Audino,,
|
||||
37,Pansage,,
|
||||
38,Simisage,,
|
||||
39,Pansear,,
|
||||
40,Simisear,,
|
||||
41,Panpour,,
|
||||
42,Simipour,,
|
||||
43,Venipede,,
|
||||
44,Whirlipede,,
|
||||
45,Scolipede,,
|
||||
46,Koffing,,
|
||||
47,Weezing,,
|
||||
48,Magnemite,,
|
||||
49,Magneton,,
|
||||
50,Magnezone,,
|
||||
51,Growlithe,,
|
||||
52,Arcanine,,
|
||||
53,Magby,,
|
||||
54,Magmar,,
|
||||
55,Magmortar,,
|
||||
56,Elekid,,
|
||||
57,Electabuzz,,
|
||||
58,Electivire,,
|
||||
59,Rattata,,
|
||||
60,Raticate,,
|
||||
61,Zubat,,
|
||||
62,Golbat,,
|
||||
63,Crobat,,
|
||||
64,Grimer,,
|
||||
65,Muk,,
|
||||
66,Woobat,,
|
||||
67,Swoobat,,
|
||||
68,Roggenrola,,
|
||||
69,Boldore,,
|
||||
70,Gigalith,,
|
||||
71,Onix,,
|
||||
72,Steelix,,
|
||||
73,Timburr,,
|
||||
74,Gurdurr,,
|
||||
75,Conkeldurr,,
|
||||
76,Drilbur,,
|
||||
77,Excadrill,,
|
||||
78,Skitty,,
|
||||
79,Delcatty,,
|
||||
80,Buneary,,
|
||||
81,Lopunny,,
|
||||
82,Cottonee,,
|
||||
83,Whimsicott,,
|
||||
84,Petilil,,
|
||||
85,Lilligant,,
|
||||
86,Munna,,
|
||||
87,Musharna,,
|
||||
88,Cleffa,,
|
||||
89,Clefairy,,
|
||||
90,Clefable,,
|
||||
91,Eevee,,
|
||||
92,Vaporeon,,
|
||||
93,Jolteon,,
|
||||
94,Flareon,,
|
||||
95,Espeon,,
|
||||
96,Umbreon,,
|
||||
97,Leafeon,,
|
||||
98,Glaceon,,
|
||||
99,Sandile,,
|
||||
100,Krokorok,,
|
||||
101,Krookodile,,
|
||||
102,Darumaka,,
|
||||
103,Darmanitan,,
|
||||
104,Basculin,,
|
||||
105,Trubbish,,
|
||||
106,Garbodor,,
|
||||
107,Minccino,,
|
||||
108,Cinccino,,
|
||||
109,Rufflet,,
|
||||
110,Braviary,,
|
||||
111,Vullaby,,
|
||||
112,Mandibuzz,,
|
||||
113,Sandshrew,,
|
||||
114,Sandslash,,
|
||||
115,Dwebble,,
|
||||
116,Crustle,,
|
||||
117,Scraggy,,
|
||||
118,Scrafty,,
|
||||
119,Maractus,,
|
||||
120,Sigilyph,,
|
||||
121,Trapinch,,
|
||||
122,Vibrava,,
|
||||
123,Flygon,,
|
||||
124,Yamask,,
|
||||
125,Cofagrigus,,
|
||||
126,Tirtouga,,
|
||||
127,Carracosta,,
|
||||
128,Archen,,
|
||||
129,Archeops,,
|
||||
130,Klink,,
|
||||
131,Klang,,
|
||||
132,Klinklang,,
|
||||
133,Budew,,
|
||||
134,Roselia,,
|
||||
135,Roserade,,
|
||||
136,Gothita,,
|
||||
137,Gothorita,,
|
||||
138,Gothitelle,,
|
||||
139,Solosis,,
|
||||
140,Duosion,,
|
||||
141,Reuniclus,,
|
||||
142,Combee,,
|
||||
143,Vespiquen,,
|
||||
144,Emolga,,
|
||||
145,Heracross,,
|
||||
146,Pinsir,,
|
||||
147,Blitzle,,
|
||||
148,Zebstrika,,
|
||||
149,Buizel,,
|
||||
150,Floatzel,,
|
||||
151,Zorua,,
|
||||
152,Zoroark,,
|
||||
153,Ducklett,,
|
||||
154,Swanna,,
|
||||
155,Karrablast,,
|
||||
156,Escavalier,,
|
||||
157,Shelmet,,
|
||||
158,Accelgor,,
|
||||
159,Deerling,,
|
||||
160,Sawsbuck,,
|
||||
161,Foongus,,
|
||||
162,Amoonguss,,
|
||||
163,Castform,,
|
||||
164,Nosepass,,
|
||||
165,Probopass,,
|
||||
166,Aron,,
|
||||
167,Lairon,,
|
||||
168,Aggron,,
|
||||
169,Baltoy,,
|
||||
170,Claydol,,
|
||||
171,Larvesta,,
|
||||
172,Volcarona,,
|
||||
173,Joltik,,
|
||||
174,Galvantula,,
|
||||
175,Ferroseed,,
|
||||
176,Ferrothorn,,
|
||||
177,Tynamo,,
|
||||
178,Eelektrik,,
|
||||
179,Eelektross,,
|
||||
180,Frillish,,
|
||||
181,Jellicent,,
|
||||
182,Alomomola,,
|
||||
183,Axew,,
|
||||
184,Fraxure,,
|
||||
185,Haxorus,,
|
||||
186,Zangoose,,
|
||||
187,Seviper,,
|
||||
188,Elgyem,,
|
||||
189,Beheeyem,,
|
||||
190,Litwick,,
|
||||
191,Lampent,,
|
||||
192,Chandelure,,
|
||||
193,Heatmor,,
|
||||
194,Durant,,
|
||||
195,Cubchoo,,
|
||||
196,Beartic,,
|
||||
197,Cryogonal,,
|
||||
198,Tornadus,,
|
||||
199,Thundurus,,
|
||||
200,Landorus,,
|
||||
201,Skorupi,,
|
||||
202,Drapion,,
|
||||
203,Skarmory,,
|
||||
204,Numel,,
|
||||
205,Camerupt,,
|
||||
206,Spoink,,
|
||||
207,Grumpig,,
|
||||
208,Drifloon,,
|
||||
209,Drifblim,,
|
||||
210,Shuppet,,
|
||||
211,Banette,,
|
||||
212,Wingull,,
|
||||
213,Pelipper,,
|
||||
214,Lunatone,,
|
||||
215,Solrock,,
|
||||
216,Absol,,
|
||||
217,Tangela,,
|
||||
218,Tangrowth,,
|
||||
219,Mienfoo,,
|
||||
220,Mienshao,,
|
||||
221,Gligar,,
|
||||
222,Gliscor,,
|
||||
223,Pawniard,,
|
||||
224,Bisharp,,
|
||||
225,Cobalion,,
|
||||
226,Terrakion,,
|
||||
227,Virizion,,
|
||||
228,Tympole,,
|
||||
229,Palpitoad,,
|
||||
230,Seismitoad,,
|
||||
231,Stunfisk,,
|
||||
232,Shuckle,,
|
||||
233,Mantyke,,
|
||||
234,Mantine,,
|
||||
235,Remoraid,,
|
||||
236,Octillery,,
|
||||
237,Corsola,,
|
||||
238,Staryu,,
|
||||
239,Starmie,,
|
||||
240,Wailmer,,
|
||||
241,Wailord,,
|
||||
242,Lapras,,
|
||||
243,Spheal,,
|
||||
244,Sealeo,,
|
||||
245,Walrein,,
|
||||
246,Swablu,,
|
||||
247,Altaria,,
|
||||
248,Vulpix,,
|
||||
249,Ninetales,,
|
||||
250,Bronzor,,
|
||||
251,Bronzong,,
|
||||
252,Sneasel,,
|
||||
253,Weavile,,
|
||||
254,Delibird,,
|
||||
255,Vanillite,,
|
||||
256,Vanillish,,
|
||||
257,Vanilluxe,,
|
||||
258,Swinub,,
|
||||
259,Piloswine,,
|
||||
260,Mamoswine,,
|
||||
261,Ditto,,
|
||||
262,Beldum,,
|
||||
263,Metang,,
|
||||
264,Metagross,,
|
||||
265,Seel,,
|
||||
266,Dewgong,,
|
||||
267,Throh,,
|
||||
268,Sawk,,
|
||||
269,Bouffalant,,
|
||||
270,Druddigon,,
|
||||
271,Golett,,
|
||||
272,Golurk,,
|
||||
273,Deino,,
|
||||
274,Zweilous,,
|
||||
275,Hydreigon,,
|
||||
276,Slakoth,,
|
||||
277,Vigoroth,,
|
||||
278,Slaking,,
|
||||
279,Corphish,,
|
||||
280,Crawdaunt,,
|
||||
281,Igglybuff,,
|
||||
282,Jigglypuff,,
|
||||
283,Wigglytuff,,
|
||||
284,Lickitung,,
|
||||
285,Lickilicky,,
|
||||
286,Yanma,,
|
||||
287,Yanmega,,
|
||||
288,Tropius,,
|
||||
289,Carnivine,,
|
||||
290,Croagunk,,
|
||||
291,Toxicroak,,
|
||||
292,Larvitar,,
|
||||
293,Pupitar,,
|
||||
294,Tyranitar,,
|
||||
295,Reshiram,,
|
||||
296,Zekrom,,
|
||||
297,Kyurem,,
|
||||
298,Keldeo,,
|
||||
299,Meloetta,,
|
||||
300,Genesect,,
|
||||
|
@@ -1,157 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
0,Victini,,
|
||||
1,Snivy,,
|
||||
2,Servine,,
|
||||
3,Serperior,,
|
||||
4,Tepig,,
|
||||
5,Pignite,,
|
||||
6,Emboar,,
|
||||
7,Oshawott,,
|
||||
8,Dewott,,
|
||||
9,Samurott,,
|
||||
10,Patrat,,
|
||||
11,Watchog,,
|
||||
12,Lillipup,,
|
||||
13,Herdier,,
|
||||
14,Stoutland,,
|
||||
15,Purrloin,,
|
||||
16,Liepard,,
|
||||
17,Pansage,,
|
||||
18,Simisage,,
|
||||
19,Pansear,,
|
||||
20,Simisear,,
|
||||
21,Panpour,,
|
||||
22,Simipour,,
|
||||
23,Munna,,
|
||||
24,Musharna,,
|
||||
25,Pidove,,
|
||||
26,Tranquill,,
|
||||
27,Unfezant,,
|
||||
28,Blitzle,,
|
||||
29,Zebstrika,,
|
||||
30,Roggenrola,,
|
||||
31,Boldore,,
|
||||
32,Gigalith,,
|
||||
33,Woobat,,
|
||||
34,Swoobat,,
|
||||
35,Drilbur,,
|
||||
36,Excadrill,,
|
||||
37,Audino,,
|
||||
38,Timburr,,
|
||||
39,Gurdurr,,
|
||||
40,Conkeldurr,,
|
||||
41,Tympole,,
|
||||
42,Palpitoad,,
|
||||
43,Seismitoad,,
|
||||
44,Throh,,
|
||||
45,Sawk,,
|
||||
46,Sewaddle,,
|
||||
47,Swadloon,,
|
||||
48,Leavanny,,
|
||||
49,Venipede,,
|
||||
50,Whirlipede,,
|
||||
51,Scolipede,,
|
||||
52,Cottonee,,
|
||||
53,Whimsicott,,
|
||||
54,Petilil,,
|
||||
55,Lilligant,,
|
||||
56,Basculin,,
|
||||
57,Sandile,,
|
||||
58,Krokorok,,
|
||||
59,Krookodile,,
|
||||
60,Darumaka,,
|
||||
61,Darmanitan,,
|
||||
62,Maractus,,
|
||||
63,Dwebble,,
|
||||
64,Crustle,,
|
||||
65,Scraggy,,
|
||||
66,Scrafty,,
|
||||
67,Sigilyph,,
|
||||
68,Yamask,,
|
||||
69,Cofagrigus,,
|
||||
70,Tirtouga,,
|
||||
71,Carracosta,,
|
||||
72,Archen,,
|
||||
73,Archeops,,
|
||||
74,Trubbish,,
|
||||
75,Garbodor,,
|
||||
76,Zorua,,
|
||||
77,Zoroark,,
|
||||
78,Minccino,,
|
||||
79,Cinccino,,
|
||||
80,Gothita,,
|
||||
81,Gothorita,,
|
||||
82,Gothitelle,,
|
||||
83,Solosis,,
|
||||
84,Duosion,,
|
||||
85,Reuniclus,,
|
||||
86,Ducklett,,
|
||||
87,Swanna,,
|
||||
88,Vanillite,,
|
||||
89,Vanillish,,
|
||||
90,Vanilluxe,,
|
||||
91,Deerling,,
|
||||
92,Sawsbuck,,
|
||||
93,Emolga,,
|
||||
94,Karrablast,,
|
||||
95,Escavalier,,
|
||||
96,Foongus,,
|
||||
97,Amoonguss,,
|
||||
98,Frillish,,
|
||||
99,Jellicent,,
|
||||
100,Alomomola,,
|
||||
101,Joltik,,
|
||||
102,Galvantula,,
|
||||
103,Ferroseed,,
|
||||
104,Ferrothorn,,
|
||||
105,Klink,,
|
||||
106,Klang,,
|
||||
107,Klinklang,,
|
||||
108,Tynamo,,
|
||||
109,Eelektrik,,
|
||||
110,Eelektross,,
|
||||
111,Elgyem,,
|
||||
112,Beheeyem,,
|
||||
113,Litwick,,
|
||||
114,Lampent,,
|
||||
115,Chandelure,,
|
||||
116,Axew,,
|
||||
117,Fraxure,,
|
||||
118,Haxorus,,
|
||||
119,Cubchoo,,
|
||||
120,Beartic,,
|
||||
121,Cryogonal,,
|
||||
122,Shelmet,,
|
||||
123,Accelgor,,
|
||||
124,Stunfisk,,
|
||||
125,Mienfoo,,
|
||||
126,Mienshao,,
|
||||
127,Druddigon,,
|
||||
128,Golett,,
|
||||
129,Golurk,,
|
||||
130,Pawniard,,
|
||||
131,Bisharp,,
|
||||
132,Bouffalant,,
|
||||
133,Rufflet,,
|
||||
134,Braviary,,
|
||||
135,Vullaby,,
|
||||
136,Mandibuzz,,
|
||||
137,Heatmor,,
|
||||
138,Durant,,
|
||||
139,Deino,,
|
||||
140,Zweilous,,
|
||||
141,Hydreigon,,
|
||||
142,Larvesta,,
|
||||
143,Volcarona,,
|
||||
144,Cobalion,,
|
||||
145,Terrakion,,
|
||||
146,Virizion,,
|
||||
147,Tornadus,,
|
||||
148,Thundurus,,
|
||||
149,Reshiram,,
|
||||
150,Zekrom,,
|
||||
151,Landorus,,
|
||||
152,Kyurem,,
|
||||
153,Keldeo,,
|
||||
154,Meloetta,,
|
||||
155,Genesect,,
|
||||
|
@@ -1,152 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Turtwig,,
|
||||
2,Grotle,,
|
||||
3,Torterra,,
|
||||
4,Chimchar,,
|
||||
5,Monferno,,
|
||||
6,Infernape,,
|
||||
7,Piplup,,
|
||||
8,Prinplup,,
|
||||
9,Empoleon,,
|
||||
10,Starly,,
|
||||
11,Staravia,,
|
||||
12,Staraptor,,
|
||||
13,Bidoof,,
|
||||
14,Bibarel,,
|
||||
15,Kricketot,,
|
||||
16,Kricketune,,
|
||||
17,Shinx,,
|
||||
18,Luxio,,
|
||||
19,Luxray,,
|
||||
20,Abra,,
|
||||
21,Kadabra,,
|
||||
22,Alakazam,,
|
||||
23,Magikarp,,
|
||||
24,Gyarados,,
|
||||
25,Budew,,
|
||||
26,Roselia,,
|
||||
27,Roserade,,
|
||||
28,Zubat,,
|
||||
29,Golbat,,
|
||||
30,Crobat,,
|
||||
31,Geodude,,
|
||||
32,Graveler,,
|
||||
33,Golem,,
|
||||
34,Onix,,
|
||||
35,Steelix,,
|
||||
36,Cranidos,,
|
||||
37,Rampardos,,
|
||||
38,Shieldon,,
|
||||
39,Bastiodon,,
|
||||
40,Machop,,
|
||||
41,Machoke,,
|
||||
42,Machamp,,
|
||||
43,Psyduck,,
|
||||
44,Golduck,,
|
||||
45,Burmy,,
|
||||
46,Wormadam,,
|
||||
47,Mothim,,
|
||||
48,Wurmple,,
|
||||
49,Silcoon,,
|
||||
50,Beautifly,,
|
||||
51,Cascoon,,
|
||||
52,Dustox,,
|
||||
53,Combee,,
|
||||
54,Vespiquen,,
|
||||
55,Pachirisu,,
|
||||
56,Buizel,,
|
||||
57,Floatzel,,
|
||||
58,Cherubi,,
|
||||
59,Cherrim,,
|
||||
60,Shellos,,
|
||||
61,Gastrodon,,
|
||||
62,Heracross,,
|
||||
63,Aipom,,
|
||||
64,Ambipom,,
|
||||
65,Drifloon,,
|
||||
66,Drifblim,,
|
||||
67,Buneary,,
|
||||
68,Lopunny,,
|
||||
69,Gastly,,
|
||||
70,Haunter,,
|
||||
71,Gengar,,
|
||||
72,Misdreavus,,
|
||||
73,Mismagius,,
|
||||
74,Murkrow,,
|
||||
75,Honchkrow,,
|
||||
76,Glameow,,
|
||||
77,Purugly,,
|
||||
78,Goldeen,,
|
||||
79,Seaking,,
|
||||
80,Barboach,,
|
||||
81,Whiscash,,
|
||||
82,Chingling,,
|
||||
83,Chimecho,,
|
||||
84,Stunky,,
|
||||
85,Skuntank,,
|
||||
86,Meditite,,
|
||||
87,Medicham,,
|
||||
88,Bronzor,,
|
||||
89,Bronzong,,
|
||||
90,Ponyta,,
|
||||
91,Rapidash,,
|
||||
92,Bonsly,,
|
||||
93,Sudowoodo,,
|
||||
94,Mime Jr.,,
|
||||
95,Mr. Mime,,
|
||||
96,Happiny,,
|
||||
97,Chansey,,
|
||||
98,Blissey,,
|
||||
99,Cleffa,,
|
||||
100,Clefairy,,
|
||||
101,Clefable,,
|
||||
102,Chatot,,
|
||||
103,Pichu,,
|
||||
104,Pikachu,,
|
||||
105,Raichu,,
|
||||
106,Hoothoot,,
|
||||
107,Noctowl,,
|
||||
108,Spiritomb,,
|
||||
109,Gible,,
|
||||
110,Gabite,,
|
||||
111,Garchomp,,
|
||||
112,Munchlax,,
|
||||
113,Snorlax,,
|
||||
114,Unown,,
|
||||
115,Riolu,,
|
||||
116,Lucario,,
|
||||
117,Wooper,,
|
||||
118,Quagsire,,
|
||||
119,Wingull,,
|
||||
120,Pelipper,,
|
||||
121,Girafarig,,
|
||||
122,Hippopotas,,
|
||||
123,Hippowdon,,
|
||||
124,Azurill,,
|
||||
125,Marill,,
|
||||
126,Azumarill,,
|
||||
127,Skorupi,,
|
||||
128,Drapion,,
|
||||
129,Croagunk,,
|
||||
130,Toxicroak,,
|
||||
131,Carnivine,,
|
||||
132,Remoraid,,
|
||||
133,Octillery,,
|
||||
134,Finneon,,
|
||||
135,Lumineon,,
|
||||
136,Tentacool,,
|
||||
137,Tentacruel,,
|
||||
138,Feebas,,
|
||||
139,Milotic,,
|
||||
140,Mantyke,,
|
||||
141,Mantine,,
|
||||
142,Snover,,
|
||||
143,Abomasnow,,
|
||||
144,Sneasel,,
|
||||
145,Weavile,,
|
||||
146,Uxie,,
|
||||
147,Mesprit,,
|
||||
148,Azelf,,
|
||||
149,Dialga,,
|
||||
150,Palkia,,
|
||||
151,Manaphy,,
|
||||
|
@@ -1,152 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Turtwig,,
|
||||
2,Grotle,,
|
||||
3,Torterra,,
|
||||
4,Chimchar,,
|
||||
5,Monferno,,
|
||||
6,Infernape,,
|
||||
7,Piplup,,
|
||||
8,Prinplup,,
|
||||
9,Empoleon,,
|
||||
10,Starly,,
|
||||
11,Staravia,,
|
||||
12,Staraptor,,
|
||||
13,Bidoof,,
|
||||
14,Bibarel,,
|
||||
15,Kricketot,,
|
||||
16,Kricketune,,
|
||||
17,Shinx,,
|
||||
18,Luxio,,
|
||||
19,Luxray,,
|
||||
20,Abra,,
|
||||
21,Kadabra,,
|
||||
22,Alakazam,,
|
||||
23,Magikarp,,
|
||||
24,Gyarados,,
|
||||
25,Budew,,
|
||||
26,Roselia,,
|
||||
27,Roserade,,
|
||||
28,Zubat,,
|
||||
29,Golbat,,
|
||||
30,Crobat,,
|
||||
31,Geodude,,
|
||||
32,Graveler,,
|
||||
33,Golem,,
|
||||
34,Onix,,
|
||||
35,Steelix,,
|
||||
36,Cranidos,,
|
||||
37,Rampardos,,
|
||||
38,Shieldon,,
|
||||
39,Bastiodon,,
|
||||
40,Machop,,
|
||||
41,Machoke,,
|
||||
42,Machamp,,
|
||||
43,Psyduck,,
|
||||
44,Golduck,,
|
||||
45,Burmy,,
|
||||
46,Wormadam,,
|
||||
47,Mothim,,
|
||||
48,Wurmple,,
|
||||
49,Silcoon,,
|
||||
50,Beautifly,,
|
||||
51,Cascoon,,
|
||||
52,Dustox,,
|
||||
53,Combee,,
|
||||
54,Vespiquen,,
|
||||
55,Pachirisu,,
|
||||
56,Buizel,,
|
||||
57,Floatzel,,
|
||||
58,Cherubi,,
|
||||
59,Cherrim,,
|
||||
60,Shellos,,
|
||||
61,Gastrodon,,
|
||||
62,Heracross,,
|
||||
63,Aipom,,
|
||||
64,Ambipom,,
|
||||
65,Drifloon,,
|
||||
66,Drifblim,,
|
||||
67,Buneary,,
|
||||
68,Lopunny,,
|
||||
69,Gastly,,
|
||||
70,Haunter,,
|
||||
71,Gengar,,
|
||||
72,Misdreavus,,
|
||||
73,Mismagius,,
|
||||
74,Murkrow,,
|
||||
75,Honchkrow,,
|
||||
76,Glameow,,
|
||||
77,Purugly,,
|
||||
78,Goldeen,,
|
||||
79,Seaking,,
|
||||
80,Barboach,,
|
||||
81,Whiscash,,
|
||||
82,Chingling,,
|
||||
83,Chimecho,,
|
||||
84,Stunky,,
|
||||
85,Skuntank,,
|
||||
86,Meditite,,
|
||||
87,Medicham,,
|
||||
88,Bronzor,,
|
||||
89,Bronzong,,
|
||||
90,Ponyta,,
|
||||
91,Rapidash,,
|
||||
92,Bonsly,,
|
||||
93,Sudowoodo,,
|
||||
94,Mime Jr.,,
|
||||
95,Mr. Mime,,
|
||||
96,Happiny,,
|
||||
97,Chansey,,
|
||||
98,Blissey,,
|
||||
99,Cleffa,,
|
||||
100,Clefairy,,
|
||||
101,Clefable,,
|
||||
102,Chatot,,
|
||||
103,Pichu,,
|
||||
104,Pikachu,,
|
||||
105,Raichu,,
|
||||
106,Hoothoot,,
|
||||
107,Noctowl,,
|
||||
108,Spiritomb,,
|
||||
109,Gible,,
|
||||
110,Gabite,,
|
||||
111,Garchomp,,
|
||||
112,Munchlax,,
|
||||
113,Snorlax,,
|
||||
114,Unown,,
|
||||
115,Riolu,,
|
||||
116,Lucario,,
|
||||
117,Wooper,,
|
||||
118,Quagsire,,
|
||||
119,Wingull,,
|
||||
120,Pelipper,,
|
||||
121,Girafarig,,
|
||||
122,Hippopotas,,
|
||||
123,Hippowdon,,
|
||||
124,Azurill,,
|
||||
125,Marill,,
|
||||
126,Azumarill,,
|
||||
127,Skorupi,,
|
||||
128,Drapion,,
|
||||
129,Croagunk,,
|
||||
130,Toxicroak,,
|
||||
131,Carnivine,,
|
||||
132,Remoraid,,
|
||||
133,Octillery,,
|
||||
134,Finneon,,
|
||||
135,Lumineon,,
|
||||
136,Tentacool,,
|
||||
137,Tentacruel,,
|
||||
138,Feebas,,
|
||||
139,Milotic,,
|
||||
140,Mantyke,,
|
||||
141,Mantine,,
|
||||
142,Snover,,
|
||||
143,Abomasnow,,
|
||||
144,Sneasel,,
|
||||
145,Weavile,,
|
||||
146,Uxie,,
|
||||
147,Mesprit,,
|
||||
148,Azelf,,
|
||||
149,Dialga,,
|
||||
150,Palkia,,
|
||||
151,Manaphy,,
|
||||
|
@@ -1,152 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Bulbasaur,,
|
||||
2,Ivysaur,,
|
||||
3,Venusaur,,
|
||||
4,Charmander,,
|
||||
5,Charmeleon,,
|
||||
6,Charizard,,
|
||||
7,Squirtle,,
|
||||
8,Wartortle,,
|
||||
9,Blastoise,,
|
||||
10,Caterpie,,
|
||||
11,Metapod,,
|
||||
12,Butterfree,,
|
||||
13,Weedle,,
|
||||
14,Kakuna,,
|
||||
15,Beedrill,,
|
||||
16,Pidgey,,
|
||||
17,Pidgeotto,,
|
||||
18,Pidgeot,,
|
||||
19,Rattata,,
|
||||
20,Raticate,,
|
||||
21,Spearow,,
|
||||
22,Fearow,,
|
||||
23,Ekans,,
|
||||
24,Arbok,,
|
||||
25,Pikachu,,
|
||||
26,Raichu,,
|
||||
27,Sandshrew,,
|
||||
28,Sandslash,,
|
||||
29,Nidoran♀,,
|
||||
30,Nidorina,,
|
||||
31,Nidoqueen,,
|
||||
32,Nidoran♂,,
|
||||
33,Nidorino,,
|
||||
34,Nidoking,,
|
||||
35,Clefairy,,
|
||||
36,Clefable,,
|
||||
37,Vulpix,,
|
||||
38,Ninetales,,
|
||||
39,Jigglypuff,,
|
||||
40,Wigglytuff,,
|
||||
41,Zubat,,
|
||||
42,Golbat,,
|
||||
43,Oddish,,
|
||||
44,Gloom,,
|
||||
45,Vileplume,,
|
||||
46,Paras,,
|
||||
47,Parasect,,
|
||||
48,Venonat,,
|
||||
49,Venomoth,,
|
||||
50,Diglett,,
|
||||
51,Dugtrio,,
|
||||
52,Meowth,,
|
||||
53,Persian,,
|
||||
54,Psyduck,,
|
||||
55,Golduck,,
|
||||
56,Mankey,,
|
||||
57,Primeape,,
|
||||
58,Growlithe,,
|
||||
59,Arcanine,,
|
||||
60,Poliwag,,
|
||||
61,Poliwhirl,,
|
||||
62,Poliwrath,,
|
||||
63,Abra,,
|
||||
64,Kadabra,,
|
||||
65,Alakazam,,
|
||||
66,Machop,,
|
||||
67,Machoke,,
|
||||
68,Machamp,,
|
||||
69,Bellsprout,,
|
||||
70,Weepinbell,,
|
||||
71,Victreebel,,
|
||||
72,Tentacool,,
|
||||
73,Tentacruel,,
|
||||
74,Geodude,,
|
||||
75,Graveler,,
|
||||
76,Golem,,
|
||||
77,Ponyta,,
|
||||
78,Rapidash,,
|
||||
79,Slowpoke,,
|
||||
80,Slowbro,,
|
||||
81,Magnemite,,
|
||||
82,Magneton,,
|
||||
83,Farfetch’d,,
|
||||
84,Doduo,,
|
||||
85,Dodrio,,
|
||||
86,Seel,,
|
||||
87,Dewgong,,
|
||||
88,Grimer,,
|
||||
89,Muk,,
|
||||
90,Shellder,,
|
||||
91,Cloyster,,
|
||||
92,Gastly,,
|
||||
93,Haunter,,
|
||||
94,Gengar,,
|
||||
95,Onix,,
|
||||
96,Drowzee,,
|
||||
97,Hypno,,
|
||||
98,Krabby,,
|
||||
99,Kingler,,
|
||||
100,Voltorb,,
|
||||
101,Electrode,,
|
||||
102,Exeggcute,,
|
||||
103,Exeggutor,,
|
||||
104,Cubone,,
|
||||
105,Marowak,,
|
||||
106,Hitmonlee,,
|
||||
107,Hitmonchan,,
|
||||
108,Lickitung,,
|
||||
109,Koffing,,
|
||||
110,Weezing,,
|
||||
111,Rhyhorn,,
|
||||
112,Rhydon,,
|
||||
113,Chansey,,
|
||||
114,Tangela,,
|
||||
115,Kangaskhan,,
|
||||
116,Horsea,,
|
||||
117,Seadra,,
|
||||
118,Goldeen,,
|
||||
119,Seaking,,
|
||||
120,Staryu,,
|
||||
121,Starmie,,
|
||||
122,Mr. Mime,,
|
||||
123,Scyther,,
|
||||
124,Jynx,,
|
||||
125,Electabuzz,,
|
||||
126,Magmar,,
|
||||
127,Pinsir,,
|
||||
128,Tauros,,
|
||||
129,Magikarp,,
|
||||
130,Gyarados,,
|
||||
131,Lapras,,
|
||||
132,Ditto,,
|
||||
133,Eevee,,
|
||||
134,Vaporeon,,
|
||||
135,Jolteon,,
|
||||
136,Flareon,,
|
||||
137,Porygon,,
|
||||
138,Omanyte,,
|
||||
139,Omastar,,
|
||||
140,Kabuto,,
|
||||
141,Kabutops,,
|
||||
142,Aerodactyl,,
|
||||
143,Snorlax,,
|
||||
144,Articuno,,
|
||||
145,Zapdos,,
|
||||
146,Moltres,,
|
||||
147,Dratini,,
|
||||
148,Dragonair,,
|
||||
149,Dragonite,,
|
||||
150,Mewtwo,,
|
||||
151,Mew,,
|
||||
|
@@ -1,252 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Chikorita,,
|
||||
2,Bayleef,,
|
||||
3,Meganium,,
|
||||
4,Cyndaquil,,
|
||||
5,Quilava,,
|
||||
6,Typhlosion,,
|
||||
7,Totodile,,
|
||||
8,Croconaw,,
|
||||
9,Feraligatr,,
|
||||
10,Pidgey,,
|
||||
11,Pidgeotto,,
|
||||
12,Pidgeot,,
|
||||
13,Spearow,,
|
||||
14,Fearow,,
|
||||
15,Hoothoot,,
|
||||
16,Noctowl,,
|
||||
17,Rattata,,
|
||||
18,Raticate,,
|
||||
19,Sentret,,
|
||||
20,Furret,,
|
||||
21,Pichu,,
|
||||
22,Pikachu,,
|
||||
23,Raichu,,
|
||||
24,Caterpie,,
|
||||
25,Metapod,,
|
||||
26,Butterfree,,
|
||||
27,Weedle,,
|
||||
28,Kakuna,,
|
||||
29,Beedrill,,
|
||||
30,Ledyba,,
|
||||
31,Ledian,,
|
||||
32,Spinarak,,
|
||||
33,Ariados,,
|
||||
34,Geodude,,
|
||||
35,Graveler,,
|
||||
36,Golem,,
|
||||
37,Zubat,,
|
||||
38,Golbat,,
|
||||
39,Crobat,,
|
||||
40,Cleffa,,
|
||||
41,Clefairy,,
|
||||
42,Clefable,,
|
||||
43,Igglybuff,,
|
||||
44,Jigglypuff,,
|
||||
45,Wigglytuff,,
|
||||
46,Togepi,,
|
||||
47,Togetic,,
|
||||
48,Sandshrew,,
|
||||
49,Sandslash,,
|
||||
50,Ekans,,
|
||||
51,Arbok,,
|
||||
52,Dunsparce,,
|
||||
53,Mareep,,
|
||||
54,Flaaffy,,
|
||||
55,Ampharos,,
|
||||
56,Wooper,,
|
||||
57,Quagsire,,
|
||||
58,Gastly,,
|
||||
59,Haunter,,
|
||||
60,Gengar,,
|
||||
61,Unown,,
|
||||
62,Onix,,
|
||||
63,Steelix,,
|
||||
64,Bellsprout,,
|
||||
65,Weepinbell,,
|
||||
66,Victreebel,,
|
||||
67,Hoppip,,
|
||||
68,Skiploom,,
|
||||
69,Jumpluff,,
|
||||
70,Paras,,
|
||||
71,Parasect,,
|
||||
72,Poliwag,,
|
||||
73,Poliwhirl,,
|
||||
74,Poliwrath,,
|
||||
75,Politoed,,
|
||||
76,Magikarp,,
|
||||
77,Gyarados,,
|
||||
78,Goldeen,,
|
||||
79,Seaking,,
|
||||
80,Slowpoke,,
|
||||
81,Slowbro,,
|
||||
82,Slowking,,
|
||||
83,Oddish,,
|
||||
84,Gloom,,
|
||||
85,Vileplume,,
|
||||
86,Bellossom,,
|
||||
87,Drowzee,,
|
||||
88,Hypno,,
|
||||
89,Abra,,
|
||||
90,Kadabra,,
|
||||
91,Alakazam,,
|
||||
92,Ditto,,
|
||||
93,Pineco,,
|
||||
94,Forretress,,
|
||||
95,Nidoran♀,,
|
||||
96,Nidorina,,
|
||||
97,Nidoqueen,,
|
||||
98,Nidoran♂,,
|
||||
99,Nidorino,,
|
||||
100,Nidoking,,
|
||||
101,Yanma,,
|
||||
102,Sunkern,,
|
||||
103,Sunflora,,
|
||||
104,Exeggcute,,
|
||||
105,Exeggutor,,
|
||||
106,Sudowoodo,,
|
||||
107,Wobbuffet,,
|
||||
108,Venonat,,
|
||||
109,Venomoth,,
|
||||
110,Scyther,,
|
||||
111,Scizor,,
|
||||
112,Pinsir,,
|
||||
113,Heracross,,
|
||||
114,Koffing,,
|
||||
115,Weezing,,
|
||||
116,Grimer,,
|
||||
117,Muk,,
|
||||
118,Magnemite,,
|
||||
119,Magneton,,
|
||||
120,Voltorb,,
|
||||
121,Electrode,,
|
||||
122,Aipom,,
|
||||
123,Snubbull,,
|
||||
124,Granbull,,
|
||||
125,Vulpix,,
|
||||
126,Ninetales,,
|
||||
127,Growlithe,,
|
||||
128,Arcanine,,
|
||||
129,Stantler,,
|
||||
130,Marill,,
|
||||
131,Azumarill,,
|
||||
132,Diglett,,
|
||||
133,Dugtrio,,
|
||||
134,Mankey,,
|
||||
135,Primeape,,
|
||||
136,Meowth,,
|
||||
137,Persian,,
|
||||
138,Psyduck,,
|
||||
139,Golduck,,
|
||||
140,Machop,,
|
||||
141,Machoke,,
|
||||
142,Machamp,,
|
||||
143,Tyrogue,,
|
||||
144,Hitmonlee,,
|
||||
145,Hitmonchan,,
|
||||
146,Hitmontop,,
|
||||
147,Girafarig,,
|
||||
148,Tauros,,
|
||||
149,Miltank,,
|
||||
150,Magby,,
|
||||
151,Magmar,,
|
||||
152,Smoochum,,
|
||||
153,Jynx,,
|
||||
154,Elekid,,
|
||||
155,Electabuzz,,
|
||||
156,Mr. Mime,,
|
||||
157,Smeargle,,
|
||||
158,Farfetch’d,,
|
||||
159,Natu,,
|
||||
160,Xatu,,
|
||||
161,Qwilfish,,
|
||||
162,Tentacool,,
|
||||
163,Tentacruel,,
|
||||
164,Krabby,,
|
||||
165,Kingler,,
|
||||
166,Shuckle,,
|
||||
167,Staryu,,
|
||||
168,Starmie,,
|
||||
169,Shellder,,
|
||||
170,Cloyster,,
|
||||
171,Corsola,,
|
||||
172,Remoraid,,
|
||||
173,Octillery,,
|
||||
174,Chinchou,,
|
||||
175,Lanturn,,
|
||||
176,Seel,,
|
||||
177,Dewgong,,
|
||||
178,Lickitung,,
|
||||
179,Tangela,,
|
||||
180,Eevee,,
|
||||
181,Vaporeon,,
|
||||
182,Jolteon,,
|
||||
183,Flareon,,
|
||||
184,Espeon,,
|
||||
185,Umbreon,,
|
||||
186,Horsea,,
|
||||
187,Seadra,,
|
||||
188,Kingdra,,
|
||||
189,Gligar,,
|
||||
190,Delibird,,
|
||||
191,Swinub,,
|
||||
192,Piloswine,,
|
||||
193,Teddiursa,,
|
||||
194,Ursaring,,
|
||||
195,Phanpy,,
|
||||
196,Donphan,,
|
||||
197,Mantine,,
|
||||
198,Skarmory,,
|
||||
199,Doduo,,
|
||||
200,Dodrio,,
|
||||
201,Ponyta,,
|
||||
202,Rapidash,,
|
||||
203,Cubone,,
|
||||
204,Marowak,,
|
||||
205,Kangaskhan,,
|
||||
206,Rhyhorn,,
|
||||
207,Rhydon,,
|
||||
208,Murkrow,,
|
||||
209,Houndour,,
|
||||
210,Houndoom,,
|
||||
211,Slugma,,
|
||||
212,Magcargo,,
|
||||
213,Sneasel,,
|
||||
214,Misdreavus,,
|
||||
215,Porygon,,
|
||||
216,Porygon2,,
|
||||
217,Chansey,,
|
||||
218,Blissey,,
|
||||
219,Lapras,,
|
||||
220,Omanyte,,
|
||||
221,Omastar,,
|
||||
222,Kabuto,,
|
||||
223,Kabutops,,
|
||||
224,Aerodactyl,,
|
||||
225,Snorlax,,
|
||||
226,Bulbasaur,,
|
||||
227,Ivysaur,,
|
||||
228,Venusaur,,
|
||||
229,Charmander,,
|
||||
230,Charmeleon,,
|
||||
231,Charizard,,
|
||||
232,Squirtle,,
|
||||
233,Wartortle,,
|
||||
234,Blastoise,,
|
||||
235,Articuno,,
|
||||
236,Zapdos,,
|
||||
237,Moltres,,
|
||||
238,Raikou,,
|
||||
239,Entei,,
|
||||
240,Suicune,,
|
||||
241,Dratini,,
|
||||
242,Dragonair,,
|
||||
243,Dragonite,,
|
||||
244,Larvitar,,
|
||||
245,Pupitar,,
|
||||
246,Tyranitar,,
|
||||
247,Lugia,,
|
||||
248,Ho-Oh,,
|
||||
249,Mewtwo,,
|
||||
250,Mew,,
|
||||
251,Celebi,,
|
||||
|
@@ -1,257 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Chikorita,,
|
||||
2,Bayleef,,
|
||||
3,Meganium,,
|
||||
4,Cyndaquil,,
|
||||
5,Quilava,,
|
||||
6,Typhlosion,,
|
||||
7,Totodile,,
|
||||
8,Croconaw,,
|
||||
9,Feraligatr,,
|
||||
10,Pidgey,,
|
||||
11,Pidgeotto,,
|
||||
12,Pidgeot,,
|
||||
13,Spearow,,
|
||||
14,Fearow,,
|
||||
15,Hoothoot,,
|
||||
16,Noctowl,,
|
||||
17,Rattata,,
|
||||
18,Raticate,,
|
||||
19,Sentret,,
|
||||
20,Furret,,
|
||||
21,Pichu,,
|
||||
22,Pikachu,,
|
||||
23,Raichu,,
|
||||
24,Caterpie,,
|
||||
25,Metapod,,
|
||||
26,Butterfree,,
|
||||
27,Weedle,,
|
||||
28,Kakuna,,
|
||||
29,Beedrill,,
|
||||
30,Ledyba,,
|
||||
31,Ledian,,
|
||||
32,Spinarak,,
|
||||
33,Ariados,,
|
||||
34,Geodude,,
|
||||
35,Graveler,,
|
||||
36,Golem,,
|
||||
37,Zubat,,
|
||||
38,Golbat,,
|
||||
39,Crobat,,
|
||||
40,Cleffa,,
|
||||
41,Clefairy,,
|
||||
42,Clefable,,
|
||||
43,Igglybuff,,
|
||||
44,Jigglypuff,,
|
||||
45,Wigglytuff,,
|
||||
46,Togepi,,
|
||||
47,Togetic,,
|
||||
48,Sandshrew,,
|
||||
49,Sandslash,,
|
||||
50,Ekans,,
|
||||
51,Arbok,,
|
||||
52,Dunsparce,,
|
||||
53,Mareep,,
|
||||
54,Flaaffy,,
|
||||
55,Ampharos,,
|
||||
56,Wooper,,
|
||||
57,Quagsire,,
|
||||
58,Gastly,,
|
||||
59,Haunter,,
|
||||
60,Gengar,,
|
||||
61,Unown,,
|
||||
62,Onix,,
|
||||
63,Steelix,,
|
||||
64,Bellsprout,,
|
||||
65,Weepinbell,,
|
||||
66,Victreebel,,
|
||||
67,Hoppip,,
|
||||
68,Skiploom,,
|
||||
69,Jumpluff,,
|
||||
70,Paras,,
|
||||
71,Parasect,,
|
||||
72,Poliwag,,
|
||||
73,Poliwhirl,,
|
||||
74,Poliwrath,,
|
||||
75,Politoed,,
|
||||
76,Magikarp,,
|
||||
77,Gyarados,,
|
||||
78,Goldeen,,
|
||||
79,Seaking,,
|
||||
80,Slowpoke,,
|
||||
81,Slowbro,,
|
||||
82,Slowking,,
|
||||
83,Oddish,,
|
||||
84,Gloom,,
|
||||
85,Vileplume,,
|
||||
86,Bellossom,,
|
||||
87,Drowzee,,
|
||||
88,Hypno,,
|
||||
89,Abra,,
|
||||
90,Kadabra,,
|
||||
91,Alakazam,,
|
||||
92,Ditto,,
|
||||
93,Pineco,,
|
||||
94,Forretress,,
|
||||
95,Nidoran♀,,
|
||||
96,Nidorina,,
|
||||
97,Nidoqueen,,
|
||||
98,Nidoran♂,,
|
||||
99,Nidorino,,
|
||||
100,Nidoking,,
|
||||
101,Yanma,,
|
||||
102,Yanmega,,
|
||||
103,Sunkern,,
|
||||
104,Sunflora,,
|
||||
105,Exeggcute,,
|
||||
106,Exeggutor,,
|
||||
107,Sudowoodo,,
|
||||
108,Wobbuffet,,
|
||||
109,Venonat,,
|
||||
110,Venomoth,,
|
||||
111,Scyther,,
|
||||
112,Scizor,,
|
||||
113,Pinsir,,
|
||||
114,Heracross,,
|
||||
115,Koffing,,
|
||||
116,Weezing,,
|
||||
117,Grimer,,
|
||||
118,Muk,,
|
||||
119,Magnemite,,
|
||||
120,Magneton,,
|
||||
121,Voltorb,,
|
||||
122,Electrode,,
|
||||
123,Aipom,,
|
||||
124,Ambipom,,
|
||||
125,Snubbull,,
|
||||
126,Granbull,,
|
||||
127,Vulpix,,
|
||||
128,Ninetales,,
|
||||
129,Growlithe,,
|
||||
130,Arcanine,,
|
||||
131,Stantler,,
|
||||
132,Marill,,
|
||||
133,Azumarill,,
|
||||
134,Diglett,,
|
||||
135,Dugtrio,,
|
||||
136,Mankey,,
|
||||
137,Primeape,,
|
||||
138,Meowth,,
|
||||
139,Persian,,
|
||||
140,Psyduck,,
|
||||
141,Golduck,,
|
||||
142,Machop,,
|
||||
143,Machoke,,
|
||||
144,Machamp,,
|
||||
145,Tyrogue,,
|
||||
146,Hitmonlee,,
|
||||
147,Hitmonchan,,
|
||||
148,Hitmontop,,
|
||||
149,Girafarig,,
|
||||
150,Tauros,,
|
||||
151,Miltank,,
|
||||
152,Magby,,
|
||||
153,Magmar,,
|
||||
154,Smoochum,,
|
||||
155,Jynx,,
|
||||
156,Elekid,,
|
||||
157,Electabuzz,,
|
||||
158,Mr. Mime,,
|
||||
159,Smeargle,,
|
||||
160,Farfetch’d,,
|
||||
161,Natu,,
|
||||
162,Xatu,,
|
||||
163,Qwilfish,,
|
||||
164,Tentacool,,
|
||||
165,Tentacruel,,
|
||||
166,Krabby,,
|
||||
167,Kingler,,
|
||||
168,Shuckle,,
|
||||
169,Staryu,,
|
||||
170,Starmie,,
|
||||
171,Shellder,,
|
||||
172,Cloyster,,
|
||||
173,Corsola,,
|
||||
174,Remoraid,,
|
||||
175,Octillery,,
|
||||
176,Chinchou,,
|
||||
177,Lanturn,,
|
||||
178,Seel,,
|
||||
179,Dewgong,,
|
||||
180,Lickitung,,
|
||||
181,Lickilicky,,
|
||||
182,Tangela,,
|
||||
183,Tangrowth,,
|
||||
184,Eevee,,
|
||||
185,Vaporeon,,
|
||||
186,Jolteon,,
|
||||
187,Flareon,,
|
||||
188,Espeon,,
|
||||
189,Umbreon,,
|
||||
190,Horsea,,
|
||||
191,Seadra,,
|
||||
192,Kingdra,,
|
||||
193,Gligar,,
|
||||
194,Delibird,,
|
||||
195,Swinub,,
|
||||
196,Piloswine,,
|
||||
197,Mamoswine,,
|
||||
198,Teddiursa,,
|
||||
199,Ursaring,,
|
||||
200,Phanpy,,
|
||||
201,Donphan,,
|
||||
202,Mantine,,
|
||||
203,Skarmory,,
|
||||
204,Doduo,,
|
||||
205,Dodrio,,
|
||||
206,Ponyta,,
|
||||
207,Rapidash,,
|
||||
208,Cubone,,
|
||||
209,Marowak,,
|
||||
210,Kangaskhan,,
|
||||
211,Rhyhorn,,
|
||||
212,Rhydon,,
|
||||
213,Murkrow,,
|
||||
214,Houndour,,
|
||||
215,Houndoom,,
|
||||
216,Slugma,,
|
||||
217,Magcargo,,
|
||||
218,Sneasel,,
|
||||
219,Misdreavus,,
|
||||
220,Porygon,,
|
||||
221,Porygon2,,
|
||||
222,Chansey,,
|
||||
223,Blissey,,
|
||||
224,Lapras,,
|
||||
225,Omanyte,,
|
||||
226,Omastar,,
|
||||
227,Kabuto,,
|
||||
228,Kabutops,,
|
||||
229,Aerodactyl,,
|
||||
230,Snorlax,,
|
||||
231,Bulbasaur,,
|
||||
232,Ivysaur,,
|
||||
233,Venusaur,,
|
||||
234,Charmander,,
|
||||
235,Charmeleon,,
|
||||
236,Charizard,,
|
||||
237,Squirtle,,
|
||||
238,Wartortle,,
|
||||
239,Blastoise,,
|
||||
240,Articuno,,
|
||||
241,Zapdos,,
|
||||
242,Moltres,,
|
||||
243,Raikou,,
|
||||
244,Entei,,
|
||||
245,Suicune,,
|
||||
246,Dratini,,
|
||||
247,Dragonair,,
|
||||
248,Dragonite,,
|
||||
249,Larvitar,,
|
||||
250,Pupitar,,
|
||||
251,Tyranitar,,
|
||||
252,Lugia,,
|
||||
253,Ho-Oh,,
|
||||
254,Mewtwo,,
|
||||
255,Mew,,
|
||||
256,Celebi,,
|
||||
|
@@ -1,243 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Rowlet,,
|
||||
2,Dartrix,,
|
||||
3,Decidueye,,
|
||||
4,Cyndaquil,,
|
||||
5,Quilava,,
|
||||
6,Typhlosion,,
|
||||
7,Oshawott,,
|
||||
8,Dewott,,
|
||||
9,Samurott,,
|
||||
10,Bidoof,,
|
||||
11,Bibarel,,
|
||||
12,Starly,,
|
||||
13,Staravia,,
|
||||
14,Staraptor,,
|
||||
15,Shinx,,
|
||||
16,Luxio,,
|
||||
17,Luxray,,
|
||||
18,Wurmple,,
|
||||
19,Silcoon,,
|
||||
20,Beautifly,,
|
||||
21,Cascoon,,
|
||||
22,Dustox,,
|
||||
23,Ponyta,,
|
||||
24,Rapidash,,
|
||||
25,Eevee,,
|
||||
26,Vaporeon,,
|
||||
27,Jolteon,,
|
||||
28,Flareon,,
|
||||
29,Espeon,,
|
||||
30,Umbreon,,
|
||||
31,Leafeon,,
|
||||
32,Glaceon,,
|
||||
33,Sylveon,,
|
||||
34,Zubat,,
|
||||
35,Golbat,,
|
||||
36,Crobat,,
|
||||
37,Drifloon,,
|
||||
38,Drifblim,,
|
||||
39,Kricketot,,
|
||||
40,Kricketune,,
|
||||
41,Buizel,,
|
||||
42,Floatzel,,
|
||||
43,Burmy,,
|
||||
44,Wormadam,,
|
||||
45,Mothim,,
|
||||
46,Geodude,,
|
||||
47,Graveler,,
|
||||
48,Golem,,
|
||||
49,Stantler,,
|
||||
50,Wyrdeer,,
|
||||
51,Munchlax,,
|
||||
52,Snorlax,,
|
||||
53,Paras,,
|
||||
54,Parasect,,
|
||||
55,Pichu,,
|
||||
56,Pikachu,,
|
||||
57,Raichu,,
|
||||
58,Abra,,
|
||||
59,Kadabra,,
|
||||
60,Alakazam,,
|
||||
61,Chimchar,,
|
||||
62,Monferno,,
|
||||
63,Infernape,,
|
||||
64,Buneary,,
|
||||
65,Lopunny,,
|
||||
66,Cherubi,,
|
||||
67,Cherrim,,
|
||||
68,Psyduck,,
|
||||
69,Golduck,,
|
||||
70,Combee,,
|
||||
71,Vespiquen,,
|
||||
72,Scyther,,
|
||||
73,Kleavor,,
|
||||
74,Scizor,,
|
||||
75,Heracross,,
|
||||
76,Mime Jr.,,
|
||||
77,Mr. Mime,,
|
||||
78,Aipom,,
|
||||
79,Ambipom,,
|
||||
80,Magikarp,,
|
||||
81,Gyarados,,
|
||||
82,Shellos,,
|
||||
83,Gastrodon,,
|
||||
84,Qwilfish,,
|
||||
85,Overqwil,,
|
||||
86,Happiny,,
|
||||
87,Chansey,,
|
||||
88,Blissey,,
|
||||
89,Budew,,
|
||||
90,Roselia,,
|
||||
91,Roserade,,
|
||||
92,Carnivine,,
|
||||
93,Petilil,,
|
||||
94,Lilligant,,
|
||||
95,Tangela,,
|
||||
96,Tangrowth,,
|
||||
97,Barboach,,
|
||||
98,Whiscash,,
|
||||
99,Croagunk,,
|
||||
100,Toxicroak,,
|
||||
101,Ralts,,
|
||||
102,Kirlia,,
|
||||
103,Gardevoir,,
|
||||
104,Gallade,,
|
||||
105,Yanma,,
|
||||
106,Yanmega,,
|
||||
107,Hippopotas,,
|
||||
108,Hippowdon,,
|
||||
109,Pachirisu,,
|
||||
110,Stunky,,
|
||||
111,Skuntank,,
|
||||
112,Teddiursa,,
|
||||
113,Ursaring,,
|
||||
114,Ursaluna,,
|
||||
115,Goomy,,
|
||||
116,Sliggoo,,
|
||||
117,Goodra,,
|
||||
118,Onix,,
|
||||
119,Steelix,,
|
||||
120,Rhyhorn,,
|
||||
121,Rhydon,,
|
||||
122,Rhyperior,,
|
||||
123,Bonsly,,
|
||||
124,Sudowoodo,,
|
||||
125,Lickitung,,
|
||||
126,Lickilicky,,
|
||||
127,Togepi,,
|
||||
128,Togetic,,
|
||||
129,Togekiss,,
|
||||
130,Turtwig,,
|
||||
131,Grotle,,
|
||||
132,Torterra,,
|
||||
133,Porygon,,
|
||||
134,Porygon2,,
|
||||
135,Porygon-Z,,
|
||||
136,Gastly,,
|
||||
137,Haunter,,
|
||||
138,Gengar,,
|
||||
139,Spiritomb,,
|
||||
140,Murkrow,,
|
||||
141,Honchkrow,,
|
||||
142,Unown,,
|
||||
143,Spheal,,
|
||||
144,Sealeo,,
|
||||
145,Walrein,,
|
||||
146,Remoraid,,
|
||||
147,Octillery,,
|
||||
148,Skorupi,,
|
||||
149,Drapion,,
|
||||
150,Growlithe,,
|
||||
151,Arcanine,,
|
||||
152,Glameow,,
|
||||
153,Purugly,,
|
||||
154,Machop,,
|
||||
155,Machoke,,
|
||||
156,Machamp,,
|
||||
157,Chatot,,
|
||||
158,Duskull,,
|
||||
159,Dusclops,,
|
||||
160,Dusknoir,,
|
||||
161,Piplup,,
|
||||
162,Prinplup,,
|
||||
163,Empoleon,,
|
||||
164,Mantyke,,
|
||||
165,Mantine,,
|
||||
166,Basculin,,
|
||||
167,Basculegion,,
|
||||
168,Vulpix,,
|
||||
169,Ninetales,,
|
||||
170,Tentacool,,
|
||||
171,Tentacruel,,
|
||||
172,Finneon,,
|
||||
173,Lumineon,,
|
||||
174,Magby,,
|
||||
175,Magmar,,
|
||||
176,Magmortar,,
|
||||
177,Magnemite,,
|
||||
178,Magneton,,
|
||||
179,Magnezone,,
|
||||
180,Bronzor,,
|
||||
181,Bronzong,,
|
||||
182,Elekid,,
|
||||
183,Electabuzz,,
|
||||
184,Electivire,,
|
||||
185,Gligar,,
|
||||
186,Gliscor,,
|
||||
187,Gible,,
|
||||
188,Gabite,,
|
||||
189,Garchomp,,
|
||||
190,Nosepass,,
|
||||
191,Probopass,,
|
||||
192,Voltorb,,
|
||||
193,Electrode,,
|
||||
194,Rotom,,
|
||||
195,Chingling,,
|
||||
196,Chimecho,,
|
||||
197,Misdreavus,,
|
||||
198,Mismagius,,
|
||||
199,Cleffa,,
|
||||
200,Clefairy,,
|
||||
201,Clefable,,
|
||||
202,Sneasel,,
|
||||
203,Sneasler,,
|
||||
204,Weavile,,
|
||||
205,Snorunt,,
|
||||
206,Glalie,,
|
||||
207,Froslass,,
|
||||
208,Cranidos,,
|
||||
209,Rampardos,,
|
||||
210,Shieldon,,
|
||||
211,Bastiodon,,
|
||||
212,Swinub,,
|
||||
213,Piloswine,,
|
||||
214,Mamoswine,,
|
||||
215,Bergmite,,
|
||||
216,Avalugg,,
|
||||
217,Snover,,
|
||||
218,Abomasnow,,
|
||||
219,Zorua,,
|
||||
220,Zoroark,,
|
||||
221,Rufflet,,
|
||||
222,Braviary,,
|
||||
223,Riolu,,
|
||||
224,Lucario,,
|
||||
225,Uxie,,
|
||||
226,Mesprit,,
|
||||
227,Azelf,,
|
||||
228,Heatran,,
|
||||
229,Regigigas,,
|
||||
230,Cresselia,,
|
||||
231,Tornadus,,
|
||||
232,Thundurus,,
|
||||
233,Landorus,,
|
||||
234,Enamorus,,
|
||||
235,Dialga,,
|
||||
236,Palkia,,
|
||||
237,Giratina,,
|
||||
238,Arceus,,
|
||||
239,Phione,,
|
||||
240,Manaphy,,
|
||||
241,Shaymin,,
|
||||
242,Darkrai,,
|
||||
|
@@ -1,133 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Mankey,,
|
||||
2,Primeape,,
|
||||
3,Annihilape,,
|
||||
4,Meowth,,
|
||||
5,Persian,,
|
||||
6,Perrserker,,
|
||||
7,Farfetch’d,,
|
||||
8,Sirfetch’d,,
|
||||
9,Cubone,,
|
||||
10,Marowak,,
|
||||
11,Porygon,,
|
||||
12,Porygon2,,
|
||||
13,Porygon-Z,,
|
||||
14,Capsakid,,
|
||||
15,Scovillain,,
|
||||
16,Tinkatink,,
|
||||
17,Tinkatuff,,
|
||||
18,Tinkaton,,
|
||||
19,Cyclizar,,
|
||||
20,Glimmet,,
|
||||
21,Glimmora,,
|
||||
22,Rotom,,
|
||||
23,Greavard,,
|
||||
24,Houndstone,,
|
||||
25,Sandygast,,
|
||||
26,Palossand,,
|
||||
27,Kecleon,,
|
||||
28,Flamigo,,
|
||||
29,Cryogonal,,
|
||||
30,Dondozo,,
|
||||
31,Tatsugiri,,
|
||||
32,Frigibax,,
|
||||
33,Arctibax,,
|
||||
34,Baxcalibur,,
|
||||
35,Gimmighoul,,
|
||||
36,Gholdengo,,
|
||||
37,Qwilfish,,
|
||||
38,Overqwil,,
|
||||
39,Treecko,,
|
||||
40,Grovyle,,
|
||||
41,Sceptile,,
|
||||
42,Torchic,,
|
||||
43,Combusken,,
|
||||
44,Blaziken,,
|
||||
45,Mudkip,,
|
||||
46,Marshtomp,,
|
||||
47,Swampert,,
|
||||
48,Feebas,,
|
||||
49,Milotic,,
|
||||
50,Chingling,,
|
||||
51,Chimecho,,
|
||||
52,Indeedee,,
|
||||
53,Purrloin,,
|
||||
54,Liepard,,
|
||||
55,Munna,,
|
||||
56,Musharna,,
|
||||
57,Throh,,
|
||||
58,Sawk,,
|
||||
59,Yamask,,
|
||||
60,Cofagrigus,,
|
||||
61,Runerigus,,
|
||||
62,Wimpod,,
|
||||
63,Golisopod,,
|
||||
64,Nickit,,
|
||||
65,Thievul,,
|
||||
66,Clobbopus,,
|
||||
67,Grapploct,,
|
||||
68,Mimikyu,,
|
||||
69,Kleavor,,
|
||||
70,Morpeko,,
|
||||
71,Golett,,
|
||||
72,Golurk,,
|
||||
73,Rookidee,,
|
||||
74,Corvisquire,,
|
||||
75,Corviknight,,
|
||||
76,Igglybuff,,
|
||||
77,Jigglypuff,,
|
||||
78,Wigglytuff,,
|
||||
79,Fidough,,
|
||||
80,Dachsbun,,
|
||||
81,Starly,,
|
||||
82,Staravia,,
|
||||
83,Staraptor,,
|
||||
84,Spoink,,
|
||||
85,Grumpig,,
|
||||
86,Squawkabilly,,
|
||||
87,Crabrawler,,
|
||||
88,Crabominable,,
|
||||
89,Nacli,,
|
||||
90,Naclstack,,
|
||||
91,Garganacl,,
|
||||
92,Gulpin,,
|
||||
93,Swalot,,
|
||||
94,Zubat,,
|
||||
95,Golbat,,
|
||||
96,Crobat,,
|
||||
97,Charcadet,,
|
||||
98,Armarouge,,
|
||||
99,Ceruledge,,
|
||||
100,Maschiff,,
|
||||
101,Mabosstiff,,
|
||||
102,Toxel,,
|
||||
103,Toxtricity,,
|
||||
104,Shroodle,,
|
||||
105,Grafaiai,,
|
||||
106,Zangoose,,
|
||||
107,Seviper,,
|
||||
108,Mime Jr.,,
|
||||
109,Mr. Mime,,
|
||||
110,Mr. Rime,,
|
||||
111,Foongus,,
|
||||
112,Amoonguss,,
|
||||
113,Heatran,,
|
||||
114,Volcanion,,
|
||||
115,Cobalion,,
|
||||
116,Terrakion,,
|
||||
117,Virizion,,
|
||||
118,Keldeo,,
|
||||
119,Meloetta,,
|
||||
120,Genesect,,
|
||||
121,Hoopa,,
|
||||
122,Marshadow,,
|
||||
123,Meltan,,
|
||||
124,Melmetal,,
|
||||
125,Darkrai,,
|
||||
126,Latias,,
|
||||
127,Latios,,
|
||||
128,Kyogre,,
|
||||
129,Groudon,,
|
||||
130,Rayquaza,,
|
||||
131,Magearna,,
|
||||
132,Zeraora,,
|
||||
|
@@ -1,233 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Chikorita,,
|
||||
2,Bayleef,,
|
||||
3,Meganium,,
|
||||
4,Tepig,,
|
||||
5,Pignite,,
|
||||
6,Emboar,,
|
||||
7,Totodile,,
|
||||
8,Croconaw,,
|
||||
9,Feraligatr,,
|
||||
10,Fletchling,,
|
||||
11,Fletchinder,,
|
||||
12,Talonflame,,
|
||||
13,Bunnelby,,
|
||||
14,Diggersby,,
|
||||
15,Scatterbug,,
|
||||
16,Spewpa,,
|
||||
17,Vivillon,,
|
||||
18,Weedle,,
|
||||
19,Kakuna,,
|
||||
20,Beedrill,,
|
||||
21,Pidgey,,
|
||||
22,Pidgeotto,,
|
||||
23,Pidgeot,,
|
||||
24,Mareep,,
|
||||
25,Flaaffy,,
|
||||
26,Ampharos,,
|
||||
27,Patrat,,
|
||||
28,Watchog,,
|
||||
29,Budew,,
|
||||
30,Roselia,,
|
||||
31,Roserade,,
|
||||
32,Magikarp,,
|
||||
33,Gyarados,,
|
||||
34,Binacle,,
|
||||
35,Barbaracle,,
|
||||
36,Staryu,,
|
||||
37,Starmie,,
|
||||
38,Flabébé,,
|
||||
39,Floette,,
|
||||
40,Florges,,
|
||||
41,Skiddo,,
|
||||
42,Gogoat,,
|
||||
43,Espurr,,
|
||||
44,Meowstic,,
|
||||
45,Litleo,,
|
||||
46,Pyroar,,
|
||||
47,Pancham,,
|
||||
48,Pangoro,,
|
||||
49,Trubbish,,
|
||||
50,Garbodor,,
|
||||
51,Dedenne,,
|
||||
52,Pichu,,
|
||||
53,Pikachu,,
|
||||
54,Raichu,,
|
||||
55,Cleffa,,
|
||||
56,Clefairy,,
|
||||
57,Clefable,,
|
||||
58,Spinarak,,
|
||||
59,Ariados,,
|
||||
60,Ekans,,
|
||||
61,Arbok,,
|
||||
62,Abra,,
|
||||
63,Kadabra,,
|
||||
64,Alakazam,,
|
||||
65,Gastly,,
|
||||
66,Haunter,,
|
||||
67,Gengar,,
|
||||
68,Venipede,,
|
||||
69,Whirlipede,,
|
||||
70,Scolipede,,
|
||||
71,Honedge,,
|
||||
72,Doublade,,
|
||||
73,Aegislash,,
|
||||
74,Bellsprout,,
|
||||
75,Weepinbell,,
|
||||
76,Victreebel,,
|
||||
77,Pansage,,
|
||||
78,Simisage,,
|
||||
79,Pansear,,
|
||||
80,Simisear,,
|
||||
81,Panpour,,
|
||||
82,Simipour,,
|
||||
83,Meditite,,
|
||||
84,Medicham,,
|
||||
85,Electrike,,
|
||||
86,Manectric,,
|
||||
87,Ralts,,
|
||||
88,Kirlia,,
|
||||
89,Gardevoir,,
|
||||
90,Gallade,,
|
||||
91,Houndour,,
|
||||
92,Houndoom,,
|
||||
93,Swablu,,
|
||||
94,Altaria,,
|
||||
95,Audino,,
|
||||
96,Spritzee,,
|
||||
97,Aromatisse,,
|
||||
98,Swirlix,,
|
||||
99,Slurpuff,,
|
||||
100,Eevee,,
|
||||
101,Vaporeon,,
|
||||
102,Jolteon,,
|
||||
103,Flareon,,
|
||||
104,Espeon,,
|
||||
105,Umbreon,,
|
||||
106,Leafeon,,
|
||||
107,Glaceon,,
|
||||
108,Sylveon,,
|
||||
109,Buneary,,
|
||||
110,Lopunny,,
|
||||
111,Shuppet,,
|
||||
112,Banette,,
|
||||
113,Vanillite,,
|
||||
114,Vanillish,,
|
||||
115,Vanilluxe,,
|
||||
116,Numel,,
|
||||
117,Camerupt,,
|
||||
118,Hippopotas,,
|
||||
119,Hippowdon,,
|
||||
120,Drilbur,,
|
||||
121,Excadrill,,
|
||||
122,Sandile,,
|
||||
123,Krokorok,,
|
||||
124,Krookodile,,
|
||||
125,Machop,,
|
||||
126,Machoke,,
|
||||
127,Machamp,,
|
||||
128,Gible,,
|
||||
129,Gabite,,
|
||||
130,Garchomp,,
|
||||
131,Carbink,,
|
||||
132,Sableye,,
|
||||
133,Mawile,,
|
||||
134,Absol,,
|
||||
135,Riolu,,
|
||||
136,Lucario,,
|
||||
137,Slowpoke,,
|
||||
138,Slowbro,,
|
||||
139,Slowking,,
|
||||
140,Carvanha,,
|
||||
141,Sharpedo,,
|
||||
142,Tynamo,,
|
||||
143,Eelektrik,,
|
||||
144,Eelektross,,
|
||||
145,Dratini,,
|
||||
146,Dragonair,,
|
||||
147,Dragonite,,
|
||||
148,Bulbasaur,,
|
||||
149,Ivysaur,,
|
||||
150,Venusaur,,
|
||||
151,Charmander,,
|
||||
152,Charmeleon,,
|
||||
153,Charizard,,
|
||||
154,Squirtle,,
|
||||
155,Wartortle,,
|
||||
156,Blastoise,,
|
||||
157,Stunfisk,,
|
||||
158,Furfrou,,
|
||||
159,Inkay,,
|
||||
160,Malamar,,
|
||||
161,Skrelp,,
|
||||
162,Dragalge,,
|
||||
163,Clauncher,,
|
||||
164,Clawitzer,,
|
||||
165,Goomy,,
|
||||
166,Sliggoo,,
|
||||
167,Goodra,,
|
||||
168,Delibird,,
|
||||
169,Snorunt,,
|
||||
170,Glalie,,
|
||||
171,Froslass,,
|
||||
172,Snover,,
|
||||
173,Abomasnow,,
|
||||
174,Bergmite,,
|
||||
175,Avalugg,,
|
||||
176,Scyther,,
|
||||
177,Scizor,,
|
||||
178,Pinsir,,
|
||||
179,Heracross,,
|
||||
180,Emolga,,
|
||||
181,Hawlucha,,
|
||||
182,Phantump,,
|
||||
183,Trevenant,,
|
||||
184,Scraggy,,
|
||||
185,Scrafty,,
|
||||
186,Noibat,,
|
||||
187,Noivern,,
|
||||
188,Klefki,,
|
||||
189,Litwick,,
|
||||
190,Lampent,,
|
||||
191,Chandelure,,
|
||||
192,Aerodactyl,,
|
||||
193,Tyrunt,,
|
||||
194,Tyrantrum,,
|
||||
195,Amaura,,
|
||||
196,Aurorus,,
|
||||
197,Onix,,
|
||||
198,Steelix,,
|
||||
199,Aron,,
|
||||
200,Lairon,,
|
||||
201,Aggron,,
|
||||
202,Helioptile,,
|
||||
203,Heliolisk,,
|
||||
204,Pumpkaboo,,
|
||||
205,Gourgeist,,
|
||||
206,Larvitar,,
|
||||
207,Pupitar,,
|
||||
208,Tyranitar,,
|
||||
209,Froakie,,
|
||||
210,Frogadier,,
|
||||
211,Greninja,,
|
||||
212,Falinks,,
|
||||
213,Chespin,,
|
||||
214,Quilladin,,
|
||||
215,Chesnaught,,
|
||||
216,Skarmory,,
|
||||
217,Fennekin,,
|
||||
218,Braixen,,
|
||||
219,Delphox,,
|
||||
220,Bagon,,
|
||||
221,Shelgon,,
|
||||
222,Salamence,,
|
||||
223,Kangaskhan,,
|
||||
224,Drampa,,
|
||||
225,Beldum,,
|
||||
226,Metang,,
|
||||
227,Metagross,,
|
||||
228,Xerneas,,
|
||||
229,Yveltal,,
|
||||
230,Zygarde,,
|
||||
231,Diancie,,
|
||||
232,Mewtwo,,
|
||||
|
@@ -1,154 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Bulbasaur,,
|
||||
2,Ivysaur,,
|
||||
3,Venusaur,,
|
||||
4,Charmander,,
|
||||
5,Charmeleon,,
|
||||
6,Charizard,,
|
||||
7,Squirtle,,
|
||||
8,Wartortle,,
|
||||
9,Blastoise,,
|
||||
10,Caterpie,,
|
||||
11,Metapod,,
|
||||
12,Butterfree,,
|
||||
13,Weedle,,
|
||||
14,Kakuna,,
|
||||
15,Beedrill,,
|
||||
16,Pidgey,,
|
||||
17,Pidgeotto,,
|
||||
18,Pidgeot,,
|
||||
19,Rattata,,
|
||||
20,Raticate,,
|
||||
21,Spearow,,
|
||||
22,Fearow,,
|
||||
23,Ekans,,
|
||||
24,Arbok,,
|
||||
25,Pikachu,,
|
||||
26,Raichu,,
|
||||
27,Sandshrew,,
|
||||
28,Sandslash,,
|
||||
29,Nidoran♀,,
|
||||
30,Nidorina,,
|
||||
31,Nidoqueen,,
|
||||
32,Nidoran♂,,
|
||||
33,Nidorino,,
|
||||
34,Nidoking,,
|
||||
35,Clefairy,,
|
||||
36,Clefable,,
|
||||
37,Vulpix,,
|
||||
38,Ninetales,,
|
||||
39,Jigglypuff,,
|
||||
40,Wigglytuff,,
|
||||
41,Zubat,,
|
||||
42,Golbat,,
|
||||
43,Oddish,,
|
||||
44,Gloom,,
|
||||
45,Vileplume,,
|
||||
46,Paras,,
|
||||
47,Parasect,,
|
||||
48,Venonat,,
|
||||
49,Venomoth,,
|
||||
50,Diglett,,
|
||||
51,Dugtrio,,
|
||||
52,Meowth,,
|
||||
53,Persian,,
|
||||
54,Psyduck,,
|
||||
55,Golduck,,
|
||||
56,Mankey,,
|
||||
57,Primeape,,
|
||||
58,Growlithe,,
|
||||
59,Arcanine,,
|
||||
60,Poliwag,,
|
||||
61,Poliwhirl,,
|
||||
62,Poliwrath,,
|
||||
63,Abra,,
|
||||
64,Kadabra,,
|
||||
65,Alakazam,,
|
||||
66,Machop,,
|
||||
67,Machoke,,
|
||||
68,Machamp,,
|
||||
69,Bellsprout,,
|
||||
70,Weepinbell,,
|
||||
71,Victreebel,,
|
||||
72,Tentacool,,
|
||||
73,Tentacruel,,
|
||||
74,Geodude,,
|
||||
75,Graveler,,
|
||||
76,Golem,,
|
||||
77,Ponyta,,
|
||||
78,Rapidash,,
|
||||
79,Slowpoke,,
|
||||
80,Slowbro,,
|
||||
81,Magnemite,,
|
||||
82,Magneton,,
|
||||
83,Farfetch’d,,
|
||||
84,Doduo,,
|
||||
85,Dodrio,,
|
||||
86,Seel,,
|
||||
87,Dewgong,,
|
||||
88,Grimer,,
|
||||
89,Muk,,
|
||||
90,Shellder,,
|
||||
91,Cloyster,,
|
||||
92,Gastly,,
|
||||
93,Haunter,,
|
||||
94,Gengar,,
|
||||
95,Onix,,
|
||||
96,Drowzee,,
|
||||
97,Hypno,,
|
||||
98,Krabby,,
|
||||
99,Kingler,,
|
||||
100,Voltorb,,
|
||||
101,Electrode,,
|
||||
102,Exeggcute,,
|
||||
103,Exeggutor,,
|
||||
104,Cubone,,
|
||||
105,Marowak,,
|
||||
106,Hitmonlee,,
|
||||
107,Hitmonchan,,
|
||||
108,Lickitung,,
|
||||
109,Koffing,,
|
||||
110,Weezing,,
|
||||
111,Rhyhorn,,
|
||||
112,Rhydon,,
|
||||
113,Chansey,,
|
||||
114,Tangela,,
|
||||
115,Kangaskhan,,
|
||||
116,Horsea,,
|
||||
117,Seadra,,
|
||||
118,Goldeen,,
|
||||
119,Seaking,,
|
||||
120,Staryu,,
|
||||
121,Starmie,,
|
||||
122,Mr. Mime,,
|
||||
123,Scyther,,
|
||||
124,Jynx,,
|
||||
125,Electabuzz,,
|
||||
126,Magmar,,
|
||||
127,Pinsir,,
|
||||
128,Tauros,,
|
||||
129,Magikarp,,
|
||||
130,Gyarados,,
|
||||
131,Lapras,,
|
||||
132,Ditto,,
|
||||
133,Eevee,,
|
||||
134,Vaporeon,,
|
||||
135,Jolteon,,
|
||||
136,Flareon,,
|
||||
137,Porygon,,
|
||||
138,Omanyte,,
|
||||
139,Omastar,,
|
||||
140,Kabuto,,
|
||||
141,Kabutops,,
|
||||
142,Aerodactyl,,
|
||||
143,Snorlax,,
|
||||
144,Articuno,,
|
||||
145,Zapdos,,
|
||||
146,Moltres,,
|
||||
147,Dratini,,
|
||||
148,Dragonair,,
|
||||
149,Dragonite,,
|
||||
150,Mewtwo,,
|
||||
151,Mew,,
|
||||
152,Meltan,,
|
||||
153,Melmetal,,
|
||||
|
@@ -1,212 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Treecko,,
|
||||
2,Grovyle,,
|
||||
3,Sceptile,,
|
||||
4,Torchic,,
|
||||
5,Combusken,,
|
||||
6,Blaziken,,
|
||||
7,Mudkip,,
|
||||
8,Marshtomp,,
|
||||
9,Swampert,,
|
||||
10,Poochyena,,
|
||||
11,Mightyena,,
|
||||
12,Zigzagoon,,
|
||||
13,Linoone,,
|
||||
14,Wurmple,,
|
||||
15,Silcoon,,
|
||||
16,Beautifly,,
|
||||
17,Cascoon,,
|
||||
18,Dustox,,
|
||||
19,Lotad,,
|
||||
20,Lombre,,
|
||||
21,Ludicolo,,
|
||||
22,Seedot,,
|
||||
23,Nuzleaf,,
|
||||
24,Shiftry,,
|
||||
25,Taillow,,
|
||||
26,Swellow,,
|
||||
27,Wingull,,
|
||||
28,Pelipper,,
|
||||
29,Ralts,,
|
||||
30,Kirlia,,
|
||||
31,Gardevoir,,
|
||||
32,Gallade,,
|
||||
33,Surskit,,
|
||||
34,Masquerain,,
|
||||
35,Shroomish,,
|
||||
36,Breloom,,
|
||||
37,Slakoth,,
|
||||
38,Vigoroth,,
|
||||
39,Slaking,,
|
||||
40,Abra,,
|
||||
41,Kadabra,,
|
||||
42,Alakazam,,
|
||||
43,Nincada,,
|
||||
44,Ninjask,,
|
||||
45,Shedinja,,
|
||||
46,Whismur,,
|
||||
47,Loudred,,
|
||||
48,Exploud,,
|
||||
49,Makuhita,,
|
||||
50,Hariyama,,
|
||||
51,Goldeen,,
|
||||
52,Seaking,,
|
||||
53,Magikarp,,
|
||||
54,Gyarados,,
|
||||
55,Azurill,,
|
||||
56,Marill,,
|
||||
57,Azumarill,,
|
||||
58,Geodude,,
|
||||
59,Graveler,,
|
||||
60,Golem,,
|
||||
61,Nosepass,,
|
||||
62,Probopass,,
|
||||
63,Skitty,,
|
||||
64,Delcatty,,
|
||||
65,Zubat,,
|
||||
66,Golbat,,
|
||||
67,Crobat,,
|
||||
68,Tentacool,,
|
||||
69,Tentacruel,,
|
||||
70,Sableye,,
|
||||
71,Mawile,,
|
||||
72,Aron,,
|
||||
73,Lairon,,
|
||||
74,Aggron,,
|
||||
75,Machop,,
|
||||
76,Machoke,,
|
||||
77,Machamp,,
|
||||
78,Meditite,,
|
||||
79,Medicham,,
|
||||
80,Electrike,,
|
||||
81,Manectric,,
|
||||
82,Plusle,,
|
||||
83,Minun,,
|
||||
84,Magnemite,,
|
||||
85,Magneton,,
|
||||
86,Magnezone,,
|
||||
87,Voltorb,,
|
||||
88,Electrode,,
|
||||
89,Volbeat,,
|
||||
90,Illumise,,
|
||||
91,Oddish,,
|
||||
92,Gloom,,
|
||||
93,Vileplume,,
|
||||
94,Bellossom,,
|
||||
95,Doduo,,
|
||||
96,Dodrio,,
|
||||
97,Budew,,
|
||||
98,Roselia,,
|
||||
99,Roserade,,
|
||||
100,Gulpin,,
|
||||
101,Swalot,,
|
||||
102,Carvanha,,
|
||||
103,Sharpedo,,
|
||||
104,Wailmer,,
|
||||
105,Wailord,,
|
||||
106,Numel,,
|
||||
107,Camerupt,,
|
||||
108,Slugma,,
|
||||
109,Magcargo,,
|
||||
110,Torkoal,,
|
||||
111,Grimer,,
|
||||
112,Muk,,
|
||||
113,Koffing,,
|
||||
114,Weezing,,
|
||||
115,Spoink,,
|
||||
116,Grumpig,,
|
||||
117,Sandshrew,,
|
||||
118,Sandslash,,
|
||||
119,Spinda,,
|
||||
120,Skarmory,,
|
||||
121,Trapinch,,
|
||||
122,Vibrava,,
|
||||
123,Flygon,,
|
||||
124,Cacnea,,
|
||||
125,Cacturne,,
|
||||
126,Swablu,,
|
||||
127,Altaria,,
|
||||
128,Zangoose,,
|
||||
129,Seviper,,
|
||||
130,Lunatone,,
|
||||
131,Solrock,,
|
||||
132,Barboach,,
|
||||
133,Whiscash,,
|
||||
134,Corphish,,
|
||||
135,Crawdaunt,,
|
||||
136,Baltoy,,
|
||||
137,Claydol,,
|
||||
138,Lileep,,
|
||||
139,Cradily,,
|
||||
140,Anorith,,
|
||||
141,Armaldo,,
|
||||
142,Igglybuff,,
|
||||
143,Jigglypuff,,
|
||||
144,Wigglytuff,,
|
||||
145,Feebas,,
|
||||
146,Milotic,,
|
||||
147,Castform,,
|
||||
148,Staryu,,
|
||||
149,Starmie,,
|
||||
150,Kecleon,,
|
||||
151,Shuppet,,
|
||||
152,Banette,,
|
||||
153,Duskull,,
|
||||
154,Dusclops,,
|
||||
155,Dusknoir,,
|
||||
156,Tropius,,
|
||||
157,Chingling,,
|
||||
158,Chimecho,,
|
||||
159,Absol,,
|
||||
160,Vulpix,,
|
||||
161,Ninetales,,
|
||||
162,Pichu,,
|
||||
163,Pikachu,,
|
||||
164,Raichu,,
|
||||
165,Psyduck,,
|
||||
166,Golduck,,
|
||||
167,Wynaut,,
|
||||
168,Wobbuffet,,
|
||||
169,Natu,,
|
||||
170,Xatu,,
|
||||
171,Girafarig,,
|
||||
172,Phanpy,,
|
||||
173,Donphan,,
|
||||
174,Pinsir,,
|
||||
175,Heracross,,
|
||||
176,Rhyhorn,,
|
||||
177,Rhydon,,
|
||||
178,Rhyperior,,
|
||||
179,Snorunt,,
|
||||
180,Glalie,,
|
||||
181,Froslass,,
|
||||
182,Spheal,,
|
||||
183,Sealeo,,
|
||||
184,Walrein,,
|
||||
185,Clamperl,,
|
||||
186,Huntail,,
|
||||
187,Gorebyss,,
|
||||
188,Relicanth,,
|
||||
189,Corsola,,
|
||||
190,Chinchou,,
|
||||
191,Lanturn,,
|
||||
192,Luvdisc,,
|
||||
193,Horsea,,
|
||||
194,Seadra,,
|
||||
195,Kingdra,,
|
||||
196,Bagon,,
|
||||
197,Shelgon,,
|
||||
198,Salamence,,
|
||||
199,Beldum,,
|
||||
200,Metang,,
|
||||
201,Metagross,,
|
||||
202,Regirock,,
|
||||
203,Regice,,
|
||||
204,Registeel,,
|
||||
205,Latias,,
|
||||
206,Latios,,
|
||||
207,Kyogre,,
|
||||
208,Groudon,,
|
||||
209,Rayquaza,,
|
||||
210,Jirachi,,
|
||||
211,Deoxys,,
|
||||
|
@@ -1,211 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Turtwig,,
|
||||
2,Grotle,,
|
||||
3,Torterra,,
|
||||
4,Chimchar,,
|
||||
5,Monferno,,
|
||||
6,Infernape,,
|
||||
7,Piplup,,
|
||||
8,Prinplup,,
|
||||
9,Empoleon,,
|
||||
10,Starly,,
|
||||
11,Staravia,,
|
||||
12,Staraptor,,
|
||||
13,Bidoof,,
|
||||
14,Bibarel,,
|
||||
15,Kricketot,,
|
||||
16,Kricketune,,
|
||||
17,Shinx,,
|
||||
18,Luxio,,
|
||||
19,Luxray,,
|
||||
20,Abra,,
|
||||
21,Kadabra,,
|
||||
22,Alakazam,,
|
||||
23,Magikarp,,
|
||||
24,Gyarados,,
|
||||
25,Budew,,
|
||||
26,Roselia,,
|
||||
27,Roserade,,
|
||||
28,Zubat,,
|
||||
29,Golbat,,
|
||||
30,Crobat,,
|
||||
31,Geodude,,
|
||||
32,Graveler,,
|
||||
33,Golem,,
|
||||
34,Onix,,
|
||||
35,Steelix,,
|
||||
36,Cranidos,,
|
||||
37,Rampardos,,
|
||||
38,Shieldon,,
|
||||
39,Bastiodon,,
|
||||
40,Machop,,
|
||||
41,Machoke,,
|
||||
42,Machamp,,
|
||||
43,Psyduck,,
|
||||
44,Golduck,,
|
||||
45,Burmy,,
|
||||
46,Wormadam,,
|
||||
47,Mothim,,
|
||||
48,Wurmple,,
|
||||
49,Silcoon,,
|
||||
50,Beautifly,,
|
||||
51,Cascoon,,
|
||||
52,Dustox,,
|
||||
53,Combee,,
|
||||
54,Vespiquen,,
|
||||
55,Pachirisu,,
|
||||
56,Buizel,,
|
||||
57,Floatzel,,
|
||||
58,Cherubi,,
|
||||
59,Cherrim,,
|
||||
60,Shellos,,
|
||||
61,Gastrodon,,
|
||||
62,Heracross,,
|
||||
63,Aipom,,
|
||||
64,Ambipom,,
|
||||
65,Drifloon,,
|
||||
66,Drifblim,,
|
||||
67,Buneary,,
|
||||
68,Lopunny,,
|
||||
69,Gastly,,
|
||||
70,Haunter,,
|
||||
71,Gengar,,
|
||||
72,Misdreavus,,
|
||||
73,Mismagius,,
|
||||
74,Murkrow,,
|
||||
75,Honchkrow,,
|
||||
76,Glameow,,
|
||||
77,Purugly,,
|
||||
78,Goldeen,,
|
||||
79,Seaking,,
|
||||
80,Barboach,,
|
||||
81,Whiscash,,
|
||||
82,Chingling,,
|
||||
83,Chimecho,,
|
||||
84,Stunky,,
|
||||
85,Skuntank,,
|
||||
86,Meditite,,
|
||||
87,Medicham,,
|
||||
88,Bronzor,,
|
||||
89,Bronzong,,
|
||||
90,Ponyta,,
|
||||
91,Rapidash,,
|
||||
92,Bonsly,,
|
||||
93,Sudowoodo,,
|
||||
94,Mime Jr.,,
|
||||
95,Mr. Mime,,
|
||||
96,Happiny,,
|
||||
97,Chansey,,
|
||||
98,Blissey,,
|
||||
99,Cleffa,,
|
||||
100,Clefairy,,
|
||||
101,Clefable,,
|
||||
102,Chatot,,
|
||||
103,Pichu,,
|
||||
104,Pikachu,,
|
||||
105,Raichu,,
|
||||
106,Hoothoot,,
|
||||
107,Noctowl,,
|
||||
108,Spiritomb,,
|
||||
109,Gible,,
|
||||
110,Gabite,,
|
||||
111,Garchomp,,
|
||||
112,Munchlax,,
|
||||
113,Snorlax,,
|
||||
114,Unown,,
|
||||
115,Riolu,,
|
||||
116,Lucario,,
|
||||
117,Wooper,,
|
||||
118,Quagsire,,
|
||||
119,Wingull,,
|
||||
120,Pelipper,,
|
||||
121,Girafarig,,
|
||||
122,Hippopotas,,
|
||||
123,Hippowdon,,
|
||||
124,Azurill,,
|
||||
125,Marill,,
|
||||
126,Azumarill,,
|
||||
127,Skorupi,,
|
||||
128,Drapion,,
|
||||
129,Croagunk,,
|
||||
130,Toxicroak,,
|
||||
131,Carnivine,,
|
||||
132,Remoraid,,
|
||||
133,Octillery,,
|
||||
134,Finneon,,
|
||||
135,Lumineon,,
|
||||
136,Tentacool,,
|
||||
137,Tentacruel,,
|
||||
138,Feebas,,
|
||||
139,Milotic,,
|
||||
140,Mantyke,,
|
||||
141,Mantine,,
|
||||
142,Snover,,
|
||||
143,Abomasnow,,
|
||||
144,Sneasel,,
|
||||
145,Weavile,,
|
||||
146,Uxie,,
|
||||
147,Mesprit,,
|
||||
148,Azelf,,
|
||||
149,Dialga,,
|
||||
150,Palkia,,
|
||||
151,Manaphy,,
|
||||
152,Rotom,,
|
||||
153,Gligar,,
|
||||
154,Gliscor,,
|
||||
155,Nosepass,,
|
||||
156,Probopass,,
|
||||
157,Ralts,,
|
||||
158,Kirlia,,
|
||||
159,Gardevoir,,
|
||||
160,Gallade,,
|
||||
161,Lickitung,,
|
||||
162,Lickilicky,,
|
||||
163,Eevee,,
|
||||
164,Vaporeon,,
|
||||
165,Jolteon,,
|
||||
166,Flareon,,
|
||||
167,Espeon,,
|
||||
168,Umbreon,,
|
||||
169,Leafeon,,
|
||||
170,Glaceon,,
|
||||
171,Swablu,,
|
||||
172,Altaria,,
|
||||
173,Togepi,,
|
||||
174,Togetic,,
|
||||
175,Togekiss,,
|
||||
176,Houndour,,
|
||||
177,Houndoom,,
|
||||
178,Magnemite,,
|
||||
179,Magneton,,
|
||||
180,Magnezone,,
|
||||
181,Tangela,,
|
||||
182,Tangrowth,,
|
||||
183,Yanma,,
|
||||
184,Yanmega,,
|
||||
185,Tropius,,
|
||||
186,Rhyhorn,,
|
||||
187,Rhydon,,
|
||||
188,Rhyperior,,
|
||||
189,Duskull,,
|
||||
190,Dusclops,,
|
||||
191,Dusknoir,,
|
||||
192,Porygon,,
|
||||
193,Porygon2,,
|
||||
194,Porygon-Z,,
|
||||
195,Scyther,,
|
||||
196,Scizor,,
|
||||
197,Elekid,,
|
||||
198,Electabuzz,,
|
||||
199,Electivire,,
|
||||
200,Magby,,
|
||||
201,Magmar,,
|
||||
202,Magmortar,,
|
||||
203,Swinub,,
|
||||
204,Piloswine,,
|
||||
205,Mamoswine,,
|
||||
206,Snorunt,,
|
||||
207,Glalie,,
|
||||
208,Froslass,,
|
||||
209,Absol,,
|
||||
210,Giratina,,
|
||||
|
@@ -1,152 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Bulbasaur,,
|
||||
2,Ivysaur,,
|
||||
3,Venusaur,,
|
||||
4,Charmander,,
|
||||
5,Charmeleon,,
|
||||
6,Charizard,,
|
||||
7,Squirtle,,
|
||||
8,Wartortle,,
|
||||
9,Blastoise,,
|
||||
10,Caterpie,,
|
||||
11,Metapod,,
|
||||
12,Butterfree,,
|
||||
13,Weedle,,
|
||||
14,Kakuna,,
|
||||
15,Beedrill,,
|
||||
16,Pidgey,,
|
||||
17,Pidgeotto,,
|
||||
18,Pidgeot,,
|
||||
19,Rattata,,
|
||||
20,Raticate,,
|
||||
21,Spearow,,
|
||||
22,Fearow,,
|
||||
23,Ekans,,
|
||||
24,Arbok,,
|
||||
25,Pikachu,,
|
||||
26,Raichu,,
|
||||
27,Sandshrew,,
|
||||
28,Sandslash,,
|
||||
29,Nidoran♀,,
|
||||
30,Nidorina,,
|
||||
31,Nidoqueen,,
|
||||
32,Nidoran♂,,
|
||||
33,Nidorino,,
|
||||
34,Nidoking,,
|
||||
35,Clefairy,,
|
||||
36,Clefable,,
|
||||
37,Vulpix,,
|
||||
38,Ninetales,,
|
||||
39,Jigglypuff,,
|
||||
40,Wigglytuff,,
|
||||
41,Zubat,,
|
||||
42,Golbat,,
|
||||
43,Oddish,,
|
||||
44,Gloom,,
|
||||
45,Vileplume,,
|
||||
46,Paras,,
|
||||
47,Parasect,,
|
||||
48,Venonat,,
|
||||
49,Venomoth,,
|
||||
50,Diglett,,
|
||||
51,Dugtrio,,
|
||||
52,Meowth,,
|
||||
53,Persian,,
|
||||
54,Psyduck,,
|
||||
55,Golduck,,
|
||||
56,Mankey,,
|
||||
57,Primeape,,
|
||||
58,Growlithe,,
|
||||
59,Arcanine,,
|
||||
60,Poliwag,,
|
||||
61,Poliwhirl,,
|
||||
62,Poliwrath,,
|
||||
63,Abra,,
|
||||
64,Kadabra,,
|
||||
65,Alakazam,,
|
||||
66,Machop,,
|
||||
67,Machoke,,
|
||||
68,Machamp,,
|
||||
69,Bellsprout,,
|
||||
70,Weepinbell,,
|
||||
71,Victreebel,,
|
||||
72,Tentacool,,
|
||||
73,Tentacruel,,
|
||||
74,Geodude,,
|
||||
75,Graveler,,
|
||||
76,Golem,,
|
||||
77,Ponyta,,
|
||||
78,Rapidash,,
|
||||
79,Slowpoke,,
|
||||
80,Slowbro,,
|
||||
81,Magnemite,,
|
||||
82,Magneton,,
|
||||
83,Farfetch’d,,
|
||||
84,Doduo,,
|
||||
85,Dodrio,,
|
||||
86,Seel,,
|
||||
87,Dewgong,,
|
||||
88,Grimer,,
|
||||
89,Muk,,
|
||||
90,Shellder,,
|
||||
91,Cloyster,,
|
||||
92,Gastly,,
|
||||
93,Haunter,,
|
||||
94,Gengar,,
|
||||
95,Onix,,
|
||||
96,Drowzee,,
|
||||
97,Hypno,,
|
||||
98,Krabby,,
|
||||
99,Kingler,,
|
||||
100,Voltorb,,
|
||||
101,Electrode,,
|
||||
102,Exeggcute,,
|
||||
103,Exeggutor,,
|
||||
104,Cubone,,
|
||||
105,Marowak,,
|
||||
106,Hitmonlee,,
|
||||
107,Hitmonchan,,
|
||||
108,Lickitung,,
|
||||
109,Koffing,,
|
||||
110,Weezing,,
|
||||
111,Rhyhorn,,
|
||||
112,Rhydon,,
|
||||
113,Chansey,,
|
||||
114,Tangela,,
|
||||
115,Kangaskhan,,
|
||||
116,Horsea,,
|
||||
117,Seadra,,
|
||||
118,Goldeen,,
|
||||
119,Seaking,,
|
||||
120,Staryu,,
|
||||
121,Starmie,,
|
||||
122,Mr. Mime,,
|
||||
123,Scyther,,
|
||||
124,Jynx,,
|
||||
125,Electabuzz,,
|
||||
126,Magmar,,
|
||||
127,Pinsir,,
|
||||
128,Tauros,,
|
||||
129,Magikarp,,
|
||||
130,Gyarados,,
|
||||
131,Lapras,,
|
||||
132,Ditto,,
|
||||
133,Eevee,,
|
||||
134,Vaporeon,,
|
||||
135,Jolteon,,
|
||||
136,Flareon,,
|
||||
137,Porygon,,
|
||||
138,Omanyte,,
|
||||
139,Omastar,,
|
||||
140,Kabuto,,
|
||||
141,Kabutops,,
|
||||
142,Aerodactyl,,
|
||||
143,Snorlax,,
|
||||
144,Articuno,,
|
||||
145,Zapdos,,
|
||||
146,Moltres,,
|
||||
147,Dratini,,
|
||||
148,Dragonair,,
|
||||
149,Dragonite,,
|
||||
150,Mewtwo,,
|
||||
151,Mew,,
|
||||
|
@@ -1,203 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Treecko,,
|
||||
2,Grovyle,,
|
||||
3,Sceptile,,
|
||||
4,Torchic,,
|
||||
5,Combusken,,
|
||||
6,Blaziken,,
|
||||
7,Mudkip,,
|
||||
8,Marshtomp,,
|
||||
9,Swampert,,
|
||||
10,Poochyena,,
|
||||
11,Mightyena,,
|
||||
12,Zigzagoon,,
|
||||
13,Linoone,,
|
||||
14,Wurmple,,
|
||||
15,Silcoon,,
|
||||
16,Beautifly,,
|
||||
17,Cascoon,,
|
||||
18,Dustox,,
|
||||
19,Lotad,,
|
||||
20,Lombre,,
|
||||
21,Ludicolo,,
|
||||
22,Seedot,,
|
||||
23,Nuzleaf,,
|
||||
24,Shiftry,,
|
||||
25,Taillow,,
|
||||
26,Swellow,,
|
||||
27,Wingull,,
|
||||
28,Pelipper,,
|
||||
29,Ralts,,
|
||||
30,Kirlia,,
|
||||
31,Gardevoir,,
|
||||
32,Surskit,,
|
||||
33,Masquerain,,
|
||||
34,Shroomish,,
|
||||
35,Breloom,,
|
||||
36,Slakoth,,
|
||||
37,Vigoroth,,
|
||||
38,Slaking,,
|
||||
39,Abra,,
|
||||
40,Kadabra,,
|
||||
41,Alakazam,,
|
||||
42,Nincada,,
|
||||
43,Ninjask,,
|
||||
44,Shedinja,,
|
||||
45,Whismur,,
|
||||
46,Loudred,,
|
||||
47,Exploud,,
|
||||
48,Makuhita,,
|
||||
49,Hariyama,,
|
||||
50,Goldeen,,
|
||||
51,Seaking,,
|
||||
52,Magikarp,,
|
||||
53,Gyarados,,
|
||||
54,Azurill,,
|
||||
55,Marill,,
|
||||
56,Azumarill,,
|
||||
57,Geodude,,
|
||||
58,Graveler,,
|
||||
59,Golem,,
|
||||
60,Nosepass,,
|
||||
61,Skitty,,
|
||||
62,Delcatty,,
|
||||
63,Zubat,,
|
||||
64,Golbat,,
|
||||
65,Crobat,,
|
||||
66,Tentacool,,
|
||||
67,Tentacruel,,
|
||||
68,Sableye,,
|
||||
69,Mawile,,
|
||||
70,Aron,,
|
||||
71,Lairon,,
|
||||
72,Aggron,,
|
||||
73,Machop,,
|
||||
74,Machoke,,
|
||||
75,Machamp,,
|
||||
76,Meditite,,
|
||||
77,Medicham,,
|
||||
78,Electrike,,
|
||||
79,Manectric,,
|
||||
80,Plusle,,
|
||||
81,Minun,,
|
||||
82,Magnemite,,
|
||||
83,Magneton,,
|
||||
84,Voltorb,,
|
||||
85,Electrode,,
|
||||
86,Volbeat,,
|
||||
87,Illumise,,
|
||||
88,Oddish,,
|
||||
89,Gloom,,
|
||||
90,Vileplume,,
|
||||
91,Bellossom,,
|
||||
92,Doduo,,
|
||||
93,Dodrio,,
|
||||
94,Roselia,,
|
||||
95,Gulpin,,
|
||||
96,Swalot,,
|
||||
97,Carvanha,,
|
||||
98,Sharpedo,,
|
||||
99,Wailmer,,
|
||||
100,Wailord,,
|
||||
101,Numel,,
|
||||
102,Camerupt,,
|
||||
103,Slugma,,
|
||||
104,Magcargo,,
|
||||
105,Torkoal,,
|
||||
106,Grimer,,
|
||||
107,Muk,,
|
||||
108,Koffing,,
|
||||
109,Weezing,,
|
||||
110,Spoink,,
|
||||
111,Grumpig,,
|
||||
112,Sandshrew,,
|
||||
113,Sandslash,,
|
||||
114,Spinda,,
|
||||
115,Skarmory,,
|
||||
116,Trapinch,,
|
||||
117,Vibrava,,
|
||||
118,Flygon,,
|
||||
119,Cacnea,,
|
||||
120,Cacturne,,
|
||||
121,Swablu,,
|
||||
122,Altaria,,
|
||||
123,Zangoose,,
|
||||
124,Seviper,,
|
||||
125,Lunatone,,
|
||||
126,Solrock,,
|
||||
127,Barboach,,
|
||||
128,Whiscash,,
|
||||
129,Corphish,,
|
||||
130,Crawdaunt,,
|
||||
131,Baltoy,,
|
||||
132,Claydol,,
|
||||
133,Lileep,,
|
||||
134,Cradily,,
|
||||
135,Anorith,,
|
||||
136,Armaldo,,
|
||||
137,Igglybuff,,
|
||||
138,Jigglypuff,,
|
||||
139,Wigglytuff,,
|
||||
140,Feebas,,
|
||||
141,Milotic,,
|
||||
142,Castform,,
|
||||
143,Staryu,,
|
||||
144,Starmie,,
|
||||
145,Kecleon,,
|
||||
146,Shuppet,,
|
||||
147,Banette,,
|
||||
148,Duskull,,
|
||||
149,Dusclops,,
|
||||
150,Tropius,,
|
||||
151,Chimecho,,
|
||||
152,Absol,,
|
||||
153,Vulpix,,
|
||||
154,Ninetales,,
|
||||
155,Pichu,,
|
||||
156,Pikachu,,
|
||||
157,Raichu,,
|
||||
158,Psyduck,,
|
||||
159,Golduck,,
|
||||
160,Wynaut,,
|
||||
161,Wobbuffet,,
|
||||
162,Natu,,
|
||||
163,Xatu,,
|
||||
164,Girafarig,,
|
||||
165,Phanpy,,
|
||||
166,Donphan,,
|
||||
167,Pinsir,,
|
||||
168,Heracross,,
|
||||
169,Rhyhorn,,
|
||||
170,Rhydon,,
|
||||
171,Snorunt,,
|
||||
172,Glalie,,
|
||||
173,Spheal,,
|
||||
174,Sealeo,,
|
||||
175,Walrein,,
|
||||
176,Clamperl,,
|
||||
177,Huntail,,
|
||||
178,Gorebyss,,
|
||||
179,Relicanth,,
|
||||
180,Corsola,,
|
||||
181,Chinchou,,
|
||||
182,Lanturn,,
|
||||
183,Luvdisc,,
|
||||
184,Horsea,,
|
||||
185,Seadra,,
|
||||
186,Kingdra,,
|
||||
187,Bagon,,
|
||||
188,Shelgon,,
|
||||
189,Salamence,,
|
||||
190,Beldum,,
|
||||
191,Metang,,
|
||||
192,Metagross,,
|
||||
193,Regirock,,
|
||||
194,Regice,,
|
||||
195,Registeel,,
|
||||
196,Latias,,
|
||||
197,Latios,,
|
||||
198,Kyogre,,
|
||||
199,Groudon,,
|
||||
200,Rayquaza,,
|
||||
201,Jirachi,,
|
||||
202,Deoxys,,
|
||||
|
@@ -1,244 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Doduo,,
|
||||
2,Dodrio,,
|
||||
3,Exeggcute,,
|
||||
4,Exeggutor,,
|
||||
5,Rhyhorn,,
|
||||
6,Rhydon,,
|
||||
7,Rhyperior,,
|
||||
8,Venonat,,
|
||||
9,Venomoth,,
|
||||
10,Elekid,,
|
||||
11,Electabuzz,,
|
||||
12,Electivire,,
|
||||
13,Magby,,
|
||||
14,Magmar,,
|
||||
15,Magmortar,,
|
||||
16,Happiny,,
|
||||
17,Chansey,,
|
||||
18,Blissey,,
|
||||
19,Scyther,,
|
||||
20,Scizor,,
|
||||
21,Kleavor,,
|
||||
22,Tauros,,
|
||||
23,Blitzle,,
|
||||
24,Zebstrika,,
|
||||
25,Girafarig,,
|
||||
26,Farigiraf,,
|
||||
27,Sandile,,
|
||||
28,Krokorok,,
|
||||
29,Krookodile,,
|
||||
30,Rellor,,
|
||||
31,Rabsca,,
|
||||
32,Rufflet,,
|
||||
33,Braviary,,
|
||||
34,Vullaby,,
|
||||
35,Mandibuzz,,
|
||||
36,Litleo,,
|
||||
37,Pyroar,,
|
||||
38,Deerling,,
|
||||
39,Sawsbuck,,
|
||||
40,Smeargle,,
|
||||
41,Rotom,,
|
||||
42,Milcery,,
|
||||
43,Alcremie,,
|
||||
44,Trapinch,,
|
||||
45,Vibrava,,
|
||||
46,Flygon,,
|
||||
47,Pikipek,,
|
||||
48,Trumbeak,,
|
||||
49,Toucannon,,
|
||||
50,Tentacool,,
|
||||
51,Tentacruel,,
|
||||
52,Horsea,,
|
||||
53,Seadra,,
|
||||
54,Kingdra,,
|
||||
55,Bruxish,,
|
||||
56,Cottonee,,
|
||||
57,Whimsicott,,
|
||||
58,Comfey,,
|
||||
59,Slakoth,,
|
||||
60,Vigoroth,,
|
||||
61,Slaking,,
|
||||
62,Oddish,,
|
||||
63,Gloom,,
|
||||
64,Vileplume,,
|
||||
65,Bellossom,,
|
||||
66,Diglett,,
|
||||
67,Dugtrio,,
|
||||
68,Grimer,,
|
||||
69,Muk,,
|
||||
70,Zangoose,,
|
||||
71,Seviper,,
|
||||
72,Crabrawler,,
|
||||
73,Crabominable,,
|
||||
74,Oricorio,,
|
||||
75,Slowpoke,,
|
||||
76,Slowbro,,
|
||||
77,Slowking,,
|
||||
78,Chinchou,,
|
||||
79,Lanturn,,
|
||||
80,Inkay,,
|
||||
81,Malamar,,
|
||||
82,Luvdisc,,
|
||||
83,Finneon,,
|
||||
84,Lumineon,,
|
||||
85,Alomomola,,
|
||||
86,Torkoal,,
|
||||
87,Fletchling,,
|
||||
88,Fletchinder,,
|
||||
89,Talonflame,,
|
||||
90,Dewpider,,
|
||||
91,Araquanid,,
|
||||
92,Tyrogue,,
|
||||
93,Hitmonlee,,
|
||||
94,Hitmonchan,,
|
||||
95,Hitmontop,,
|
||||
96,Geodude,,
|
||||
97,Graveler,,
|
||||
98,Golem,,
|
||||
99,Drilbur,,
|
||||
100,Excadrill,,
|
||||
101,Gothita,,
|
||||
102,Gothorita,,
|
||||
103,Gothitelle,,
|
||||
104,Espurr,,
|
||||
105,Meowstic,,
|
||||
106,Minior,,
|
||||
107,Cranidos,,
|
||||
108,Rampardos,,
|
||||
109,Shieldon,,
|
||||
110,Bastiodon,,
|
||||
111,Minccino,,
|
||||
112,Cinccino,,
|
||||
113,Skarmory,,
|
||||
114,Swablu,,
|
||||
115,Altaria,,
|
||||
116,Magnemite,,
|
||||
117,Magneton,,
|
||||
118,Magnezone,,
|
||||
119,Plusle,,
|
||||
120,Minun,,
|
||||
121,Scraggy,,
|
||||
122,Scrafty,,
|
||||
123,Golett,,
|
||||
124,Golurk,,
|
||||
125,Numel,,
|
||||
126,Camerupt,,
|
||||
127,Sinistea,,
|
||||
128,Polteageist,,
|
||||
129,Porygon,,
|
||||
130,Porygon2,,
|
||||
131,Porygon-Z,,
|
||||
132,Joltik,,
|
||||
133,Galvantula,,
|
||||
134,Tynamo,,
|
||||
135,Eelektrik,,
|
||||
136,Eelektross,,
|
||||
137,Beldum,,
|
||||
138,Metang,,
|
||||
139,Metagross,,
|
||||
140,Axew,,
|
||||
141,Fraxure,,
|
||||
142,Haxorus,,
|
||||
143,Seel,,
|
||||
144,Dewgong,,
|
||||
145,Lapras,,
|
||||
146,Qwilfish,,
|
||||
147,Overqwil,,
|
||||
148,Solosis,,
|
||||
149,Duosion,,
|
||||
150,Reuniclus,,
|
||||
151,Snubbull,,
|
||||
152,Granbull,,
|
||||
153,Cubchoo,,
|
||||
154,Beartic,,
|
||||
155,Sandshrew,,
|
||||
156,Sandslash,,
|
||||
157,Vulpix,,
|
||||
158,Ninetales,,
|
||||
159,Snover,,
|
||||
160,Abomasnow,,
|
||||
161,Duraludon,,
|
||||
162,Archaludon,,
|
||||
163,Hydrapple,,
|
||||
164,Bulbasaur,,
|
||||
165,Ivysaur,,
|
||||
166,Venusaur,,
|
||||
167,Charmander,,
|
||||
168,Charmeleon,,
|
||||
169,Charizard,,
|
||||
170,Squirtle,,
|
||||
171,Wartortle,,
|
||||
172,Blastoise,,
|
||||
173,Chikorita,,
|
||||
174,Bayleef,,
|
||||
175,Meganium,,
|
||||
176,Cyndaquil,,
|
||||
177,Quilava,,
|
||||
178,Typhlosion,,
|
||||
179,Totodile,,
|
||||
180,Croconaw,,
|
||||
181,Feraligatr,,
|
||||
182,Treecko,,
|
||||
183,Grovyle,,
|
||||
184,Sceptile,,
|
||||
185,Torchic,,
|
||||
186,Combusken,,
|
||||
187,Blaziken,,
|
||||
188,Mudkip,,
|
||||
189,Marshtomp,,
|
||||
190,Swampert,,
|
||||
191,Turtwig,,
|
||||
192,Grotle,,
|
||||
193,Torterra,,
|
||||
194,Chimchar,,
|
||||
195,Monferno,,
|
||||
196,Infernape,,
|
||||
197,Piplup,,
|
||||
198,Prinplup,,
|
||||
199,Empoleon,,
|
||||
200,Snivy,,
|
||||
201,Servine,,
|
||||
202,Serperior,,
|
||||
203,Tepig,,
|
||||
204,Pignite,,
|
||||
205,Emboar,,
|
||||
206,Oshawott,,
|
||||
207,Dewott,,
|
||||
208,Samurott,,
|
||||
209,Chespin,,
|
||||
210,Quilladin,,
|
||||
211,Chesnaught,,
|
||||
212,Fennekin,,
|
||||
213,Braixen,,
|
||||
214,Delphox,,
|
||||
215,Froakie,,
|
||||
216,Frogadier,,
|
||||
217,Greninja,,
|
||||
218,Rowlet,,
|
||||
219,Dartrix,,
|
||||
220,Decidueye,,
|
||||
221,Litten,,
|
||||
222,Torracat,,
|
||||
223,Incineroar,,
|
||||
224,Popplio,,
|
||||
225,Brionne,,
|
||||
226,Primarina,,
|
||||
227,Grookey,,
|
||||
228,Thwackey,,
|
||||
229,Rillaboom,,
|
||||
230,Scorbunny,,
|
||||
231,Raboot,,
|
||||
232,Cinderace,,
|
||||
233,Sobble,,
|
||||
234,Drizzile,,
|
||||
235,Inteleon,,
|
||||
236,Gouging Fire,,
|
||||
237,Raging Bolt,,
|
||||
238,Iron Crown,,
|
||||
239,Iron Boulder,,
|
||||
240,Terapagos,,
|
||||
241,Walking Wake,,
|
||||
242,Iron Leaves,,
|
||||
243,Pecharunt,,
|
||||
|
@@ -1,201 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Spinarak,,
|
||||
2,Ariados,,
|
||||
3,Yanma,,
|
||||
4,Yanmega,,
|
||||
5,Wooper,,
|
||||
6,Quagsire,,
|
||||
7,Poochyena,,
|
||||
8,Mightyena,,
|
||||
9,Volbeat,,
|
||||
10,Illumise,,
|
||||
11,Corphish,,
|
||||
12,Crawdaunt,,
|
||||
13,Sewaddle,,
|
||||
14,Swadloon,,
|
||||
15,Leavanny,,
|
||||
16,Cutiefly,,
|
||||
17,Ribombee,,
|
||||
18,Ekans,,
|
||||
19,Arbok,,
|
||||
20,Pichu,,
|
||||
21,Pikachu,,
|
||||
22,Raichu,,
|
||||
23,Bellsprout,,
|
||||
24,Weepinbell,,
|
||||
25,Victreebel,,
|
||||
26,Sentret,,
|
||||
27,Furret,,
|
||||
28,Starly,,
|
||||
29,Staravia,,
|
||||
30,Staraptor,,
|
||||
31,Fomantis,,
|
||||
32,Lurantis,,
|
||||
33,Applin,,
|
||||
34,Flapple,,
|
||||
35,Appletun,,
|
||||
36,Dipplin,,
|
||||
37,Vulpix,,
|
||||
38,Ninetales,,
|
||||
39,Poliwag,,
|
||||
40,Poliwhirl,,
|
||||
41,Poliwrath,,
|
||||
42,Politoed,,
|
||||
43,Magikarp,,
|
||||
44,Gyarados,,
|
||||
45,Hoothoot,,
|
||||
46,Noctowl,,
|
||||
47,Aipom,,
|
||||
48,Ambipom,,
|
||||
49,Heracross,,
|
||||
50,Swinub,,
|
||||
51,Piloswine,,
|
||||
52,Mamoswine,,
|
||||
53,Stantler,,
|
||||
54,Seedot,,
|
||||
55,Nuzleaf,,
|
||||
56,Shiftry,,
|
||||
57,Ralts,,
|
||||
58,Kirlia,,
|
||||
59,Gardevoir,,
|
||||
60,Gallade,,
|
||||
61,Kricketot,,
|
||||
62,Kricketune,,
|
||||
63,Pachirisu,,
|
||||
64,Riolu,,
|
||||
65,Lucario,,
|
||||
66,Petilil,,
|
||||
67,Lilligant,,
|
||||
68,Phantump,,
|
||||
69,Trevenant,,
|
||||
70,Rockruff,,
|
||||
71,Lycanroc,,
|
||||
72,Skwovet,,
|
||||
73,Greedent,,
|
||||
74,Toedscool,,
|
||||
75,Toedscruel,,
|
||||
76,Poltchageist,,
|
||||
77,Sinistcha,,
|
||||
78,Growlithe,,
|
||||
79,Arcanine,,
|
||||
80,Geodude,,
|
||||
81,Graveler,,
|
||||
82,Golem,,
|
||||
83,Bonsly,,
|
||||
84,Sudowoodo,,
|
||||
85,Timburr,,
|
||||
86,Gurdurr,,
|
||||
87,Conkeldurr,,
|
||||
88,Noibat,,
|
||||
89,Noivern,,
|
||||
90,Arrokuda,,
|
||||
91,Barraskewda,,
|
||||
92,Hatenna,,
|
||||
93,Hattrem,,
|
||||
94,Hatterene,,
|
||||
95,Morpeko,,
|
||||
96,Orthworm,,
|
||||
97,Tandemaus,,
|
||||
98,Maushold,,
|
||||
99,Mankey,,
|
||||
100,Primeape,,
|
||||
101,Annihilape,,
|
||||
102,Munchlax,,
|
||||
103,Snorlax,,
|
||||
104,Lotad,,
|
||||
105,Lombre,,
|
||||
106,Ludicolo,,
|
||||
107,Nosepass,,
|
||||
108,Probopass,,
|
||||
109,Shinx,,
|
||||
110,Luxio,,
|
||||
111,Luxray,,
|
||||
112,Grubbin,,
|
||||
113,Charjabug,,
|
||||
114,Vikavolt,,
|
||||
115,Oricorio,,
|
||||
116,Sandshrew,,
|
||||
117,Sandslash,,
|
||||
118,Gastly,,
|
||||
119,Haunter,,
|
||||
120,Gengar,,
|
||||
121,Gligar,,
|
||||
122,Gliscor,,
|
||||
123,Houndour,,
|
||||
124,Houndoom,,
|
||||
125,Spoink,,
|
||||
126,Grumpig,,
|
||||
127,Vullaby,,
|
||||
128,Mandibuzz,,
|
||||
129,Mudbray,,
|
||||
130,Mudsdale,,
|
||||
131,Jangmo-o,,
|
||||
132,Hakamo-o,,
|
||||
133,Kommo-o,,
|
||||
134,Bombirdier,,
|
||||
135,Koffing,,
|
||||
136,Weezing,,
|
||||
137,Mienfoo,,
|
||||
138,Mienshao,,
|
||||
139,Duskull,,
|
||||
140,Dusclops,,
|
||||
141,Dusknoir,,
|
||||
142,Chingling,,
|
||||
143,Chimecho,,
|
||||
144,Slugma,,
|
||||
145,Magcargo,,
|
||||
146,Litwick,,
|
||||
147,Lampent,,
|
||||
148,Chandelure,,
|
||||
149,Surskit,,
|
||||
150,Masquerain,,
|
||||
151,Cleffa,,
|
||||
152,Clefairy,,
|
||||
153,Clefable,,
|
||||
154,Bronzor,,
|
||||
155,Bronzong,,
|
||||
156,Glimmet,,
|
||||
157,Glimmora,,
|
||||
158,Feebas,,
|
||||
159,Milotic,,
|
||||
160,Dunsparce,,
|
||||
161,Dudunsparce,,
|
||||
162,Barboach,,
|
||||
163,Whiscash,,
|
||||
164,Gible,,
|
||||
165,Gabite,,
|
||||
166,Garchomp,,
|
||||
167,Carbink,,
|
||||
168,Salandit,,
|
||||
169,Salazzle,,
|
||||
170,Sneasel,,
|
||||
171,Weavile,,
|
||||
172,Snorunt,,
|
||||
173,Glalie,,
|
||||
174,Froslass,,
|
||||
175,Tynamo,,
|
||||
176,Eelektrik,,
|
||||
177,Eelektross,,
|
||||
178,Goomy,,
|
||||
179,Sliggoo,,
|
||||
180,Goodra,,
|
||||
181,Ducklett,,
|
||||
182,Swanna,,
|
||||
183,Chewtle,,
|
||||
184,Drednaw,,
|
||||
185,Cramorant,,
|
||||
186,Pawniard,,
|
||||
187,Bisharp,,
|
||||
188,Kingambit,,
|
||||
189,Mimikyu,,
|
||||
190,Impidimp,,
|
||||
191,Morgrem,,
|
||||
192,Grimmsnarl,,
|
||||
193,Indeedee,,
|
||||
194,Basculin,,
|
||||
195,Basculegion,,
|
||||
196,Ursaluna,,
|
||||
197,Okidogi,,
|
||||
198,Munkidori,,
|
||||
199,Fezandipiti,,
|
||||
200,Ogerpon,,
|
||||
|
@@ -1,401 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Sprigatito,,
|
||||
2,Floragato,,
|
||||
3,Meowscarada,,
|
||||
4,Fuecoco,,
|
||||
5,Crocalor,,
|
||||
6,Skeledirge,,
|
||||
7,Quaxly,,
|
||||
8,Quaxwell,,
|
||||
9,Quaquaval,,
|
||||
10,Lechonk,,
|
||||
11,Oinkologne,,
|
||||
12,Tarountula,,
|
||||
13,Spidops,,
|
||||
14,Nymble,,
|
||||
15,Lokix,,
|
||||
16,Hoppip,,
|
||||
17,Skiploom,,
|
||||
18,Jumpluff,,
|
||||
19,Fletchling,,
|
||||
20,Fletchinder,,
|
||||
21,Talonflame,,
|
||||
22,Pawmi,,
|
||||
23,Pawmo,,
|
||||
24,Pawmot,,
|
||||
25,Houndour,,
|
||||
26,Houndoom,,
|
||||
27,Yungoos,,
|
||||
28,Gumshoos,,
|
||||
29,Skwovet,,
|
||||
30,Greedent,,
|
||||
31,Sunkern,,
|
||||
32,Sunflora,,
|
||||
33,Kricketot,,
|
||||
34,Kricketune,,
|
||||
35,Scatterbug,,
|
||||
36,Spewpa,,
|
||||
37,Vivillon,,
|
||||
38,Combee,,
|
||||
39,Vespiquen,,
|
||||
40,Rookidee,,
|
||||
41,Corvisquire,,
|
||||
42,Corviknight,,
|
||||
43,Happiny,,
|
||||
44,Chansey,,
|
||||
45,Blissey,,
|
||||
46,Azurill,,
|
||||
47,Marill,,
|
||||
48,Azumarill,,
|
||||
49,Surskit,,
|
||||
50,Masquerain,,
|
||||
51,Buizel,,
|
||||
52,Floatzel,,
|
||||
53,Wooper,,
|
||||
54,Clodsire,,
|
||||
55,Psyduck,,
|
||||
56,Golduck,,
|
||||
57,Chewtle,,
|
||||
58,Drednaw,,
|
||||
59,Igglybuff,,
|
||||
60,Jigglypuff,,
|
||||
61,Wigglytuff,,
|
||||
62,Ralts,,
|
||||
63,Kirlia,,
|
||||
64,Gardevoir,,
|
||||
65,Gallade,,
|
||||
66,Drowzee,,
|
||||
67,Hypno,,
|
||||
68,Gastly,,
|
||||
69,Haunter,,
|
||||
70,Gengar,,
|
||||
71,Tandemaus,,
|
||||
72,Maushold,,
|
||||
73,Pichu,,
|
||||
74,Pikachu,,
|
||||
75,Raichu,,
|
||||
76,Fidough,,
|
||||
77,Dachsbun,,
|
||||
78,Slakoth,,
|
||||
79,Vigoroth,,
|
||||
80,Slaking,,
|
||||
81,Bounsweet,,
|
||||
82,Steenee,,
|
||||
83,Tsareena,,
|
||||
84,Smoliv,,
|
||||
85,Dolliv,,
|
||||
86,Arboliva,,
|
||||
87,Bonsly,,
|
||||
88,Sudowoodo,,
|
||||
89,Rockruff,,
|
||||
90,Lycanroc,,
|
||||
91,Rolycoly,,
|
||||
92,Carkol,,
|
||||
93,Coalossal,,
|
||||
94,Shinx,,
|
||||
95,Luxio,,
|
||||
96,Luxray,,
|
||||
97,Starly,,
|
||||
98,Staravia,,
|
||||
99,Staraptor,,
|
||||
100,Oricorio,,
|
||||
101,Mareep,,
|
||||
102,Flaaffy,,
|
||||
103,Ampharos,,
|
||||
104,Petilil,,
|
||||
105,Lilligant,,
|
||||
106,Shroomish,,
|
||||
107,Breloom,,
|
||||
108,Applin,,
|
||||
109,Flapple,,
|
||||
110,Appletun,,
|
||||
111,Spoink,,
|
||||
112,Grumpig,,
|
||||
113,Squawkabilly,,
|
||||
114,Misdreavus,,
|
||||
115,Mismagius,,
|
||||
116,Makuhita,,
|
||||
117,Hariyama,,
|
||||
118,Crabrawler,,
|
||||
119,Crabominable,,
|
||||
120,Salandit,,
|
||||
121,Salazzle,,
|
||||
122,Phanpy,,
|
||||
123,Donphan,,
|
||||
124,Cufant,,
|
||||
125,Copperajah,,
|
||||
126,Gible,,
|
||||
127,Gabite,,
|
||||
128,Garchomp,,
|
||||
129,Nacli,,
|
||||
130,Naclstack,,
|
||||
131,Garganacl,,
|
||||
132,Wingull,,
|
||||
133,Pelipper,,
|
||||
134,Magikarp,,
|
||||
135,Gyarados,,
|
||||
136,Arrokuda,,
|
||||
137,Barraskewda,,
|
||||
138,Basculin,,
|
||||
139,Gulpin,,
|
||||
140,Swalot,,
|
||||
141,Meowth,,
|
||||
142,Persian,,
|
||||
143,Drifloon,,
|
||||
144,Drifblim,,
|
||||
145,Flabébé,,
|
||||
146,Floette,,
|
||||
147,Florges,,
|
||||
148,Diglett,,
|
||||
149,Dugtrio,,
|
||||
150,Torkoal,,
|
||||
151,Numel,,
|
||||
152,Camerupt,,
|
||||
153,Bronzor,,
|
||||
154,Bronzong,,
|
||||
155,Axew,,
|
||||
156,Fraxure,,
|
||||
157,Haxorus,,
|
||||
158,Mankey,,
|
||||
159,Primeape,,
|
||||
160,Annihilape,,
|
||||
161,Meditite,,
|
||||
162,Medicham,,
|
||||
163,Riolu,,
|
||||
164,Lucario,,
|
||||
165,Charcadet,,
|
||||
166,Armarouge,,
|
||||
167,Ceruledge,,
|
||||
168,Barboach,,
|
||||
169,Whiscash,,
|
||||
170,Tadbulb,,
|
||||
171,Bellibolt,,
|
||||
172,Goomy,,
|
||||
173,Sliggoo,,
|
||||
174,Goodra,,
|
||||
175,Croagunk,,
|
||||
176,Toxicroak,,
|
||||
177,Wattrel,,
|
||||
178,Kilowattrel,,
|
||||
179,Eevee,,
|
||||
180,Vaporeon,,
|
||||
181,Jolteon,,
|
||||
182,Flareon,,
|
||||
183,Espeon,,
|
||||
184,Umbreon,,
|
||||
185,Leafeon,,
|
||||
186,Glaceon,,
|
||||
187,Sylveon,,
|
||||
188,Dunsparce,,
|
||||
189,Dudunsparce,,
|
||||
190,Deerling,,
|
||||
191,Sawsbuck,,
|
||||
192,Girafarig,,
|
||||
193,Farigiraf,,
|
||||
194,Grimer,,
|
||||
195,Muk,,
|
||||
196,Maschiff,,
|
||||
197,Mabosstiff,,
|
||||
198,Toxel,,
|
||||
199,Toxtricity,,
|
||||
200,Dedenne,,
|
||||
201,Pachirisu,,
|
||||
202,Shroodle,,
|
||||
203,Grafaiai,,
|
||||
204,Stantler,,
|
||||
205,Foongus,,
|
||||
206,Amoonguss,,
|
||||
207,Voltorb,,
|
||||
208,Electrode,,
|
||||
209,Magnemite,,
|
||||
210,Magneton,,
|
||||
211,Magnezone,,
|
||||
212,Ditto,,
|
||||
213,Growlithe,,
|
||||
214,Arcanine,,
|
||||
215,Teddiursa,,
|
||||
216,Ursaring,,
|
||||
217,Zangoose,,
|
||||
218,Seviper,,
|
||||
219,Swablu,,
|
||||
220,Altaria,,
|
||||
221,Skiddo,,
|
||||
222,Gogoat,,
|
||||
223,Tauros,,
|
||||
224,Litleo,,
|
||||
225,Pyroar,,
|
||||
226,Stunky,,
|
||||
227,Skuntank,,
|
||||
228,Zorua,,
|
||||
229,Zoroark,,
|
||||
230,Sneasel,,
|
||||
231,Weavile,,
|
||||
232,Murkrow,,
|
||||
233,Honchkrow,,
|
||||
234,Gothita,,
|
||||
235,Gothorita,,
|
||||
236,Gothitelle,,
|
||||
237,Sinistea,,
|
||||
238,Polteageist,,
|
||||
239,Mimikyu,,
|
||||
240,Klefki,,
|
||||
241,Indeedee,,
|
||||
242,Bramblin,,
|
||||
243,Brambleghast,,
|
||||
244,Toedscool,,
|
||||
245,Toedscruel,,
|
||||
246,Tropius,,
|
||||
247,Fomantis,,
|
||||
248,Lurantis,,
|
||||
249,Klawf,,
|
||||
250,Capsakid,,
|
||||
251,Scovillain,,
|
||||
252,Cacnea,,
|
||||
253,Cacturne,,
|
||||
254,Rellor,,
|
||||
255,Rabsca,,
|
||||
256,Venonat,,
|
||||
257,Venomoth,,
|
||||
258,Pineco,,
|
||||
259,Forretress,,
|
||||
260,Scyther,,
|
||||
261,Scizor,,
|
||||
262,Heracross,,
|
||||
263,Flittle,,
|
||||
264,Espathra,,
|
||||
265,Hippopotas,,
|
||||
266,Hippowdon,,
|
||||
267,Sandile,,
|
||||
268,Krokorok,,
|
||||
269,Krookodile,,
|
||||
270,Silicobra,,
|
||||
271,Sandaconda,,
|
||||
272,Mudbray,,
|
||||
273,Mudsdale,,
|
||||
274,Larvesta,,
|
||||
275,Volcarona,,
|
||||
276,Bagon,,
|
||||
277,Shelgon,,
|
||||
278,Salamence,,
|
||||
279,Tinkatink,,
|
||||
280,Tinkatuff,,
|
||||
281,Tinkaton,,
|
||||
282,Hatenna,,
|
||||
283,Hattrem,,
|
||||
284,Hatterene,,
|
||||
285,Impidimp,,
|
||||
286,Morgrem,,
|
||||
287,Grimmsnarl,,
|
||||
288,Wiglett,,
|
||||
289,Wugtrio,,
|
||||
290,Bombirdier,,
|
||||
291,Finizen,,
|
||||
292,Palafin,,
|
||||
293,Varoom,,
|
||||
294,Revavroom,,
|
||||
295,Cyclizar,,
|
||||
296,Orthworm,,
|
||||
297,Sableye,,
|
||||
298,Shuppet,,
|
||||
299,Banette,,
|
||||
300,Falinks,,
|
||||
301,Hawlucha,,
|
||||
302,Spiritomb,,
|
||||
303,Noibat,,
|
||||
304,Noivern,,
|
||||
305,Dreepy,,
|
||||
306,Drakloak,,
|
||||
307,Dragapult,,
|
||||
308,Glimmet,,
|
||||
309,Glimmora,,
|
||||
310,Rotom,,
|
||||
311,Greavard,,
|
||||
312,Houndstone,,
|
||||
313,Oranguru,,
|
||||
314,Passimian,,
|
||||
315,Komala,,
|
||||
316,Larvitar,,
|
||||
317,Pupitar,,
|
||||
318,Tyranitar,,
|
||||
319,Stonjourner,,
|
||||
320,Eiscue,,
|
||||
321,Pincurchin,,
|
||||
322,Sandygast,,
|
||||
323,Palossand,,
|
||||
324,Slowpoke,,
|
||||
325,Slowbro,,
|
||||
326,Slowking,,
|
||||
327,Shellos,,
|
||||
328,Gastrodon,,
|
||||
329,Shellder,,
|
||||
330,Cloyster,,
|
||||
331,Qwilfish,,
|
||||
332,Luvdisc,,
|
||||
333,Finneon,,
|
||||
334,Lumineon,,
|
||||
335,Bruxish,,
|
||||
336,Alomomola,,
|
||||
337,Skrelp,,
|
||||
338,Dragalge,,
|
||||
339,Clauncher,,
|
||||
340,Clawitzer,,
|
||||
341,Tynamo,,
|
||||
342,Eelektrik,,
|
||||
343,Eelektross,,
|
||||
344,Mareanie,,
|
||||
345,Toxapex,,
|
||||
346,Flamigo,,
|
||||
347,Dratini,,
|
||||
348,Dragonair,,
|
||||
349,Dragonite,,
|
||||
350,Snom,,
|
||||
351,Frosmoth,,
|
||||
352,Snover,,
|
||||
353,Abomasnow,,
|
||||
354,Delibird,,
|
||||
355,Cubchoo,,
|
||||
356,Beartic,,
|
||||
357,Snorunt,,
|
||||
358,Glalie,,
|
||||
359,Froslass,,
|
||||
360,Cryogonal,,
|
||||
361,Cetoddle,,
|
||||
362,Cetitan,,
|
||||
363,Bergmite,,
|
||||
364,Avalugg,,
|
||||
365,Rufflet,,
|
||||
366,Braviary,,
|
||||
367,Pawniard,,
|
||||
368,Bisharp,,
|
||||
369,Kingambit,,
|
||||
370,Deino,,
|
||||
371,Zweilous,,
|
||||
372,Hydreigon,,
|
||||
373,Veluza,,
|
||||
374,Dondozo,,
|
||||
375,Tatsugiri,,
|
||||
376,Great Tusk,,
|
||||
377,Scream Tail,,
|
||||
378,Brute Bonnet,,
|
||||
379,Flutter Mane,,
|
||||
380,Slither Wing,,
|
||||
381,Sandy Shocks,,
|
||||
382,Iron Treads,,
|
||||
383,Iron Bundle,,
|
||||
384,Iron Hands,,
|
||||
385,Iron Jugulis,,
|
||||
386,Iron Moth,,
|
||||
387,Iron Thorns,,
|
||||
388,Frigibax,,
|
||||
389,Arctibax,,
|
||||
390,Baxcalibur,,
|
||||
391,Gimmighoul,,
|
||||
392,Gholdengo,,
|
||||
393,Wo-Chien,,
|
||||
394,Chien-Pao,,
|
||||
395,Ting-Lu,,
|
||||
396,Chi-Yu,,
|
||||
397,Roaring Moon,,
|
||||
398,Iron Valiant,,
|
||||
399,Koraidon,,
|
||||
400,Miraidon,,
|
||||
|
@@ -1,131 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Pikipek,,
|
||||
2,Trumbeak,,
|
||||
3,Toucannon,,
|
||||
4,Yungoos,,
|
||||
5,Gumshoos,,
|
||||
6,Rattata,,
|
||||
7,Raticate,,
|
||||
8,Caterpie,,
|
||||
9,Metapod,,
|
||||
10,Butterfree,,
|
||||
11,Grubbin,,
|
||||
12,Charjabug,,
|
||||
13,Vikavolt,,
|
||||
14,Bonsly,,
|
||||
15,Sudowoodo,,
|
||||
16,Happiny,,
|
||||
17,Chansey,,
|
||||
18,Blissey,,
|
||||
19,Wingull,,
|
||||
20,Pelipper,,
|
||||
21,Crabrawler,,
|
||||
22,Crabominable,,
|
||||
23,Gastly,,
|
||||
24,Haunter,,
|
||||
25,Gengar,,
|
||||
26,Zubat,,
|
||||
27,Golbat,,
|
||||
28,Crobat,,
|
||||
29,Diglett,,
|
||||
30,Dugtrio,,
|
||||
31,Oricorio,,
|
||||
32,Psyduck,,
|
||||
33,Golduck,,
|
||||
34,Magikarp,,
|
||||
35,Gyarados,,
|
||||
36,Barboach,,
|
||||
37,Whiscash,,
|
||||
38,Tentacool,,
|
||||
39,Tentacruel,,
|
||||
40,Finneon,,
|
||||
41,Lumineon,,
|
||||
42,Wishiwashi,,
|
||||
43,Luvdisc,,
|
||||
44,Corsola,,
|
||||
45,Mareanie,,
|
||||
46,Toxapex,,
|
||||
47,Lillipup,,
|
||||
48,Herdier,,
|
||||
49,Stoutland,,
|
||||
50,Eevee,,
|
||||
51,Vaporeon,,
|
||||
52,Jolteon,,
|
||||
53,Flareon,,
|
||||
54,Espeon,,
|
||||
55,Umbreon,,
|
||||
56,Leafeon,,
|
||||
57,Glaceon,,
|
||||
58,Sylveon,,
|
||||
59,Mudbray,,
|
||||
60,Mudsdale,,
|
||||
61,Igglybuff,,
|
||||
62,Jigglypuff,,
|
||||
63,Wigglytuff,,
|
||||
64,Tauros,,
|
||||
65,Miltank,,
|
||||
66,Surskit,,
|
||||
67,Masquerain,,
|
||||
68,Dewpider,,
|
||||
69,Araquanid,,
|
||||
70,Fomantis,,
|
||||
71,Lurantis,,
|
||||
72,Morelull,,
|
||||
73,Shiinotic,,
|
||||
74,Paras,,
|
||||
75,Parasect,,
|
||||
76,Poliwag,,
|
||||
77,Poliwhirl,,
|
||||
78,Poliwrath,,
|
||||
79,Politoed,,
|
||||
80,Goldeen,,
|
||||
81,Seaking,,
|
||||
82,Feebas,,
|
||||
83,Milotic,,
|
||||
84,Alomomola,,
|
||||
85,Fletchling,,
|
||||
86,Fletchinder,,
|
||||
87,Talonflame,,
|
||||
88,Salandit,,
|
||||
89,Salazzle,,
|
||||
90,Cubone,,
|
||||
91,Marowak,,
|
||||
92,Kangaskhan,,
|
||||
93,Magby,,
|
||||
94,Magmar,,
|
||||
95,Magmortar,,
|
||||
96,Stufful,,
|
||||
97,Bewear,,
|
||||
98,Bounsweet,,
|
||||
99,Steenee,,
|
||||
100,Tsareena,,
|
||||
101,Comfey,,
|
||||
102,Pinsir,,
|
||||
103,Oranguru,,
|
||||
104,Passimian,,
|
||||
105,Goomy,,
|
||||
106,Sliggoo,,
|
||||
107,Goodra,,
|
||||
108,Castform,,
|
||||
109,Wimpod,,
|
||||
110,Golisopod,,
|
||||
111,Staryu,,
|
||||
112,Starmie,,
|
||||
113,Sandygast,,
|
||||
114,Palossand,,
|
||||
115,Cranidos,,
|
||||
116,Rampardos,,
|
||||
117,Shieldon,,
|
||||
118,Bastiodon,,
|
||||
119,Archen,,
|
||||
120,Archeops,,
|
||||
121,Tirtouga,,
|
||||
122,Carracosta,,
|
||||
123,Phantump,,
|
||||
124,Trevenant,,
|
||||
125,Nosepass,,
|
||||
126,Probopass,,
|
||||
127,Pyukumuku,,
|
||||
128,Chinchou,,
|
||||
129,Lanturn,,
|
||||
130,Tapu Lele,,
|
||||
|
@@ -1,303 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Rowlet,,
|
||||
2,Dartrix,,
|
||||
3,Decidueye,,
|
||||
4,Litten,,
|
||||
5,Torracat,,
|
||||
6,Incineroar,,
|
||||
7,Popplio,,
|
||||
8,Brionne,,
|
||||
9,Primarina,,
|
||||
10,Pikipek,,
|
||||
11,Trumbeak,,
|
||||
12,Toucannon,,
|
||||
13,Yungoos,,
|
||||
14,Gumshoos,,
|
||||
15,Rattata,,
|
||||
16,Raticate,,
|
||||
17,Caterpie,,
|
||||
18,Metapod,,
|
||||
19,Butterfree,,
|
||||
20,Ledyba,,
|
||||
21,Ledian,,
|
||||
22,Spinarak,,
|
||||
23,Ariados,,
|
||||
24,Pichu,,
|
||||
25,Pikachu,,
|
||||
26,Raichu,,
|
||||
27,Grubbin,,
|
||||
28,Charjabug,,
|
||||
29,Vikavolt,,
|
||||
30,Bonsly,,
|
||||
31,Sudowoodo,,
|
||||
32,Happiny,,
|
||||
33,Chansey,,
|
||||
34,Blissey,,
|
||||
35,Munchlax,,
|
||||
36,Snorlax,,
|
||||
37,Slowpoke,,
|
||||
38,Slowbro,,
|
||||
39,Slowking,,
|
||||
40,Wingull,,
|
||||
41,Pelipper,,
|
||||
42,Abra,,
|
||||
43,Kadabra,,
|
||||
44,Alakazam,,
|
||||
45,Meowth,,
|
||||
46,Persian,,
|
||||
47,Magnemite,,
|
||||
48,Magneton,,
|
||||
49,Magnezone,,
|
||||
50,Grimer,,
|
||||
51,Muk,,
|
||||
52,Growlithe,,
|
||||
53,Arcanine,,
|
||||
54,Drowzee,,
|
||||
55,Hypno,,
|
||||
56,Makuhita,,
|
||||
57,Hariyama,,
|
||||
58,Smeargle,,
|
||||
59,Crabrawler,,
|
||||
60,Crabominable,,
|
||||
61,Gastly,,
|
||||
62,Haunter,,
|
||||
63,Gengar,,
|
||||
64,Drifloon,,
|
||||
65,Drifblim,,
|
||||
66,Misdreavus,,
|
||||
67,Mismagius,,
|
||||
68,Zubat,,
|
||||
69,Golbat,,
|
||||
70,Crobat,,
|
||||
71,Diglett,,
|
||||
72,Dugtrio,,
|
||||
73,Spearow,,
|
||||
74,Fearow,,
|
||||
75,Rufflet,,
|
||||
76,Braviary,,
|
||||
77,Vullaby,,
|
||||
78,Mandibuzz,,
|
||||
79,Mankey,,
|
||||
80,Primeape,,
|
||||
81,Delibird,,
|
||||
82,Oricorio,,
|
||||
83,Cutiefly,,
|
||||
84,Ribombee,,
|
||||
85,Petilil,,
|
||||
86,Lilligant,,
|
||||
87,Cottonee,,
|
||||
88,Whimsicott,,
|
||||
89,Psyduck,,
|
||||
90,Golduck,,
|
||||
91,Magikarp,,
|
||||
92,Gyarados,,
|
||||
93,Barboach,,
|
||||
94,Whiscash,,
|
||||
95,Machop,,
|
||||
96,Machoke,,
|
||||
97,Machamp,,
|
||||
98,Roggenrola,,
|
||||
99,Boldore,,
|
||||
100,Gigalith,,
|
||||
101,Carbink,,
|
||||
102,Sableye,,
|
||||
103,Rockruff,,
|
||||
104,Lycanroc,,
|
||||
105,Spinda,,
|
||||
106,Tentacool,,
|
||||
107,Tentacruel,,
|
||||
108,Finneon,,
|
||||
109,Lumineon,,
|
||||
110,Wishiwashi,,
|
||||
111,Luvdisc,,
|
||||
112,Corsola,,
|
||||
113,Mareanie,,
|
||||
114,Toxapex,,
|
||||
115,Shellder,,
|
||||
116,Cloyster,,
|
||||
117,Bagon,,
|
||||
118,Shelgon,,
|
||||
119,Salamence,,
|
||||
120,Lillipup,,
|
||||
121,Herdier,,
|
||||
122,Stoutland,,
|
||||
123,Eevee,,
|
||||
124,Vaporeon,,
|
||||
125,Jolteon,,
|
||||
126,Flareon,,
|
||||
127,Espeon,,
|
||||
128,Umbreon,,
|
||||
129,Leafeon,,
|
||||
130,Glaceon,,
|
||||
131,Sylveon,,
|
||||
132,Mudbray,,
|
||||
133,Mudsdale,,
|
||||
134,Igglybuff,,
|
||||
135,Jigglypuff,,
|
||||
136,Wigglytuff,,
|
||||
137,Tauros,,
|
||||
138,Miltank,,
|
||||
139,Surskit,,
|
||||
140,Masquerain,,
|
||||
141,Dewpider,,
|
||||
142,Araquanid,,
|
||||
143,Fomantis,,
|
||||
144,Lurantis,,
|
||||
145,Morelull,,
|
||||
146,Shiinotic,,
|
||||
147,Paras,,
|
||||
148,Parasect,,
|
||||
149,Poliwag,,
|
||||
150,Poliwhirl,,
|
||||
151,Poliwrath,,
|
||||
152,Politoed,,
|
||||
153,Goldeen,,
|
||||
154,Seaking,,
|
||||
155,Feebas,,
|
||||
156,Milotic,,
|
||||
157,Alomomola,,
|
||||
158,Fletchling,,
|
||||
159,Fletchinder,,
|
||||
160,Talonflame,,
|
||||
161,Salandit,,
|
||||
162,Salazzle,,
|
||||
163,Cubone,,
|
||||
164,Marowak,,
|
||||
165,Kangaskhan,,
|
||||
166,Magby,,
|
||||
167,Magmar,,
|
||||
168,Magmortar,,
|
||||
169,Stufful,,
|
||||
170,Bewear,,
|
||||
171,Bounsweet,,
|
||||
172,Steenee,,
|
||||
173,Tsareena,,
|
||||
174,Comfey,,
|
||||
175,Pinsir,,
|
||||
176,Oranguru,,
|
||||
177,Passimian,,
|
||||
178,Goomy,,
|
||||
179,Sliggoo,,
|
||||
180,Goodra,,
|
||||
181,Castform,,
|
||||
182,Wimpod,,
|
||||
183,Golisopod,,
|
||||
184,Staryu,,
|
||||
185,Starmie,,
|
||||
186,Sandygast,,
|
||||
187,Palossand,,
|
||||
188,Cranidos,,
|
||||
189,Rampardos,,
|
||||
190,Shieldon,,
|
||||
191,Bastiodon,,
|
||||
192,Archen,,
|
||||
193,Archeops,,
|
||||
194,Tirtouga,,
|
||||
195,Carracosta,,
|
||||
196,Phantump,,
|
||||
197,Trevenant,,
|
||||
198,Nosepass,,
|
||||
199,Probopass,,
|
||||
200,Pyukumuku,,
|
||||
201,Chinchou,,
|
||||
202,Lanturn,,
|
||||
203,Type: Null,,
|
||||
204,Silvally,,
|
||||
205,Zygarde,,
|
||||
206,Trubbish,,
|
||||
207,Garbodor,,
|
||||
208,Skarmory,,
|
||||
209,Ditto,,
|
||||
210,Cleffa,,
|
||||
211,Clefairy,,
|
||||
212,Clefable,,
|
||||
213,Minior,,
|
||||
214,Beldum,,
|
||||
215,Metang,,
|
||||
216,Metagross,,
|
||||
217,Porygon,,
|
||||
218,Porygon2,,
|
||||
219,Porygon-Z,,
|
||||
220,Pancham,,
|
||||
221,Pangoro,,
|
||||
222,Komala,,
|
||||
223,Torkoal,,
|
||||
224,Turtonator,,
|
||||
225,Togedemaru,,
|
||||
226,Elekid,,
|
||||
227,Electabuzz,,
|
||||
228,Electivire,,
|
||||
229,Geodude,,
|
||||
230,Graveler,,
|
||||
231,Golem,,
|
||||
232,Sandile,,
|
||||
233,Krokorok,,
|
||||
234,Krookodile,,
|
||||
235,Trapinch,,
|
||||
236,Vibrava,,
|
||||
237,Flygon,,
|
||||
238,Gible,,
|
||||
239,Gabite,,
|
||||
240,Garchomp,,
|
||||
241,Klefki,,
|
||||
242,Mimikyu,,
|
||||
243,Bruxish,,
|
||||
244,Drampa,,
|
||||
245,Absol,,
|
||||
246,Snorunt,,
|
||||
247,Glalie,,
|
||||
248,Froslass,,
|
||||
249,Sneasel,,
|
||||
250,Weavile,,
|
||||
251,Sandshrew,,
|
||||
252,Sandslash,,
|
||||
253,Vulpix,,
|
||||
254,Ninetales,,
|
||||
255,Vanillite,,
|
||||
256,Vanillish,,
|
||||
257,Vanilluxe,,
|
||||
258,Snubbull,,
|
||||
259,Granbull,,
|
||||
260,Shellos,,
|
||||
261,Gastrodon,,
|
||||
262,Relicanth,,
|
||||
263,Dhelmise,,
|
||||
264,Carvanha,,
|
||||
265,Sharpedo,,
|
||||
266,Wailmer,,
|
||||
267,Wailord,,
|
||||
268,Lapras,,
|
||||
269,Exeggcute,,
|
||||
270,Exeggutor,,
|
||||
271,Jangmo-o,,
|
||||
272,Hakamo-o,,
|
||||
273,Kommo-o,,
|
||||
274,Emolga,,
|
||||
275,Scyther,,
|
||||
276,Scizor,,
|
||||
277,Murkrow,,
|
||||
278,Honchkrow,,
|
||||
279,Riolu,,
|
||||
280,Lucario,,
|
||||
281,Dratini,,
|
||||
282,Dragonair,,
|
||||
283,Dragonite,,
|
||||
284,Aerodactyl,,
|
||||
285,Tapu Koko,,
|
||||
286,Tapu Lele,,
|
||||
287,Tapu Bulu,,
|
||||
288,Tapu Fini,,
|
||||
289,Cosmog,,
|
||||
290,Cosmoem,,
|
||||
291,Solgaleo,,
|
||||
292,Lunala,,
|
||||
293,Nihilego,,
|
||||
294,Buzzwole,,
|
||||
295,Pheromosa,,
|
||||
296,Xurkitree,,
|
||||
297,Celesteela,,
|
||||
298,Kartana,,
|
||||
299,Guzzlord,,
|
||||
300,Necrozma,,
|
||||
301,Magearna,,
|
||||
302,Marshadow,,
|
||||
|
@@ -1,121 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Rowlet,,
|
||||
2,Dartrix,,
|
||||
3,Decidueye,,
|
||||
4,Litten,,
|
||||
5,Torracat,,
|
||||
6,Incineroar,,
|
||||
7,Popplio,,
|
||||
8,Brionne,,
|
||||
9,Primarina,,
|
||||
10,Pikipek,,
|
||||
11,Trumbeak,,
|
||||
12,Toucannon,,
|
||||
13,Yungoos,,
|
||||
14,Gumshoos,,
|
||||
15,Rattata,,
|
||||
16,Raticate,,
|
||||
17,Caterpie,,
|
||||
18,Metapod,,
|
||||
19,Butterfree,,
|
||||
20,Ledyba,,
|
||||
21,Ledian,,
|
||||
22,Spinarak,,
|
||||
23,Ariados,,
|
||||
24,Pichu,,
|
||||
25,Pikachu,,
|
||||
26,Raichu,,
|
||||
27,Grubbin,,
|
||||
28,Charjabug,,
|
||||
29,Vikavolt,,
|
||||
30,Bonsly,,
|
||||
31,Sudowoodo,,
|
||||
32,Happiny,,
|
||||
33,Chansey,,
|
||||
34,Blissey,,
|
||||
35,Munchlax,,
|
||||
36,Snorlax,,
|
||||
37,Slowpoke,,
|
||||
38,Slowbro,,
|
||||
39,Slowking,,
|
||||
40,Wingull,,
|
||||
41,Pelipper,,
|
||||
42,Abra,,
|
||||
43,Kadabra,,
|
||||
44,Alakazam,,
|
||||
45,Meowth,,
|
||||
46,Persian,,
|
||||
47,Magnemite,,
|
||||
48,Magneton,,
|
||||
49,Magnezone,,
|
||||
50,Grimer,,
|
||||
51,Muk,,
|
||||
52,Growlithe,,
|
||||
53,Arcanine,,
|
||||
54,Drowzee,,
|
||||
55,Hypno,,
|
||||
56,Makuhita,,
|
||||
57,Hariyama,,
|
||||
58,Smeargle,,
|
||||
59,Crabrawler,,
|
||||
60,Crabominable,,
|
||||
61,Gastly,,
|
||||
62,Haunter,,
|
||||
63,Gengar,,
|
||||
64,Drifloon,,
|
||||
65,Drifblim,,
|
||||
66,Misdreavus,,
|
||||
67,Mismagius,,
|
||||
68,Zubat,,
|
||||
69,Golbat,,
|
||||
70,Crobat,,
|
||||
71,Diglett,,
|
||||
72,Dugtrio,,
|
||||
73,Spearow,,
|
||||
74,Fearow,,
|
||||
75,Rufflet,,
|
||||
76,Braviary,,
|
||||
77,Vullaby,,
|
||||
78,Mandibuzz,,
|
||||
79,Mankey,,
|
||||
80,Primeape,,
|
||||
81,Delibird,,
|
||||
82,Oricorio,,
|
||||
83,Cutiefly,,
|
||||
84,Ribombee,,
|
||||
85,Petilil,,
|
||||
86,Lilligant,,
|
||||
87,Cottonee,,
|
||||
88,Whimsicott,,
|
||||
89,Psyduck,,
|
||||
90,Golduck,,
|
||||
91,Magikarp,,
|
||||
92,Gyarados,,
|
||||
93,Barboach,,
|
||||
94,Whiscash,,
|
||||
95,Machop,,
|
||||
96,Machoke,,
|
||||
97,Machamp,,
|
||||
98,Roggenrola,,
|
||||
99,Boldore,,
|
||||
100,Gigalith,,
|
||||
101,Carbink,,
|
||||
102,Sableye,,
|
||||
103,Rockruff,,
|
||||
104,Lycanroc,,
|
||||
105,Spinda,,
|
||||
106,Tentacool,,
|
||||
107,Tentacruel,,
|
||||
108,Finneon,,
|
||||
109,Lumineon,,
|
||||
110,Wishiwashi,,
|
||||
111,Luvdisc,,
|
||||
112,Corsola,,
|
||||
113,Mareanie,,
|
||||
114,Toxapex,,
|
||||
115,Shellder,,
|
||||
116,Cloyster,,
|
||||
117,Bagon,,
|
||||
118,Shelgon,,
|
||||
119,Salamence,,
|
||||
120,Tapu Koko,,
|
||||
|
@@ -1,101 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Pikipek,,
|
||||
2,Trumbeak,,
|
||||
3,Toucannon,,
|
||||
4,Yungoos,,
|
||||
5,Gumshoos,,
|
||||
6,Rattata,,
|
||||
7,Raticate,,
|
||||
8,Happiny,,
|
||||
9,Chansey,,
|
||||
10,Blissey,,
|
||||
11,Wingull,,
|
||||
12,Pelipper,,
|
||||
13,Drowzee,,
|
||||
14,Hypno,,
|
||||
15,Makuhita,,
|
||||
16,Hariyama,,
|
||||
17,Crabrawler,,
|
||||
18,Crabominable,,
|
||||
19,Zubat,,
|
||||
20,Golbat,,
|
||||
21,Crobat,,
|
||||
22,Diglett,,
|
||||
23,Dugtrio,,
|
||||
24,Spearow,,
|
||||
25,Fearow,,
|
||||
26,Rufflet,,
|
||||
27,Braviary,,
|
||||
28,Vullaby,,
|
||||
29,Mandibuzz,,
|
||||
30,Mankey,,
|
||||
31,Primeape,,
|
||||
32,Oricorio,,
|
||||
33,Cutiefly,,
|
||||
34,Ribombee,,
|
||||
35,Petilil,,
|
||||
36,Lilligant,,
|
||||
37,Cottonee,,
|
||||
38,Whimsicott,,
|
||||
39,Psyduck,,
|
||||
40,Golduck,,
|
||||
41,Magikarp,,
|
||||
42,Gyarados,,
|
||||
43,Barboach,,
|
||||
44,Whiscash,,
|
||||
45,Machop,,
|
||||
46,Machoke,,
|
||||
47,Machamp,,
|
||||
48,Roggenrola,,
|
||||
49,Boldore,,
|
||||
50,Gigalith,,
|
||||
51,Carbink,,
|
||||
52,Sableye,,
|
||||
53,Rockruff,,
|
||||
54,Lycanroc,,
|
||||
55,Tentacool,,
|
||||
56,Tentacruel,,
|
||||
57,Finneon,,
|
||||
58,Lumineon,,
|
||||
59,Mudbray,,
|
||||
60,Mudsdale,,
|
||||
61,Tauros,,
|
||||
62,Miltank,,
|
||||
63,Stufful,,
|
||||
64,Bewear,,
|
||||
65,Pinsir,,
|
||||
66,Goomy,,
|
||||
67,Sliggoo,,
|
||||
68,Goodra,,
|
||||
69,Castform,,
|
||||
70,Wimpod,,
|
||||
71,Golisopod,,
|
||||
72,Skarmory,,
|
||||
73,Snubbull,,
|
||||
74,Granbull,,
|
||||
75,Shellos,,
|
||||
76,Gastrodon,,
|
||||
77,Relicanth,,
|
||||
78,Dhelmise,,
|
||||
79,Carvanha,,
|
||||
80,Sharpedo,,
|
||||
81,Wailmer,,
|
||||
82,Wailord,,
|
||||
83,Lapras,,
|
||||
84,Exeggcute,,
|
||||
85,Exeggutor,,
|
||||
86,Jangmo-o,,
|
||||
87,Hakamo-o,,
|
||||
88,Kommo-o,,
|
||||
89,Emolga,,
|
||||
90,Scyther,,
|
||||
91,Scizor,,
|
||||
92,Murkrow,,
|
||||
93,Honchkrow,,
|
||||
94,Riolu,,
|
||||
95,Lucario,,
|
||||
96,Dratini,,
|
||||
97,Dragonair,,
|
||||
98,Dragonite,,
|
||||
99,Aerodactyl,,
|
||||
100,Tapu Fini,,
|
||||
|
@@ -1,131 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Pikipek,,
|
||||
2,Trumbeak,,
|
||||
3,Toucannon,,
|
||||
4,Yungoos,,
|
||||
5,Gumshoos,,
|
||||
6,Rattata,,
|
||||
7,Raticate,,
|
||||
8,Ledyba,,
|
||||
9,Ledian,,
|
||||
10,Spinarak,,
|
||||
11,Ariados,,
|
||||
12,Grubbin,,
|
||||
13,Charjabug,,
|
||||
14,Vikavolt,,
|
||||
15,Happiny,,
|
||||
16,Chansey,,
|
||||
17,Blissey,,
|
||||
18,Slowpoke,,
|
||||
19,Slowbro,,
|
||||
20,Slowking,,
|
||||
21,Wingull,,
|
||||
22,Pelipper,,
|
||||
23,Meowth,,
|
||||
24,Persian,,
|
||||
25,Magnemite,,
|
||||
26,Magneton,,
|
||||
27,Magnezone,,
|
||||
28,Grimer,,
|
||||
29,Muk,,
|
||||
30,Crabrawler,,
|
||||
31,Crabominable,,
|
||||
32,Gastly,,
|
||||
33,Haunter,,
|
||||
34,Gengar,,
|
||||
35,Zubat,,
|
||||
36,Golbat,,
|
||||
37,Crobat,,
|
||||
38,Diglett,,
|
||||
39,Dugtrio,,
|
||||
40,Spearow,,
|
||||
41,Fearow,,
|
||||
42,Oricorio,,
|
||||
43,Cutiefly,,
|
||||
44,Ribombee,,
|
||||
45,Petilil,,
|
||||
46,Lilligant,,
|
||||
47,Cottonee,,
|
||||
48,Whimsicott,,
|
||||
49,Psyduck,,
|
||||
50,Golduck,,
|
||||
51,Magikarp,,
|
||||
52,Gyarados,,
|
||||
53,Tentacool,,
|
||||
54,Tentacruel,,
|
||||
55,Finneon,,
|
||||
56,Lumineon,,
|
||||
57,Wishiwashi,,
|
||||
58,Mudbray,,
|
||||
59,Mudsdale,,
|
||||
60,Surskit,,
|
||||
61,Masquerain,,
|
||||
62,Dewpider,,
|
||||
63,Araquanid,,
|
||||
64,Morelull,,
|
||||
65,Shiinotic,,
|
||||
66,Paras,,
|
||||
67,Parasect,,
|
||||
68,Poliwag,,
|
||||
69,Poliwhirl,,
|
||||
70,Poliwrath,,
|
||||
71,Politoed,,
|
||||
72,Goldeen,,
|
||||
73,Seaking,,
|
||||
74,Goomy,,
|
||||
75,Sliggoo,,
|
||||
76,Goodra,,
|
||||
77,Castform,,
|
||||
78,Trubbish,,
|
||||
79,Garbodor,,
|
||||
80,Skarmory,,
|
||||
81,Ditto,,
|
||||
82,Cleffa,,
|
||||
83,Clefairy,,
|
||||
84,Clefable,,
|
||||
85,Minior,,
|
||||
86,Beldum,,
|
||||
87,Metang,,
|
||||
88,Metagross,,
|
||||
89,Porygon,,
|
||||
90,Porygon2,,
|
||||
91,Porygon-Z,,
|
||||
92,Pancham,,
|
||||
93,Pangoro,,
|
||||
94,Komala,,
|
||||
95,Torkoal,,
|
||||
96,Turtonator,,
|
||||
97,Togedemaru,,
|
||||
98,Elekid,,
|
||||
99,Electabuzz,,
|
||||
100,Electivire,,
|
||||
101,Geodude,,
|
||||
102,Graveler,,
|
||||
103,Golem,,
|
||||
104,Sandile,,
|
||||
105,Krokorok,,
|
||||
106,Krookodile,,
|
||||
107,Trapinch,,
|
||||
108,Vibrava,,
|
||||
109,Flygon,,
|
||||
110,Gible,,
|
||||
111,Gabite,,
|
||||
112,Garchomp,,
|
||||
113,Klefki,,
|
||||
114,Mimikyu,,
|
||||
115,Bruxish,,
|
||||
116,Drampa,,
|
||||
117,Absol,,
|
||||
118,Snorunt,,
|
||||
119,Glalie,,
|
||||
120,Froslass,,
|
||||
121,Sneasel,,
|
||||
122,Weavile,,
|
||||
123,Sandshrew,,
|
||||
124,Sandslash,,
|
||||
125,Vulpix,,
|
||||
126,Ninetales,,
|
||||
127,Vanillite,,
|
||||
128,Vanillish,,
|
||||
129,Vanilluxe,,
|
||||
130,Tapu Bulu,,
|
||||
|
@@ -1,211 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Snom,,
|
||||
2,Frosmoth,,
|
||||
3,Wooloo,,
|
||||
4,Dubwool,,
|
||||
5,Skwovet,,
|
||||
6,Greedent,,
|
||||
7,Swinub,,
|
||||
8,Piloswine,,
|
||||
9,Mamoswine,,
|
||||
10,Mime Jr.,,
|
||||
11,Mr. Mime,,
|
||||
12,Mr. Rime,,
|
||||
13,Smoochum,,
|
||||
14,Jynx,,
|
||||
15,Elekid,,
|
||||
16,Electabuzz,,
|
||||
17,Electivire,,
|
||||
18,Magby,,
|
||||
19,Magmar,,
|
||||
20,Magmortar,,
|
||||
21,Audino,,
|
||||
22,Vanillite,,
|
||||
23,Vanillish,,
|
||||
24,Vanilluxe,,
|
||||
25,Snorunt,,
|
||||
26,Glalie,,
|
||||
27,Froslass,,
|
||||
28,Sneasel,,
|
||||
29,Weavile,,
|
||||
30,Cryogonal,,
|
||||
31,Snover,,
|
||||
32,Abomasnow,,
|
||||
33,Phantump,,
|
||||
34,Trevenant,,
|
||||
35,Swablu,,
|
||||
36,Altaria,,
|
||||
37,Impidimp,,
|
||||
38,Morgrem,,
|
||||
39,Grimmsnarl,,
|
||||
40,Hatenna,,
|
||||
41,Hattrem,,
|
||||
42,Hatterene,,
|
||||
43,Cleffa,,
|
||||
44,Clefairy,,
|
||||
45,Clefable,,
|
||||
46,Mimikyu,,
|
||||
47,Spiritomb,,
|
||||
48,Litwick,,
|
||||
49,Lampent,,
|
||||
50,Chandelure,,
|
||||
51,Gothita,,
|
||||
52,Gothorita,,
|
||||
53,Gothitelle,,
|
||||
54,Solosis,,
|
||||
55,Duosion,,
|
||||
56,Reuniclus,,
|
||||
57,Timburr,,
|
||||
58,Gurdurr,,
|
||||
59,Conkeldurr,,
|
||||
60,Barboach,,
|
||||
61,Whiscash,,
|
||||
62,Magikarp,,
|
||||
63,Gyarados,,
|
||||
64,Basculin,,
|
||||
65,Nidoran♀,,
|
||||
66,Nidorina,,
|
||||
67,Nidoqueen,,
|
||||
68,Nidoran♂,,
|
||||
69,Nidorino,,
|
||||
70,Nidoking,,
|
||||
71,Zigzagoon,,
|
||||
72,Linoone,,
|
||||
73,Obstagoon,,
|
||||
74,Eevee,,
|
||||
75,Vaporeon,,
|
||||
76,Jolteon,,
|
||||
77,Flareon,,
|
||||
78,Umbreon,,
|
||||
79,Espeon,,
|
||||
80,Glaceon,,
|
||||
81,Leafeon,,
|
||||
82,Sylveon,,
|
||||
83,Tyrunt,,
|
||||
84,Tyrantrum,,
|
||||
85,Amaura,,
|
||||
86,Aurorus,,
|
||||
87,Bronzor,,
|
||||
88,Bronzong,,
|
||||
89,Stonjourner,,
|
||||
90,Eiscue,,
|
||||
91,Dewpider,,
|
||||
92,Araquanid,,
|
||||
93,Joltik,,
|
||||
94,Galvantula,,
|
||||
95,Karrablast,,
|
||||
96,Escavalier,,
|
||||
97,Shelmet,,
|
||||
98,Accelgor,,
|
||||
99,Sizzlipede,,
|
||||
100,Centiskorch,,
|
||||
101,Durant,,
|
||||
102,Heatmor,,
|
||||
103,Darumaka,,
|
||||
104,Darmanitan,,
|
||||
105,Ponyta,,
|
||||
106,Rapidash,,
|
||||
107,Absol,,
|
||||
108,Cufant,,
|
||||
109,Copperajah,,
|
||||
110,Dreepy,,
|
||||
111,Drakloak,,
|
||||
112,Dragapult,,
|
||||
113,Bagon,,
|
||||
114,Shelgon,,
|
||||
115,Salamence,,
|
||||
116,Gible,,
|
||||
117,Gabite,,
|
||||
118,Garchomp,,
|
||||
119,Druddigon,,
|
||||
120,Delibird,,
|
||||
121,Cubchoo,,
|
||||
122,Beartic,,
|
||||
123,Omanyte,,
|
||||
124,Omastar,,
|
||||
125,Kabuto,,
|
||||
126,Kabutops,,
|
||||
127,Aerodactyl,,
|
||||
128,Carbink,,
|
||||
129,Beldum,,
|
||||
130,Metang,,
|
||||
131,Metagross,,
|
||||
132,Sinistea,,
|
||||
133,Polteageist,,
|
||||
134,Riolu,,
|
||||
135,Lucario,,
|
||||
136,Deino,,
|
||||
137,Zweilous,,
|
||||
138,Hydreigon,,
|
||||
139,Larvitar,,
|
||||
140,Pupitar,,
|
||||
141,Tyranitar,,
|
||||
142,Bergmite,,
|
||||
143,Avalugg,,
|
||||
144,Zubat,,
|
||||
145,Golbat,,
|
||||
146,Crobat,,
|
||||
147,Tirtouga,,
|
||||
148,Carracosta,,
|
||||
149,Archen,,
|
||||
150,Archeops,,
|
||||
151,Baltoy,,
|
||||
152,Claydol,,
|
||||
153,Golett,,
|
||||
154,Golurk,,
|
||||
155,Yamper,,
|
||||
156,Boltund,,
|
||||
157,Morpeko,,
|
||||
158,Pincurchin,,
|
||||
159,Spheal,,
|
||||
160,Sealeo,,
|
||||
161,Walrein,,
|
||||
162,Dhelmise,,
|
||||
163,Rookidee,,
|
||||
164,Corvisquire,,
|
||||
165,Corviknight,,
|
||||
166,Gossifleur,,
|
||||
167,Eldegoss,,
|
||||
168,Cottonee,,
|
||||
169,Whimsicott,,
|
||||
170,Shuckle,,
|
||||
171,Indeedee,,
|
||||
172,Munchlax,,
|
||||
173,Snorlax,,
|
||||
174,Sableye,,
|
||||
175,Mawile,,
|
||||
176,Rolycoly,,
|
||||
177,Carkol,,
|
||||
178,Coalossal,,
|
||||
179,Ferroseed,,
|
||||
180,Ferrothorn,,
|
||||
181,Noibat,,
|
||||
182,Noivern,,
|
||||
183,Lileep,,
|
||||
184,Cradily,,
|
||||
185,Anorith,,
|
||||
186,Armaldo,,
|
||||
187,Relicanth,,
|
||||
188,Feebas,,
|
||||
189,Milotic,,
|
||||
190,Lapras,,
|
||||
191,Aron,,
|
||||
192,Lairon,,
|
||||
193,Aggron,,
|
||||
194,Dratini,,
|
||||
195,Dragonair,,
|
||||
196,Dragonite,,
|
||||
197,Regirock,,
|
||||
198,Regice,,
|
||||
199,Registeel,,
|
||||
200,Regieleki,,
|
||||
201,Regidrago,,
|
||||
202,Articuno,,
|
||||
203,Zapdos,,
|
||||
204,Moltres,,
|
||||
205,Cobalion,,
|
||||
206,Terrakion,,
|
||||
207,Virizion,,
|
||||
208,Glastrier,,
|
||||
209,Spectrier,,
|
||||
210,Calyrex,,
|
||||
|
@@ -1,212 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Slowpoke,,
|
||||
2,Slowbro,,
|
||||
3,Slowking,,
|
||||
4,Buneary,,
|
||||
5,Lopunny,,
|
||||
6,Happiny,,
|
||||
7,Chansey,,
|
||||
8,Blissey,,
|
||||
9,Skwovet,,
|
||||
10,Greedent,,
|
||||
11,Igglybuff,,
|
||||
12,Jigglypuff,,
|
||||
13,Wigglytuff,,
|
||||
14,Blipbug,,
|
||||
15,Dottler,,
|
||||
16,Orbeetle,,
|
||||
17,Fomantis,,
|
||||
18,Lurantis,,
|
||||
19,Applin,,
|
||||
20,Flapple,,
|
||||
21,Appletun,,
|
||||
22,Fletchling,,
|
||||
23,Fletchinder,,
|
||||
24,Talonflame,,
|
||||
25,Shinx,,
|
||||
26,Luxio,,
|
||||
27,Luxray,,
|
||||
28,Klefki,,
|
||||
29,Pawniard,,
|
||||
30,Bisharp,,
|
||||
31,Abra,,
|
||||
32,Kadabra,,
|
||||
33,Alakazam,,
|
||||
34,Ralts,,
|
||||
35,Kirlia,,
|
||||
36,Gardevoir,,
|
||||
37,Gallade,,
|
||||
38,Krabby,,
|
||||
39,Kingler,,
|
||||
40,Tentacool,,
|
||||
41,Tentacruel,,
|
||||
42,Magikarp,,
|
||||
43,Gyarados,,
|
||||
44,Remoraid,,
|
||||
45,Octillery,,
|
||||
46,Mantyke,,
|
||||
47,Mantine,,
|
||||
48,Wingull,,
|
||||
49,Pelipper,,
|
||||
50,Skorupi,,
|
||||
51,Drapion,,
|
||||
52,Dunsparce,,
|
||||
53,Bouffalant,,
|
||||
54,Lickitung,,
|
||||
55,Lickilicky,,
|
||||
56,Chewtle,,
|
||||
57,Drednaw,,
|
||||
58,Wooper,,
|
||||
59,Quagsire,,
|
||||
60,Goomy,,
|
||||
61,Sliggoo,,
|
||||
62,Goodra,,
|
||||
63,Druddigon,,
|
||||
64,Shelmet,,
|
||||
65,Accelgor,,
|
||||
66,Karrablast,,
|
||||
67,Escavalier,,
|
||||
68,Bulbasaur,,
|
||||
69,Ivysaur,,
|
||||
70,Venusaur,,
|
||||
71,Squirtle,,
|
||||
72,Wartortle,,
|
||||
73,Blastoise,,
|
||||
74,Venipede,,
|
||||
75,Whirlipede,,
|
||||
76,Scolipede,,
|
||||
77,Foongus,,
|
||||
78,Amoonguss,,
|
||||
79,Comfey,,
|
||||
80,Tangela,,
|
||||
81,Tangrowth,,
|
||||
82,Croagunk,,
|
||||
83,Toxicroak,,
|
||||
84,Pichu,,
|
||||
85,Pikachu,,
|
||||
86,Raichu,,
|
||||
87,Zorua,,
|
||||
88,Zoroark,,
|
||||
89,Oranguru,,
|
||||
90,Passimian,,
|
||||
91,Corphish,,
|
||||
92,Crawdaunt,,
|
||||
93,Cramorant,,
|
||||
94,Goldeen,,
|
||||
95,Seaking,,
|
||||
96,Arrokuda,,
|
||||
97,Barraskewda,,
|
||||
98,Staryu,,
|
||||
99,Starmie,,
|
||||
100,Kubfu,,
|
||||
101,Urshifu,,
|
||||
102,Emolga,,
|
||||
103,Dedenne,,
|
||||
104,Morpeko,,
|
||||
105,Magnemite,,
|
||||
106,Magneton,,
|
||||
107,Magnezone,,
|
||||
108,Inkay,,
|
||||
109,Malamar,,
|
||||
110,Wishiwashi,,
|
||||
111,Carvanha,,
|
||||
112,Sharpedo,,
|
||||
113,Lillipup,,
|
||||
114,Herdier,,
|
||||
115,Stoutland,,
|
||||
116,Tauros,,
|
||||
117,Miltank,,
|
||||
118,Scyther,,
|
||||
119,Scizor,,
|
||||
120,Pinsir,,
|
||||
121,Heracross,,
|
||||
122,Dwebble,,
|
||||
123,Crustle,,
|
||||
124,Wimpod,,
|
||||
125,Golisopod,,
|
||||
126,Pincurchin,,
|
||||
127,Mareanie,,
|
||||
128,Toxapex,,
|
||||
129,Clobbopus,,
|
||||
130,Grapploct,,
|
||||
131,Shellder,,
|
||||
132,Cloyster,,
|
||||
133,Sandygast,,
|
||||
134,Palossand,,
|
||||
135,Drifloon,,
|
||||
136,Drifblim,,
|
||||
137,Barboach,,
|
||||
138,Whiscash,,
|
||||
139,Azurill,,
|
||||
140,Marill,,
|
||||
141,Azumarill,,
|
||||
142,Poliwag,,
|
||||
143,Poliwhirl,,
|
||||
144,Poliwrath,,
|
||||
145,Politoed,,
|
||||
146,Psyduck,,
|
||||
147,Golduck,,
|
||||
148,Whismur,,
|
||||
149,Loudred,,
|
||||
150,Exploud,,
|
||||
151,Woobat,,
|
||||
152,Swoobat,,
|
||||
153,Skarmory,,
|
||||
154,Roggenrola,,
|
||||
155,Boldore,,
|
||||
156,Gigalith,,
|
||||
157,Rockruff,,
|
||||
158,Lycanroc,,
|
||||
159,Salandit,,
|
||||
160,Salazzle,,
|
||||
161,Scraggy,,
|
||||
162,Scrafty,,
|
||||
163,Mienfoo,,
|
||||
164,Mienshao,,
|
||||
165,Jangmo-o,,
|
||||
166,Hakamo-o,,
|
||||
167,Kommo-o,,
|
||||
168,Sandshrew,,
|
||||
169,Sandslash,,
|
||||
170,Cubone,,
|
||||
171,Marowak,,
|
||||
172,Kangaskhan,,
|
||||
173,Torkoal,,
|
||||
174,Silicobra,,
|
||||
175,Sandaconda,,
|
||||
176,Sandile,,
|
||||
177,Krokorok,,
|
||||
178,Krookodile,,
|
||||
179,Rufflet,,
|
||||
180,Braviary,,
|
||||
181,Vullaby,,
|
||||
182,Mandibuzz,,
|
||||
183,Rhyhorn,,
|
||||
184,Rhydon,,
|
||||
185,Rhyperior,,
|
||||
186,Larvesta,,
|
||||
187,Volcarona,,
|
||||
188,Chinchou,,
|
||||
189,Lanturn,,
|
||||
190,Wailmer,,
|
||||
191,Wailord,,
|
||||
192,Frillish,,
|
||||
193,Jellicent,,
|
||||
194,Skrelp,,
|
||||
195,Dragalge,,
|
||||
196,Clauncher,,
|
||||
197,Clawitzer,,
|
||||
198,Horsea,,
|
||||
199,Seadra,,
|
||||
200,Kingdra,,
|
||||
201,Petilil,,
|
||||
202,Lilligant,,
|
||||
203,Combee,,
|
||||
204,Vespiquen,,
|
||||
205,Exeggcute,,
|
||||
206,Exeggutor,,
|
||||
207,Ditto,,
|
||||
208,Porygon,,
|
||||
209,Porygon2,,
|
||||
210,Porygon-Z,,
|
||||
211,Zarude,,
|
||||
|
@@ -1,401 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Grookey,,
|
||||
2,Thwackey,,
|
||||
3,Rillaboom,,
|
||||
4,Scorbunny,,
|
||||
5,Raboot,,
|
||||
6,Cinderace,,
|
||||
7,Sobble,,
|
||||
8,Drizzile,,
|
||||
9,Inteleon,,
|
||||
10,Blipbug,,
|
||||
11,Dottler,,
|
||||
12,Orbeetle,,
|
||||
13,Caterpie,,
|
||||
14,Metapod,,
|
||||
15,Butterfree,,
|
||||
16,Grubbin,,
|
||||
17,Charjabug,,
|
||||
18,Vikavolt,,
|
||||
19,Hoothoot,,
|
||||
20,Noctowl,,
|
||||
21,Rookidee,,
|
||||
22,Corvisquire,,
|
||||
23,Corviknight,,
|
||||
24,Skwovet,,
|
||||
25,Greedent,,
|
||||
26,Pidove,,
|
||||
27,Tranquill,,
|
||||
28,Unfezant,,
|
||||
29,Nickit,,
|
||||
30,Thievul,,
|
||||
31,Zigzagoon,,
|
||||
32,Linoone,,
|
||||
33,Obstagoon,,
|
||||
34,Wooloo,,
|
||||
35,Dubwool,,
|
||||
36,Lotad,,
|
||||
37,Lombre,,
|
||||
38,Ludicolo,,
|
||||
39,Seedot,,
|
||||
40,Nuzleaf,,
|
||||
41,Shiftry,,
|
||||
42,Chewtle,,
|
||||
43,Drednaw,,
|
||||
44,Purrloin,,
|
||||
45,Liepard,,
|
||||
46,Yamper,,
|
||||
47,Boltund,,
|
||||
48,Bunnelby,,
|
||||
49,Diggersby,,
|
||||
50,Minccino,,
|
||||
51,Cinccino,,
|
||||
52,Bounsweet,,
|
||||
53,Steenee,,
|
||||
54,Tsareena,,
|
||||
55,Oddish,,
|
||||
56,Gloom,,
|
||||
57,Vileplume,,
|
||||
58,Bellossom,,
|
||||
59,Budew,,
|
||||
60,Roselia,,
|
||||
61,Roserade,,
|
||||
62,Wingull,,
|
||||
63,Pelipper,,
|
||||
64,Joltik,,
|
||||
65,Galvantula,,
|
||||
66,Electrike,,
|
||||
67,Manectric,,
|
||||
68,Vulpix,,
|
||||
69,Ninetales,,
|
||||
70,Growlithe,,
|
||||
71,Arcanine,,
|
||||
72,Vanillite,,
|
||||
73,Vanillish,,
|
||||
74,Vanilluxe,,
|
||||
75,Swinub,,
|
||||
76,Piloswine,,
|
||||
77,Mamoswine,,
|
||||
78,Delibird,,
|
||||
79,Snorunt,,
|
||||
80,Glalie,,
|
||||
81,Froslass,,
|
||||
82,Baltoy,,
|
||||
83,Claydol,,
|
||||
84,Mudbray,,
|
||||
85,Mudsdale,,
|
||||
86,Dwebble,,
|
||||
87,Crustle,,
|
||||
88,Golett,,
|
||||
89,Golurk,,
|
||||
90,Munna,,
|
||||
91,Musharna,,
|
||||
92,Natu,,
|
||||
93,Xatu,,
|
||||
94,Stufful,,
|
||||
95,Bewear,,
|
||||
96,Snover,,
|
||||
97,Abomasnow,,
|
||||
98,Krabby,,
|
||||
99,Kingler,,
|
||||
100,Wooper,,
|
||||
101,Quagsire,,
|
||||
102,Corphish,,
|
||||
103,Crawdaunt,,
|
||||
104,Nincada,,
|
||||
105,Ninjask,,
|
||||
106,Shedinja,,
|
||||
107,Tyrogue,,
|
||||
108,Hitmonlee,,
|
||||
109,Hitmonchan,,
|
||||
110,Hitmontop,,
|
||||
111,Pancham,,
|
||||
112,Pangoro,,
|
||||
113,Klink,,
|
||||
114,Klang,,
|
||||
115,Klinklang,,
|
||||
116,Combee,,
|
||||
117,Vespiquen,,
|
||||
118,Bronzor,,
|
||||
119,Bronzong,,
|
||||
120,Ralts,,
|
||||
121,Kirlia,,
|
||||
122,Gardevoir,,
|
||||
123,Gallade,,
|
||||
124,Drifloon,,
|
||||
125,Drifblim,,
|
||||
126,Gossifleur,,
|
||||
127,Eldegoss,,
|
||||
128,Cherubi,,
|
||||
129,Cherrim,,
|
||||
130,Stunky,,
|
||||
131,Skuntank,,
|
||||
132,Tympole,,
|
||||
133,Palpitoad,,
|
||||
134,Seismitoad,,
|
||||
135,Duskull,,
|
||||
136,Dusclops,,
|
||||
137,Dusknoir,,
|
||||
138,Machop,,
|
||||
139,Machoke,,
|
||||
140,Machamp,,
|
||||
141,Gastly,,
|
||||
142,Haunter,,
|
||||
143,Gengar,,
|
||||
144,Magikarp,,
|
||||
145,Gyarados,,
|
||||
146,Goldeen,,
|
||||
147,Seaking,,
|
||||
148,Remoraid,,
|
||||
149,Octillery,,
|
||||
150,Shellder,,
|
||||
151,Cloyster,,
|
||||
152,Feebas,,
|
||||
153,Milotic,,
|
||||
154,Basculin,,
|
||||
155,Wishiwashi,,
|
||||
156,Pyukumuku,,
|
||||
157,Trubbish,,
|
||||
158,Garbodor,,
|
||||
159,Sizzlipede,,
|
||||
160,Centiskorch,,
|
||||
161,Rolycoly,,
|
||||
162,Carkol,,
|
||||
163,Coalossal,,
|
||||
164,Diglett,,
|
||||
165,Dugtrio,,
|
||||
166,Drilbur,,
|
||||
167,Excadrill,,
|
||||
168,Roggenrola,,
|
||||
169,Boldore,,
|
||||
170,Gigalith,,
|
||||
171,Timburr,,
|
||||
172,Gurdurr,,
|
||||
173,Conkeldurr,,
|
||||
174,Woobat,,
|
||||
175,Swoobat,,
|
||||
176,Noibat,,
|
||||
177,Noivern,,
|
||||
178,Onix,,
|
||||
179,Steelix,,
|
||||
180,Arrokuda,,
|
||||
181,Barraskewda,,
|
||||
182,Meowth,,
|
||||
183,Perrserker,,
|
||||
184,Persian,,
|
||||
185,Milcery,,
|
||||
186,Alcremie,,
|
||||
187,Cutiefly,,
|
||||
188,Ribombee,,
|
||||
189,Ferroseed,,
|
||||
190,Ferrothorn,,
|
||||
191,Pumpkaboo,,
|
||||
192,Gourgeist,,
|
||||
193,Pichu,,
|
||||
194,Pikachu,,
|
||||
195,Raichu,,
|
||||
196,Eevee,,
|
||||
197,Vaporeon,,
|
||||
198,Jolteon,,
|
||||
199,Flareon,,
|
||||
200,Espeon,,
|
||||
201,Umbreon,,
|
||||
202,Leafeon,,
|
||||
203,Glaceon,,
|
||||
204,Sylveon,,
|
||||
205,Applin,,
|
||||
206,Flapple,,
|
||||
207,Appletun,,
|
||||
208,Espurr,,
|
||||
209,Meowstic,,
|
||||
210,Swirlix,,
|
||||
211,Slurpuff,,
|
||||
212,Spritzee,,
|
||||
213,Aromatisse,,
|
||||
214,Dewpider,,
|
||||
215,Araquanid,,
|
||||
216,Wynaut,,
|
||||
217,Wobbuffet,,
|
||||
218,Farfetch’d,,
|
||||
219,Sirfetch’d,,
|
||||
220,Chinchou,,
|
||||
221,Lanturn,,
|
||||
222,Croagunk,,
|
||||
223,Toxicroak,,
|
||||
224,Scraggy,,
|
||||
225,Scrafty,,
|
||||
226,Stunfisk,,
|
||||
227,Shuckle,,
|
||||
228,Barboach,,
|
||||
229,Whiscash,,
|
||||
230,Shellos,,
|
||||
231,Gastrodon,,
|
||||
232,Wimpod,,
|
||||
233,Golisopod,,
|
||||
234,Binacle,,
|
||||
235,Barbaracle,,
|
||||
236,Corsola,,
|
||||
237,Cursola,,
|
||||
238,Impidimp,,
|
||||
239,Morgrem,,
|
||||
240,Grimmsnarl,,
|
||||
241,Hatenna,,
|
||||
242,Hattrem,,
|
||||
243,Hatterene,,
|
||||
244,Salandit,,
|
||||
245,Salazzle,,
|
||||
246,Pawniard,,
|
||||
247,Bisharp,,
|
||||
248,Throh,,
|
||||
249,Sawk,,
|
||||
250,Koffing,,
|
||||
251,Weezing,,
|
||||
252,Bonsly,,
|
||||
253,Sudowoodo,,
|
||||
254,Cleffa,,
|
||||
255,Clefairy,,
|
||||
256,Clefable,,
|
||||
257,Togepi,,
|
||||
258,Togetic,,
|
||||
259,Togekiss,,
|
||||
260,Munchlax,,
|
||||
261,Snorlax,,
|
||||
262,Cottonee,,
|
||||
263,Whimsicott,,
|
||||
264,Rhyhorn,,
|
||||
265,Rhydon,,
|
||||
266,Rhyperior,,
|
||||
267,Gothita,,
|
||||
268,Gothorita,,
|
||||
269,Gothitelle,,
|
||||
270,Solosis,,
|
||||
271,Duosion,,
|
||||
272,Reuniclus,,
|
||||
273,Karrablast,,
|
||||
274,Escavalier,,
|
||||
275,Shelmet,,
|
||||
276,Accelgor,,
|
||||
277,Elgyem,,
|
||||
278,Beheeyem,,
|
||||
279,Cubchoo,,
|
||||
280,Beartic,,
|
||||
281,Rufflet,,
|
||||
282,Braviary,,
|
||||
283,Vullaby,,
|
||||
284,Mandibuzz,,
|
||||
285,Skorupi,,
|
||||
286,Drapion,,
|
||||
287,Litwick,,
|
||||
288,Lampent,,
|
||||
289,Chandelure,,
|
||||
290,Inkay,,
|
||||
291,Malamar,,
|
||||
292,Sneasel,,
|
||||
293,Weavile,,
|
||||
294,Sableye,,
|
||||
295,Mawile,,
|
||||
296,Maractus,,
|
||||
297,Sigilyph,,
|
||||
298,Riolu,,
|
||||
299,Lucario,,
|
||||
300,Torkoal,,
|
||||
301,Mimikyu,,
|
||||
302,Cufant,,
|
||||
303,Copperajah,,
|
||||
304,Qwilfish,,
|
||||
305,Frillish,,
|
||||
306,Jellicent,,
|
||||
307,Mareanie,,
|
||||
308,Toxapex,,
|
||||
309,Cramorant,,
|
||||
310,Toxel,,
|
||||
311,Toxtricity,,
|
||||
312,Silicobra,,
|
||||
313,Sandaconda,,
|
||||
314,Hippopotas,,
|
||||
315,Hippowdon,,
|
||||
316,Durant,,
|
||||
317,Heatmor,,
|
||||
318,Helioptile,,
|
||||
319,Heliolisk,,
|
||||
320,Hawlucha,,
|
||||
321,Trapinch,,
|
||||
322,Vibrava,,
|
||||
323,Flygon,,
|
||||
324,Axew,,
|
||||
325,Fraxure,,
|
||||
326,Haxorus,,
|
||||
327,Yamask,,
|
||||
328,Runerigus,,
|
||||
329,Cofagrigus,,
|
||||
330,Honedge,,
|
||||
331,Doublade,,
|
||||
332,Aegislash,,
|
||||
333,Ponyta,,
|
||||
334,Rapidash,,
|
||||
335,Sinistea,,
|
||||
336,Polteageist,,
|
||||
337,Indeedee,,
|
||||
338,Phantump,,
|
||||
339,Trevenant,,
|
||||
340,Morelull,,
|
||||
341,Shiinotic,,
|
||||
342,Oranguru,,
|
||||
343,Passimian,,
|
||||
344,Morpeko,,
|
||||
345,Falinks,,
|
||||
346,Drampa,,
|
||||
347,Turtonator,,
|
||||
348,Togedemaru,,
|
||||
349,Snom,,
|
||||
350,Frosmoth,,
|
||||
351,Clobbopus,,
|
||||
352,Grapploct,,
|
||||
353,Pincurchin,,
|
||||
354,Mantyke,,
|
||||
355,Mantine,,
|
||||
356,Wailmer,,
|
||||
357,Wailord,,
|
||||
358,Bergmite,,
|
||||
359,Avalugg,,
|
||||
360,Dhelmise,,
|
||||
361,Lapras,,
|
||||
362,Lunatone,,
|
||||
363,Solrock,,
|
||||
364,Mime Jr.,,
|
||||
365,Mr. Mime,,
|
||||
366,Mr. Rime,,
|
||||
367,Darumaka,,
|
||||
368,Darmanitan,,
|
||||
369,Stonjourner,,
|
||||
370,Eiscue,,
|
||||
371,Duraludon,,
|
||||
372,Rotom,,
|
||||
373,Ditto,,
|
||||
374,Dracozolt,,
|
||||
375,Arctozolt,,
|
||||
376,Dracovish,,
|
||||
377,Arctovish,,
|
||||
378,Charmander,,
|
||||
379,Charmeleon,,
|
||||
380,Charizard,,
|
||||
381,Type: Null,,
|
||||
382,Silvally,,
|
||||
383,Larvitar,,
|
||||
384,Pupitar,,
|
||||
385,Tyranitar,,
|
||||
386,Deino,,
|
||||
387,Zweilous,,
|
||||
388,Hydreigon,,
|
||||
389,Goomy,,
|
||||
390,Sliggoo,,
|
||||
391,Goodra,,
|
||||
392,Jangmo-o,,
|
||||
393,Hakamo-o,,
|
||||
394,Kommo-o,,
|
||||
395,Dreepy,,
|
||||
396,Drakloak,,
|
||||
397,Dragapult,,
|
||||
398,Zacian,,
|
||||
399,Zamazenta,,
|
||||
400,Eternatus,,
|
||||
|
@@ -1,161 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Pikipek,,
|
||||
2,Trumbeak,,
|
||||
3,Toucannon,,
|
||||
4,Yungoos,,
|
||||
5,Gumshoos,,
|
||||
6,Rattata,,
|
||||
7,Raticate,,
|
||||
8,Caterpie,,
|
||||
9,Metapod,,
|
||||
10,Butterfree,,
|
||||
11,Grubbin,,
|
||||
12,Charjabug,,
|
||||
13,Vikavolt,,
|
||||
14,Bonsly,,
|
||||
15,Sudowoodo,,
|
||||
16,Happiny,,
|
||||
17,Chansey,,
|
||||
18,Blissey,,
|
||||
19,Wingull,,
|
||||
20,Pelipper,,
|
||||
21,Crabrawler,,
|
||||
22,Crabominable,,
|
||||
23,Gastly,,
|
||||
24,Haunter,,
|
||||
25,Gengar,,
|
||||
26,Zubat,,
|
||||
27,Golbat,,
|
||||
28,Crobat,,
|
||||
29,Diglett,,
|
||||
30,Dugtrio,,
|
||||
31,Oricorio,,
|
||||
32,Psyduck,,
|
||||
33,Golduck,,
|
||||
34,Magikarp,,
|
||||
35,Gyarados,,
|
||||
36,Barboach,,
|
||||
37,Whiscash,,
|
||||
38,Tentacool,,
|
||||
39,Tentacruel,,
|
||||
40,Finneon,,
|
||||
41,Lumineon,,
|
||||
42,Wishiwashi,,
|
||||
43,Luvdisc,,
|
||||
44,Corsola,,
|
||||
45,Mareanie,,
|
||||
46,Toxapex,,
|
||||
47,Remoraid,,
|
||||
48,Octillery,,
|
||||
49,Mantyke,,
|
||||
50,Mantine,,
|
||||
51,Lillipup,,
|
||||
52,Herdier,,
|
||||
53,Stoutland,,
|
||||
54,Eevee,,
|
||||
55,Vaporeon,,
|
||||
56,Jolteon,,
|
||||
57,Flareon,,
|
||||
58,Espeon,,
|
||||
59,Umbreon,,
|
||||
60,Leafeon,,
|
||||
61,Glaceon,,
|
||||
62,Sylveon,,
|
||||
63,Mareep,,
|
||||
64,Flaaffy,,
|
||||
65,Ampharos,,
|
||||
66,Mudbray,,
|
||||
67,Mudsdale,,
|
||||
68,Igglybuff,,
|
||||
69,Jigglypuff,,
|
||||
70,Wigglytuff,,
|
||||
71,Tauros,,
|
||||
72,Miltank,,
|
||||
73,Surskit,,
|
||||
74,Masquerain,,
|
||||
75,Dewpider,,
|
||||
76,Araquanid,,
|
||||
77,Fomantis,,
|
||||
78,Lurantis,,
|
||||
79,Morelull,,
|
||||
80,Shiinotic,,
|
||||
81,Paras,,
|
||||
82,Parasect,,
|
||||
83,Poliwag,,
|
||||
84,Poliwhirl,,
|
||||
85,Poliwrath,,
|
||||
86,Politoed,,
|
||||
87,Goldeen,,
|
||||
88,Seaking,,
|
||||
89,Basculin,,
|
||||
90,Feebas,,
|
||||
91,Milotic,,
|
||||
92,Alomomola,,
|
||||
93,Fletchling,,
|
||||
94,Fletchinder,,
|
||||
95,Talonflame,,
|
||||
96,Salandit,,
|
||||
97,Salazzle,,
|
||||
98,Cubone,,
|
||||
99,Marowak,,
|
||||
100,Kangaskhan,,
|
||||
101,Magby,,
|
||||
102,Magmar,,
|
||||
103,Magmortar,,
|
||||
104,Larvesta,,
|
||||
105,Volcarona,,
|
||||
106,Stufful,,
|
||||
107,Bewear,,
|
||||
108,Bounsweet,,
|
||||
109,Steenee,,
|
||||
110,Tsareena,,
|
||||
111,Comfey,,
|
||||
112,Pinsir,,
|
||||
113,Hoothoot,,
|
||||
114,Noctowl,,
|
||||
115,Kecleon,,
|
||||
116,Oranguru,,
|
||||
117,Passimian,,
|
||||
118,Goomy,,
|
||||
119,Sliggoo,,
|
||||
120,Goodra,,
|
||||
121,Castform,,
|
||||
122,Wimpod,,
|
||||
123,Golisopod,,
|
||||
124,Staryu,,
|
||||
125,Starmie,,
|
||||
126,Sandygast,,
|
||||
127,Palossand,,
|
||||
128,Omanyte,,
|
||||
129,Omastar,,
|
||||
130,Kabuto,,
|
||||
131,Kabutops,,
|
||||
132,Lileep,,
|
||||
133,Cradily,,
|
||||
134,Anorith,,
|
||||
135,Armaldo,,
|
||||
136,Cranidos,,
|
||||
137,Rampardos,,
|
||||
138,Shieldon,,
|
||||
139,Bastiodon,,
|
||||
140,Archen,,
|
||||
141,Archeops,,
|
||||
142,Tirtouga,,
|
||||
143,Carracosta,,
|
||||
144,Tyrunt,,
|
||||
145,Tyrantrum,,
|
||||
146,Amaura,,
|
||||
147,Aurorus,,
|
||||
148,Larvitar,,
|
||||
149,Pupitar,,
|
||||
150,Tyranitar,,
|
||||
151,Phantump,,
|
||||
152,Trevenant,,
|
||||
153,Natu,,
|
||||
154,Xatu,,
|
||||
155,Nosepass,,
|
||||
156,Probopass,,
|
||||
157,Pyukumuku,,
|
||||
158,Chinchou,,
|
||||
159,Lanturn,,
|
||||
160,Tapu Lele,,
|
||||
|
@@ -1,404 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Rowlet,,
|
||||
2,Dartrix,,
|
||||
3,Decidueye,,
|
||||
4,Litten,,
|
||||
5,Torracat,,
|
||||
6,Incineroar,,
|
||||
7,Popplio,,
|
||||
8,Brionne,,
|
||||
9,Primarina,,
|
||||
10,Pikipek,,
|
||||
11,Trumbeak,,
|
||||
12,Toucannon,,
|
||||
13,Yungoos,,
|
||||
14,Gumshoos,,
|
||||
15,Rattata,,
|
||||
16,Raticate,,
|
||||
17,Caterpie,,
|
||||
18,Metapod,,
|
||||
19,Butterfree,,
|
||||
20,Ledyba,,
|
||||
21,Ledian,,
|
||||
22,Spinarak,,
|
||||
23,Ariados,,
|
||||
24,Buneary,,
|
||||
25,Lopunny,,
|
||||
26,Inkay,,
|
||||
27,Malamar,,
|
||||
28,Zorua,,
|
||||
29,Zoroark,,
|
||||
30,Furfrou,,
|
||||
31,Pichu,,
|
||||
32,Pikachu,,
|
||||
33,Raichu,,
|
||||
34,Grubbin,,
|
||||
35,Charjabug,,
|
||||
36,Vikavolt,,
|
||||
37,Bonsly,,
|
||||
38,Sudowoodo,,
|
||||
39,Happiny,,
|
||||
40,Chansey,,
|
||||
41,Blissey,,
|
||||
42,Munchlax,,
|
||||
43,Snorlax,,
|
||||
44,Slowpoke,,
|
||||
45,Slowbro,,
|
||||
46,Slowking,,
|
||||
47,Wingull,,
|
||||
48,Pelipper,,
|
||||
49,Abra,,
|
||||
50,Kadabra,,
|
||||
51,Alakazam,,
|
||||
52,Meowth,,
|
||||
53,Persian,,
|
||||
54,Magnemite,,
|
||||
55,Magneton,,
|
||||
56,Magnezone,,
|
||||
57,Grimer,,
|
||||
58,Muk,,
|
||||
59,Mime Jr.,,
|
||||
60,Mr. Mime,,
|
||||
61,Ekans,,
|
||||
62,Arbok,,
|
||||
63,Dunsparce,,
|
||||
64,Growlithe,,
|
||||
65,Arcanine,,
|
||||
66,Drowzee,,
|
||||
67,Hypno,,
|
||||
68,Makuhita,,
|
||||
69,Hariyama,,
|
||||
70,Smeargle,,
|
||||
71,Crabrawler,,
|
||||
72,Crabominable,,
|
||||
73,Gastly,,
|
||||
74,Haunter,,
|
||||
75,Gengar,,
|
||||
76,Drifloon,,
|
||||
77,Drifblim,,
|
||||
78,Murkrow,,
|
||||
79,Honchkrow,,
|
||||
80,Zubat,,
|
||||
81,Golbat,,
|
||||
82,Crobat,,
|
||||
83,Noibat,,
|
||||
84,Noivern,,
|
||||
85,Diglett,,
|
||||
86,Dugtrio,,
|
||||
87,Spearow,,
|
||||
88,Fearow,,
|
||||
89,Rufflet,,
|
||||
90,Braviary,,
|
||||
91,Vullaby,,
|
||||
92,Mandibuzz,,
|
||||
93,Mankey,,
|
||||
94,Primeape,,
|
||||
95,Delibird,,
|
||||
96,Hawlucha,,
|
||||
97,Oricorio,,
|
||||
98,Cutiefly,,
|
||||
99,Ribombee,,
|
||||
100,Flabébé,,
|
||||
101,Floette,,
|
||||
102,Florges,,
|
||||
103,Petilil,,
|
||||
104,Lilligant,,
|
||||
105,Cottonee,,
|
||||
106,Whimsicott,,
|
||||
107,Psyduck,,
|
||||
108,Golduck,,
|
||||
109,Smoochum,,
|
||||
110,Jynx,,
|
||||
111,Magikarp,,
|
||||
112,Gyarados,,
|
||||
113,Barboach,,
|
||||
114,Whiscash,,
|
||||
115,Seel,,
|
||||
116,Dewgong,,
|
||||
117,Machop,,
|
||||
118,Machoke,,
|
||||
119,Machamp,,
|
||||
120,Roggenrola,,
|
||||
121,Boldore,,
|
||||
122,Gigalith,,
|
||||
123,Carbink,,
|
||||
124,Sableye,,
|
||||
125,Mawile,,
|
||||
126,Rockruff,,
|
||||
127,Lycanroc,,
|
||||
128,Spinda,,
|
||||
129,Tentacool,,
|
||||
130,Tentacruel,,
|
||||
131,Finneon,,
|
||||
132,Lumineon,,
|
||||
133,Wishiwashi,,
|
||||
134,Luvdisc,,
|
||||
135,Corsola,,
|
||||
136,Mareanie,,
|
||||
137,Toxapex,,
|
||||
138,Shellder,,
|
||||
139,Cloyster,,
|
||||
140,Clamperl,,
|
||||
141,Huntail,,
|
||||
142,Gorebyss,,
|
||||
143,Remoraid,,
|
||||
144,Octillery,,
|
||||
145,Mantyke,,
|
||||
146,Mantine,,
|
||||
147,Bagon,,
|
||||
148,Shelgon,,
|
||||
149,Salamence,,
|
||||
150,Lillipup,,
|
||||
151,Herdier,,
|
||||
152,Stoutland,,
|
||||
153,Eevee,,
|
||||
154,Vaporeon,,
|
||||
155,Jolteon,,
|
||||
156,Flareon,,
|
||||
157,Espeon,,
|
||||
158,Umbreon,,
|
||||
159,Leafeon,,
|
||||
160,Glaceon,,
|
||||
161,Sylveon,,
|
||||
162,Mareep,,
|
||||
163,Flaaffy,,
|
||||
164,Ampharos,,
|
||||
165,Mudbray,,
|
||||
166,Mudsdale,,
|
||||
167,Igglybuff,,
|
||||
168,Jigglypuff,,
|
||||
169,Wigglytuff,,
|
||||
170,Tauros,,
|
||||
171,Miltank,,
|
||||
172,Surskit,,
|
||||
173,Masquerain,,
|
||||
174,Dewpider,,
|
||||
175,Araquanid,,
|
||||
176,Fomantis,,
|
||||
177,Lurantis,,
|
||||
178,Morelull,,
|
||||
179,Shiinotic,,
|
||||
180,Paras,,
|
||||
181,Parasect,,
|
||||
182,Poliwag,,
|
||||
183,Poliwhirl,,
|
||||
184,Poliwrath,,
|
||||
185,Politoed,,
|
||||
186,Goldeen,,
|
||||
187,Seaking,,
|
||||
188,Basculin,,
|
||||
189,Feebas,,
|
||||
190,Milotic,,
|
||||
191,Alomomola,,
|
||||
192,Fletchling,,
|
||||
193,Fletchinder,,
|
||||
194,Talonflame,,
|
||||
195,Salandit,,
|
||||
196,Salazzle,,
|
||||
197,Cubone,,
|
||||
198,Marowak,,
|
||||
199,Kangaskhan,,
|
||||
200,Magby,,
|
||||
201,Magmar,,
|
||||
202,Magmortar,,
|
||||
203,Larvesta,,
|
||||
204,Volcarona,,
|
||||
205,Stufful,,
|
||||
206,Bewear,,
|
||||
207,Bounsweet,,
|
||||
208,Steenee,,
|
||||
209,Tsareena,,
|
||||
210,Comfey,,
|
||||
211,Pinsir,,
|
||||
212,Hoothoot,,
|
||||
213,Noctowl,,
|
||||
214,Kecleon,,
|
||||
215,Oranguru,,
|
||||
216,Passimian,,
|
||||
217,Goomy,,
|
||||
218,Sliggoo,,
|
||||
219,Goodra,,
|
||||
220,Castform,,
|
||||
221,Wimpod,,
|
||||
222,Golisopod,,
|
||||
223,Staryu,,
|
||||
224,Starmie,,
|
||||
225,Sandygast,,
|
||||
226,Palossand,,
|
||||
227,Omanyte,,
|
||||
228,Omastar,,
|
||||
229,Kabuto,,
|
||||
230,Kabutops,,
|
||||
231,Lileep,,
|
||||
232,Cradily,,
|
||||
233,Anorith,,
|
||||
234,Armaldo,,
|
||||
235,Cranidos,,
|
||||
236,Rampardos,,
|
||||
237,Shieldon,,
|
||||
238,Bastiodon,,
|
||||
239,Archen,,
|
||||
240,Archeops,,
|
||||
241,Tirtouga,,
|
||||
242,Carracosta,,
|
||||
243,Tyrunt,,
|
||||
244,Tyrantrum,,
|
||||
245,Amaura,,
|
||||
246,Aurorus,,
|
||||
247,Larvitar,,
|
||||
248,Pupitar,,
|
||||
249,Tyranitar,,
|
||||
250,Phantump,,
|
||||
251,Trevenant,,
|
||||
252,Natu,,
|
||||
253,Xatu,,
|
||||
254,Nosepass,,
|
||||
255,Probopass,,
|
||||
256,Pyukumuku,,
|
||||
257,Chinchou,,
|
||||
258,Lanturn,,
|
||||
259,Type: Null,,
|
||||
260,Silvally,,
|
||||
261,Poipole,,
|
||||
262,Naganadel,,
|
||||
263,Zygarde,,
|
||||
264,Trubbish,,
|
||||
265,Garbodor,,
|
||||
266,Minccino,,
|
||||
267,Cinccino,,
|
||||
268,Pineco,,
|
||||
269,Forretress,,
|
||||
270,Skarmory,,
|
||||
271,Ditto,,
|
||||
272,Cleffa,,
|
||||
273,Clefairy,,
|
||||
274,Clefable,,
|
||||
275,Elgyem,,
|
||||
276,Beheeyem,,
|
||||
277,Minior,,
|
||||
278,Beldum,,
|
||||
279,Metang,,
|
||||
280,Metagross,,
|
||||
281,Porygon,,
|
||||
282,Porygon2,,
|
||||
283,Porygon-Z,,
|
||||
284,Pancham,,
|
||||
285,Pangoro,,
|
||||
286,Komala,,
|
||||
287,Torkoal,,
|
||||
288,Turtonator,,
|
||||
289,Houndour,,
|
||||
290,Houndoom,,
|
||||
291,Dedenne,,
|
||||
292,Togedemaru,,
|
||||
293,Electrike,,
|
||||
294,Manectric,,
|
||||
295,Elekid,,
|
||||
296,Electabuzz,,
|
||||
297,Electivire,,
|
||||
298,Geodude,,
|
||||
299,Graveler,,
|
||||
300,Golem,,
|
||||
301,Sandile,,
|
||||
302,Krokorok,,
|
||||
303,Krookodile,,
|
||||
304,Trapinch,,
|
||||
305,Vibrava,,
|
||||
306,Flygon,,
|
||||
307,Gible,,
|
||||
308,Gabite,,
|
||||
309,Garchomp,,
|
||||
310,Baltoy,,
|
||||
311,Claydol,,
|
||||
312,Golett,,
|
||||
313,Golurk,,
|
||||
314,Klefki,,
|
||||
315,Mimikyu,,
|
||||
316,Shuppet,,
|
||||
317,Banette,,
|
||||
318,Frillish,,
|
||||
319,Jellicent,,
|
||||
320,Bruxish,,
|
||||
321,Drampa,,
|
||||
322,Absol,,
|
||||
323,Snorunt,,
|
||||
324,Glalie,,
|
||||
325,Froslass,,
|
||||
326,Sneasel,,
|
||||
327,Weavile,,
|
||||
328,Sandshrew,,
|
||||
329,Sandslash,,
|
||||
330,Vulpix,,
|
||||
331,Ninetales,,
|
||||
332,Vanillite,,
|
||||
333,Vanillish,,
|
||||
334,Vanilluxe,,
|
||||
335,Scraggy,,
|
||||
336,Scrafty,,
|
||||
337,Pawniard,,
|
||||
338,Bisharp,,
|
||||
339,Snubbull,,
|
||||
340,Granbull,,
|
||||
341,Shellos,,
|
||||
342,Gastrodon,,
|
||||
343,Relicanth,,
|
||||
344,Dhelmise,,
|
||||
345,Carvanha,,
|
||||
346,Sharpedo,,
|
||||
347,Skrelp,,
|
||||
348,Dragalge,,
|
||||
349,Clauncher,,
|
||||
350,Clawitzer,,
|
||||
351,Wailmer,,
|
||||
352,Wailord,,
|
||||
353,Lapras,,
|
||||
354,Tropius,,
|
||||
355,Exeggcute,,
|
||||
356,Exeggutor,,
|
||||
357,Corphish,,
|
||||
358,Crawdaunt,,
|
||||
359,Mienfoo,,
|
||||
360,Mienshao,,
|
||||
361,Jangmo-o,,
|
||||
362,Hakamo-o,,
|
||||
363,Kommo-o,,
|
||||
364,Emolga,,
|
||||
365,Scyther,,
|
||||
366,Scizor,,
|
||||
367,Heracross,,
|
||||
368,Aipom,,
|
||||
369,Ambipom,,
|
||||
370,Litleo,,
|
||||
371,Pyroar,,
|
||||
372,Misdreavus,,
|
||||
373,Mismagius,,
|
||||
374,Druddigon,,
|
||||
375,Lickitung,,
|
||||
376,Lickilicky,,
|
||||
377,Riolu,,
|
||||
378,Lucario,,
|
||||
379,Dratini,,
|
||||
380,Dragonair,,
|
||||
381,Dragonite,,
|
||||
382,Aerodactyl,,
|
||||
383,Tapu Koko,,
|
||||
384,Tapu Lele,,
|
||||
385,Tapu Bulu,,
|
||||
386,Tapu Fini,,
|
||||
387,Cosmog,,
|
||||
388,Cosmoem,,
|
||||
389,Solgaleo,,
|
||||
390,Lunala,,
|
||||
391,Stakataka,,
|
||||
392,Blacephalon,,
|
||||
393,Nihilego,,
|
||||
394,Buzzwole,,
|
||||
395,Pheromosa,,
|
||||
396,Xurkitree,,
|
||||
397,Celesteela,,
|
||||
398,Kartana,,
|
||||
399,Guzzlord,,
|
||||
400,Necrozma,,
|
||||
401,Magearna,,
|
||||
402,Marshadow,,
|
||||
403,Zeraora,,
|
||||
|
@@ -1,151 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Rowlet,,
|
||||
2,Dartrix,,
|
||||
3,Decidueye,,
|
||||
4,Litten,,
|
||||
5,Torracat,,
|
||||
6,Incineroar,,
|
||||
7,Popplio,,
|
||||
8,Brionne,,
|
||||
9,Primarina,,
|
||||
10,Pikipek,,
|
||||
11,Trumbeak,,
|
||||
12,Toucannon,,
|
||||
13,Yungoos,,
|
||||
14,Gumshoos,,
|
||||
15,Rattata,,
|
||||
16,Raticate,,
|
||||
17,Caterpie,,
|
||||
18,Metapod,,
|
||||
19,Butterfree,,
|
||||
20,Ledyba,,
|
||||
21,Ledian,,
|
||||
22,Spinarak,,
|
||||
23,Ariados,,
|
||||
24,Buneary,,
|
||||
25,Lopunny,,
|
||||
26,Inkay,,
|
||||
27,Malamar,,
|
||||
28,Zorua,,
|
||||
29,Zoroark,,
|
||||
30,Furfrou,,
|
||||
31,Pichu,,
|
||||
32,Pikachu,,
|
||||
33,Raichu,,
|
||||
34,Grubbin,,
|
||||
35,Charjabug,,
|
||||
36,Vikavolt,,
|
||||
37,Bonsly,,
|
||||
38,Sudowoodo,,
|
||||
39,Happiny,,
|
||||
40,Chansey,,
|
||||
41,Blissey,,
|
||||
42,Munchlax,,
|
||||
43,Snorlax,,
|
||||
44,Slowpoke,,
|
||||
45,Slowbro,,
|
||||
46,Slowking,,
|
||||
47,Wingull,,
|
||||
48,Pelipper,,
|
||||
49,Abra,,
|
||||
50,Kadabra,,
|
||||
51,Alakazam,,
|
||||
52,Meowth,,
|
||||
53,Persian,,
|
||||
54,Magnemite,,
|
||||
55,Magneton,,
|
||||
56,Magnezone,,
|
||||
57,Grimer,,
|
||||
58,Muk,,
|
||||
59,Mime Jr.,,
|
||||
60,Mr. Mime,,
|
||||
61,Ekans,,
|
||||
62,Arbok,,
|
||||
63,Dunsparce,,
|
||||
64,Growlithe,,
|
||||
65,Arcanine,,
|
||||
66,Drowzee,,
|
||||
67,Hypno,,
|
||||
68,Makuhita,,
|
||||
69,Hariyama,,
|
||||
70,Smeargle,,
|
||||
71,Crabrawler,,
|
||||
72,Crabominable,,
|
||||
73,Gastly,,
|
||||
74,Haunter,,
|
||||
75,Gengar,,
|
||||
76,Drifloon,,
|
||||
77,Drifblim,,
|
||||
78,Murkrow,,
|
||||
79,Honchkrow,,
|
||||
80,Zubat,,
|
||||
81,Golbat,,
|
||||
82,Crobat,,
|
||||
83,Noibat,,
|
||||
84,Noivern,,
|
||||
85,Diglett,,
|
||||
86,Dugtrio,,
|
||||
87,Spearow,,
|
||||
88,Fearow,,
|
||||
89,Rufflet,,
|
||||
90,Braviary,,
|
||||
91,Vullaby,,
|
||||
92,Mandibuzz,,
|
||||
93,Mankey,,
|
||||
94,Primeape,,
|
||||
95,Delibird,,
|
||||
96,Hawlucha,,
|
||||
97,Oricorio,,
|
||||
98,Cutiefly,,
|
||||
99,Ribombee,,
|
||||
100,Flabébé,,
|
||||
101,Floette,,
|
||||
102,Florges,,
|
||||
103,Petilil,,
|
||||
104,Lilligant,,
|
||||
105,Cottonee,,
|
||||
106,Whimsicott,,
|
||||
107,Psyduck,,
|
||||
108,Golduck,,
|
||||
109,Smoochum,,
|
||||
110,Jynx,,
|
||||
111,Magikarp,,
|
||||
112,Gyarados,,
|
||||
113,Barboach,,
|
||||
114,Whiscash,,
|
||||
115,Seel,,
|
||||
116,Dewgong,,
|
||||
117,Machop,,
|
||||
118,Machoke,,
|
||||
119,Machamp,,
|
||||
120,Roggenrola,,
|
||||
121,Boldore,,
|
||||
122,Gigalith,,
|
||||
123,Carbink,,
|
||||
124,Sableye,,
|
||||
125,Mawile,,
|
||||
126,Rockruff,,
|
||||
127,Lycanroc,,
|
||||
128,Spinda,,
|
||||
129,Tentacool,,
|
||||
130,Tentacruel,,
|
||||
131,Finneon,,
|
||||
132,Lumineon,,
|
||||
133,Wishiwashi,,
|
||||
134,Luvdisc,,
|
||||
135,Corsola,,
|
||||
136,Mareanie,,
|
||||
137,Toxapex,,
|
||||
138,Shellder,,
|
||||
139,Cloyster,,
|
||||
140,Clamperl,,
|
||||
141,Huntail,,
|
||||
142,Gorebyss,,
|
||||
143,Remoraid,,
|
||||
144,Octillery,,
|
||||
145,Mantyke,,
|
||||
146,Mantine,,
|
||||
147,Bagon,,
|
||||
148,Shelgon,,
|
||||
149,Salamence,,
|
||||
150,Tapu Koko,,
|
||||
|
@@ -1,131 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Pikipek,,
|
||||
2,Trumbeak,,
|
||||
3,Toucannon,,
|
||||
4,Yungoos,,
|
||||
5,Gumshoos,,
|
||||
6,Rattata,,
|
||||
7,Raticate,,
|
||||
8,Buneary,,
|
||||
9,Lopunny,,
|
||||
10,Inkay,,
|
||||
11,Malamar,,
|
||||
12,Zorua,,
|
||||
13,Zoroark,,
|
||||
14,Furfrou,,
|
||||
15,Happiny,,
|
||||
16,Chansey,,
|
||||
17,Blissey,,
|
||||
18,Wingull,,
|
||||
19,Pelipper,,
|
||||
20,Drowzee,,
|
||||
21,Hypno,,
|
||||
22,Makuhita,,
|
||||
23,Hariyama,,
|
||||
24,Crabrawler,,
|
||||
25,Crabominable,,
|
||||
26,Zubat,,
|
||||
27,Golbat,,
|
||||
28,Crobat,,
|
||||
29,Noibat,,
|
||||
30,Noivern,,
|
||||
31,Diglett,,
|
||||
32,Dugtrio,,
|
||||
33,Spearow,,
|
||||
34,Fearow,,
|
||||
35,Rufflet,,
|
||||
36,Braviary,,
|
||||
37,Vullaby,,
|
||||
38,Mandibuzz,,
|
||||
39,Mankey,,
|
||||
40,Primeape,,
|
||||
41,Oricorio,,
|
||||
42,Cutiefly,,
|
||||
43,Ribombee,,
|
||||
44,Flabébé,,
|
||||
45,Floette,,
|
||||
46,Florges,,
|
||||
47,Petilil,,
|
||||
48,Lilligant,,
|
||||
49,Cottonee,,
|
||||
50,Whimsicott,,
|
||||
51,Psyduck,,
|
||||
52,Golduck,,
|
||||
53,Magikarp,,
|
||||
54,Gyarados,,
|
||||
55,Barboach,,
|
||||
56,Whiscash,,
|
||||
57,Machop,,
|
||||
58,Machoke,,
|
||||
59,Machamp,,
|
||||
60,Roggenrola,,
|
||||
61,Boldore,,
|
||||
62,Gigalith,,
|
||||
63,Carbink,,
|
||||
64,Sableye,,
|
||||
65,Rockruff,,
|
||||
66,Lycanroc,,
|
||||
67,Tentacool,,
|
||||
68,Tentacruel,,
|
||||
69,Finneon,,
|
||||
70,Lumineon,,
|
||||
71,Mudbray,,
|
||||
72,Mudsdale,,
|
||||
73,Tauros,,
|
||||
74,Miltank,,
|
||||
75,Basculin,,
|
||||
76,Stufful,,
|
||||
77,Bewear,,
|
||||
78,Pinsir,,
|
||||
79,Goomy,,
|
||||
80,Sliggoo,,
|
||||
81,Goodra,,
|
||||
82,Castform,,
|
||||
83,Wimpod,,
|
||||
84,Golisopod,,
|
||||
85,Skarmory,,
|
||||
86,Snubbull,,
|
||||
87,Granbull,,
|
||||
88,Shellos,,
|
||||
89,Gastrodon,,
|
||||
90,Relicanth,,
|
||||
91,Dhelmise,,
|
||||
92,Carvanha,,
|
||||
93,Sharpedo,,
|
||||
94,Skrelp,,
|
||||
95,Dragalge,,
|
||||
96,Clauncher,,
|
||||
97,Clawitzer,,
|
||||
98,Wailmer,,
|
||||
99,Wailord,,
|
||||
100,Lapras,,
|
||||
101,Tropius,,
|
||||
102,Exeggcute,,
|
||||
103,Exeggutor,,
|
||||
104,Corphish,,
|
||||
105,Crawdaunt,,
|
||||
106,Mienfoo,,
|
||||
107,Mienshao,,
|
||||
108,Jangmo-o,,
|
||||
109,Hakamo-o,,
|
||||
110,Kommo-o,,
|
||||
111,Emolga,,
|
||||
112,Scyther,,
|
||||
113,Scizor,,
|
||||
114,Heracross,,
|
||||
115,Aipom,,
|
||||
116,Ambipom,,
|
||||
117,Litleo,,
|
||||
118,Pyroar,,
|
||||
119,Misdreavus,,
|
||||
120,Mismagius,,
|
||||
121,Druddigon,,
|
||||
122,Lickitung,,
|
||||
123,Lickilicky,,
|
||||
124,Riolu,,
|
||||
125,Lucario,,
|
||||
126,Dratini,,
|
||||
127,Dragonair,,
|
||||
128,Dragonite,,
|
||||
129,Aerodactyl,,
|
||||
130,Tapu Fini,,
|
||||
|
@@ -1,161 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Pikipek,,
|
||||
2,Trumbeak,,
|
||||
3,Toucannon,,
|
||||
4,Yungoos,,
|
||||
5,Gumshoos,,
|
||||
6,Rattata,,
|
||||
7,Raticate,,
|
||||
8,Ledyba,,
|
||||
9,Ledian,,
|
||||
10,Spinarak,,
|
||||
11,Ariados,,
|
||||
12,Grubbin,,
|
||||
13,Charjabug,,
|
||||
14,Vikavolt,,
|
||||
15,Happiny,,
|
||||
16,Chansey,,
|
||||
17,Blissey,,
|
||||
18,Slowpoke,,
|
||||
19,Slowbro,,
|
||||
20,Slowking,,
|
||||
21,Wingull,,
|
||||
22,Pelipper,,
|
||||
23,Meowth,,
|
||||
24,Persian,,
|
||||
25,Magnemite,,
|
||||
26,Magneton,,
|
||||
27,Magnezone,,
|
||||
28,Grimer,,
|
||||
29,Muk,,
|
||||
30,Crabrawler,,
|
||||
31,Crabominable,,
|
||||
32,Gastly,,
|
||||
33,Haunter,,
|
||||
34,Gengar,,
|
||||
35,Zubat,,
|
||||
36,Golbat,,
|
||||
37,Crobat,,
|
||||
38,Diglett,,
|
||||
39,Dugtrio,,
|
||||
40,Spearow,,
|
||||
41,Fearow,,
|
||||
42,Oricorio,,
|
||||
43,Cutiefly,,
|
||||
44,Ribombee,,
|
||||
45,Flabébé,,
|
||||
46,Floette,,
|
||||
47,Florges,,
|
||||
48,Petilil,,
|
||||
49,Lilligant,,
|
||||
50,Cottonee,,
|
||||
51,Whimsicott,,
|
||||
52,Psyduck,,
|
||||
53,Golduck,,
|
||||
54,Magikarp,,
|
||||
55,Gyarados,,
|
||||
56,Tentacool,,
|
||||
57,Tentacruel,,
|
||||
58,Finneon,,
|
||||
59,Lumineon,,
|
||||
60,Wishiwashi,,
|
||||
61,Clamperl,,
|
||||
62,Huntail,,
|
||||
63,Gorebyss,,
|
||||
64,Mudbray,,
|
||||
65,Mudsdale,,
|
||||
66,Surskit,,
|
||||
67,Masquerain,,
|
||||
68,Dewpider,,
|
||||
69,Araquanid,,
|
||||
70,Morelull,,
|
||||
71,Shiinotic,,
|
||||
72,Paras,,
|
||||
73,Parasect,,
|
||||
74,Poliwag,,
|
||||
75,Poliwhirl,,
|
||||
76,Poliwrath,,
|
||||
77,Politoed,,
|
||||
78,Goldeen,,
|
||||
79,Seaking,,
|
||||
80,Basculin,,
|
||||
81,Goomy,,
|
||||
82,Sliggoo,,
|
||||
83,Goodra,,
|
||||
84,Castform,,
|
||||
85,Trubbish,,
|
||||
86,Garbodor,,
|
||||
87,Minccino,,
|
||||
88,Cinccino,,
|
||||
89,Pineco,,
|
||||
90,Forretress,,
|
||||
91,Skarmory,,
|
||||
92,Ditto,,
|
||||
93,Cleffa,,
|
||||
94,Clefairy,,
|
||||
95,Clefable,,
|
||||
96,Elgyem,,
|
||||
97,Beheeyem,,
|
||||
98,Minior,,
|
||||
99,Beldum,,
|
||||
100,Metang,,
|
||||
101,Metagross,,
|
||||
102,Porygon,,
|
||||
103,Porygon2,,
|
||||
104,Porygon-Z,,
|
||||
105,Pancham,,
|
||||
106,Pangoro,,
|
||||
107,Komala,,
|
||||
108,Torkoal,,
|
||||
109,Turtonator,,
|
||||
110,Houndour,,
|
||||
111,Houndoom,,
|
||||
112,Dedenne,,
|
||||
113,Togedemaru,,
|
||||
114,Electrike,,
|
||||
115,Manectric,,
|
||||
116,Elekid,,
|
||||
117,Electabuzz,,
|
||||
118,Electivire,,
|
||||
119,Geodude,,
|
||||
120,Graveler,,
|
||||
121,Golem,,
|
||||
122,Sandile,,
|
||||
123,Krokorok,,
|
||||
124,Krookodile,,
|
||||
125,Trapinch,,
|
||||
126,Vibrava,,
|
||||
127,Flygon,,
|
||||
128,Gible,,
|
||||
129,Gabite,,
|
||||
130,Garchomp,,
|
||||
131,Baltoy,,
|
||||
132,Claydol,,
|
||||
133,Golett,,
|
||||
134,Golurk,,
|
||||
135,Klefki,,
|
||||
136,Mimikyu,,
|
||||
137,Shuppet,,
|
||||
138,Banette,,
|
||||
139,Frillish,,
|
||||
140,Jellicent,,
|
||||
141,Bruxish,,
|
||||
142,Drampa,,
|
||||
143,Absol,,
|
||||
144,Snorunt,,
|
||||
145,Glalie,,
|
||||
146,Froslass,,
|
||||
147,Sneasel,,
|
||||
148,Weavile,,
|
||||
149,Sandshrew,,
|
||||
150,Sandslash,,
|
||||
151,Vulpix,,
|
||||
152,Ninetales,,
|
||||
153,Vanillite,,
|
||||
154,Vanillish,,
|
||||
155,Vanilluxe,,
|
||||
156,Scraggy,,
|
||||
157,Scrafty,,
|
||||
158,Pawniard,,
|
||||
159,Bisharp,,
|
||||
160,Tapu Bulu,,
|
||||
|
@@ -1,154 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Chespin,,
|
||||
2,Quilladin,,
|
||||
3,Chesnaught,,
|
||||
4,Fennekin,,
|
||||
5,Braixen,,
|
||||
6,Delphox,,
|
||||
7,Froakie,,
|
||||
8,Frogadier,,
|
||||
9,Greninja,,
|
||||
10,Bunnelby,,
|
||||
11,Diggersby,,
|
||||
12,Zigzagoon,,
|
||||
13,Linoone,,
|
||||
14,Fletchling,,
|
||||
15,Fletchinder,,
|
||||
16,Talonflame,,
|
||||
17,Pidgey,,
|
||||
18,Pidgeotto,,
|
||||
19,Pidgeot,,
|
||||
20,Scatterbug,,
|
||||
21,Spewpa,,
|
||||
22,Vivillon,,
|
||||
23,Caterpie,,
|
||||
24,Metapod,,
|
||||
25,Butterfree,,
|
||||
26,Weedle,,
|
||||
27,Kakuna,,
|
||||
28,Beedrill,,
|
||||
29,Pansage,,
|
||||
30,Simisage,,
|
||||
31,Pansear,,
|
||||
32,Simisear,,
|
||||
33,Panpour,,
|
||||
34,Simipour,,
|
||||
35,Pichu,,
|
||||
36,Pikachu,,
|
||||
37,Raichu,,
|
||||
38,Bidoof,,
|
||||
39,Bibarel,,
|
||||
40,Dunsparce,,
|
||||
41,Azurill,,
|
||||
42,Marill,,
|
||||
43,Azumarill,,
|
||||
44,Burmy,,
|
||||
45,Wormadam,,
|
||||
46,Mothim,,
|
||||
47,Surskit,,
|
||||
48,Masquerain,,
|
||||
49,Magikarp,,
|
||||
50,Gyarados,,
|
||||
51,Corphish,,
|
||||
52,Crawdaunt,,
|
||||
53,Goldeen,,
|
||||
54,Seaking,,
|
||||
55,Carvanha,,
|
||||
56,Sharpedo,,
|
||||
57,Litleo,,
|
||||
58,Pyroar,,
|
||||
59,Psyduck,,
|
||||
60,Golduck,,
|
||||
61,Farfetch’d,,
|
||||
62,Riolu,,
|
||||
63,Lucario,,
|
||||
64,Ralts,,
|
||||
65,Kirlia,,
|
||||
66,Gardevoir,,
|
||||
67,Gallade,,
|
||||
68,Flabébé,,
|
||||
69,Floette,,
|
||||
70,Florges,,
|
||||
71,Budew,,
|
||||
72,Roselia,,
|
||||
73,Roserade,,
|
||||
74,Ledyba,,
|
||||
75,Ledian,,
|
||||
76,Combee,,
|
||||
77,Vespiquen,,
|
||||
78,Skitty,,
|
||||
79,Delcatty,,
|
||||
80,Bulbasaur,,
|
||||
81,Ivysaur,,
|
||||
82,Venusaur,,
|
||||
83,Charmander,,
|
||||
84,Charmeleon,,
|
||||
85,Charizard,,
|
||||
86,Squirtle,,
|
||||
87,Wartortle,,
|
||||
88,Blastoise,,
|
||||
89,Skiddo,,
|
||||
90,Gogoat,,
|
||||
91,Pancham,,
|
||||
92,Pangoro,,
|
||||
93,Furfrou,,
|
||||
94,Doduo,,
|
||||
95,Dodrio,,
|
||||
96,Plusle,,
|
||||
97,Minun,,
|
||||
98,Gulpin,,
|
||||
99,Swalot,,
|
||||
100,Scraggy,,
|
||||
101,Scrafty,,
|
||||
102,Abra,,
|
||||
103,Kadabra,,
|
||||
104,Alakazam,,
|
||||
105,Oddish,,
|
||||
106,Gloom,,
|
||||
107,Vileplume,,
|
||||
108,Bellossom,,
|
||||
109,Sentret,,
|
||||
110,Furret,,
|
||||
111,Nincada,,
|
||||
112,Ninjask,,
|
||||
113,Shedinja,,
|
||||
114,Espurr,,
|
||||
115,Meowstic,,
|
||||
116,Kecleon,,
|
||||
117,Honedge,,
|
||||
118,Doublade,,
|
||||
119,Aegislash,,
|
||||
120,Venipede,,
|
||||
121,Whirlipede,,
|
||||
122,Scolipede,,
|
||||
123,Audino,,
|
||||
124,Smeargle,,
|
||||
125,Croagunk,,
|
||||
126,Toxicroak,,
|
||||
127,Ducklett,,
|
||||
128,Swanna,,
|
||||
129,Spritzee,,
|
||||
130,Aromatisse,,
|
||||
131,Swirlix,,
|
||||
132,Slurpuff,,
|
||||
133,Volbeat,,
|
||||
134,Illumise,,
|
||||
135,Hoppip,,
|
||||
136,Skiploom,,
|
||||
137,Jumpluff,,
|
||||
138,Munchlax,,
|
||||
139,Snorlax,,
|
||||
140,Whismur,,
|
||||
141,Loudred,,
|
||||
142,Exploud,,
|
||||
143,Meditite,,
|
||||
144,Medicham,,
|
||||
145,Zubat,,
|
||||
146,Golbat,,
|
||||
147,Crobat,,
|
||||
148,Axew,,
|
||||
149,Fraxure,,
|
||||
150,Haxorus,,
|
||||
151,Diancie,,
|
||||
152,Hoopa,,
|
||||
153,Volcanion,,
|
||||
|
@@ -1,154 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Drifloon,,
|
||||
2,Drifblim,,
|
||||
3,Mienfoo,,
|
||||
4,Mienshao,,
|
||||
5,Zangoose,,
|
||||
6,Seviper,,
|
||||
7,Spoink,,
|
||||
8,Grumpig,,
|
||||
9,Absol,,
|
||||
10,Inkay,,
|
||||
11,Malamar,,
|
||||
12,Lunatone,,
|
||||
13,Solrock,,
|
||||
14,Bagon,,
|
||||
15,Shelgon,,
|
||||
16,Salamence,,
|
||||
17,Wingull,,
|
||||
18,Pelipper,,
|
||||
19,Taillow,,
|
||||
20,Swellow,,
|
||||
21,Binacle,,
|
||||
22,Barbaracle,,
|
||||
23,Dwebble,,
|
||||
24,Crustle,,
|
||||
25,Tentacool,,
|
||||
26,Tentacruel,,
|
||||
27,Wailmer,,
|
||||
28,Wailord,,
|
||||
29,Luvdisc,,
|
||||
30,Skrelp,,
|
||||
31,Dragalge,,
|
||||
32,Clauncher,,
|
||||
33,Clawitzer,,
|
||||
34,Staryu,,
|
||||
35,Starmie,,
|
||||
36,Shellder,,
|
||||
37,Cloyster,,
|
||||
38,Qwilfish,,
|
||||
39,Horsea,,
|
||||
40,Seadra,,
|
||||
41,Kingdra,,
|
||||
42,Relicanth,,
|
||||
43,Sandile,,
|
||||
44,Krokorok,,
|
||||
45,Krookodile,,
|
||||
46,Helioptile,,
|
||||
47,Heliolisk,,
|
||||
48,Hippopotas,,
|
||||
49,Hippowdon,,
|
||||
50,Rhyhorn,,
|
||||
51,Rhydon,,
|
||||
52,Rhyperior,,
|
||||
53,Onix,,
|
||||
54,Steelix,,
|
||||
55,Woobat,,
|
||||
56,Swoobat,,
|
||||
57,Machop,,
|
||||
58,Machoke,,
|
||||
59,Machamp,,
|
||||
60,Cubone,,
|
||||
61,Marowak,,
|
||||
62,Kangaskhan,,
|
||||
63,Mawile,,
|
||||
64,Tyrunt,,
|
||||
65,Tyrantrum,,
|
||||
66,Amaura,,
|
||||
67,Aurorus,,
|
||||
68,Aerodactyl,,
|
||||
69,Ferroseed,,
|
||||
70,Ferrothorn,,
|
||||
71,Snubbull,,
|
||||
72,Granbull,,
|
||||
73,Electrike,,
|
||||
74,Manectric,,
|
||||
75,Houndour,,
|
||||
76,Houndoom,,
|
||||
77,Eevee,,
|
||||
78,Vaporeon,,
|
||||
79,Jolteon,,
|
||||
80,Flareon,,
|
||||
81,Espeon,,
|
||||
82,Umbreon,,
|
||||
83,Leafeon,,
|
||||
84,Glaceon,,
|
||||
85,Sylveon,,
|
||||
86,Emolga,,
|
||||
87,Yanma,,
|
||||
88,Yanmega,,
|
||||
89,Hawlucha,,
|
||||
90,Sigilyph,,
|
||||
91,Golett,,
|
||||
92,Golurk,,
|
||||
93,Nosepass,,
|
||||
94,Probopass,,
|
||||
95,Makuhita,,
|
||||
96,Hariyama,,
|
||||
97,Throh,,
|
||||
98,Sawk,,
|
||||
99,Starly,,
|
||||
100,Staravia,,
|
||||
101,Staraptor,,
|
||||
102,Stunky,,
|
||||
103,Skuntank,,
|
||||
104,Nidoran♀,,
|
||||
105,Nidorina,,
|
||||
106,Nidoqueen,,
|
||||
107,Nidoran♂,,
|
||||
108,Nidorino,,
|
||||
109,Nidoking,,
|
||||
110,Dedenne,,
|
||||
111,Chingling,,
|
||||
112,Chimecho,,
|
||||
113,Mime Jr.,,
|
||||
114,Mr. Mime,,
|
||||
115,Solosis,,
|
||||
116,Duosion,,
|
||||
117,Reuniclus,,
|
||||
118,Wynaut,,
|
||||
119,Wobbuffet,,
|
||||
120,Roggenrola,,
|
||||
121,Boldore,,
|
||||
122,Gigalith,,
|
||||
123,Sableye,,
|
||||
124,Carbink,,
|
||||
125,Tauros,,
|
||||
126,Miltank,,
|
||||
127,Mareep,,
|
||||
128,Flaaffy,,
|
||||
129,Ampharos,,
|
||||
130,Pinsir,,
|
||||
131,Heracross,,
|
||||
132,Pachirisu,,
|
||||
133,Slowpoke,,
|
||||
134,Slowbro,,
|
||||
135,Slowking,,
|
||||
136,Exeggcute,,
|
||||
137,Exeggutor,,
|
||||
138,Chatot,,
|
||||
139,Mantyke,,
|
||||
140,Mantine,,
|
||||
141,Clamperl,,
|
||||
142,Huntail,,
|
||||
143,Gorebyss,,
|
||||
144,Remoraid,,
|
||||
145,Octillery,,
|
||||
146,Corsola,,
|
||||
147,Chinchou,,
|
||||
148,Lanturn,,
|
||||
149,Alomomola,,
|
||||
150,Lapras,,
|
||||
151,Articuno,,
|
||||
152,Zapdos,,
|
||||
153,Moltres,,
|
||||
|
@@ -1,152 +0,0 @@
|
||||
dexNumber,pokemon,form,notes
|
||||
1,Diglett,,
|
||||
2,Dugtrio,,
|
||||
3,Trapinch,,
|
||||
4,Vibrava,,
|
||||
5,Flygon,,
|
||||
6,Gible,,
|
||||
7,Gabite,,
|
||||
8,Garchomp,,
|
||||
9,Geodude,,
|
||||
10,Graveler,,
|
||||
11,Golem,,
|
||||
12,Slugma,,
|
||||
13,Magcargo,,
|
||||
14,Shuckle,,
|
||||
15,Skorupi,,
|
||||
16,Drapion,,
|
||||
17,Wooper,,
|
||||
18,Quagsire,,
|
||||
19,Goomy,,
|
||||
20,Sliggoo,,
|
||||
21,Goodra,,
|
||||
22,Karrablast,,
|
||||
23,Escavalier,,
|
||||
24,Shelmet,,
|
||||
25,Accelgor,,
|
||||
26,Bellsprout,,
|
||||
27,Weepinbell,,
|
||||
28,Victreebel,,
|
||||
29,Carnivine,,
|
||||
30,Gastly,,
|
||||
31,Haunter,,
|
||||
32,Gengar,,
|
||||
33,Poliwag,,
|
||||
34,Poliwhirl,,
|
||||
35,Poliwrath,,
|
||||
36,Politoed,,
|
||||
37,Ekans,,
|
||||
38,Arbok,,
|
||||
39,Stunfisk,,
|
||||
40,Barboach,,
|
||||
41,Whiscash,,
|
||||
42,Purrloin,,
|
||||
43,Liepard,,
|
||||
44,Poochyena,,
|
||||
45,Mightyena,,
|
||||
46,Patrat,,
|
||||
47,Watchog,,
|
||||
48,Pawniard,,
|
||||
49,Bisharp,,
|
||||
50,Klefki,,
|
||||
51,Murkrow,,
|
||||
52,Honchkrow,,
|
||||
53,Foongus,,
|
||||
54,Amoonguss,,
|
||||
55,Lotad,,
|
||||
56,Lombre,,
|
||||
57,Ludicolo,,
|
||||
58,Buizel,,
|
||||
59,Floatzel,,
|
||||
60,Basculin,,
|
||||
61,Phantump,,
|
||||
62,Trevenant,,
|
||||
63,Pumpkaboo,,
|
||||
64,Gourgeist,,
|
||||
65,Litwick,,
|
||||
66,Lampent,,
|
||||
67,Chandelure,,
|
||||
68,Rotom,,
|
||||
69,Magnemite,,
|
||||
70,Magneton,,
|
||||
71,Magnezone,,
|
||||
72,Voltorb,,
|
||||
73,Electrode,,
|
||||
74,Trubbish,,
|
||||
75,Garbodor,,
|
||||
76,Swinub,,
|
||||
77,Piloswine,,
|
||||
78,Mamoswine,,
|
||||
79,Bergmite,,
|
||||
80,Avalugg,,
|
||||
81,Cubchoo,,
|
||||
82,Beartic,,
|
||||
83,Smoochum,,
|
||||
84,Jynx,,
|
||||
85,Vanillite,,
|
||||
86,Vanillish,,
|
||||
87,Vanilluxe,,
|
||||
88,Snover,,
|
||||
89,Abomasnow,,
|
||||
90,Delibird,,
|
||||
91,Sneasel,,
|
||||
92,Weavile,,
|
||||
93,Timburr,,
|
||||
94,Gurdurr,,
|
||||
95,Conkeldurr,,
|
||||
96,Torkoal,,
|
||||
97,Sandshrew,,
|
||||
98,Sandslash,,
|
||||
99,Aron,,
|
||||
100,Lairon,,
|
||||
101,Aggron,,
|
||||
102,Larvitar,,
|
||||
103,Pupitar,,
|
||||
104,Tyranitar,,
|
||||
105,Heatmor,,
|
||||
106,Durant,,
|
||||
107,Spinarak,,
|
||||
108,Ariados,,
|
||||
109,Spearow,,
|
||||
110,Fearow,,
|
||||
111,Cryogonal,,
|
||||
112,Skarmory,,
|
||||
113,Noibat,,
|
||||
114,Noivern,,
|
||||
115,Gligar,,
|
||||
116,Gliscor,,
|
||||
117,Hoothoot,,
|
||||
118,Noctowl,,
|
||||
119,Igglybuff,,
|
||||
120,Jigglypuff,,
|
||||
121,Wigglytuff,,
|
||||
122,Shuppet,,
|
||||
123,Banette,,
|
||||
124,Zorua,,
|
||||
125,Zoroark,,
|
||||
126,Gothita,,
|
||||
127,Gothorita,,
|
||||
128,Gothitelle,,
|
||||
129,Bonsly,,
|
||||
130,Sudowoodo,,
|
||||
131,Spinda,,
|
||||
132,Teddiursa,,
|
||||
133,Ursaring,,
|
||||
134,Lickitung,,
|
||||
135,Lickilicky,,
|
||||
136,Scyther,,
|
||||
137,Scizor,,
|
||||
138,Ditto,,
|
||||
139,Swablu,,
|
||||
140,Altaria,,
|
||||
141,Druddigon,,
|
||||
142,Deino,,
|
||||
143,Zweilous,,
|
||||
144,Hydreigon,,
|
||||
145,Dratini,,
|
||||
146,Dragonair,,
|
||||
147,Dragonite,,
|
||||
148,Xerneas,,
|
||||
149,Yveltal,,
|
||||
150,Zygarde,,
|
||||
151,Mewtwo,,
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
region,releaseOrder
|
||||
Kanto,1
|
||||
Johto,2
|
||||
Hoenn,3
|
||||
Orre,4
|
||||
Sinnoh,5
|
||||
Ranch,6
|
||||
Unova,7
|
||||
Kalos,8
|
||||
Alola,9
|
||||
Go,10
|
||||
Galar,11
|
||||
Home,12
|
||||
Hisui,13
|
||||
Paldea,14
|
||||
|
@@ -1,57 +0,0 @@
|
||||
# Hosting evaluation: retain Netlify pending deployed evidence
|
||||
|
||||
Date: 15 September 2026. This is an interim decision report. **Do not cut over to Cloudflare yet.** The application improvements and selectable preview build are implemented, but the agreed real-host comparison cannot be completed with the current access and compatibility state.
|
||||
|
||||
## Evidence and decision
|
||||
|
||||
| Item | Observed result | Consequence |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| Production Netlify site | `livingdextracker`, `www.dextracker.uk`; published revision inspected: `31fd959350962c1f1b973a06ecd5cf4eeef86514` | This is the existing deployment, not this working tree |
|
||||
| Function/database regions | Netlify `us-east-1`; linked Supabase `eu-north-1`; production public database URL matches that project | Cross-region round trips are a plausible contributor; their latency share is unmeasured |
|
||||
| Node production checks | Compact SSR, details, status writes, offline behavior and rendering budgets exercised locally | Application evidence only |
|
||||
| Cloudflare build | Adapter 7.2.9, Wrangler 4.131.2, compatibility date 2026-09-15, `nodejs_compat`; build and dry-run passed | Packaging is viable |
|
||||
| Final Worker artifact | Approximately 1,876 KiB uncompressed / 336 KiB gzip; 9,621 static assets | Within published size/count limits; re-audit after renderer changes |
|
||||
| Actual local workerd | Home, authenticated dex and public share page returned 200 | Basic runtime support demonstrated |
|
||||
| Share image in workerd | `/shared/:token/preview.png` returned 500; `sharp` native module dynamic require is unsupported | **Compatibility gate failed**; see separate renderer proposal below |
|
||||
| Remote preview access | Netlify CLI authenticated; Wrangler not authenticated; equivalent staging fixtures/accounts not supplied | No paired deployed samples collected |
|
||||
|
||||
No production deployment, DNS change, database migration or region change was performed. Retaining Netlify now is a decision under incomplete evidence and a failed compatibility gate, not evidence that Netlify is faster.
|
||||
|
||||
Netlify supports streaming; the investigation's buffering describes the installed adapter/runtime combination, not the entire platform. Cloudflare's supported SvelteKit adapter and partial Node compatibility do not imply support for native `sharp`. Sources: [Netlify streaming](https://docs.netlify.com/build/functions/lambda-compatibility/), [SvelteKit Cloudflare adapter](https://svelte.dev/docs/kit/adapter-cloudflare), [Cloudflare Node compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/).
|
||||
|
||||
## Build and environment requirements
|
||||
|
||||
- Default builds remain Netlify. `NODE_ADAPTER=true` or `DEPLOY_TARGET=node` selects Node. `npm run build:cloudflare` selects Workers with Static Assets; `npm run check:cloudflare` checks packaging; `npm run preview:cloudflare` runs workerd locally. Do not run builds concurrently.
|
||||
- Cloudflare uses platform compression rather than the Node compression implementation. Verify actual deployed content encoding for HTML, SvelteKit data and JSON responses.
|
||||
- Supply public Supabase URL/anon key at build time, and private Supabase/export/OAuth secrets through platform bindings. Preview secrets must belong to the same staging project on both hosts. Keep provider redirect allowlists and cookie/domain settings explicit.
|
||||
- Authenticate Wrangler and select the intended account before creating a remote preview. Build/dry-run success does not authorize production routing.
|
||||
- Current documented limits include 128 MB memory, one-second startup, 64 MiB uncompressed Worker size and 20,000/100,000 static files on Free/Paid. Each static file must be at most 25 MiB. Free CPU allowance is only 10 ms per invocation; do not assume the app or renderer fits it. Measure CPU/memory/startup under actual workerd and deployment. [Workers limits](https://developers.cloudflare.com/workers/platform/limits/)
|
||||
|
||||
## Separate proposal: Worker-compatible share-image renderer
|
||||
|
||||
This is the concrete implementation proposal required by the migration gate. It is **not implemented** in this change.
|
||||
|
||||
1. Extract the existing SVG construction, XML escaping and text truncation into a platform-neutral module. Preserve the endpoint, 1,200 × 630 dimensions, progress values, privacy behavior and cache headers.
|
||||
2. Keep a Node renderer using `sharp`. Add a Cloudflare renderer using `@resvg/resvg-wasm`, selected through the same build-time platform alias pattern as compression. Pin a verified version in the renderer change; exclude native `sharp` from the Worker dependency graph.
|
||||
3. Bundle the renderer's WASM as a module and initialize lazily once per isolate through a shared promise. Import a compiled WebAssembly module using the supported Workers approach, rather than assuming a browser-style URL fetch loader works. Release per-render allocations after producing a PNG byte array. [Workers WASM support](https://developers.cloudflare.com/workers/runtime-apis/webassembly/javascript/), [resvg WASM package](https://github.com/thx/resvg-js/tree/main/wasm)
|
||||
4. Bundle licensed regular/bold fonts, for example Noto Sans with its license. Explicitly map SVG weights to those fonts; do not rely on operating-system fonts. Verify accented Pokémon names, punctuation, long descriptions and the intended non-Latin fallback policy. Font appearance will need review against existing PNGs.
|
||||
5. Add golden-image/metadata tests for empty/long names, XML special characters, all badges and 0/100% progress. Exercise concurrent requests, first invocation, repeated renders and error recovery in built workerd. Require HTTP 200, PNG signature/dimensions, correct sharing access and no private notes.
|
||||
6. Re-audit bundle/assets, CPU, memory and startup after adding WASM/fonts. Run the deployed share-image test and backup/export/auth matrix before setting compatibility to passed. If this renderer exceeds operating constraints, compare a separately hosted image service in a new proposal; do not silently redirect rendering to production Netlify.
|
||||
|
||||
## Remaining deployed benchmark
|
||||
|
||||
1. Supply equivalent staging credentials and disposable national/scoped-form fixtures, then publish this exact code to both preview hosts. Preserve actual deployed revision and environment metadata.
|
||||
2. Establish improved Netlify document/data baselines. Consider a separate region-aligned Netlify preview as an additional experiment; do not change the primary paired comparison mid-run.
|
||||
3. Exercise sign-in, token refresh, account switching, Google/Dropbox OAuth and refresh, export generation, public shares/images, compression, secrets/bindings, static cache headers, service-worker install/update/offline routing and custom preview domains on both real hosts.
|
||||
4. Use the [comparison harness](pokedex-implementation.md#instrumentation-and-comparison-harness) for at least 30 warm samples per host × fixture × direct/client navigation. Keep code, Supabase project, fixtures, browser settings and client locations identical. Run additional deliberately idled samples and record the idle duration separately; first-observed is not a cold-start claim.
|
||||
5. Recommend migration only when overall first-interactive p75 improves by **both 20% and 200 ms**, no fixture/navigation combination regresses by more than 10%, and functional checks pass. Include costs and operating requirements before making the final decision.
|
||||
|
||||
## Cost projection and operations
|
||||
|
||||
Actual monthly traffic, CPU usage and the account's current Netlify billing arrangement are not available, so no savings claim is justified.
|
||||
|
||||
Cloudflare Paid has a $5/month minimum, includes 10 million dynamic requests and 30 million CPU-ms, then charges $0.30/million requests and $0.02/million CPU-ms. Direct static asset requests are free; requests served through Workers Caching have different billing. Illustrative direct-static configuration: 1 million dynamic requests at 20 ms CPU each remains within $5; 10 million at 20 ms is approximately $8.40. These exclude other services and are assumptions, not measured app CPU. [Workers pricing](https://developers.cloudflare.com/workers/platform/pricing/)
|
||||
|
||||
For credit-based Netlify plans, estimate credits as `15 × production deploys + 10 × compute GB-hours + 20 × bandwidth GB + 2 × requests/10,000`. Confirm whether this account uses credit-based or legacy billing and apply its actual plan/allowance. Thumbnail byte savings affect bandwidth, while increased first-party sprite requests affect request metering. [Netlify pricing](https://www.netlify.com/pricing/)
|
||||
|
||||
Before a qualifying cutover: move database migrations out of host builds into one serialized CI release step; provision secrets and OAuth redirects; validate the custom domain and cookies; monitor error/latency/export metrics; retain the working Netlify deployment and a documented DNS rollback. These production changes remain conditional on completing the gates above.
|
||||
@@ -1,115 +0,0 @@
|
||||
# Pokédex performance implementation
|
||||
|
||||
Status: application changes implemented; hosting comparison remains gated. Measurements below are local production-build evidence, not deployed latency improvements. See [hosting evaluation](pokedex-hosting-evaluation.md) for the migration decision and remaining work.
|
||||
|
||||
## Application changes
|
||||
|
||||
- Ownership and saved scope links are read together. Scoped entry retrieval is shared by rows/count consumers; the full grid performs no count query. Catch joins use ID maps. Scope deduplication, named default forms, supplements and ordering remain covered by repository tests.
|
||||
- The page awaits the entire compact grid and renders initial boxes on the server. Authentication state reaches SSR through the validated layout user. Successful navigation needs no grid API request; failed grid loads expose retry.
|
||||
- `PokedexGridRow` contains identity, sprite resolution fields and catch flags. The page and authenticated `/api/pokedexes/[id]/grid` endpoint transport named tuples defined in `PokedexGridRow.ts`; `packGrid`/`unpackGrid` keep that wire format out of components. Instructions, notes, origin games and repeated owner/dex IDs are absent. Existing full combined-data consumers retain their contracts.
|
||||
- The detail endpoint verifies ownership and membership before returning one full `CombinedData` row. The modal opens immediately with identity and full artwork, then loads editable details. Its account/dex/entry cache, abort/sequence checks and pending-patch merge protect rapid selection changes and optimistic edits.
|
||||
- Status writes send changed fields. Bulk writes group records by supplied columns, preserving omitted notes and flags; explicit empty notes clear them. New records use database defaults. Full-record callers and exports remain supported. Bulk box actions still target all original 30 slots, including dimmed entries.
|
||||
- Grid placeholders preserve geometry; visible boxes and one row of overscan mount populated cells. Focused boxes remain mounted, keyboard navigation crosses boundaries, modal close restores focus, and an accessible render-all option exposes the complete document. Each Pokémon uses one button with identity/status and a noninteractive tooltip.
|
||||
- Density is persisted in a cookie for stable SSR geometry and in local storage. Resize/density changes preserve the current box anchor. Existing local-storage-only preferences are replaced by the cookie after choosing a density.
|
||||
- Automatic snapshot/backup work starts at the interactive/idle boundary with a five-second fallback. Concurrent backup refreshes coalesce. Explicit sync and edit/account/reconnect invalidations retain freshness behavior. Backup-status GET no longer revalidates via `setSession`.
|
||||
- Complete offline snapshot format 2 is retained. Offline modal details come only from the matching account snapshot; missing copies report unavailable details. Offline details are read-only, as before for offline mutations.
|
||||
|
||||
## Artwork and build behavior
|
||||
|
||||
`npm run sprites:grid` generates 3,170 first-party WebP thumbnails, at most 128 pixels per side, quality 80. Builds run this automatically. Generated assets live in ignored `static/sprites-grid/v1/`; the generator emits a manifest. Originals remain 512 pixels for details and explicit full-artwork downloads.
|
||||
|
||||
The generated catalog is 10,007,504 bytes versus 53,659,230 bytes for the source WebP catalog (81.4% smaller). Shiny, female and named-form resolution uses the existing key/fallback chain. The service worker recognizes versioned grid sprites in the existing artwork cache; neither thumbnail nor original catalogs are blanket precached. Root `_headers` supplies immutable caching on Netlify/Cloudflare. Bump the URL version whenever generation settings or source images change; generation skips existing files within a version.
|
||||
|
||||
## Local acceptance evidence
|
||||
|
||||
Fixtures: national (1,025 entries), Scarlet/Paldea forms (439 entries), mixed catch flags and nonempty notes. Integration tests additionally cover missing catches and overlapping scopes through repository tests. Browser viewport: 1,350 × 940, comfortable density, Chromium, local Node production build and local seeded Supabase.
|
||||
|
||||
The packed grid is approximately 81 KB national and 36 KB scoped, versus the investigation's approximately 593 KB and 255 KB full-data JSON. The same-fixture integration assertion separately verifies at least 60% reduction against full combined rows. These are serialized row sizes, not compressed HTML document sizes.
|
||||
|
||||
The local browser checks verify at most 180 populated mounted cells, fewer than 2,500 DOM elements and CLS at most 0.1; observed initial population is 120 cells. Density/mobile checks include all three densities at 1,350 and 390 pixels. Detail artwork is asserted to load at 512 pixels. No redundant grid request is allowed; intentional detail requests are allowed.
|
||||
|
||||
Warm results from the corrected harness (30 samples per row; [sanitized summary](pokedex-local-results.json)):
|
||||
|
||||
| Fixture | Navigation | First visible p75 | First interactive p75 | Decoded response bytes p75 | Maximum DOM |
|
||||
| ------------ | ---------- | ----------------: | --------------------: | -------------------------: | ----------: |
|
||||
| National | Direct | 123.5 ms | 145.4 ms | 248,668 | 2,088 |
|
||||
| National | Client | 495.6 ms | 494.1 ms | 102,687 | 2,090 |
|
||||
| Scoped forms | Direct | 114.5 ms | 133.1 ms | 203,601 | 2,073 |
|
||||
| Scoped forms | Client | 491.7 ms | 490.6 ms | 45,645 | 2,075 |
|
||||
|
||||
Every warm sample had 120 populated cells, zero observed CLS, zero redundant grid requests and a measured response size. Direct response bytes include SSR markup and SvelteKit data; client bytes are SvelteKit data responses, not just the packed rows. Interactive marks can precede the next animation-frame visibility observation by a few milliseconds. Client timings include Playwright click overhead, as explained below. This benchmark preceded the final account-switch backup invalidation guard; the final production smoke suite also passed after that guard.
|
||||
|
||||
### Validation completed
|
||||
|
||||
| Check | Result |
|
||||
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Svelte/TypeScript | 0 errors; one existing `Tooltip.svelte` CSS `@apply` warning |
|
||||
| Unit tests | 193 passed |
|
||||
| Data tests | 18 passed |
|
||||
| Coverage | Existing thresholds passed: 66.81% lines/statements, 88.79% branches, 90.99% functions |
|
||||
| Local Supabase integration | 16 passed, including same-fixture payload reduction and note preservation |
|
||||
| Authenticated browser regressions | 53 passed, including mock Google/Dropbox OAuth, exports and service-worker/offline flows |
|
||||
| Build matrix | Four checks in each of Netlify/Node × generateSW/injectManifest passed |
|
||||
| Production performance/behavior | Both fixtures, all densities/mobile, resize anchor, focus/keyboard, modal race, full artwork and offline snapshot isolation passed |
|
||||
| Cloudflare | Final build and Wrangler dry-run passed; local workerd share-image generation failed as documented in the hosting report |
|
||||
| Formatting/lint | All changed/new implementation files pass Prettier; ESLint and `git diff --check` pass |
|
||||
|
||||
Repository-wide `npm run lint` also scans the pre-existing untracked `dex.har` and `POKEDEX_PERFORMANCE_PROMPT.md`; those two files have formatting warnings and were intentionally left untouched. No coverage threshold was lowered. Disposable performance accounts and local test servers were cleaned up.
|
||||
|
||||
### Reproduce
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
npx supabase start
|
||||
npx playwright install chromium
|
||||
npm run check
|
||||
npm run test:unit
|
||||
npm run test:integration
|
||||
npm run test:build
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER=true npm run test:bdd
|
||||
npm run test:performance
|
||||
PERF_BENCHMARK=true npm run test:performance
|
||||
```
|
||||
|
||||
Run builds sequentially: they share `.svelte-kit` and `build`. Performance tests create a disposable local-only account, use private temporary session files, clean up afterward, and copy sanitized reports/screenshots to ignored `test-results/performance/`. Cleanup failures retain recovery files. Never commit session/configuration files.
|
||||
|
||||
## Instrumentation and comparison harness
|
||||
|
||||
Set `POKEDEX_PERFORMANCE=true` on a preview to emit fixed-label `Server-Timing` stages: auth, ownership, scopes, entries, catches, preparation and total. Timings do not include IDs, cookies, notes or query text. Authentication includes the request hook's validation. Total measures the page/service span, not total platform request lifetime; stages may therefore not sum to total.
|
||||
|
||||
The browser harness measures first visible cells via animation frames and first interactive cells via `pokedex:first-interactive`, independently of response completion. It collects DOM/cells, layout shifts, intentional/redundant API counts and response sizes. For Chromium service-worker responses where Playwright cannot read the body, decoded Resource Timing bytes are used; absent measurements stay null. Decoded, encoded and transferred bytes are distinguished.
|
||||
|
||||
`benchmark.mjs` requires at least 30 warm samples per fixture/navigation combination. It records the first observed run separately; that is **not** evidence of a cold start or first-after-idle run. For real-host work, collect deliberately idled runs separately and record the idle interval. Client timing starts before Playwright's click, so includes its actionability overhead; preserve identical tooling/settings between hosts and report this limitation.
|
||||
|
||||
Private configuration example (replace all placeholders):
|
||||
|
||||
```json
|
||||
{
|
||||
"samples": 30,
|
||||
"revision": "exact-deployed-commit",
|
||||
"databaseLabel": "same-staging-project",
|
||||
"clientLocation": "London-fixed-runner",
|
||||
"environment": "deployed",
|
||||
"compatibilityPassed": false,
|
||||
"hosts": [
|
||||
{
|
||||
"label": "netlify",
|
||||
"url": "https://NETLIFY-PREVIEW",
|
||||
"storageState": "/PRIVATE/netlify-session.json",
|
||||
"fixtures": [
|
||||
{ "id": "NATIONAL-ID", "name": "Performance National", "label": "national" },
|
||||
{ "id": "SCOPED-ID", "name": "Performance Scarlet Forms", "label": "scoped-forms" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Add an equivalent `cloudflare` host using the same database and fixtures. Run:
|
||||
|
||||
```sh
|
||||
node scripts/performance/benchmark.mjs /PRIVATE/config.json /PRIVATE/results.json
|
||||
node scripts/performance/compare.mjs /PRIVATE/results.json
|
||||
```
|
||||
|
||||
The comparison refuses insufficient samples and retains Netlify unless deployed evidence, compatibility and the agreed improvement thresholds all pass. Human review must still establish costs, environment equivalence and deployment prerequisites.
|
||||
@@ -1,227 +0,0 @@
|
||||
# Signed-in Pokédex performance investigation
|
||||
|
||||
Investigated 14–15 September 2026 at commit `31fd959350962c1f1b973a06ecd5cf4eeef86514`.
|
||||
|
||||
## Findings
|
||||
|
||||
The production capture establishes a server-response bottleneck: **3,506 ms waiting versus 261 ms receiving** the Pokédex data response. The current loading design exposes all entry work to that wait on the installed Netlify adapter. It requests practically the entire dex, and the adapter buffers the response before returning it. Independently, mounting the entire grid causes avoidable browser work.
|
||||
|
||||
Local measurements confirm the request sequence, duplicate game-scoped queries, large payload and rendering cost. **They do not explain the exact allocation of production's 3.5 seconds.** Regions, function startup and production query timings remain unverified. At the user's direction, no further production/dashboard access was attempted; subsequent measurements used local Supabase.
|
||||
|
||||
No performance fixes, migrations, deployment settings, public APIs or types were changed. Temporary instrumentation and the diagnostic rendering cap were removed. Full-resolution artwork must remain available when a user opens a Pokémon's detail view.
|
||||
|
||||
The numerical evidence, including individual runs and sanitized server traces, is in [pokedex-results.json](pokedex-results.json).
|
||||
|
||||
## Production evidence and its limits
|
||||
|
||||
| Evidence | Result | Interpretation |
|
||||
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
|
||||
| Supplied `dex.har`, data navigation | Wait 3,506.100 ms; receive 260.671 ms; 401,199 uncompressed bytes | Waiting dominates transfer. This includes server and network effects; it is not a database timer. |
|
||||
| Response headers | Brotli; `private,no-store`; Netlify request ID present | Compression already exists. User-specific content is not served as a public CDN cache hit. |
|
||||
| HAR content | Response text absent | Cannot measure production field sizes or infer the dex's form/scope configuration from this file. |
|
||||
| HAR sprites | 36 requests, 531,086 bytes total, `max-age=300` | Visible artwork has a substantial transfer cost independent of entry data. |
|
||||
| Supplied Lighthouse summary | Root response 3,373 ms; about 386 KB serialized inline data; 10,442 DOM elements; 1.9 s rendering; CLS 0.127 | Corroborates both server wait and browser cost. These are user-supplied findings, not a new run. |
|
||||
|
||||
The HAR contains only one application request plus the 36 sprite requests. It does **not** contain the offline-snapshot or backup-status requests; their production sizes/durations come from the supplied Lighthouse summary. Extension findings were excluded.
|
||||
|
||||
### Why streaming currently fails to hide the work
|
||||
|
||||
[The server load](../../src/routes/pokedex/[id]/+page.server.ts) returns an unresolved `initialCombinedData` promise, but `INITIAL_PAGE_SIZE` is **9,999**, matching the client. Its `.then()` discards the count/pagination metadata after the shared service has calculated it.
|
||||
|
||||
[adapter.mjs](../../adapter.mjs) selects standard Netlify functions (`edge: false`), except for Node test builds. Installed `@sveltejs/adapter-netlify` **4.4.2**, in `node_modules/@sveltejs/adapter-netlify/files/esm/serverless.js`, awaits `response.text()` for text responses and `response.arrayBuffer()` for binary responses. Both buffer the body before the handler returns. This proves the behavior of the installed adapter; the exact deployed package/build was not accessible. The HAR's long wait is consistent with it.
|
||||
|
||||
[Compression](../../src/lib/server/compression.ts) already skips application compression in Lambda because Netlify handles it. Enabling more compression would not fix this buffering boundary. The page also assigns its entries client-side, so even genuine transport streaming does not mean the grid is server-rendered and interactive immediately.
|
||||
|
||||
An isolated local identity-encoding stream probe received first bytes before completion:
|
||||
|
||||
| Dex | First-byte median | Complete-body median |
|
||||
| ------------- | ----------------: | -------------------: |
|
||||
| National | 48.3 ms | 104.7 ms |
|
||||
| Scarlet forms | 36.8 ms | 72.5 ms |
|
||||
|
||||
These are Vite measurements, not evidence that Netlify streams.
|
||||
|
||||
## Server request map and timings
|
||||
|
||||
Sources: [session hook](../../src/hooks.server.ts), [ownership repository](../../src/lib/repositories/PokedexRepository.ts), [scope service](../../src/lib/services/PokedexDexScopeService.ts), [combined-data service](../../src/lib/services/CombinedDataService.ts), [combined-data repository](../../src/lib/repositories/CombinedDataRepository.ts).
|
||||
|
||||
For an unexpired authenticated session, both measured dexes make **eight Supabase HTTP requests per page/data request**:
|
||||
|
||||
| Stage | National: 1,025 entries | Scarlet Paldea forms: 439 entries |
|
||||
| ---------------------------------------- | ------------------------------------------ | ------------------------------------------ |
|
||||
| Authentication | 1 `getUser` | 1 `getUser` |
|
||||
| Ownership, then scope links | 1 `pokedexes`, then 1 `pokedex_dex_scopes` | Same |
|
||||
| Entry branch | 2 sequential entry pages: 1,000 + 25 | 1 game-dex query, then 1 forms query |
|
||||
| Count branch, parallel with entry branch | 1 HEAD exact-count query | Repeats the game-dex query and forms query |
|
||||
| Catches, after entry retrieval | 2 sequential chunks: 1,000 + 25 | 1 chunk: 439 |
|
||||
| Total / longest chain of requests | **8 / 7** | **8 / 6** |
|
||||
|
||||
The game query returns 400 rows. The named-form query returns 45 rows; six are already represented, leaving 39 supplements and 439 unique entries. The count branch repeats both reads, transferring another **277,047 bytes** from Supabase just to count those entries. The two branches run concurrently: eliminating duplication reduces work and traffic, but does not necessarily remove their summed durations from the critical path.
|
||||
|
||||
General case:
|
||||
|
||||
- National path: `3 + entry-page requests + 1 count + catch chunks`. Pagination continues until a short/empty batch or the requested limit. An exact multiple of 1,000 may require an empty terminal request. Catch chunks use at most 1,000 IDs.
|
||||
- Explicit game scopes: `3 + 2 × (dex-page requests + applicable form-page requests) + catch chunks`. Counting fetches the full scope even when the requested page is small. Deduplication and slicing happen in JavaScript.
|
||||
- A game scope without saved dex scopes adds two sequential reads (`games`, then `game_dexes`). National dexes still pay the scope-link query even though scope resolution itself returns immediately.
|
||||
- Token refresh can add an authentication request. A separate probe using an expired saved cookie produced nine requests; the reported baseline was rerun with a fresh session. Replaying that stale cookie every time was a probe artifact, not normal browser session behavior.
|
||||
- `safeGetSession` is shared within a request, not across navigation and background requests.
|
||||
|
||||
### Isolated local server timings
|
||||
|
||||
Five warm requests per dex, one first run excluded. Queries were instrumented at the Supabase fetch boundary, including response-body completion; repository methods were timed separately. The cloned response used for instrumentation adds overhead. All values below are medians, so columns are not additive.
|
||||
|
||||
| Measure | National | Scarlet forms |
|
||||
| --------------------------------------------------- | -------: | ------------: |
|
||||
| Auth `getUser` HTTP duration | 35.5 ms | 25.3 ms |
|
||||
| Ownership + scope-link HTTP durations | 8.0 ms | 7.2 ms |
|
||||
| `findCombinedData`, including entries and catches | 50.7 ms | 33.3 ms |
|
||||
| `countCombinedData`, concurrent with find | 6.6 ms | 25.6 ms |
|
||||
| Catch retrieval, included within find | 16.4 ms | 6.5 ms |
|
||||
| Sum of all query durations | 92.9 ms | 86.0 ms |
|
||||
| Elapsed request time to final query-body completion | 87.6 ms | 64.1 ms |
|
||||
| Complete HTTP response | 104.7 ms | 72.5 ms |
|
||||
| Supabase response-body bytes, summed | 917,728 | 709,996 |
|
||||
|
||||
The difference between summed query durations and elapsed completion reflects concurrency. Repository work, serialization, streaming and measurement overhead sit outside or between query timings. Do not subtract these local medians from production TTFB to invent a region/startup estimate.
|
||||
|
||||
### Query plans and indexes
|
||||
|
||||
`EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)` ran under the local `authenticated` role with the fixture user's JWT subject. These are SQL execution times, excluding HTTP, PostgREST JSON serialization and function/network latency.
|
||||
|
||||
| Query shape | Median of five warm executions |
|
||||
| ------------------------------------------------- | -----------------------------: |
|
||||
| National ordered entries, first 1,000 | 5.557 ms |
|
||||
| National ordered entries, offset 1,000 | 5.866 ms |
|
||||
| National exact count through the view | 2.484 ms |
|
||||
| Scarlet game-dex detail rows | 10.706 ms |
|
||||
| Named forms available in Scarlet | 2.934 ms |
|
||||
| Catch query with the actual first 1,000 entry IDs | 0.476 ms |
|
||||
|
||||
The entry view joins and aggregates origin-game data before returning rows. The game-dex plan aggregates 1,390 Pokémon before joining the 400 scoped entries. The offset query repeats aggregation/sorting for the last 25 rows. These are real work amplification, but small absolute local costs.
|
||||
|
||||
The game membership index and existing catch-record dex index are used. The exact catch query is already sub-millisecond locally. **No new index is justified by these measurements.** Prefer avoiding duplicate/full-view work before speculative indexing. The evidence JSON also retains an earlier catch-plan probe with an ID subquery; the literal-ID result above is the closer match to the application.
|
||||
|
||||
## Payload and browser measurements
|
||||
|
||||
### Method and fixtures
|
||||
|
||||
- Existing local Supabase seed: 1,390 catalog entries, 1,025 default forms. No reset or migration was run.
|
||||
- Dedicated disposable account: national living dex with 1,025 catch records; Scarlet/Paldea form dex with 1,390 seeded catch records, of which 439 match this view. Mixed deterministic statuses and empty personal notes. Existing user data was preserved.
|
||||
- Headless Playwright Chromium, 1,350 × 940 viewport, device scale 1, no extensions, no CPU/network throttling. Vite 5.4.21 development server, Node 24.18.0, same-origin sprites.
|
||||
- One first run plus five warm runs for each navigation/dex combination. Client navigation clicks the real card's View button from My Pokédexes. Each client sample starts from a loaded list page with a 1.1-second settling period.
|
||||
- “Visible cells” uses Playwright's first-cell visibility check and includes automation overhead. Response completion is network response completion, not full page idle. Layout/style time is CDP `LayoutDuration + RecalcStyleDuration` over the sample and 1.5-second follow-up; it is not Lighthouse's entire rendering category.
|
||||
|
||||
| Dex / navigation | First-run visible | Five warm visible times | Median TTFB | Median response complete | Median visible | DOM |
|
||||
| ---------------------- | ----------------: | -------------------------- | ----------: | -----------------------: | -------------: | -----: |
|
||||
| National / direct | 798 ms | 549, 986, 519, 474, 432 ms | 84 ms | 268 ms | **519 ms** | 10,236 |
|
||||
| National / client | 407 ms | 322, 335, 369, 423, 381 ms | 63 ms | 133 ms | **369 ms** | 10,236 |
|
||||
| Scarlet forms / direct | 462 ms | 309, 278, 299, 312, 317 ms | 68 ms | 148 ms | **309 ms** | 4,476 |
|
||||
| Scarlet forms / client | 224 ms | 272, 227, 233, 363, 274 ms | 59 ms | 111 ms | **272 ms** | 4,476 |
|
||||
|
||||
These development results must not be equated with optimized production performance. Local full-dataset fixture content, development scripts/styles and serialized session metadata differ from the production capture. The initial Vite dependency-optimization failure was resolved before collecting this matrix. One first-run streamed response could not be read through Chromium's body-capture API; its bytes are recorded as null, not zero.
|
||||
|
||||
### What is needed at first paint
|
||||
|
||||
At this viewport the national dex mounts 35 boxes and 1,025 populated cells, with only **60 cells visible**. Scarlet mounts 15 boxes and 439 cells, also with 60 visible. The national grid is 10,532 px tall. IntersectionObserver already defers distant sprite downloads; it does not prevent mounting those cells, tooltip components and loading spinners. There were 73 image elements, not 1,025 downloaded sprites.
|
||||
|
||||
The box and tooltip need identity/name/form/number/sprite key and catch-status flags. Evolution/catch instructions, origin games, dex notes and personal notes belong to the detail workflow. Repeated catch `userId`/`pokedexId` values are also expensive in ordinary JSON. The page still needs global totals and stable placement, so simply lowering the limit would break existing behavior.
|
||||
|
||||
| JSON measurement | National | Scarlet forms |
|
||||
| ------------------------------------------------- | --------: | ------------: |
|
||||
| Full combined rows | 592,997 B | 255,319 B |
|
||||
| Display-only rows for every entry | 170,636 B | 74,154 B |
|
||||
| Display-only first 60 entries | 9,751 B | 10,757 B |
|
||||
| Detail-only Pokémon fields, separately serialized | 214,064 B | 91,887 B |
|
||||
|
||||
“Display-only” is a sizing experiment, **not a proposed drop-in API**: it excludes identifiers/fields that writes and details currently need. JSON partitions are serialized separately and are not additive. Actual SvelteKit data responses use a different serialization format: warm client response medians were 518,649 B and 222,892 B. Direct development documents were about 686 KB and 375 KB; their largest inline scripts were 545,897 B and 235,175 B.
|
||||
|
||||
### Controlled rendering comparison
|
||||
|
||||
A temporary diagnostic changed only the rendered box loop to its first two boxes, leaving the fetched full dataset intact. Both comparisons blocked the two background endpoints, used the existing offline worker, and measured five warm direct loads plus 2.5 seconds after visibility. The full loop was restored afterward.
|
||||
|
||||
| Measure | All 35 boxes | First 2 boxes only |
|
||||
| ----------------------------------------------- | -----------: | -----------------: |
|
||||
| Mounted populated cells | 1,025 | 60 |
|
||||
| DOM elements | 10,236 | 728 |
|
||||
| Median time to visible cells | 464 ms | 318 ms |
|
||||
| Median layout/style work | 63.9 ms | 13.4 ms |
|
||||
| Median main-thread task time during observation | 1,124 ms | 82.8 ms |
|
||||
| Median accumulated long-task duration | 147 ms | 0 ms |
|
||||
|
||||
This isolates a substantial browser cost from mounting the full view; it does not demonstrate a complete virtualization implementation or guarantee the same production savings. Off-screen spinner components remain mounted in the full view, but this experiment does not separately attribute their cost.
|
||||
|
||||
Direct-load layout-shift observations varied (roughly 0.082–0.657), with footer/content shifts recorded. Client-click samples recorded zero, partly because recent-input shifts are excluded. These short development observations are not interchangeable with production's Lighthouse CLS of 0.127. Reserve appropriate loading geometry when redesigning rendering.
|
||||
|
||||
## Background requests and artwork
|
||||
|
||||
[Offline synchronization](../../src/lib/stores/offlineSync.ts) schedules startup after one second and reuses a matching format-2 snapshot younger than 15 minutes. Explicit edits, retry, account changes and reconnect trigger a fresh copy. [The snapshot endpoint](../../src/routes/api/offline-snapshot/+server.ts) loads every owned dex in parallel, including full entry/detail/catch data. For this account that is ten Supabase calls with a fresh token, independent of the page's eight.
|
||||
|
||||
A separate local comparison activated the existing offline worker and controlled metadata age in the disposable browser cache. Five warm direct loads per condition:
|
||||
|
||||
| Condition | Median visible cells | Snapshot requests per load | Backup requests per load |
|
||||
| -------------------------------------- | -------------------: | -------------------------: | -----------------------: |
|
||||
| Fresh snapshot, enabled | 449 ms | 0 | 1 |
|
||||
| Stale snapshot, enabled | 431 ms | 1 | 1 |
|
||||
| Stale snapshot, both endpoints blocked | 464 ms | 0 completed | 0 completed |
|
||||
|
||||
The stale snapshot was **849,107 B**, typically around 130–160 ms locally. It started around 1.2 seconds after navigation, after cells were visible. Backup status began around 0.2–0.3 seconds, overlapping rendering. Blocking requests did **not** improve time to visible cells consistently in these runs. Thus background work is a secondary bandwidth/CPU/DB contention concern, not a demonstrated explanation of initial local TTFB. An already-running snapshot could overlap a later client navigation; the production HAR cannot establish that.
|
||||
|
||||
[Backup status GET](../../src/routes/api/export-integrations/+server.ts) calls `requireAuth`, reads the memoized session, then calls `supabase.auth.setSession(session)`. Instrumentation observed **two `GET /auth/v1/user` requests plus one integration query**. Memoizing `safeGetSession` does not eliminate validation triggered by this separate `setSession` call.
|
||||
|
||||
The worker stores snapshot data without automatically fetching all artwork; artwork fetches are cached as viewed, and full artwork download is explicit. The initial dev navigation matrix had no registered worker, so its snapshot requests did not establish cache-reuse behavior; that is why the separate worker-enabled comparison was necessary.
|
||||
|
||||
### Preserve detail-image resolution
|
||||
|
||||
[Sprite URL resolution](../../src/lib/utils/spriteUrl.ts) selects local assets only when `PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER` is `true`; otherwise it uses GitHub raw URLs. The HAR confirms the latter for production. Local `home/1.webp` and `home/shiny/1.webp` are both 512 × 512. In-memory 128 px WebP experiments at quality 80 reduced 13,530 → 2,726 B and 12,634 → 2,522 B (about 80%). Two samples are not a catalog-wide saving estimate.
|
||||
|
||||
**Retain the 512 px originals for the clicked Pokémon detail view.** If pursuing this optimization, introduce separate grid thumbnails and retain full-resolution detail URLs, with fallback coverage for shiny, female and named forms. Update manifest/cache versioning deliberately; replacing assets at existing URLs conflicts with the worker's cache-forever assumption. The sprite build script currently defaults `SPRITE_MAX_SIZE` to zero, so “sprites-small” does not imply smaller dimensions. No sprite files were changed.
|
||||
|
||||
## Ranked recommendations
|
||||
|
||||
| Priority | Recommended next change | Evidence / confidence | Effort and risk |
|
||||
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 1 | Stop computing an unused count in the page preload; share one ordered scoped-entry retrieval when rows and count are both needed. Combine ownership and scope retrieval where practical. | Confirmed redundant work: one national count or two full scoped queries, plus serial metadata reads. High confidence in work reduction; production milliseconds unknown. | Low–medium. Preserve ownership/RLS, scope deduplication, named default forms and ordering. |
|
||||
| 2 | Reduce initial data and the number of mounted boxes together. Evaluate viewport rendering with reserved space, lightweight global totals/order, and on-demand detail data. | Only 60/1,025 cells visible; capped rendering reduced visible time 31%, layout/style work 79%, DOM 93%. Display-only JSON is about 71% smaller. | Medium–high. Preserve box numbering, scroll/keyboard access, global filters/counts, bulk edits, navigation races and offline details. Do not just change `9999` to `60`. |
|
||||
| 3 | Resolve the Netlify buffering constraint as part of the loading design. Validate a supported streaming-capable adapter/runtime in a future deployment, or make the initial response intentionally small without relying on deferred promises. | Installed adapter demonstrably buffers. High confidence in the constraint; no deployed streaming alternative verified. | Medium. Keep Netlify as target; test both document and data navigation. A shell alone is not an interactive grid. |
|
||||
| 4 | Remove redundant backup-session validation if authentication remains guaranteed; schedule nonessential background work after critical page work while preserving snapshot freshness and explicit syncs. | Three backup calls confirmed; stale snapshot 849 KB. No consistent local first-visibility gain when blocked. | Low–medium. Verify reconnect status and account isolation; do not disable offline support. |
|
||||
| 5 | Serve separate grid thumbnails, retaining full-resolution detail artwork. Evaluate first-party hosting/cache headers for those assets. | Two samples about 80% smaller; production HAR sprite transfer 531 KB. | Medium. Preserve all variants and offline cache behavior. Requires separate thumbnail URLs, not destructive resizing. |
|
||||
| 6 | Replace per-entry linear catch lookup with a map when touching repository joins. | `.find()` per row has quadratic scaling, but current local processing is small. | Low. Lower priority than network, payload and DOM work. |
|
||||
|
||||
Do not propose a database index or a region move as an established fix from the available evidence. Production-specific stage instrumentation and region verification remain future work requiring access.
|
||||
|
||||
### Regression coverage and secondary findings
|
||||
|
||||
[Lighthouse CI](../../lighthouserc.cjs) audits only five signed-out pages using the Node adapter. [Signed-in BDD scenarios](../../tests/bdd/features/performance.feature) allow five seconds for direct load and three seconds per switch, assert first-cell visibility and prohibit a separate entry request. Those checks do not exercise Netlify's response buffering, assert authenticated data/DOM sizes, or distinguish shell, first cells and completed loading.
|
||||
|
||||
Future checks should use a full national fixture and a scoped form fixture, measure document and client navigation separately, and track response bytes, mounted cells, first useful view and background request counts. Preserve ordering, overlapping dex deduplication, named default forms, missing catches, filters, bulk status updates, modal notes, shiny/female artwork, account changes and fresh/stale offline behavior. A deliberate loading redesign may require replacing the “no separate entry request” assertion with a user-visible timing/data budget. Deployment checks must exercise actual Netlify responses, not infer parity from Node CI.
|
||||
|
||||
The nested `<button>` in [Tooltip](../../src/lib/components/Tooltip.svelte) is confirmed by source inside each Pokémon button. Address its accessible name and invalid nesting in follow-up UI work. Contrast, target-size and missing-meta-description findings are secondary to the performance diagnosis; contrast/target size require rendered theme/mobile verification, while the missing page description is visible in source. Preserve detail-view artwork quality as requested.
|
||||
|
||||
## Reproduction and cleanup
|
||||
|
||||
Raw captures, disposable credentials and diagnostic scripts are local-only under `/tmp/pokedex-perf-investigation` (directory mode 700); they are not included in the repository evidence. The evidence JSON omits sessions, fixture IDs, personal data and full query filters. The original `dex.har` and prompt remain untouched and untracked.
|
||||
|
||||
The seeded stack was already running. Instead of resetting it or invoking Tailwind output generation, the investigation used the project's loopback-checked Supabase environment wrapper and the same Vite dev server used by `dev:supabase`:
|
||||
|
||||
```sh
|
||||
# From the repository root; these diagnostic scripts are retained locally, not installed tooling.
|
||||
node /tmp/pokedex-perf-investigation/setup.mjs
|
||||
node scripts/run-with-local-supabase.mjs node /tmp/pokedex-perf-investigation/seed.mjs
|
||||
node scripts/run-with-local-supabase.mjs node /tmp/pokedex-perf-investigation/auth.mjs
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER=true node scripts/run-with-local-supabase.mjs npx vite dev --host 127.0.0.1 --port 4173 > /tmp/pokedex-perf-investigation/dev.log 2>&1
|
||||
|
||||
# In another terminal, after Vite dependency optimization has settled:
|
||||
node /tmp/pokedex-perf-investigation/measure.mjs
|
||||
node /tmp/pokedex-perf-investigation/background.mjs
|
||||
node /tmp/pokedex-perf-investigation/server-probe.mjs
|
||||
node /tmp/pokedex-perf-investigation/analyze-server.mjs
|
||||
node /tmp/pokedex-perf-investigation/explain.mjs
|
||||
node /tmp/pokedex-perf-investigation/catch-plan.mjs
|
||||
|
||||
# After stopping the dev server and restoring the instrumented files:
|
||||
node scripts/run-with-local-supabase.mjs node /tmp/pokedex-perf-investigation/cleanup.mjs
|
||||
```
|
||||
|
||||
The rendering-cap experiment temporarily changed `{#each boxNumbers as boxNumber}` to `{#each boxNumbers.slice(0, 2) as boxNumber}`, then ran `PERF_VARIANT=grid-cap node /tmp/pokedex-perf-investigation/grid-cap.mjs` and restored the file. Run comparisons sequentially, discard the first run and keep viewport/cache/fixture settings equal. The full methodology and sanitized per-run evidence above remain usable if the temporary scripts are later removed.
|
||||
|
||||
At completion, original application files were restored byte-for-byte, the investigation server was stopped, and only the dedicated fixture account and its cascading test records were deleted. Existing database data and production configuration were unchanged. No commit was created.
|
||||
@@ -1,60 +0,0 @@
|
||||
{
|
||||
"capturedAt": "2026-09-15T16:28:41.386Z",
|
||||
"environment": "local-node",
|
||||
"revision": "local-working-tree",
|
||||
"clientLocation": "local-loopback",
|
||||
"groups": [
|
||||
{
|
||||
"fixture": "national",
|
||||
"navigation": "direct",
|
||||
"samples": 30,
|
||||
"visibleP75": 123.5,
|
||||
"interactiveP75": 145.40000009536743,
|
||||
"responseBytesP75": 248668,
|
||||
"missingBytes": 0,
|
||||
"maxDOM": 2088,
|
||||
"maxCells": 120,
|
||||
"maxCLS": 0,
|
||||
"gridRequests": 0
|
||||
},
|
||||
{
|
||||
"fixture": "national",
|
||||
"navigation": "client",
|
||||
"samples": 30,
|
||||
"visibleP75": 495.59999990463257,
|
||||
"interactiveP75": 494.09999990463257,
|
||||
"responseBytesP75": 102687,
|
||||
"missingBytes": 0,
|
||||
"maxDOM": 2090,
|
||||
"maxCells": 120,
|
||||
"maxCLS": 0,
|
||||
"gridRequests": 0
|
||||
},
|
||||
{
|
||||
"fixture": "scoped-forms",
|
||||
"navigation": "direct",
|
||||
"samples": 30,
|
||||
"visibleP75": 114.5,
|
||||
"interactiveP75": 133.10000014305115,
|
||||
"responseBytesP75": 203601,
|
||||
"missingBytes": 0,
|
||||
"maxDOM": 2073,
|
||||
"maxCells": 120,
|
||||
"maxCLS": 0,
|
||||
"gridRequests": 0
|
||||
},
|
||||
{
|
||||
"fixture": "scoped-forms",
|
||||
"navigation": "client",
|
||||
"samples": 30,
|
||||
"visibleP75": 491.7000000476837,
|
||||
"interactiveP75": 490.60000014305115,
|
||||
"responseBytesP75": 45645,
|
||||
"missingBytes": 0,
|
||||
"maxDOM": 2075,
|
||||
"maxCells": 120,
|
||||
"maxCLS": 0,
|
||||
"gridRequests": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,55 +0,0 @@
|
||||
// Lighthouse CI: `npm run test:lighthouse` builds the Node adapter output, serves it and audits the
|
||||
// public pages. Any category below 90 fails the run (and so the PR).
|
||||
// Set LIGHTHOUSE_FORM_FACTOR=desktop to audit with the desktop profile; the default is Lighthouse's
|
||||
// mobile profile (slow 4G + CPU throttling), which is the stricter of the two. The variable must not
|
||||
// start with LHCI_: lhci treats those as CLI flags, so LHCI_PRESET was passed to `lhci assert` as
|
||||
// an invalid `--preset`.
|
||||
const preset = process.env.LIGHTHOUSE_FORM_FACTOR === 'desktop' ? 'desktop' : undefined;
|
||||
|
||||
module.exports = {
|
||||
ci: {
|
||||
collect: {
|
||||
startServerCommand: 'npm run preview-node',
|
||||
startServerReadyPattern: 'Listening on',
|
||||
url: [
|
||||
'http://localhost:4173/',
|
||||
'http://localhost:4173/signin',
|
||||
'http://localhost:4173/welcome',
|
||||
'http://localhost:4173/offline-guide',
|
||||
'http://localhost:4173/forgot-password'
|
||||
],
|
||||
// Median of three runs smooths out noise from shared CI runners.
|
||||
numberOfRuns: 3,
|
||||
settings: {
|
||||
...(preset ? { preset } : {}),
|
||||
chromeFlags: '--no-sandbox --headless=new'
|
||||
}
|
||||
},
|
||||
assert: {
|
||||
assertions: {
|
||||
'categories:performance': ['error', { minScore: 0.9 }],
|
||||
'categories:accessibility': ['error', { minScore: 0.9 }],
|
||||
'categories:best-practices': ['error', { minScore: 0.9 }],
|
||||
'categories:seo': ['error', { minScore: 0.9 }],
|
||||
// The app compresses its own responses (see src/lib/server/compression.ts and the
|
||||
// precompressed build), so nothing may be served uncompressed.
|
||||
'uses-text-compression': ['error', { minScore: 1 }],
|
||||
// Regression budgets, set a little above what every audited page measured in September
|
||||
// 2026 (mobile profile: LCP 1.4-2.6 s, TBT 0 ms, CLS 0, ~100 KB script, ~14 KB CSS and
|
||||
// ~175 KB in total over the wire). A PR that makes pages meaningfully slower or heavier
|
||||
// fails here even while the category scores stay above 90. When a change legitimately
|
||||
// needs more, raise the number in the same PR so the cost is reviewed.
|
||||
'largest-contentful-paint': ['error', { maxNumericValue: 3000 }],
|
||||
'total-blocking-time': ['error', { maxNumericValue: 200 }],
|
||||
'cumulative-layout-shift': ['error', { maxNumericValue: 0.05 }],
|
||||
'resource-summary:script:size': ['error', { maxNumericValue: 115 * 1024 }],
|
||||
'resource-summary:stylesheet:size': ['error', { maxNumericValue: 20 * 1024 }],
|
||||
'resource-summary:total:size': ['error', { maxNumericValue: 220 * 1024 }]
|
||||
}
|
||||
},
|
||||
upload: {
|
||||
target: 'filesystem',
|
||||
outputDir: '.lighthouseci/reports'
|
||||
}
|
||||
}
|
||||
};
|
||||
+1
-7
@@ -1,9 +1,3 @@
|
||||
[build]
|
||||
command = "npm ci && ./node_modules/.bin/supabase link --project-ref $SUPABASE_PROJECT_REF && ./node_modules/.bin/supabase db push && npm run build"
|
||||
command = "npm run build"
|
||||
publish = "build"
|
||||
|
||||
[build.environment]
|
||||
NODE_VERSION = "22"
|
||||
|
||||
[context.deploy-preview]
|
||||
command = "npm ci && npm run build"
|
||||
|
||||
Generated
+1109
-6997
File diff suppressed because it is too large
Load Diff
+20
-52
@@ -1,19 +1,16 @@
|
||||
{
|
||||
"name": "livingdextracker",
|
||||
"name": "LivingDexTracker",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
||||
"dev-generate": "GENERATE_SW=true npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
||||
"dev-generate-suppress-w": "GENERATE_SW=true SUPPRESS_WARNING=true npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
||||
"sprites:build": "node scripts/optimize-sprites.mjs",
|
||||
"sprites:manifest": "node scripts/sprite-manifest.mjs",
|
||||
"build-generate-sw": "npm run sprites:grid && npm run tailwind && GENERATE_SW=true vite build",
|
||||
"build-generate-sw-node": "npm run sprites:grid && npm run tailwind && NODE_ADAPTER=true GENERATE_SW=true vite build",
|
||||
"build": "npm run sprites:grid && npm run tailwind && vite build",
|
||||
"build-inject-manifest": "npm run sprites:grid && npm run tailwind && vite build",
|
||||
"build-inject-manifest-node": "npm run sprites:grid && npm run tailwind && NODE_ADAPTER=true vite build",
|
||||
"build-self-destroying": "npm run sprites:grid && npm run tailwind && SELF_DESTROYING_SW=true vite build",
|
||||
"build-generate-sw": "GENERATE_SW=true vite build",
|
||||
"build-generate-sw-node": "NODE_ADAPTER=true GENERATE_SW=true vite build",
|
||||
"build": "npx tailwindcss -i ./static/input.css -o ./static/output.css && vite build",
|
||||
"build-inject-manifest-node": "NODE_ADAPTER=true vite build",
|
||||
"build-self-destroying": "SELF_DESTROYING_SW=true vite build",
|
||||
"preview": "vite preview --port=4173",
|
||||
"preview-node": "PORT=4173 node build",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
@@ -21,45 +18,20 @@
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"lint-fix": "npm run lint --fix",
|
||||
"format": "prettier --write .",
|
||||
"tailwind": "npx tailwindcss -i ./static/input.css -o ./static/output.css --minify",
|
||||
"test:lighthouse": "npm run build-inject-manifest-node && lhci autorun",
|
||||
"test:unit": "vitest run tests/unit",
|
||||
"test:data": "vitest run tests/data",
|
||||
"test:coverage": "vitest run tests/unit --coverage",
|
||||
"test:fast": "npm run test:coverage && npm run test:data",
|
||||
"test:integration": "node scripts/run-with-local-supabase.mjs vitest run --config vitest.integration.config.mts",
|
||||
"test:bdd:generate": "bddgen",
|
||||
"test:bdd:inner": "npm run build-inject-manifest && npm run test:bdd:generate && playwright test --project=chromium",
|
||||
"test:bdd": "ALLOW_PROVIDER_ENDPOINT_OVERRIDES=true GOOGLE_OAUTH_CLIENT_ID=mock-google GOOGLE_OAUTH_CLIENT_SECRET=mock-google-secret DROPBOX_OAUTH_CLIENT_ID=mock-dropbox DROPBOX_OAUTH_CLIENT_SECRET=mock-dropbox-secret GOOGLE_OAUTH_AUTHORIZE_URL=http://127.0.0.1:4199/google/authorize GOOGLE_OAUTH_TOKEN_URL=http://127.0.0.1:4199/google/token GOOGLE_DRIVE_API_URL=http://127.0.0.1:4199/google/drive GOOGLE_DRIVE_UPLOAD_URL=http://127.0.0.1:4199/google/upload DROPBOX_OAUTH_AUTHORIZE_URL=http://127.0.0.1:4199/dropbox/authorize DROPBOX_OAUTH_TOKEN_URL=http://127.0.0.1:4199/dropbox/token DROPBOX_UPLOAD_URL=http://127.0.0.1:4199/dropbox/files/upload node scripts/run-with-local-supabase.mjs npm run test:bdd:inner",
|
||||
"test:build:generate-static": "npm run build-generate-sw && GENERATE_SW=true vitest run --config vitest.build.config.mts",
|
||||
"test:build:generate-node": "npm run build-generate-sw-node && NODE_ADAPTER=true GENERATE_SW=true vitest run --config vitest.build.config.mts",
|
||||
"test:build:inject-static": "npm run build-inject-manifest && vitest run --config vitest.build.config.mts",
|
||||
"test:build:inject-node": "npm run build-inject-manifest-node && NODE_ADAPTER=true vitest run --config vitest.build.config.mts",
|
||||
"test:build": "npm run test:build:generate-static && npm run test:build:generate-node && npm run test:build:inject-static && npm run test:build:inject-node",
|
||||
"test:ci": "npm run test:fast && npm run test:integration && npm run test:build && npm run test:bdd && npm run test:performance",
|
||||
"test": "npm run test:ci",
|
||||
"supabase:start": "supabase start",
|
||||
"supabase:stop": "supabase stop",
|
||||
"supabase:reset": "supabase db reset",
|
||||
"supabase:studio": "supabase studio",
|
||||
"migrate:convert-tsv": "node scripts/convert-tsv-to-sql.js",
|
||||
"dev:local": "./scripts/dev-local.sh && npm run dev",
|
||||
"dev:supabase": "supabase start && npm run dev",
|
||||
"sprites:grid": "node scripts/grid-thumbnails.mjs",
|
||||
"build:cloudflare": "DEPLOY_TARGET=cloudflare npm run build",
|
||||
"preview:cloudflare": "wrangler dev",
|
||||
"check:cloudflare": "wrangler deploy --dry-run",
|
||||
"test:performance": "node scripts/run-with-local-supabase.mjs node scripts/performance/run-local.mjs"
|
||||
"tailwind": "npx tailwindcss -i ./static/input.css -o ./static/output.css",
|
||||
"test-generate-sw": "npm run build-generate-sw && GENERATE_SW=true vitest run && GENERATE_SW=true playwright test",
|
||||
"test-generate-sw-node": "npm run build-generate-sw-node && NODE_ADAPTER=true GENERATE_SW=true vitest run && NODE_ADAPTER=true GENERATE_SW=true playwright test",
|
||||
"test-inject-manifest": "npm run build-inject-manifest && vitest run && playwright test",
|
||||
"test-inject-manifest-node": "npm run build-inject-manifest-node && NODE_ADAPTER=true vitest run && NODE_ADAPTER=true playwright test",
|
||||
"test": "npm run test-generate-sw && npm run test-generate-sw-node && npm run test-inject-manifest && npm run test-inject-manifest-node"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lhci/cli": "^0.15.1",
|
||||
"@playwright/test": "1.55.1",
|
||||
"@playwright/test": "^1.37.1",
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/adapter-cloudflare": "7.2.9",
|
||||
"@sveltejs/adapter-netlify": "^4.1.0",
|
||||
"@sveltejs/adapter-node": "^2.0.0",
|
||||
"@sveltejs/adapter-static": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.5.3",
|
||||
"@sveltejs/kit": "^2.0.6",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@types/cookie": "^0.6.0",
|
||||
"@types/eslint": "^8.56.0",
|
||||
@@ -67,33 +39,29 @@
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"@vite-pwa/assets-generator": "^0.2.4",
|
||||
"@vite-pwa/sveltekit": "^0.4.0",
|
||||
"@vitest/coverage-v8": "^1.6.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"daisyui": "^4.10.1",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"playwright-bdd": "^8.5.1",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"supabase": "2.72.7",
|
||||
"svelte": "^4.2.8",
|
||||
"svelte-check": "^3.6.2",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^1.0.4",
|
||||
"wrangler": "4.131.2"
|
||||
"vitest": "^1.0.4"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@supabase/ssr": "^0.12.7",
|
||||
"@supabase/supabase-js": "^2.116.0",
|
||||
"nanoid": "^5.0.4",
|
||||
"sharp": "^0.33.4"
|
||||
"@supabase/ssr": "^0.1.0",
|
||||
"@supabase/supabase-js": "^2.42.0",
|
||||
"mongoose": "^8.0.3",
|
||||
"nanoid": "^5.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=18.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
+16
-33
@@ -1,18 +1,10 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
import { defineBddConfig } from 'playwright-bdd';
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
|
||||
const url = 'http://localhost:4173';
|
||||
|
||||
const testDir = defineBddConfig({
|
||||
features: 'tests/bdd/features/**/*.feature',
|
||||
steps: ['tests/bdd/steps/**/*.ts', 'tests/bdd/fixtures.ts'],
|
||||
outputDir: '.features-gen',
|
||||
missingSteps: 'fail-on-gen'
|
||||
});
|
||||
const url = 'http://localhost:4173'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import { nodeAdapter } from './adapter.mjs';
|
||||
import { nodeAdapter } from './adapter.mjs'
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
@@ -24,28 +16,27 @@ import { nodeAdapter } from './adapter.mjs';
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir,
|
||||
globalTeardown: './tests/bdd/globalTeardown.ts',
|
||||
testDir: './client-test',
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||
outputDir: 'test-results/',
|
||||
timeout: 90 * 1000,
|
||||
timeout: 5 * 1000,
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 10 * 1000
|
||||
timeout: 1000,
|
||||
},
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: false,
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: 1,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [['line'], ['./tests/support/no-skips-reporter.ts']],
|
||||
reporter: 'line',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
@@ -55,16 +46,15 @@ export default defineConfig({
|
||||
//offline: true,
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'retain-on-failure',
|
||||
screenshot: 'only-on-failure'
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] }
|
||||
}
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
|
||||
// {
|
||||
// name: 'firefox',
|
||||
@@ -98,16 +88,9 @@ export default defineConfig({
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: [
|
||||
{
|
||||
command: 'node scripts/mock-provider-server.mjs',
|
||||
url: 'http://127.0.0.1:4199/__mock/state',
|
||||
reuseExistingServer: !process.env.CI
|
||||
},
|
||||
{
|
||||
command: nodeAdapter ? 'npm run preview-node' : 'npm run preview',
|
||||
webServer: {
|
||||
command: nodeAdapter ? 'pnpm run preview-node' : 'pnpm run preview',
|
||||
url,
|
||||
reuseExistingServer: !process.env.CI
|
||||
}
|
||||
]
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import process from 'node:process';
|
||||
import process from 'node:process'
|
||||
|
||||
export const generateSW = process.env.GENERATE_SW === 'true';
|
||||
export const generateSW = process.env.GENERATE_SW === 'true'
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Generates SQL migration from CSV files
|
||||
* Usage: node csv-to-migration.js <Region>
|
||||
* Example: node csv-to-migration.js Kanto
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
/**
|
||||
* Parse CSV file into array of objects
|
||||
*/
|
||||
function parseCSV(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const lines = content.split('\n').filter((line) => line.trim());
|
||||
const headers = lines[0].split(',');
|
||||
|
||||
return lines.slice(1).map((line) => {
|
||||
const values = parseCSVLine(line);
|
||||
const obj = {};
|
||||
headers.forEach((header, i) => {
|
||||
obj[header] = values[i] || null;
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single CSV line, handling quoted values
|
||||
*/
|
||||
function parseCSVLine(line) {
|
||||
const values = [];
|
||||
let current = '';
|
||||
let inQuotes = false;
|
||||
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const char = line[i];
|
||||
|
||||
if (char === '"') {
|
||||
inQuotes = !inQuotes;
|
||||
} else if (char === ',' && !inQuotes) {
|
||||
values.push(current);
|
||||
current = '';
|
||||
} else {
|
||||
current += char;
|
||||
}
|
||||
}
|
||||
values.push(current);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate migration SQL from CSV data
|
||||
*/
|
||||
function generateMigration(region) {
|
||||
console.log(`\nGenerating migration for ${region}...`);
|
||||
|
||||
// Determine generation number from region
|
||||
const regionToGen = {
|
||||
Kanto: 1,
|
||||
Johto: 2,
|
||||
Hoenn: 3,
|
||||
Sinnoh: 4,
|
||||
Unova: 5,
|
||||
Kalos: 6,
|
||||
Alola: 7,
|
||||
Galar: 8,
|
||||
Hisui: 8,
|
||||
Paldea: 9
|
||||
};
|
||||
const gen = regionToGen[region] || 1;
|
||||
|
||||
// 1. Load CSV files
|
||||
const pokemonPath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'data',
|
||||
'pokemon',
|
||||
`gen${gen}-${region.toLowerCase()}.csv`
|
||||
);
|
||||
const gamesPath = path.join(__dirname, '..', 'data', 'pokemon', 'games.csv');
|
||||
|
||||
if (!fs.existsSync(pokemonPath)) {
|
||||
console.error(`Error: Pokemon CSV not found at ${pokemonPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pokemon = parseCSV(pokemonPath);
|
||||
const games = parseCSV(gamesPath);
|
||||
|
||||
// 2. Filter for this region
|
||||
const regionGames = games.filter((g) => g.region === region);
|
||||
|
||||
if (regionGames.length === 0) {
|
||||
console.error(`Error: No games found for region ${region} in games.csv`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 3. Generate SQL
|
||||
let sql = `-- Seed ${region} region Pokémon (Gen ${gen})\n`;
|
||||
sql += `-- Auto-generated from CSV files\n\n`;
|
||||
|
||||
// Region-game mappings
|
||||
sql += `-- Insert ${region} region-game mappings\n`;
|
||||
sql += `INSERT INTO region_game_mappings (region, game) VALUES\n`;
|
||||
sql += regionGames.map((g) => ` ('${region}', '${g.displayName}')`).join(',\n');
|
||||
sql += `\nON CONFLICT (region, game) DO NOTHING;\n\n`;
|
||||
|
||||
// Pokemon entries (without regional dex number)
|
||||
sql += `-- Insert ${region} Pokémon entries\n`;
|
||||
sql += `INSERT INTO pokedex_entries (\n`;
|
||||
sql += ` "pokedexNumber",\n`;
|
||||
sql += ` pokemon,\n`;
|
||||
sql += ` form,\n`;
|
||||
sql += ` "canGigantamax",\n`;
|
||||
sql += ` "regionToCatchIn",\n`;
|
||||
sql += ` "gamesToCatchIn"\n`;
|
||||
sql += `) VALUES\n`;
|
||||
|
||||
const rows = pokemon.map((p) => {
|
||||
const form = p.form ? `'${p.form}'` : 'NULL';
|
||||
// Use regionalDexGames for the database (regional dex availability)
|
||||
// originGames column is for future origin dex feature
|
||||
const gamesField = p.regionalDexGames || p.games; // Fallback to old 'games' column for compatibility
|
||||
const gamesList = gamesField.split('|');
|
||||
const gamesArray = `ARRAY[${gamesList.map((g) => `'${g}'`).join(', ')}]`;
|
||||
|
||||
return `(${p.pokedexNumber}, '${p.name}', ${form}, false, '${region}', ${gamesArray})`;
|
||||
});
|
||||
|
||||
sql += rows.join(',\n');
|
||||
sql += '\nON CONFLICT ON CONSTRAINT unique_pokemon_form DO NOTHING;\n\n';
|
||||
|
||||
// Regional dex numbers (separate table)
|
||||
sql += `-- Insert ${region} regional dex numbers\n`;
|
||||
|
||||
const dexRows = pokemon
|
||||
.filter((p) => p.regionalNumber) // Only entries with regional dex numbers
|
||||
.map((p) => {
|
||||
const formCondition = p.form ? `form = '${p.form}'` : `form IS NULL`;
|
||||
|
||||
return ` ((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = ${p.pokedexNumber} AND ${formCondition}), '${region}', ${p.regionalNumber})`;
|
||||
});
|
||||
|
||||
if (dexRows.length > 0) {
|
||||
sql += `INSERT INTO regional_dex_numbers (\n`;
|
||||
sql += ` pokedex_entry_id,\n`;
|
||||
sql += ` region,\n`;
|
||||
sql += ` dex_number\n`;
|
||||
sql += `) VALUES\n`;
|
||||
sql += dexRows.join(',\n');
|
||||
sql += ';\n\n';
|
||||
} else {
|
||||
sql += '-- No regional dex numbers for this region\n\n';
|
||||
}
|
||||
|
||||
// Add metadata
|
||||
sql += `-- Add metadata\n`;
|
||||
sql += `INSERT INTO metadata (key, value) VALUES\n`;
|
||||
sql += ` ('${region.toLowerCase()}_seeded', 'true'),\n`;
|
||||
sql += ` ('${region.toLowerCase()}_seed_date', NOW()::TEXT),\n`;
|
||||
sql += ` ('${region.toLowerCase()}_pokemon_count', '${pokemon.filter((p) => !p.form).length}');\n`;
|
||||
|
||||
// 4. Write file
|
||||
const timestamp = new Date()
|
||||
.toISOString()
|
||||
.replace(/[-:T.]/g, '')
|
||||
.slice(0, 14);
|
||||
const filename = `${timestamp}_seed_${region.toLowerCase()}.sql`;
|
||||
const outputPath = path.join(__dirname, '..', 'supabase', 'migrations', filename);
|
||||
|
||||
fs.writeFileSync(outputPath, sql);
|
||||
|
||||
console.log(`✓ Generated ${filename}`);
|
||||
console.log(` - ${pokemon.length} Pokemon entries`);
|
||||
console.log(` - ${dexRows.length} regional dex numbers`);
|
||||
console.log(` - ${regionGames.length} games\n`);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
// Run
|
||||
const region = process.argv[2];
|
||||
if (!region) {
|
||||
console.error('Usage: node csv-to-migration.js <Region>');
|
||||
console.error('Example: node csv-to-migration.js Kanto');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
generateMigration(region);
|
||||
@@ -1,35 +0,0 @@
|
||||
import { readdir, mkdir, stat, writeFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import sharp from 'sharp';
|
||||
|
||||
// Bump the URL version whenever dimensions, quality or source artwork changes.
|
||||
const source = 'static/sprites-small/home';
|
||||
const destination = 'static/sprites-grid/v1/home';
|
||||
const files = [];
|
||||
async function walk(relative = '') {
|
||||
for (const entry of await readdir(path.join(source, relative), { withFileTypes: true })) {
|
||||
const name = path.join(relative, entry.name);
|
||||
if (entry.isDirectory()) await walk(name);
|
||||
else if (entry.name.endsWith('.webp')) files.push(name);
|
||||
}
|
||||
}
|
||||
await walk();
|
||||
const manifest = [];
|
||||
for (const relative of files.sort()) {
|
||||
const target = path.join(destination, relative);
|
||||
await mkdir(path.dirname(target), { recursive: true });
|
||||
try {
|
||||
await stat(target);
|
||||
} catch {
|
||||
await sharp(path.join(source, relative))
|
||||
.resize(128, 128, { fit: 'inside', withoutEnlargement: true })
|
||||
.webp({ quality: 80 })
|
||||
.toFile(target);
|
||||
}
|
||||
manifest.push({ path: relative.split(path.sep).join('/'), bytes: (await stat(target)).size });
|
||||
}
|
||||
await writeFile(
|
||||
'static/sprites-grid/v1/manifest.json',
|
||||
JSON.stringify({ version: 1, width: 128, quality: 80, files: manifest })
|
||||
);
|
||||
console.log(`Prepared ${files.length} versioned grid thumbnails; originals preserved.`);
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { createServer } from 'node:http';
|
||||
|
||||
const port = Number(process.env.MOCK_PROVIDER_PORT ?? 4199);
|
||||
const state = { requests: [], failUploads: false, revokeRefresh: false, refreshes: 0 };
|
||||
|
||||
function send(response, status, body, headers = {}) {
|
||||
response.writeHead(status, { 'Content-Type': 'application/json', ...headers });
|
||||
response.end(typeof body === 'string' ? body : JSON.stringify(body));
|
||||
}
|
||||
|
||||
const server = createServer(async (request, response) => {
|
||||
const url = new URL(request.url ?? '/', `http://127.0.0.1:${port}`);
|
||||
let body = '';
|
||||
for await (const chunk of request) body += chunk;
|
||||
|
||||
// Control-plane calls (including Playwright's webServer readiness polling of /__mock/state)
|
||||
// must not show up as provider traffic the assertions then reason about.
|
||||
if (!url.pathname.startsWith('/__mock/')) {
|
||||
state.requests.push({ method: request.method, path: url.pathname, query: url.search, body });
|
||||
}
|
||||
|
||||
if (url.pathname === '/__mock/state') return send(response, 200, state);
|
||||
if (url.pathname === '/__mock/reset') {
|
||||
state.requests = [];
|
||||
state.failUploads = false;
|
||||
state.revokeRefresh = false;
|
||||
state.refreshes = 0;
|
||||
return send(response, 200, { ok: true });
|
||||
}
|
||||
if (url.pathname === '/__mock/fail-uploads') {
|
||||
state.failUploads = true;
|
||||
return send(response, 200, { ok: true });
|
||||
}
|
||||
if (url.pathname === '/__mock/revoke-refresh') {
|
||||
state.revokeRefresh = true;
|
||||
return send(response, 200, { ok: true });
|
||||
}
|
||||
|
||||
if (url.pathname.endsWith('/authorize')) {
|
||||
const redirectUri = url.searchParams.get('redirect_uri');
|
||||
const oauthState = url.searchParams.get('state');
|
||||
if (!redirectUri || !oauthState) return send(response, 400, { error: 'missing redirect data' });
|
||||
const callback = new URL(redirectUri);
|
||||
callback.searchParams.set('code', 'mock-authorization-code');
|
||||
callback.searchParams.set('state', oauthState);
|
||||
response.writeHead(302, { Location: callback.toString() });
|
||||
return response.end();
|
||||
}
|
||||
|
||||
if (url.pathname.endsWith('/token')) {
|
||||
if (body.includes('grant_type=refresh_token')) {
|
||||
state.refreshes++;
|
||||
// Mirrors Google and Dropbox answering a revoked or expired refresh token.
|
||||
if (state.revokeRefresh) {
|
||||
return send(response, 400, { error: 'invalid_grant', error_description: 'Bad Request' });
|
||||
}
|
||||
}
|
||||
return send(response, 200, {
|
||||
access_token: 'mock-access-token',
|
||||
refresh_token: 'mock-refresh-token',
|
||||
expires_in: 3600,
|
||||
scope: 'files.content.write drive.file'
|
||||
});
|
||||
}
|
||||
|
||||
if (url.pathname.includes('/upload') || url.pathname.endsWith('/files/upload')) {
|
||||
if (state.failUploads) return send(response, 503, { error: 'mock upload failure' });
|
||||
return send(response, 200, { id: 'mock-file-id', name: 'pokedex.csv' });
|
||||
}
|
||||
|
||||
if (url.pathname.endsWith('/drive/files')) {
|
||||
if (request.method === 'GET') return send(response, 200, { files: [{ id: 'mock-folder-id' }] });
|
||||
return send(response, 200, { id: 'mock-folder-id' });
|
||||
}
|
||||
|
||||
return send(response, 404, { error: `No mock route for ${url.pathname}` });
|
||||
});
|
||||
|
||||
server.listen(port, '127.0.0.1', () => {
|
||||
console.log(`Mock provider server listening on http://127.0.0.1:${port}`);
|
||||
});
|
||||
|
||||
for (const signal of ['SIGTERM', 'SIGINT']) {
|
||||
process.on(signal, () => server.close(() => process.exit(0)));
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { mkdir, readdir, rename } from 'node:fs/promises';
|
||||
import sharp from 'sharp';
|
||||
import { writeSpriteManifest } from './sprite-manifest.mjs';
|
||||
|
||||
const inputDir = process.env.SPRITE_INPUT_DIR ?? path.join(process.cwd(), 'static', 'sprites');
|
||||
const outputDir =
|
||||
process.env.SPRITE_OUTPUT_DIR ?? path.join(process.cwd(), 'static', 'sprites-small');
|
||||
const format = (process.env.SPRITE_FORMAT ?? 'webp').toLowerCase();
|
||||
const quality = Number(process.env.SPRITE_QUALITY ?? 80);
|
||||
const maxSize = Number(process.env.SPRITE_MAX_SIZE ?? 0);
|
||||
|
||||
if (!['png', 'webp'].includes(format)) {
|
||||
console.error(`Unsupported SPRITE_FORMAT "${format}". Use "png" or "webp".`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function walk(dir, files = []) {
|
||||
const entries = await readdir(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await walk(fullPath, files);
|
||||
continue;
|
||||
}
|
||||
if (entry.isFile() && entry.name.toLowerCase().endsWith('.png')) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
const files = await walk(inputDir);
|
||||
console.log(`Optimizing ${files.length} sprite images from ${inputDir}`);
|
||||
|
||||
for (const [index, file] of files.entries()) {
|
||||
const relative = path.relative(inputDir, file);
|
||||
const baseName = path.basename(file, path.extname(file));
|
||||
const targetDir = path.join(outputDir, path.dirname(relative));
|
||||
await mkdir(targetDir, { recursive: true });
|
||||
|
||||
const targetExt = format === 'png' ? '.png' : '.webp';
|
||||
const targetPath = path.join(targetDir, `${baseName}${targetExt}`);
|
||||
const tempPath = format === 'png' && outputDir === inputDir ? `${targetPath}.tmp` : targetPath;
|
||||
|
||||
const pipeline = sharp(file);
|
||||
if (maxSize > 0) {
|
||||
pipeline.resize({
|
||||
width: maxSize,
|
||||
height: maxSize,
|
||||
fit: 'inside',
|
||||
withoutEnlargement: true
|
||||
});
|
||||
}
|
||||
|
||||
if (format === 'png') {
|
||||
await pipeline.png({ compressionLevel: 9, palette: true, quality }).toFile(tempPath);
|
||||
} else {
|
||||
await pipeline.webp({ quality }).toFile(tempPath);
|
||||
}
|
||||
|
||||
if (tempPath !== targetPath) {
|
||||
await rename(tempPath, targetPath);
|
||||
}
|
||||
|
||||
if ((index + 1) % 250 === 0) {
|
||||
console.log(`Processed ${index + 1}/${files.length}`);
|
||||
}
|
||||
}
|
||||
|
||||
const manifest = await writeSpriteManifest(outputDir);
|
||||
console.log(`Sprite optimization complete. Manifest lists ${manifest.files.length} sprites.`);
|
||||
@@ -1,181 +0,0 @@
|
||||
import { chromium } from '@playwright/test';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
|
||||
// Configuration holds session-file paths, never passwords. Keep it outside the repository.
|
||||
const [configPath, outputPath] = process.argv.slice(2);
|
||||
if (!configPath || !outputPath)
|
||||
throw new Error('Usage: node scripts/performance/benchmark.mjs CONFIG.json RESULTS.json');
|
||||
const config = JSON.parse(await readFile(configPath, 'utf8'));
|
||||
const samples = config.samples ?? 30;
|
||||
if (samples < 30)
|
||||
throw new Error('Host decisions require at least 30 warm samples per fixture/navigation');
|
||||
if (!config.revision || !config.databaseLabel || !config.clientLocation)
|
||||
throw new Error('Record revision, databaseLabel and clientLocation for comparable results');
|
||||
const browser = await chromium.launch();
|
||||
const results = [];
|
||||
try {
|
||||
for (const host of config.hosts) {
|
||||
for (const fixture of host.fixtures) {
|
||||
for (const navigation of ['direct', 'client']) {
|
||||
const context = await browser.newContext({
|
||||
baseURL: host.url,
|
||||
storageState: host.storageState,
|
||||
viewport: { width: 1350, height: 940 },
|
||||
deviceScaleFactor: 1
|
||||
});
|
||||
await context.addInitScript(() => {
|
||||
window.__perfShifts = [];
|
||||
new PerformanceObserver((list) => {
|
||||
for (const entry of list.getEntries())
|
||||
if (!entry.hadRecentInput)
|
||||
window.__perfShifts.push({ value: entry.value, time: entry.startTime });
|
||||
}).observe({ type: 'layout-shift', buffered: true });
|
||||
window.__watchGrid = () => {
|
||||
window.__perfVisible = null;
|
||||
function check() {
|
||||
const cell = document.querySelector('[data-entry-index]');
|
||||
if (
|
||||
location.pathname.startsWith('/pokedex/') &&
|
||||
cell &&
|
||||
cell.getBoundingClientRect().top < innerHeight &&
|
||||
cell.getBoundingClientRect().height > 0
|
||||
) {
|
||||
window.__perfVisible = performance.now();
|
||||
performance.mark('pokedex:first-visible');
|
||||
} else requestAnimationFrame(check);
|
||||
}
|
||||
requestAnimationFrame(check);
|
||||
};
|
||||
window.__watchGrid();
|
||||
});
|
||||
const page = await context.newPage();
|
||||
for (let index = 0; index <= samples; index++) {
|
||||
if (navigation === 'client') {
|
||||
await page.goto('/my-pokedexes');
|
||||
await page.getByText(fixture.name, { exact: true }).first().waitFor();
|
||||
} else if (index > 0) await page.goto('about:blank');
|
||||
const bodies = [];
|
||||
const requests = { grid: 0, details: 0, snapshot: 0, backup: 0 };
|
||||
const onRequest = (request) => {
|
||||
const path = new URL(request.url()).pathname;
|
||||
if (/\/pokedexes\/[^/]+\/(grid|combined-data)$/.test(path)) requests.grid++;
|
||||
if (/\/pokedexes\/[^/]+\/entries\//.test(path)) requests.details++;
|
||||
if (path === '/api/offline-snapshot') requests.snapshot++;
|
||||
if (path === '/api/export-integrations') requests.backup++;
|
||||
};
|
||||
const onResponse = (response) => {
|
||||
const path = new URL(response.url()).pathname;
|
||||
if (!path.startsWith(`/pokedex/${fixture.id}`)) return;
|
||||
bodies.push(
|
||||
(async () => {
|
||||
// Chromium may not expose bodies routed through a service worker.
|
||||
const body = await response.body().catch(() => null);
|
||||
return {
|
||||
url: response.url(),
|
||||
bytes: body?.length ?? null,
|
||||
status: response.status(),
|
||||
fromServiceWorker: response.fromServiceWorker(),
|
||||
timings: response.request().timing(),
|
||||
serverTiming: response.headers()['server-timing'] ?? null
|
||||
};
|
||||
})()
|
||||
);
|
||||
};
|
||||
page.on('request', onRequest);
|
||||
page.on('response', onResponse);
|
||||
let started = 0;
|
||||
if (navigation === 'direct')
|
||||
await page.goto(`/pokedex/${fixture.id}`, { waitUntil: 'domcontentloaded' });
|
||||
else {
|
||||
started = await page.evaluate(() => {
|
||||
performance.clearMarks('pokedex:first-interactive');
|
||||
window.__watchGrid();
|
||||
return performance.now();
|
||||
});
|
||||
await page
|
||||
.locator('.card')
|
||||
.filter({ hasText: fixture.name })
|
||||
.first()
|
||||
.getByRole('button', { name: 'View', exact: true })
|
||||
.click();
|
||||
}
|
||||
await page.waitForSelector('[data-grid-interactive]');
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
window.__perfVisible !== null &&
|
||||
performance.getEntriesByName('pokedex:first-interactive').length > 0
|
||||
);
|
||||
await page.waitForTimeout(1500);
|
||||
const browserData = await page.evaluate(
|
||||
(start) => ({
|
||||
visibleMs: window.__perfVisible - start,
|
||||
interactiveMs:
|
||||
performance.getEntriesByName('pokedex:first-interactive').at(-1).startTime - start,
|
||||
cells: document.querySelectorAll('[data-entry-index]').length,
|
||||
dom: document.querySelectorAll('*').length,
|
||||
cls: window.__perfShifts
|
||||
.filter((entry) => entry.time >= start)
|
||||
.reduce((total, entry) => total + entry.value, 0)
|
||||
}),
|
||||
started
|
||||
);
|
||||
page.off('request', onRequest);
|
||||
page.off('response', onResponse);
|
||||
const resources = await page.evaluate(() =>
|
||||
performance.getEntriesByType('resource').map((entry) => ({
|
||||
url: entry.name,
|
||||
decodedBytes: entry.decodedBodySize,
|
||||
encodedBytes: entry.encodedBodySize,
|
||||
transferBytes: entry.transferSize
|
||||
}))
|
||||
);
|
||||
const responses = (await Promise.all(bodies)).map(({ url, ...response }) => {
|
||||
const resource = resources.findLast((entry) => entry.url === url);
|
||||
return {
|
||||
...response,
|
||||
bytes: response.bytes ?? (resource?.decodedBytes || null),
|
||||
byteSource: response.bytes !== null ? 'response-body' : 'resource-timing',
|
||||
encodedBytes: resource?.encodedBytes ?? null,
|
||||
transferBytes: resource?.transferBytes ?? null
|
||||
};
|
||||
});
|
||||
results.push({
|
||||
host: host.label,
|
||||
fixture: fixture.label,
|
||||
navigation,
|
||||
run: index === 0 ? 'first-observed' : 'warm',
|
||||
...browserData,
|
||||
requests,
|
||||
responses
|
||||
});
|
||||
}
|
||||
await context.close();
|
||||
console.log(
|
||||
`Completed ${host.label}/${fixture.label}/${navigation}: ${samples} warm samples.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
await writeFile(
|
||||
outputPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
capturedAt: new Date().toISOString(),
|
||||
revision: config.revision,
|
||||
databaseLabel: config.databaseLabel,
|
||||
clientLocation: config.clientLocation,
|
||||
environment: config.environment ?? 'deployed',
|
||||
compatibilityPassed: config.compatibilityPassed ?? false,
|
||||
results
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
`Recorded ${results.length} samples; first-observed runs are excluded from warm statistics.`
|
||||
);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
const data = JSON.parse(await readFile(process.argv[2], 'utf8'));
|
||||
const warm = data.results.filter((row) => row.run === 'warm');
|
||||
const p75 = (values) => [...values].sort((a, b) => a - b)[Math.ceil(values.length * 0.75) - 1];
|
||||
const groups = [...new Set(warm.map((row) => `${row.fixture}/${row.navigation}`))];
|
||||
const paired = groups.map((group) => {
|
||||
const rows = (host) =>
|
||||
warm.filter((row) => `${row.fixture}/${row.navigation}` === group && row.host === host);
|
||||
for (const host of ['netlify', 'cloudflare'])
|
||||
if (rows(host).length < 30) throw new Error(`Insufficient ${host} warm samples for ${group}`);
|
||||
return {
|
||||
group,
|
||||
netlify: p75(rows('netlify').map((row) => row.interactiveMs)),
|
||||
cloudflare: p75(rows('cloudflare').map((row) => row.interactiveMs))
|
||||
};
|
||||
});
|
||||
if (groups.length !== 4)
|
||||
throw new Error(
|
||||
'Expected national and scoped-form fixtures, each with direct and client navigation'
|
||||
);
|
||||
const netlify = p75(warm.filter((row) => row.host === 'netlify').map((row) => row.interactiveMs));
|
||||
const cloudflare = p75(
|
||||
warm.filter((row) => row.host === 'cloudflare').map((row) => row.interactiveMs)
|
||||
);
|
||||
const improvement = netlify - cloudflare;
|
||||
const passes =
|
||||
improvement >= 200 &&
|
||||
improvement / netlify >= 0.2 &&
|
||||
paired.every((row) => row.cloudflare <= row.netlify * 1.1);
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
overallP75: { netlify, cloudflare },
|
||||
improvementMs: improvement,
|
||||
improvementPercent: (100 * improvement) / netlify,
|
||||
groups: paired,
|
||||
performanceGatePassed: passes,
|
||||
recommendation:
|
||||
passes && data.compatibilityPassed && data.environment === 'deployed'
|
||||
? 'Cloudflare qualifies for a migration proposal; review operating cost before cutover.'
|
||||
: 'Retain Netlify: performance, deployed evidence or compatibility gate is unmet.'
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
@@ -1,113 +0,0 @@
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { createServerClient } from '@supabase/ssr';
|
||||
import { mkdir, readFile, writeFile, unlink } from 'node:fs/promises';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
const directory = process.env.PERF_FIXTURE_DIR ?? '/tmp/livingdex-grid-performance';
|
||||
const url = process.env.PUBLIC_SUPABASE_URL;
|
||||
if (!url || !['localhost', '127.0.0.1', '[::1]'].includes(new URL(url).hostname))
|
||||
throw new Error('Fixtures require the local Supabase wrapper');
|
||||
const admin = createClient(url, process.env.SUPABASE_SERVICE_ROLE_KEY, {
|
||||
auth: { persistSession: false }
|
||||
});
|
||||
const check = ({ data, error }) => {
|
||||
if (error) throw error;
|
||||
return data;
|
||||
};
|
||||
await mkdir(directory, { recursive: true, mode: 0o700 });
|
||||
const fixturePath = `${directory}/fixture.json`;
|
||||
if (process.argv.includes('--cleanup')) {
|
||||
const fixture = JSON.parse(await readFile(fixturePath, 'utf8'));
|
||||
const { user } = check(await admin.auth.admin.getUserById(fixture.userId));
|
||||
if (user.email !== fixture.email || !user.email.startsWith('grid-performance-'))
|
||||
throw new Error('Refusing to remove a non-fixture account');
|
||||
check(await admin.auth.admin.deleteUser(fixture.userId));
|
||||
await unlink(fixturePath);
|
||||
await unlink(`${directory}/storage-state.json`).catch(() => {});
|
||||
console.log('Removed disposable performance account and session.');
|
||||
process.exit(0);
|
||||
}
|
||||
try {
|
||||
await readFile(fixturePath);
|
||||
throw new Error('An existing fixture needs cleanup first');
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') throw error;
|
||||
}
|
||||
const email = `grid-performance-${randomUUID()}@example.test`;
|
||||
const password = randomUUID() + 'aA1!';
|
||||
const { user } = check(await admin.auth.admin.createUser({ email, password, email_confirm: true }));
|
||||
const fixture = { userId: user.id, email, password, dexes: [] };
|
||||
await writeFile(fixturePath, JSON.stringify(fixture), { mode: 0o600 });
|
||||
fixture.dexes = check(
|
||||
await admin
|
||||
.from('pokedexes')
|
||||
.insert([
|
||||
{ userId: user.id, name: 'Performance National', isLivingDex: true, isFormDex: false },
|
||||
{
|
||||
userId: user.id,
|
||||
name: 'Performance Scarlet Forms',
|
||||
isLivingDex: true,
|
||||
isFormDex: true,
|
||||
gameScope: 'Scarlet'
|
||||
}
|
||||
])
|
||||
.select()
|
||||
);
|
||||
const game = fixture.dexes.find((dex) => dex.gameScope);
|
||||
check(
|
||||
await admin.from('pokedex_dex_scopes').insert({ pokedexId: game.id, dexId: 'scarlet-paldea' })
|
||||
);
|
||||
const entries = [];
|
||||
for (let from = 0; ; from += 1000) {
|
||||
const page = check(
|
||||
await admin
|
||||
.from('pokemon')
|
||||
.select('id,isDefaultForm')
|
||||
.order('id')
|
||||
.range(from, from + 999)
|
||||
);
|
||||
entries.push(...page);
|
||||
if (page.length < 1000) break;
|
||||
}
|
||||
for (const dex of fixture.dexes) {
|
||||
const selected = entries.filter((entry) => dex.isFormDex || entry.isDefaultForm);
|
||||
for (let from = 0; from < selected.length; from += 500) {
|
||||
check(
|
||||
await admin.from('catch_records').insert(
|
||||
selected.slice(from, from + 500).map((entry, index) => ({
|
||||
userId: user.id,
|
||||
pokedexId: dex.id,
|
||||
pokemonId: entry.id,
|
||||
caught: (from + index) % 3 === 0,
|
||||
haveToEvolve: (from + index) % 3 === 1,
|
||||
inHome: (from + index) % 7 === 0,
|
||||
personalNotes: index === 0 ? 'Performance fixture note: preserve me' : ''
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
await writeFile(fixturePath, JSON.stringify(fixture), { mode: 0o600 });
|
||||
const cookies = [];
|
||||
const client = createServerClient(url, process.env.PUBLIC_SUPABASE_ANON_KEY, {
|
||||
cookies: { getAll: () => [], setAll: (values) => cookies.push(...values) }
|
||||
});
|
||||
check(await client.auth.signInWithPassword({ email, password }));
|
||||
const base = new URL(process.env.PERF_BASE_URL ?? 'http://127.0.0.1:4173');
|
||||
await writeFile(
|
||||
`${directory}/storage-state.json`,
|
||||
JSON.stringify({
|
||||
cookies: cookies.map(({ name, value }) => ({
|
||||
name,
|
||||
value,
|
||||
domain: base.hostname,
|
||||
path: '/',
|
||||
httpOnly: false,
|
||||
secure: base.protocol === 'https:',
|
||||
sameSite: 'Lax'
|
||||
})),
|
||||
origins: []
|
||||
}),
|
||||
{ mode: 0o600 }
|
||||
);
|
||||
console.log('Prepared national and scoped-form fixtures and a private browser session.');
|
||||
@@ -1,105 +0,0 @@
|
||||
import { spawn } from 'node:child_process';
|
||||
import { mkdtemp, mkdir, cp, rm, readFile, writeFile } from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
|
||||
const directory = await mkdtemp(path.join(os.tmpdir(), 'livingdex-grid-tests-'));
|
||||
const port = process.env.PERF_PORT ?? '4185';
|
||||
const env = {
|
||||
...process.env,
|
||||
NODE_ADAPTER: 'true',
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER: 'true',
|
||||
POKEDEX_PERFORMANCE: 'true',
|
||||
PERF_FIXTURE_DIR: directory,
|
||||
PERF_BASE_URL: `http://127.0.0.1:${port}`,
|
||||
PORT: port,
|
||||
HOST: '127.0.0.1'
|
||||
};
|
||||
function run(command, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const process = spawn(command, args, { env, stdio: 'inherit' });
|
||||
process.once('error', reject);
|
||||
process.once('exit', (code) =>
|
||||
code === 0 ? resolve() : reject(new Error(`${command} failed (${code})`))
|
||||
);
|
||||
});
|
||||
}
|
||||
let server;
|
||||
let fixture = false;
|
||||
try {
|
||||
await run('npm', ['run', 'build-inject-manifest-node']);
|
||||
fixture = true;
|
||||
await run('node', ['scripts/performance/fixture.mjs']);
|
||||
server = spawn('node', ['build'], { env, stdio: 'inherit' });
|
||||
await new Promise((resolve, reject) => {
|
||||
const timer = setInterval(async () => {
|
||||
try {
|
||||
if ((await fetch(env.PERF_BASE_URL)).ok) {
|
||||
clearInterval(timer);
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
}
|
||||
} catch {}
|
||||
}, 200);
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(timer);
|
||||
reject(new Error('Local production server did not start'));
|
||||
}, 15000);
|
||||
server.once('error', (error) => {
|
||||
clearInterval(timer);
|
||||
clearTimeout(timeout);
|
||||
reject(error);
|
||||
});
|
||||
server.once('exit', (code) => {
|
||||
clearInterval(timer);
|
||||
clearTimeout(timeout);
|
||||
reject(new Error(`Local server exited (${code})`));
|
||||
});
|
||||
});
|
||||
if (process.env.PERF_BENCHMARK === 'true') {
|
||||
const data = JSON.parse(await readFile(path.join(directory, 'fixture.json'), 'utf8'));
|
||||
await writeFile(
|
||||
path.join(directory, 'benchmark-config.json'),
|
||||
JSON.stringify({
|
||||
samples: 30,
|
||||
revision: process.env.PERF_REVISION ?? 'local-working-tree',
|
||||
databaseLabel: 'local-seeded-supabase',
|
||||
clientLocation: 'local-loopback',
|
||||
environment: 'local-node',
|
||||
hosts: [
|
||||
{
|
||||
label: 'node',
|
||||
url: env.PERF_BASE_URL,
|
||||
storageState: path.join(directory, 'storage-state.json'),
|
||||
fixtures: data.dexes.map((dex) => ({
|
||||
id: dex.id,
|
||||
name: dex.name,
|
||||
label: dex.gameScope ? 'scoped-forms' : 'national'
|
||||
}))
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
await run('node', [
|
||||
'scripts/performance/benchmark.mjs',
|
||||
path.join(directory, 'benchmark-config.json'),
|
||||
path.join(directory, 'benchmark.json')
|
||||
]);
|
||||
await mkdir('test-results/performance', { recursive: true });
|
||||
await cp(path.join(directory, 'benchmark.json'), 'test-results/performance/benchmark.json');
|
||||
}
|
||||
await run('node', ['scripts/performance/verify.mjs']);
|
||||
await run('node', ['scripts/performance/verify-behavior.mjs']);
|
||||
await mkdir('test-results/performance', { recursive: true });
|
||||
for (const file of ['verification.json', 'behavior.json', 'national.png', 'scoped.png'])
|
||||
await cp(path.join(directory, file), path.join('test-results/performance', file));
|
||||
} finally {
|
||||
server?.kill('SIGTERM');
|
||||
if (fixture)
|
||||
await run('node', ['scripts/performance/fixture.mjs', '--cleanup']).catch((error) => {
|
||||
throw new Error(`Fixture cleanup failed; retained recovery files in ${directory}`, {
|
||||
cause: error
|
||||
});
|
||||
});
|
||||
await rm(directory, { recursive: true, force: true });
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
import { chromium } from '@playwright/test';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import assert from 'node:assert/strict';
|
||||
const dir = process.env.PERF_FIXTURE_DIR ?? '/tmp/livingdex-grid-performance';
|
||||
const fixture = JSON.parse(await readFile(`${dir}/fixture.json`, 'utf8'));
|
||||
const browser = await chromium.launch();
|
||||
const context = await browser.newContext({
|
||||
baseURL: process.env.PERF_BASE_URL ?? 'http://127.0.0.1:4173',
|
||||
storageState: `${dir}/storage-state.json`,
|
||||
viewport: { width: 1350, height: 940 }
|
||||
});
|
||||
await context.addInitScript(() => {
|
||||
window.__cls = 0;
|
||||
new PerformanceObserver((list) => {
|
||||
for (const entry of list.getEntries()) if (!entry.hadRecentInput) window.__cls += entry.value;
|
||||
}).observe({ type: 'layout-shift', buffered: true });
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const errors = [];
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
const national = fixture.dexes.find((dex) => !dex.gameScope);
|
||||
const scoped = fixture.dexes.find((dex) => dex.gameScope);
|
||||
const results = [];
|
||||
try {
|
||||
await page.goto(`/pokedex/${national.id}`);
|
||||
await page.waitForSelector('[data-grid-interactive]');
|
||||
for (const width of [1350, 390]) {
|
||||
await page.setViewportSize({ width, height: 940 });
|
||||
for (const density of ['comfortable', 'compact', 'ultra']) {
|
||||
await page.getByLabel('Choose box view layout density').selectOption(density);
|
||||
await page.reload();
|
||||
await page.waitForSelector('[data-grid-interactive]');
|
||||
await page.waitForTimeout(350);
|
||||
assert.equal(await page.getByLabel('Choose box view layout density').inputValue(), density);
|
||||
const geometry = await page.evaluate(() => {
|
||||
const cell = document.querySelector('[data-entry-index="0"]').getBoundingClientRect();
|
||||
const shell = document.querySelector('[data-box-number="1"]').getBoundingClientRect();
|
||||
return {
|
||||
cellWidth: cell.width,
|
||||
cellHeight: cell.height,
|
||||
cls: window.__cls,
|
||||
horizontalOverflow: document.documentElement.scrollWidth > innerWidth,
|
||||
shellHeight: shell.height
|
||||
};
|
||||
});
|
||||
assert.ok(Math.abs(geometry.cellWidth - geometry.cellHeight) < 1, JSON.stringify(geometry));
|
||||
assert.ok(!geometry.horizontalOverflow, `Horizontal overflow at ${width}/${density}`);
|
||||
assert.ok(geometry.cls <= 0.1, `CLS ${geometry.cls} at ${width}/${density}`);
|
||||
results.push({ width, density, ...geometry });
|
||||
}
|
||||
}
|
||||
await page.setViewportSize({ width: 1350, height: 940 });
|
||||
await page.getByLabel('Choose box view layout density').selectOption('comfortable');
|
||||
await page.getByLabel('Not caught', { exact: true }).check();
|
||||
assert.equal(await page.locator('[data-entry-index="0"]').getAttribute('aria-disabled'), 'true');
|
||||
assert.equal(await page.locator('[data-entry-index="0"]').getAttribute('data-entry-id'), '1');
|
||||
await page.getByLabel('Not caught', { exact: true }).uncheck();
|
||||
// Resizing across the mobile breakpoint keeps the same box at the scroll anchor.
|
||||
await page.locator('[data-box-number="15"]').evaluate((node) => node.scrollIntoView());
|
||||
await page.waitForTimeout(100);
|
||||
const anchorTop = await page
|
||||
.locator('[data-box-number="15"]')
|
||||
.evaluate((node) => node.getBoundingClientRect().top);
|
||||
await page.setViewportSize({ width: 390, height: 940 });
|
||||
await page.waitForTimeout(200);
|
||||
const resizedTop = await page
|
||||
.locator('[data-box-number="15"]')
|
||||
.evaluate((node) => node.getBoundingClientRect().top);
|
||||
assert.ok(
|
||||
Math.abs(anchorTop - resizedTop) < 2,
|
||||
`Scroll anchor moved: ${anchorTop} to ${resizedTop}`
|
||||
);
|
||||
await page.setViewportSize({ width: 1350, height: 940 });
|
||||
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
||||
await page.waitForSelector('[data-entry-index="1024"]');
|
||||
assert.ok((await page.locator('[data-entry-index]').count()) < 300);
|
||||
await page.locator('[data-entry-index="1024"]').focus();
|
||||
await page.keyboard.press('ArrowRight');
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.activeElement.getAttribute('data-entry-index')),
|
||||
'1024'
|
||||
);
|
||||
await page.evaluate(() => window.scrollTo(0, 0));
|
||||
await page.waitForSelector('[data-entry-index="0"]');
|
||||
// Closing an in-flight modal must prevent its response from replacing the next selection.
|
||||
let release;
|
||||
const held = new Promise((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
await page.route(`**/api/pokedexes/${national.id}/entries/1`, async (route) => {
|
||||
await held;
|
||||
await route.continue().catch(() => {});
|
||||
});
|
||||
await page.locator('[data-entry-index="0"]').click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Close', exact: true }).click();
|
||||
await page.locator('[data-entry-index="1"]').click();
|
||||
release();
|
||||
await page.getByRole('dialog').getByLabel('Notes:', { exact: true }).waitFor();
|
||||
assert.equal(
|
||||
await page.getByRole('dialog').getByRole('heading', { name: 'Ivysaur', exact: true }).count(),
|
||||
1
|
||||
);
|
||||
assert.equal(
|
||||
await page.getByRole('dialog').getByRole('heading', { name: 'Bulbasaur', exact: true }).count(),
|
||||
0
|
||||
);
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Close', exact: true }).click();
|
||||
await page.unrouteAll({ behavior: 'wait' });
|
||||
// Navigate through actual list cards, preserving fixture-specific totals and positions.
|
||||
await page.getByRole('link', { name: 'My Pokédexes' }).click();
|
||||
await page
|
||||
.locator('.card')
|
||||
.filter({ hasText: scoped.name })
|
||||
.first()
|
||||
.getByRole('button', { name: 'View', exact: true })
|
||||
.click();
|
||||
await page.waitForSelector('[data-grid-interactive]');
|
||||
assert.ok((await page.locator('body').innerText()).includes('Showing 439 of 439'));
|
||||
await page.goBack();
|
||||
await page
|
||||
.locator('.card')
|
||||
.filter({ hasText: national.name })
|
||||
.first()
|
||||
.getByRole('button', { name: 'View', exact: true })
|
||||
.click();
|
||||
await page.waitForSelector('[data-grid-interactive]');
|
||||
// Wait for the existing worker's full snapshot, then read an uncached detail without a network.
|
||||
await page.waitForFunction(
|
||||
async () => {
|
||||
const meta = await (
|
||||
await caches.open('livingdex-offline-meta-v1')
|
||||
).match('/__offline/current');
|
||||
return !!(await meta?.json())?.dataCache;
|
||||
},
|
||||
{ timeout: 15000 }
|
||||
);
|
||||
await context.setOffline(true);
|
||||
await page.locator('[data-entry-index="5"]').click();
|
||||
await page.waitForFunction(() =>
|
||||
document.querySelector('[role="dialog"]')?.textContent.includes('Where to catch:')
|
||||
);
|
||||
assert.equal(await page.getByRole('dialog').locator('textarea').count(), 0);
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Close', exact: true }).click();
|
||||
// A mismatched cache owner is never used as fallback.
|
||||
await page.evaluate(async () => {
|
||||
const cache = await caches.open('livingdex-offline-meta-v1');
|
||||
const response = await cache.match('/__offline/current');
|
||||
const meta = await response.json();
|
||||
await cache.put(
|
||||
'/__offline/current',
|
||||
new Response(JSON.stringify({ ...meta, userId: 'other-account' }))
|
||||
);
|
||||
});
|
||||
await page.locator('[data-entry-index="6"]').click();
|
||||
await page.getByRole('alert').filter({ hasText: 'not saved for offline use' }).waitFor();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Close', exact: true }).click();
|
||||
assert.deepEqual(errors, []);
|
||||
await writeFile(`${dir}/behavior.json`, JSON.stringify(results, null, 2));
|
||||
console.log(
|
||||
'Passed density/mobile geometry, filtered placement, virtual boundaries, modal races, client navigation and isolated offline details.'
|
||||
);
|
||||
console.log(JSON.stringify(results, null, 2));
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import { chromium } from '@playwright/test';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
const directory = process.env.PERF_FIXTURE_DIR ?? '/tmp/livingdex-grid-performance';
|
||||
const fixture = JSON.parse(await readFile(`${directory}/fixture.json`, 'utf8'));
|
||||
const baseURL = process.env.PERF_BASE_URL ?? 'http://127.0.0.1:4173';
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const context = await browser.newContext({
|
||||
baseURL,
|
||||
storageState: `${directory}/storage-state.json`,
|
||||
viewport: { width: 1350, height: 940 }
|
||||
});
|
||||
await context.addInitScript(() => {
|
||||
window.__layoutShifts = [];
|
||||
window.__firstVisible = null;
|
||||
new PerformanceObserver((list) => {
|
||||
for (const entry of list.getEntries())
|
||||
if (!entry.hadRecentInput) window.__layoutShifts.push(entry.value);
|
||||
}).observe({ type: 'layout-shift', buffered: true });
|
||||
function visible() {
|
||||
const cell = document.querySelector('[data-entry-index]');
|
||||
if (
|
||||
cell &&
|
||||
cell.getBoundingClientRect().height > 0 &&
|
||||
cell.getBoundingClientRect().top < innerHeight
|
||||
)
|
||||
window.__firstVisible ??= performance.now();
|
||||
if (window.__firstVisible === null) requestAnimationFrame(visible);
|
||||
}
|
||||
requestAnimationFrame(visible);
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const readJson = (path) =>
|
||||
page.evaluate(async (path) => {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error(`Fixture API failed: ${response.status}`);
|
||||
return response.json();
|
||||
}, path);
|
||||
const errors = [];
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
const requests = [];
|
||||
page.on('request', (request) => requests.push(new URL(request.url()).pathname));
|
||||
const results = [];
|
||||
try {
|
||||
for (const dex of fixture.dexes) {
|
||||
requests.length = 0;
|
||||
const response = await page.goto(`/pokedex/${dex.id}`);
|
||||
assert.equal(response.status(), 200);
|
||||
const html = await response.text();
|
||||
assert.ok(/data-entry-index=["']?0["'\s>]/.test(html), 'Initial cells must be server rendered');
|
||||
await page.waitForSelector('[data-grid-interactive]');
|
||||
await page.waitForTimeout(1000);
|
||||
const stats = await page.evaluate(() => ({
|
||||
cells: document.querySelectorAll('[data-entry-index]').length,
|
||||
dom: document.querySelectorAll('*').length,
|
||||
cls: window.__layoutShifts.reduce((sum, value) => sum + value, 0),
|
||||
firstVisible: window.__firstVisible,
|
||||
interactive: performance.getEntriesByName('pokedex:first-interactive').at(-1)?.startTime
|
||||
}));
|
||||
assert.ok(stats.cells <= 180, `Mounted cells: ${stats.cells}`);
|
||||
assert.ok(stats.dom < 2500, `DOM elements: ${stats.dom}`);
|
||||
assert.ok(stats.cls <= 0.1, `CLS: ${stats.cls}`);
|
||||
assert.equal(
|
||||
requests.filter((path) => /\/api\/pokedexes\/[^/]+\/(grid|combined-data)$/.test(path)).length,
|
||||
0
|
||||
);
|
||||
assert.equal(await page.locator('button button').count(), 0);
|
||||
await page.screenshot({ path: `${directory}/${dex.gameScope ? 'scoped' : 'national'}.png` });
|
||||
const first = page.locator('[data-entry-index="0"]');
|
||||
const entryId = await first.getAttribute('data-entry-id');
|
||||
const notes = await readJson(`/api/pokedexes/${dex.id}/entries/${entryId}`);
|
||||
await first.click();
|
||||
const modal = page.getByRole('dialog', { name: 'Pokémon details' });
|
||||
await modal.getByLabel('Notes:', { exact: true }).waitFor();
|
||||
assert.equal(
|
||||
await modal.getByLabel('Notes:', { exact: true }).inputValue(),
|
||||
notes.catchRecord.personalNotes
|
||||
);
|
||||
const image = modal.locator('img').first();
|
||||
await image.waitFor();
|
||||
await page.waitForFunction(
|
||||
() => document.querySelector('[role="dialog"] img')?.naturalWidth > 0
|
||||
);
|
||||
assert.equal(await image.evaluate((node) => node.naturalWidth), 512);
|
||||
assert.ok(!(await image.getAttribute('src')).includes('sprites-grid'));
|
||||
await modal.getByRole('button', { name: 'Close', exact: true }).click();
|
||||
assert.equal(await first.evaluate((node) => document.activeElement === node), true);
|
||||
// Reopening uses cached details; bulk edits must retain the unloaded personal notes.
|
||||
const detailCalls = () =>
|
||||
requests.filter((path) => path.endsWith(`/entries/${entryId}`)).length;
|
||||
const before = detailCalls();
|
||||
await first.click();
|
||||
await modal.getByLabel('Notes:', { exact: true }).waitFor();
|
||||
assert.equal(detailCalls(), before);
|
||||
await modal.getByRole('button', { name: 'Close', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Open bulk actions menu' }).first().click();
|
||||
await page.getByRole('button', { name: 'Mark box as In HOME', exact: true }).click();
|
||||
await page.waitForResponse(
|
||||
(r) =>
|
||||
r.url().endsWith(`/pokedexes/${dex.id}/catch-records`) && r.request().method() === 'POST'
|
||||
);
|
||||
const after = await readJson(`/api/pokedexes/${dex.id}/entries/${entryId}`);
|
||||
assert.equal(after.catchRecord.personalNotes, notes.catchRecord.personalNotes);
|
||||
assert.equal(after.catchRecord.inHome, true);
|
||||
// Focus navigation must reach entries that were not initially mounted.
|
||||
await page.locator('[data-entry-index="29"]').focus();
|
||||
for (let index = 0; index < 22; index++) await page.keyboard.press('ArrowDown');
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.activeElement?.getAttribute('data-entry-index')),
|
||||
'161'
|
||||
);
|
||||
await page.getByLabel('Render all boxes').check();
|
||||
const grid = await readJson(`/api/pokedexes/${dex.id}/grid`);
|
||||
assert.equal(await page.locator('[data-entry-index]').count(), grid.grid.length);
|
||||
await page.getByLabel('Render all boxes').uncheck();
|
||||
results.push({
|
||||
fixture: dex.gameScope ? 'scoped-forms' : 'national',
|
||||
...stats,
|
||||
documentBytes: Buffer.byteLength(html),
|
||||
gridBytes: Buffer.byteLength(JSON.stringify(grid))
|
||||
});
|
||||
}
|
||||
assert.deepEqual(errors, [], 'Browser errors');
|
||||
await writeFile(`${directory}/verification.json`, JSON.stringify(results, null, 2));
|
||||
console.log(JSON.stringify(results, null, 2));
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
const [, , command, ...args] = process.argv;
|
||||
if (!command) {
|
||||
console.error('Usage: node scripts/run-with-local-supabase.mjs <command> [...args]');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const SETUP_HINT = 'Run "npm run supabase:start" followed by "npm run supabase:reset".';
|
||||
// npx is a shell script on Windows, where spawn needs a shell to find it.
|
||||
const useShell = process.platform === 'win32';
|
||||
|
||||
function fail(message, detail) {
|
||||
console.error(message);
|
||||
if (detail) console.error(String(detail).trim());
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const status = spawnSync('npx', ['supabase', 'status', '--output', 'json'], {
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
shell: useShell
|
||||
});
|
||||
|
||||
if (status.error) {
|
||||
fail(
|
||||
'Unable to run "npx supabase status" - is the supabase CLI installed?',
|
||||
status.error.message
|
||||
);
|
||||
}
|
||||
|
||||
if (status.status !== 0) {
|
||||
const stderr = status.stderr ?? '';
|
||||
// Distinguish a stopped stack from a genuinely broken CLI invocation, so the hint is only
|
||||
// printed when it is actually the advice the reader needs.
|
||||
if (/not running|supabase start/i.test(stderr)) {
|
||||
fail(`Local Supabase is required but is not running.\n${SETUP_HINT}`, stderr);
|
||||
}
|
||||
fail(`"supabase status" failed with exit code ${status.status}.`, stderr);
|
||||
}
|
||||
|
||||
let values;
|
||||
try {
|
||||
values = JSON.parse(status.stdout);
|
||||
} catch (error) {
|
||||
fail('Unable to parse "supabase status --output json".', error);
|
||||
}
|
||||
|
||||
const apiUrl = values.API_URL ?? values.api_url ?? 'http://127.0.0.1:54321';
|
||||
const anonKey = values.ANON_KEY ?? values.PUBLISHABLE_KEY ?? values.anon_key;
|
||||
const serviceRoleKey = values.SERVICE_ROLE_KEY ?? values.SECRET_KEY ?? values.service_role_key;
|
||||
|
||||
if (!anonKey || !serviceRoleKey) {
|
||||
fail(`Supabase status did not return an anonymous and service-role key.\n${SETUP_HINT}`);
|
||||
}
|
||||
|
||||
function isLoopbackUrl(value) {
|
||||
try {
|
||||
return ['127.0.0.1', 'localhost', '[::1]'].includes(new URL(value).hostname);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLoopbackUrl(apiUrl)) {
|
||||
fail(`Refusing to run local-stack tests against non-loopback Supabase URL: ${apiUrl}`);
|
||||
}
|
||||
const providerUrlVariables = [
|
||||
'MOCK_PROVIDER_URL',
|
||||
'GOOGLE_OAUTH_AUTHORIZE_URL',
|
||||
'GOOGLE_OAUTH_TOKEN_URL',
|
||||
'GOOGLE_DRIVE_API_URL',
|
||||
'GOOGLE_DRIVE_UPLOAD_URL',
|
||||
'DROPBOX_OAUTH_AUTHORIZE_URL',
|
||||
'DROPBOX_OAUTH_TOKEN_URL',
|
||||
'DROPBOX_UPLOAD_URL'
|
||||
];
|
||||
for (const name of providerUrlVariables) {
|
||||
const value = process.env[name];
|
||||
if (value && !isLoopbackUrl(value)) {
|
||||
fail(`Refusing to run provider tests with non-loopback ${name}: ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
// A running-but-unseeded database is the most common broken state, and it surfaces downstream as
|
||||
// a confusing assertion failure. Check it here instead.
|
||||
const probe = await fetch(`${apiUrl}/rest/v1/pokedex_entries?select=id&limit=1`, {
|
||||
headers: { apikey: anonKey, Authorization: `Bearer ${anonKey}` }
|
||||
}).catch((error) => {
|
||||
fail(`Unable to reach the local Supabase REST API at ${apiUrl}.\n${SETUP_HINT}`, error);
|
||||
});
|
||||
|
||||
if (!probe.ok) {
|
||||
fail(
|
||||
`The local Supabase database has no readable pokedex_entries (HTTP ${probe.status}).\n${SETUP_HINT}`,
|
||||
await probe.text()
|
||||
);
|
||||
}
|
||||
if (((await probe.json()) ?? []).length === 0) {
|
||||
fail(`The local Supabase database is empty - migrations or seeds have not run.\n${SETUP_HINT}`);
|
||||
}
|
||||
|
||||
const child = spawnSync(command, args, {
|
||||
stdio: 'inherit',
|
||||
shell: useShell,
|
||||
env: {
|
||||
...process.env,
|
||||
// Never allow an exported production value to redirect a local integration run.
|
||||
PUBLIC_SUPABASE_URL: apiUrl,
|
||||
PUBLIC_SUPABASE_ANON_KEY: anonKey,
|
||||
SUPABASE_SERVICE_ROLE_KEY: serviceRoleKey,
|
||||
TEST_SUPABASE_URL: apiUrl,
|
||||
TEST_SUPABASE_ANON_KEY: anonKey,
|
||||
E2E_SERVICE_ROLE_KEY: serviceRoleKey
|
||||
}
|
||||
});
|
||||
|
||||
if (child.error) fail(`Unable to run "${command}".`, child.error.message);
|
||||
// A signalled child reports status === null; exiting 0 there would hide the failure.
|
||||
process.exit(child.signal ? 1 : child.status ?? 1);
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// Lists every sprite (all forms, shiny and female variants) with its size, so the offline worker can
|
||||
// save the complete set on request and tell whether anything is still missing.
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { readdir, stat, writeFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export const SPRITE_MANIFEST_NAME = 'manifest.json';
|
||||
|
||||
async function walk(dir, files = []) {
|
||||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) await walk(fullPath, files);
|
||||
else if (entry.isFile() && entry.name.endsWith('.webp')) files.push(fullPath);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/** Builds the manifest for `<spritesDir>/home`, with paths relative to that folder. */
|
||||
export async function buildSpriteManifest(spritesDir) {
|
||||
const homeDir = path.join(spritesDir, 'home');
|
||||
const files = await walk(homeDir);
|
||||
const entries = await Promise.all(
|
||||
files.map(async (file) => [
|
||||
path.relative(homeDir, file).split(path.sep).join('/'),
|
||||
(await stat(file)).size
|
||||
])
|
||||
);
|
||||
entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
||||
return { version: 1, files: entries };
|
||||
}
|
||||
|
||||
export async function writeSpriteManifest(spritesDir) {
|
||||
const manifest = await buildSpriteManifest(spritesDir);
|
||||
await writeFile(path.join(spritesDir, SPRITE_MANIFEST_NAME), `${JSON.stringify(manifest)}\n`);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
const spritesDir =
|
||||
process.env.SPRITE_OUTPUT_DIR ?? path.join(process.cwd(), 'static', 'sprites-small');
|
||||
const manifest = await writeSpriteManifest(spritesDir);
|
||||
const bytes = manifest.files.reduce((total, [, size]) => total + size, 0);
|
||||
console.log(
|
||||
`Wrote ${manifest.files.length} sprites (${(bytes / 1048576).toFixed(1)} MB) to ${SPRITE_MANIFEST_NAME}`
|
||||
);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
Vendored
+1
-7
@@ -1,7 +1,6 @@
|
||||
import 'vite-plugin-pwa/svelte';
|
||||
import 'vite-plugin-pwa/info';
|
||||
import 'vite-plugin-pwa/pwa-assets';
|
||||
import { SupabaseClient, Session, User } from '@supabase/supabase-js';
|
||||
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
@@ -12,17 +11,12 @@ declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
interface Locals {
|
||||
supabase: SupabaseClient;
|
||||
pokedexAuthMs?: number;
|
||||
safeGetSession(): Promise<{ session: Session | null; user: User | null }>;
|
||||
userid: string;
|
||||
buildDate: string;
|
||||
periodicUpdates: boolean;
|
||||
}
|
||||
|
||||
interface PageData {
|
||||
session: Session | null;
|
||||
}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
|
||||
+2
-14
@@ -1,23 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-theme="pokeball">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
const storedTheme = localStorage.getItem('theme');
|
||||
const allowed = ['pokeball', 'dark'];
|
||||
const theme = storedTheme && allowed.includes(storedTheme) ? storedTheme : 'pokeball';
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
} catch (e) {
|
||||
// Fallback to default theme if localStorage is not accessible
|
||||
document.documentElement.setAttribute('data-theme', 'pokeball');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
%sveltekit.head%
|
||||
<link rel="stylesheet" href="%sveltekit.assets%/output.css" />
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
|
||||
+12
-21
@@ -1,21 +1,22 @@
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
|
||||
import { createServerClient } from '@supabase/ssr';
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
import { compressResponse } from '$lib/server/compression';
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
|
||||
cookies: {
|
||||
getAll: () => event.cookies.getAll(),
|
||||
get: (key) => event.cookies.get(key),
|
||||
/**
|
||||
* Note: You have to add the `path` variable to the set method due to sveltekit's cookie
|
||||
* API requiring this to be set, setting the path to '/' will replicate previous/standard
|
||||
* behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
|
||||
* Note: You have to add the `path` variable to the
|
||||
* set and remove method due to sveltekit's cookie API
|
||||
* requiring this to be set, setting the path to an empty string
|
||||
* will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
|
||||
*/
|
||||
setAll: (cookiesToSet) => {
|
||||
cookiesToSet.forEach(({ name, value, options }) => {
|
||||
event.cookies.set(name, value, { ...options, path: '/' });
|
||||
});
|
||||
set: (key, value, options) => {
|
||||
event.cookies.set(key, value, { ...options, path: '/' });
|
||||
},
|
||||
remove: (key, options) => {
|
||||
event.cookies.delete(key, { ...options, path: '/' });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -25,34 +26,24 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
* doesn't validate the JWT, this function validates the JWT by first calling
|
||||
* `getUser` and aborts early if the JWT signature is invalid.
|
||||
*/
|
||||
// getUser is a network round trip to Supabase Auth. Layout and page loads (and API routes) all
|
||||
// ask for the session, so validate once per request and share the result.
|
||||
let sessionPromise: ReturnType<App.Locals['safeGetSession']> | null = null;
|
||||
event.locals.safeGetSession = () => {
|
||||
sessionPromise ??= (async () => {
|
||||
const authStarted = performance.now();
|
||||
event.locals.safeGetSession = async () => {
|
||||
const {
|
||||
data: { user },
|
||||
error
|
||||
} = await event.locals.supabase.auth.getUser();
|
||||
if (error) {
|
||||
event.locals.pokedexAuthMs = performance.now() - authStarted;
|
||||
return { session: null, user: null };
|
||||
}
|
||||
|
||||
const {
|
||||
data: { session }
|
||||
} = await event.locals.supabase.auth.getSession();
|
||||
event.locals.pokedexAuthMs = performance.now() - authStarted;
|
||||
return { session, user };
|
||||
})();
|
||||
return sessionPromise;
|
||||
};
|
||||
|
||||
const response = await resolve(event, {
|
||||
return resolve(event, {
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range';
|
||||
}
|
||||
});
|
||||
return compressResponse(event.request, response);
|
||||
};
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
type InViewParams = {
|
||||
rootMargin?: string;
|
||||
threshold?: number | number[];
|
||||
once?: boolean;
|
||||
enabled?: boolean;
|
||||
onChange?: (inView: boolean, entry: IntersectionObserverEntry) => void;
|
||||
};
|
||||
|
||||
const observers = new Map<string, IntersectionObserver>();
|
||||
const callbacks = new WeakMap<Element, (entry: IntersectionObserverEntry) => void>();
|
||||
|
||||
function normalizeThreshold(threshold: number | number[] | undefined) {
|
||||
if (threshold === undefined) return [0];
|
||||
return Array.isArray(threshold) ? threshold : [threshold];
|
||||
}
|
||||
|
||||
function buildObserverKey(rootMargin: string, threshold: number | number[]) {
|
||||
return `${rootMargin}|${JSON.stringify(normalizeThreshold(threshold))}`;
|
||||
}
|
||||
|
||||
function getObserver(rootMargin: string, threshold: number | number[]) {
|
||||
const key = buildObserverKey(rootMargin, threshold);
|
||||
const existing = observers.get(key);
|
||||
if (existing) return existing;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
const callback = callbacks.get(entry.target);
|
||||
if (callback) {
|
||||
callback(entry);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootMargin, threshold }
|
||||
);
|
||||
observers.set(key, observer);
|
||||
return observer;
|
||||
}
|
||||
|
||||
export function inView(node: Element, params: InViewParams = {}) {
|
||||
let options: Required<InViewParams> = {
|
||||
rootMargin: '200px 0px',
|
||||
threshold: 0,
|
||||
once: true,
|
||||
enabled: true,
|
||||
onChange: () => undefined,
|
||||
...params
|
||||
};
|
||||
|
||||
let observer = getObserver(options.rootMargin, options.threshold);
|
||||
|
||||
const handleEntry = (entry: IntersectionObserverEntry) => {
|
||||
const isIntersecting = entry.isIntersecting || entry.intersectionRatio > 0;
|
||||
options.onChange(isIntersecting, entry);
|
||||
if (isIntersecting && options.once) {
|
||||
observer.unobserve(node);
|
||||
callbacks.delete(node);
|
||||
}
|
||||
};
|
||||
|
||||
const startObserving = () => {
|
||||
callbacks.set(node, handleEntry);
|
||||
observer.observe(node);
|
||||
};
|
||||
|
||||
const stopObserving = () => {
|
||||
observer.unobserve(node);
|
||||
callbacks.delete(node);
|
||||
};
|
||||
|
||||
if (options.enabled) {
|
||||
startObserving();
|
||||
}
|
||||
|
||||
return {
|
||||
update(next: InViewParams = {}) {
|
||||
const nextOptions: Required<InViewParams> = {
|
||||
...options,
|
||||
...next,
|
||||
onChange: next.onChange ?? options.onChange
|
||||
};
|
||||
const observerKeyChanged =
|
||||
buildObserverKey(options.rootMargin, options.threshold) !==
|
||||
buildObserverKey(nextOptions.rootMargin, nextOptions.threshold);
|
||||
const enabledChanged = options.enabled !== nextOptions.enabled;
|
||||
|
||||
if (observerKeyChanged || enabledChanged) {
|
||||
stopObserving();
|
||||
}
|
||||
|
||||
options = nextOptions;
|
||||
|
||||
if (observerKeyChanged) {
|
||||
observer = getObserver(options.rootMargin, options.threshold);
|
||||
}
|
||||
|
||||
if (options.enabled) {
|
||||
startObserving();
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
stopObserving();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
currentPage = Math.max(currentPage - 1, 1);
|
||||
}
|
||||
|
||||
function setItemsPerPage(event: Event) {
|
||||
itemsPerPage = parseInt((event.target as HTMLSelectElement).value, 10);
|
||||
function setItemsPerPage(event: any) {
|
||||
itemsPerPage = parseInt(event.target.value, 10);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,94 +1,100 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import pokeApiPokemon from '$lib/helpers/pokeapi-pokemon.json';
|
||||
import { PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER } from '$env/static/public';
|
||||
import { inView } from '$lib/actions/inView';
|
||||
import { resolveSpriteUrl, resolveGridSpriteUrl } from '$lib/utils/spriteUrl';
|
||||
|
||||
export let variant: 'detail' | 'grid' = 'detail';
|
||||
let failedPaths = new Set<string>();
|
||||
let fallbackIndex = 0;
|
||||
let candidates: string[] = [];
|
||||
$: {
|
||||
const entry = { pokedexNumber: Number(pokedexNumber), form, spriteKey };
|
||||
const full = resolveSpriteUrl(
|
||||
entry,
|
||||
!!shiny,
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER === 'true'
|
||||
);
|
||||
candidates = [
|
||||
...new Set([
|
||||
...(variant === 'grid' ? [resolveGridSpriteUrl(entry, !!shiny)] : []),
|
||||
full,
|
||||
resolveSpriteUrl(
|
||||
{ ...entry, form: form?.replace(/^female[-\s]*/i, '') },
|
||||
!!shiny,
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER === 'true'
|
||||
),
|
||||
resolveSpriteUrl(
|
||||
{ pokedexNumber: Number(pokedexNumber) },
|
||||
!!shiny,
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER === 'true'
|
||||
)
|
||||
])
|
||||
];
|
||||
fallbackIndex = candidates.findIndex((url) => !failedPaths.has(url));
|
||||
}
|
||||
function imageFailed() {
|
||||
failedPaths = new Set([...failedPaths, imagePath!]);
|
||||
}
|
||||
|
||||
export let pokemonName: string;
|
||||
export let pokedexNumber: string | number;
|
||||
export let form: string | undefined;
|
||||
export let spriteKey: string | undefined;
|
||||
export let shiny: boolean | undefined = false;
|
||||
export let loadingStrategy: 'eager' | 'lazy' | 'inView' = 'inView';
|
||||
|
||||
let imagePath = null as string | null;
|
||||
let isInView = false;
|
||||
let blah = '';
|
||||
|
||||
$: {
|
||||
if (!spriteKey?.trim()) {
|
||||
console.warn('Missing sprite key for pokemon entry', {
|
||||
pokemonName,
|
||||
pokedexNumber,
|
||||
form
|
||||
});
|
||||
}
|
||||
function setImagePath() {
|
||||
let rootFolder =
|
||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER === 'true'
|
||||
? '/sprites/home'
|
||||
: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home';
|
||||
|
||||
if (form === 'Female') {
|
||||
rootFolder += '/female';
|
||||
}
|
||||
|
||||
$: imagePath = candidates[fallbackIndex] ?? null;
|
||||
$: if (loadingStrategy !== 'inView') {
|
||||
isInView = true;
|
||||
if (shiny) {
|
||||
rootFolder += '/shiny';
|
||||
}
|
||||
|
||||
function handleInView(inView: boolean) {
|
||||
if (inView) {
|
||||
isInView = true;
|
||||
// Remove leading zeros
|
||||
const strippedPokedexNumber = pokedexNumber.toString().replace(/^0+/, '');
|
||||
|
||||
// Sanitise the pokemon name by making it all lowercase and replacing any spaces with hyphens and removing other characters
|
||||
let sanitisedPokemonName = pokemonName.toLowerCase().replace(/[^a-z]/g, '');
|
||||
|
||||
// Get the PokeApi id for the pokemon
|
||||
let pokeApiId;
|
||||
if (form && form.length > 0 && form !== 'Female') {
|
||||
// Sanitise the form by making it all lowercase and replacing spaces with hyphens
|
||||
let sanitisedForm = form
|
||||
.toLowerCase()
|
||||
.replaceAll(' ', '-')
|
||||
.replaceAll('2', 'two')
|
||||
.replaceAll('3', 'three')
|
||||
.replaceAll('4', 'four');
|
||||
|
||||
switch (sanitisedForm) {
|
||||
case 'alolan':
|
||||
sanitisedForm = 'alola';
|
||||
break;
|
||||
case 'galarian':
|
||||
sanitisedForm = 'galar';
|
||||
break;
|
||||
case 'hisuian':
|
||||
sanitisedForm = 'hisui';
|
||||
break;
|
||||
// case 'rainbow-ribbon':
|
||||
// sanitisedForm = 'rainbow-swirl-ribbon-sweet';
|
||||
// break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// if (strippedPokedexNumber == '869') {
|
||||
// console.log(sanitisedPokemonName, sanitisedForm);
|
||||
// }
|
||||
|
||||
// If the form is contained in the identifier, use that
|
||||
if (
|
||||
pokeApiPokemon.find(
|
||||
(pokemon) => pokemon.identifier === sanitisedPokemonName + '-' + sanitisedForm
|
||||
)
|
||||
) {
|
||||
pokeApiId = pokeApiPokemon.find(
|
||||
(pokemon) => pokemon.identifier === sanitisedPokemonName + '-' + sanitisedForm
|
||||
)?.id;
|
||||
} else {
|
||||
// If the form is not contained in the identifier, use the species_id
|
||||
pokeApiId = pokeApiPokemon.find(
|
||||
(pokemon) => pokemon.species_id.toString() === strippedPokedexNumber
|
||||
)?.id;
|
||||
}
|
||||
} else {
|
||||
pokeApiId = pokeApiPokemon.find(
|
||||
(pokemon) => pokemon.species_id.toString() === strippedPokedexNumber
|
||||
)?.id;
|
||||
}
|
||||
|
||||
imagePath = `${rootFolder}/${pokeApiId}.png`;
|
||||
blah = `${pokeApiId}.png`;
|
||||
// blah = `${strippedPokedexNumber}${form?.length && form !== 'Female' ? '-' + form : ''}.png`;
|
||||
}
|
||||
|
||||
onMount(setImagePath);
|
||||
</script>
|
||||
|
||||
{#if imagePath}
|
||||
<span
|
||||
use:inView={{
|
||||
enabled: loadingStrategy === 'inView',
|
||||
once: true,
|
||||
rootMargin: '200px 0px',
|
||||
onChange: handleInView
|
||||
}}
|
||||
>
|
||||
{#if loadingStrategy === 'inView' && !isInView}
|
||||
<span class="inline-block w-full h-full" aria-hidden="true"></span>
|
||||
<!-- {blah} -->
|
||||
<img src={imagePath} alt="sprite" />
|
||||
{:else}
|
||||
<img
|
||||
src={imagePath}
|
||||
alt=""
|
||||
on:error={imageFailed}
|
||||
loading={loadingStrategy === 'lazy' ? 'lazy' : 'eager'}
|
||||
decoding="async"
|
||||
/>
|
||||
{/if}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="inline-block w-full h-full" aria-hidden="true"></span>
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@@ -9,141 +8,49 @@
|
||||
|
||||
let email = '';
|
||||
let password = '';
|
||||
let isLoading = false;
|
||||
let errorMessage = '';
|
||||
|
||||
// Access the supabase client from the layout data
|
||||
export let supabase: SupabaseClient;
|
||||
export let supabase: any;
|
||||
|
||||
async function signInWithEmail() {
|
||||
isLoading = true;
|
||||
errorMessage = '';
|
||||
|
||||
try {
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
// TODO use the data and error from the response
|
||||
const { data, error } = await supabase.auth
|
||||
.signInWithPassword({
|
||||
email: email,
|
||||
password: password
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('Sign in error:', error);
|
||||
errorMessage = error.message;
|
||||
return;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
})
|
||||
.then(() => {
|
||||
emitSignedInEvent();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Sign in error:', err);
|
||||
errorMessage = 'An unexpected error occurred. Please try again.';
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyPress(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter') {
|
||||
signInWithEmail();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Error Message -->
|
||||
{#if errorMessage}
|
||||
<div class="alert alert-error text-sm">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span>{errorMessage}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Email Input -->
|
||||
<div class="form-control">
|
||||
<label class="label" for="signin-email">
|
||||
<span class="label-text font-medium">Email</span>
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
id="signin-email"
|
||||
type="email"
|
||||
placeholder="your@email.com"
|
||||
class="input input-bordered w-full pl-10"
|
||||
bind:value={email}
|
||||
on:keypress={handleKeyPress}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<label class="input input-bordered flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="h-4 w-4 opacity-70 absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none"
|
||||
>
|
||||
<path
|
||||
class="w-4 h-4 opacity-70"
|
||||
><path
|
||||
d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z"
|
||||
/>
|
||||
<path
|
||||
/><path
|
||||
d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Input -->
|
||||
<div class="form-control">
|
||||
<label class="label" for="signin-password">
|
||||
<span class="label-text font-medium">Password</span>
|
||||
/></svg
|
||||
>
|
||||
<input type="text" class="grow" placeholder="Email" bind:value={email} />
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
id="signin-password"
|
||||
type="password"
|
||||
placeholder="••••••••"
|
||||
class="input input-bordered w-full pl-10"
|
||||
bind:value={password}
|
||||
on:keypress={handleKeyPress}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<label class="input input-bordered flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="h-4 w-4 opacity-70 absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none"
|
||||
>
|
||||
<path
|
||||
class="w-4 h-4 opacity-70"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sign In Button -->
|
||||
<div class="form-control mt-6">
|
||||
<button
|
||||
class="btn btn-primary w-full"
|
||||
on:click={signInWithEmail}
|
||||
disabled={isLoading || !email || !password}
|
||||
/></svg
|
||||
>
|
||||
{#if isLoading}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
Signing in...
|
||||
{:else}
|
||||
Sign In
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="password" class="grow" placeholder="Password" bind:value={password} />
|
||||
</label>
|
||||
<button class="btn btn-active btn-primary" on:click={signInWithEmail}>Sign In</button>
|
||||
|
||||
@@ -1,46 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { clearOfflineData } from '$lib/stores/offlineSync';
|
||||
const dispatch = createEventDispatcher();
|
||||
let errorMessage = '';
|
||||
let isSigningOut = false;
|
||||
|
||||
function emitSignedOutEvent() {
|
||||
dispatch('signedOut', {});
|
||||
}
|
||||
|
||||
// Access the supabase client from the layout data
|
||||
export let supabase: SupabaseClient;
|
||||
export let supabase: any;
|
||||
|
||||
async function signOut() {
|
||||
errorMessage = '';
|
||||
isSigningOut = true;
|
||||
try {
|
||||
const { error } = await supabase.auth.signOut();
|
||||
if (error) {
|
||||
errorMessage = `Sign out failed: ${error.message || 'Please try again.'}`;
|
||||
dispatch('signOutFailed', { message: errorMessage });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await clearOfflineData();
|
||||
} catch (cacheError) {
|
||||
console.error('Signed out, but failed to clear offline data', cacheError);
|
||||
}
|
||||
// TODO use the error from the response
|
||||
const { error } = await supabase.auth.signOut().then(() => {
|
||||
emitSignedOutEvent();
|
||||
await goto('/', { invalidateAll: true });
|
||||
} catch (error) {
|
||||
console.error('Sign out failed', error);
|
||||
errorMessage = 'Sign out failed. Please try again.';
|
||||
dispatch('signOutFailed', { message: errorMessage });
|
||||
} finally {
|
||||
isSigningOut = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={signOut} disabled={isSigningOut}>
|
||||
{isSigningOut ? 'Signing Out…' : 'Sign Out'}
|
||||
</button>
|
||||
<button on:click={signOut}>Sign Out</button>
|
||||
|
||||
@@ -1,48 +1,39 @@
|
||||
<script lang="ts">
|
||||
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let email = '';
|
||||
let password = '';
|
||||
|
||||
// Access the supabase client from the layout data
|
||||
export let supabase: SupabaseClient;
|
||||
export let supabase: any;
|
||||
|
||||
async function signUpNewUser() {
|
||||
try {
|
||||
// `redirectTo` is not a signUp option - it was silently ignored, so the confirmation
|
||||
// link has always used Supabase's configured site URL. Sending the user to /welcome
|
||||
// would need `emailRedirectTo` with an absolute, allow-listed URL.
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email: email,
|
||||
password: password
|
||||
password: password,
|
||||
options: {
|
||||
// Redirect URL after successful sign-up
|
||||
redirectTo: '/welcome'
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('Sign up error:', error);
|
||||
alert(`Sign up failed: ${error.message}`);
|
||||
return;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
// Emit signedUp event to notify parent component
|
||||
dispatch('signedUp', {});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Sign up error:', err);
|
||||
alert('Sign up failed');
|
||||
// Handle success (optional)
|
||||
} catch (error) {
|
||||
console.error('Sign up error:', error.message);
|
||||
// Handle error
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<form class="card-body">
|
||||
<div class="form-control">
|
||||
<label class="label" for="signup-email">
|
||||
<label class="label">
|
||||
<span class="label-text">Email</span>
|
||||
</label>
|
||||
<input
|
||||
id="signup-email"
|
||||
type="email"
|
||||
placeholder="email"
|
||||
class="input input-bordered"
|
||||
@@ -51,11 +42,10 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="signup-password">
|
||||
<label class="label">
|
||||
<span class="label-text">Password</span>
|
||||
</label>
|
||||
<input
|
||||
id="signup-password"
|
||||
type="password"
|
||||
placeholder="password"
|
||||
class="input input-bordered"
|
||||
@@ -64,12 +54,6 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control mt-6">
|
||||
<button class="btn btn-primary" on:click={signUpNewUser}>Sign Up</button>
|
||||
<button class="btn btn btn-primary" on:click={signUpNewUser}>Sign Up</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.card-body {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { theme, applyTheme } from '$lib/stores/theme.js';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let currentTheme: 'pokeball' | 'dark' = 'pokeball';
|
||||
|
||||
// Subscribe to theme changes
|
||||
$: (currentTheme = $theme), applyTheme($theme);
|
||||
|
||||
onMount(() => {
|
||||
// Initialize theme from localStorage
|
||||
theme.init();
|
||||
});
|
||||
|
||||
function toggleTheme() {
|
||||
theme.toggle();
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="btn btn-ghost btn-circle"
|
||||
on:click={toggleTheme}
|
||||
aria-label="Toggle dark mode"
|
||||
title="Toggle dark mode"
|
||||
>
|
||||
{#if currentTheme === 'pokeball'}
|
||||
<!-- Sun icon for light mode (clicking will switch to dark) -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<!-- Moon icon for dark mode (clicking will switch to light) -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
@@ -7,16 +7,14 @@
|
||||
</script>
|
||||
|
||||
<div class="relative inline-block">
|
||||
<button
|
||||
type="button"
|
||||
class="tooltip-trigger cursor-pointer bg-transparent border-0 p-0 appearance-none"
|
||||
aria-expanded={show}
|
||||
<div
|
||||
class="tooltip-trigger cursor-pointer"
|
||||
on:click={handleClick}
|
||||
on:mouseenter={() => (show = true)}
|
||||
on:mouseleave={() => (show = false)}
|
||||
>
|
||||
<slot name="hover-target" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if show}
|
||||
<div class="tooltip-content absolute z-10 mt-2 w-52">
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { Pokedex } from '$lib/models/Pokedex';
|
||||
|
||||
export let pokedex: Pokedex;
|
||||
export let onEdit: () => void;
|
||||
export let onDelete: () => void;
|
||||
export let onView: () => void;
|
||||
|
||||
// Get active type badges
|
||||
$: typeBadges = [
|
||||
pokedex.isLivingDex && 'Living',
|
||||
pokedex.isShinyDex && 'Shiny',
|
||||
pokedex.isOriginDex && 'Origin',
|
||||
pokedex.isFormDex && 'Form'
|
||||
].filter(Boolean);
|
||||
</script>
|
||||
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">
|
||||
{pokedex.name}
|
||||
</h2>
|
||||
|
||||
{#if pokedex.description}
|
||||
<p class="text-sm">{pokedex.description}</p>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-2">
|
||||
{#each typeBadges as badge}
|
||||
<span class="badge badge-primary">{badge}</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if pokedex.gameScope}
|
||||
<div class="text-sm text-base-content/70">
|
||||
<span class="font-semibold">Game:</span>
|
||||
{pokedex.gameScope}
|
||||
</div>
|
||||
{#if pokedex.dexScopes?.length}
|
||||
<div class="text-sm text-base-content/70">
|
||||
<span class="font-semibold">Dexes:</span>
|
||||
{pokedex.dexScopes.length}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-sm text-base-content/70">
|
||||
<span class="font-semibold">Scope:</span> All Games
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="card-actions justify-end mt-4">
|
||||
<button class="btn btn-sm btn-ghost" on:click={onEdit}>Edit</button>
|
||||
<button class="btn btn-sm btn-error btn-outline" on:click={onDelete}>Delete</button>
|
||||
<button class="btn btn-sm btn-primary" on:click={onView}>View</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,72 +1,19 @@
|
||||
<script lang="ts">
|
||||
import type { CatchRecord } from '$lib/models/CatchRecord';
|
||||
import type { SharedCatchStatus } from '$lib/models/SharedPokedex';
|
||||
import type { CatchInformationItem, PokedexEntry } from '$lib/models/PokedexEntry';
|
||||
import type { PokedexEntry } from '$lib/models/PokedexEntry';
|
||||
import PokemonSprite from '../PokemonSprite.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let pokedexEntry: PokedexEntry;
|
||||
export let catchRecord: CatchRecord | null;
|
||||
export let catchRecord: CatchRecord;
|
||||
export let showOrigins: boolean;
|
||||
export let showForms: boolean;
|
||||
export let showShiny: boolean;
|
||||
export let userId: string | null = null;
|
||||
export let pokedexId: string;
|
||||
export let readOnly = false;
|
||||
export let sharedCatchStatus: SharedCatchStatus | null = null;
|
||||
|
||||
// Create a default catch record if none exists
|
||||
$: if (!readOnly && !catchRecord) {
|
||||
catchRecord = {
|
||||
_id: '', // Empty string, not temp ID - will be created by server
|
||||
userId: userId || '',
|
||||
pokemonId: pokedexEntry._id,
|
||||
pokedexId: pokedexId,
|
||||
haveToEvolve: false,
|
||||
caught: false,
|
||||
inHome: false,
|
||||
hasGigantamaxed: false,
|
||||
personalNotes: ''
|
||||
};
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
type UpdateCatchSource = 'toggle' | 'notes' | 'notes-blur';
|
||||
|
||||
const isCatchInformationItem = (
|
||||
value: string | CatchInformationItem
|
||||
): value is CatchInformationItem => typeof value !== 'string';
|
||||
|
||||
function updateCatchRecord(source: UpdateCatchSource, changes?: Partial<CatchRecord>) {
|
||||
if (readOnly) return;
|
||||
dispatch('updateCatch', { pokedexEntry, catchRecord, source, changes });
|
||||
}
|
||||
|
||||
function onCaughtChange() {
|
||||
if (readOnly) return;
|
||||
if (!catchRecord) return;
|
||||
// Mutually exclusive with "needs to evolve"
|
||||
if (catchRecord.caught) {
|
||||
catchRecord.haveToEvolve = false;
|
||||
}
|
||||
updateCatchRecord('toggle', {
|
||||
caught: catchRecord.caught,
|
||||
haveToEvolve: catchRecord.haveToEvolve
|
||||
});
|
||||
}
|
||||
|
||||
function onNeedsToEvolveChange() {
|
||||
if (readOnly) return;
|
||||
if (!catchRecord) return;
|
||||
// Mutually exclusive with "caught"
|
||||
if (catchRecord.haveToEvolve) {
|
||||
catchRecord.caught = false;
|
||||
}
|
||||
updateCatchRecord('toggle', {
|
||||
caught: catchRecord.caught,
|
||||
haveToEvolve: catchRecord.haveToEvolve
|
||||
});
|
||||
function updateCatchRecord() {
|
||||
dispatch('updateCatch', { pokedexEntry, catchRecord });
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -75,57 +22,53 @@
|
||||
>
|
||||
<div class="dex-column pokedex-entry-container">
|
||||
<div class="flex mb-2">
|
||||
<div class="pl-2">
|
||||
<h3 class="text-xl font-bold pt-1">{pokedexEntry.pokemon}</h3>
|
||||
<sub class="text-primary-content/80"
|
||||
>#{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</sub
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mb-2">
|
||||
<div class="sprite-container flex justify-center items-center bg-base-100 rounded-lg p-2">
|
||||
<div class="sprite-container flex justify-center items-center bg-white rounded-lg p-2">
|
||||
<PokemonSprite
|
||||
pokemonName={pokedexEntry.pokemon}
|
||||
pokedexNumber={pokedexEntry.pokedexNumber}
|
||||
form={pokedexEntry.form}
|
||||
spriteKey={pokedexEntry.spriteKey}
|
||||
shiny={showShiny}
|
||||
/>
|
||||
</div>
|
||||
<div class="pl-2">
|
||||
<h3 class="text-xl font-bold pt-1 text-secondary">{pokedexEntry.pokemon}</h3>
|
||||
<sub class="text-gray-200">#{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</sub>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if showForms}
|
||||
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||
<div class="bg-white text-black rounded-lg p-4 mb-2">
|
||||
<p><strong>Form:</strong> {pokedexEntry.form ? pokedexEntry.form : '-'}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if pokedexEntry.evolutionInformation.trim().length > 0}
|
||||
<div class="bg-base-100 text-base-content rounded-lg p-4">
|
||||
<p><strong>How to evolve: </strong>{pokedexEntry.evolutionInformation}</p>
|
||||
</div>
|
||||
<div class="bg-white text-black rounded-lg p-4">
|
||||
{#if showForms}
|
||||
<p><strong>Box:</strong> {pokedexEntry.boxPlacementForms.box}</p>
|
||||
<p><strong>Row:</strong> {pokedexEntry.boxPlacementForms.row}</p>
|
||||
<p><strong>Column:</strong> {pokedexEntry.boxPlacementForms.column}</p>
|
||||
<p><strong>Can Gigantamax:</strong> {pokedexEntry.canGigantamax ? 'Yes' : 'No'}</p>
|
||||
{:else}
|
||||
<p><strong>Box:</strong> {pokedexEntry.boxPlacement.box}</p>
|
||||
<p><strong>Row:</strong> {pokedexEntry.boxPlacement.row}</p>
|
||||
<p><strong>Column:</strong> {pokedexEntry.boxPlacement.column}</p>
|
||||
{/if}
|
||||
|
||||
{#if pokedexEntry.evolutionInformation}
|
||||
<p><strong>How to evolve: </strong>{pokedexEntry.evolutionInformation}</p>
|
||||
{:else}
|
||||
<p>
|
||||
<strong>How to evolve: </strong>Currently missing - can you
|
||||
<a
|
||||
href="https://github.com/jcreek/LivingDexTracker"
|
||||
class="underline text-primary hover:text-secondary">help contribute</a
|
||||
>?
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if readOnly || catchRecord}
|
||||
<div
|
||||
class="dex-column catch-record-container bg-base-100 text-base-content rounded-lg p-4 mb-4 md:mb-0"
|
||||
>
|
||||
{#if readOnly}
|
||||
<h3 class="text-lg font-semibold mb-2">Progress</h3>
|
||||
<dl class="grid grid-cols-2 gap-x-4 gap-y-2">
|
||||
<dt>Caught</dt>
|
||||
<dd class="font-semibold">{sharedCatchStatus?.caught ? 'Yes' : 'No'}</dd>
|
||||
<dt>Needs to evolve</dt>
|
||||
<dd class="font-semibold">{sharedCatchStatus?.haveToEvolve ? 'Yes' : 'No'}</dd>
|
||||
<dt>In HOME</dt>
|
||||
<dd class="font-semibold">{sharedCatchStatus?.inHome ? 'Yes' : 'No'}</dd>
|
||||
{#if pokedexEntry.canGigantamax && showForms}
|
||||
<dt>Has Gigantamaxed</dt>
|
||||
<dd class="font-semibold">{sharedCatchStatus?.hasGigantamaxed ? 'Yes' : 'No'}</dd>
|
||||
{/if}
|
||||
</dl>
|
||||
{:else if catchRecord}
|
||||
<div class="dex-column catch-record-container bg-white text-black rounded-lg p-4 mb-4 md:mb-0">
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
@@ -133,8 +76,8 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.caught}
|
||||
class="checkbox checkbox-primary"
|
||||
on:change={onCaughtChange}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -146,8 +89,8 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.haveToEvolve}
|
||||
class="checkbox checkbox-primary"
|
||||
on:change={onNeedsToEvolveChange}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -155,12 +98,12 @@
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="block font-bold mr-2">In Home:</span>
|
||||
<span class="block font-bold mr-2">In home:</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.inHome}
|
||||
class="checkbox checkbox-primary"
|
||||
on:change={() => updateCatchRecord('toggle', { inHome: catchRecord?.inHome })}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -173,35 +116,29 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.hasGigantamaxed}
|
||||
class="checkbox checkbox-primary"
|
||||
on:change={() =>
|
||||
updateCatchRecord('toggle', { hasGigantamaxed: catchRecord?.hasGigantamaxed })}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<p>
|
||||
<label
|
||||
class="block font-bold mb-1"
|
||||
for={`personalNotesInput-${catchRecord._id || pokedexEntry._id}`}>Notes:</label
|
||||
<label class="block font-bold mb-1" for={`personalNotesInput-${catchRecord._id}`}
|
||||
>Notes:</label
|
||||
>
|
||||
<textarea
|
||||
bind:value={catchRecord.personalNotes}
|
||||
id={`personalNotesInput-${catchRecord._id || pokedexEntry._id}`}
|
||||
class="textarea textarea-bordered w-full"
|
||||
style="min-height: 120px;"
|
||||
on:input={() => updateCatchRecord('notes')}
|
||||
on:change={() => updateCatchRecord('notes-blur')}
|
||||
id={`personalNotesInput-${catchRecord._id}`}
|
||||
class="form-textarea w-full p-2 border rounded"
|
||||
on:change={updateCatchRecord}
|
||||
></textarea>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="dex-column additional-details-container">
|
||||
{#if showOrigins}
|
||||
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||
<div class="bg-white text-black rounded-lg p-4 mb-2">
|
||||
<h3 class="text-xl font-semibold mb-4">Origin Dex Requirements</h3>
|
||||
<p><strong>Region to Catch In:</strong> {pokedexEntry.regionToCatchIn}</p>
|
||||
<p><strong>Games to catch in:</strong></p>
|
||||
@@ -216,20 +153,16 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if pokedexEntry.catchInformation.length > 0}
|
||||
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||
<div class="bg-white text-black rounded-lg p-4 mb-2">
|
||||
<p><strong>Where to catch: </strong></p>
|
||||
<ul class="list-disc list-inside">
|
||||
{#each pokedexEntry.catchInformation as info}
|
||||
<li>
|
||||
{#if isCatchInformationItem(info)}
|
||||
<ul>
|
||||
<li><strong>Game:</strong> {info.game}</li>
|
||||
<li><strong>Location:</strong> {info.location}</li>
|
||||
<li><strong>Notes:</strong> {info.notes}</li>
|
||||
</ul>
|
||||
{:else}
|
||||
{info}
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
@@ -242,7 +175,7 @@
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||
<div class="bg-white text-black rounded-lg p-4 mb-2">
|
||||
<p>
|
||||
<strong>Where to catch: </strong>Currently missing - can you
|
||||
<a
|
||||
@@ -252,22 +185,39 @@
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if pokedexEntry.notes}
|
||||
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||
<p><strong>Dex Notes:</strong> {pokedexEntry.notes}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.dex-column {
|
||||
flex: 1;
|
||||
min-width: 250px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.sprite-container {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
|
||||
.form-checkbox {
|
||||
appearance: none;
|
||||
background-color: #fff;
|
||||
border: 2px solid #000;
|
||||
border-radius: 0.25rem;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-checkbox:checked {
|
||||
background-color: #00bfff;
|
||||
background-size: 100% 100%;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
min-height: 3rem;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type { Pokedex } from '$lib/models/Pokedex';
|
||||
|
||||
export let pokedex: Partial<Pokedex> = {
|
||||
name: '',
|
||||
description: '',
|
||||
isLivingDex: false,
|
||||
isShinyDex: false,
|
||||
isOriginDex: false,
|
||||
isFormDex: false,
|
||||
gameScope: null,
|
||||
dexScopes: []
|
||||
};
|
||||
export let mode: 'create' | 'edit' = 'create';
|
||||
export let onSubmit: () => void;
|
||||
export let onCancel: () => void;
|
||||
|
||||
type GameDex = {
|
||||
id: string;
|
||||
displayName: string;
|
||||
sortOrder: number;
|
||||
isDlc: boolean;
|
||||
parentDexId: string | null;
|
||||
};
|
||||
type GameSummary = {
|
||||
displayName: string;
|
||||
releaseYear: number;
|
||||
};
|
||||
|
||||
let gameDexes: Record<string, GameDex[]> = {};
|
||||
let gameOrder: string[] = [];
|
||||
let gameList: GameSummary[] = [];
|
||||
let availableDexes: GameDex[] = [];
|
||||
let loadingDexes = true;
|
||||
let lastGameScope: string | null = null;
|
||||
let dexScopesInitialized = false;
|
||||
let hasSeenGameScope = false;
|
||||
|
||||
// Fetch available game dexes from the database
|
||||
onMount(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/game-dexes');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
gameDexes = data.gameDexes || {};
|
||||
gameOrder = data.gameOrder || Object.keys(gameDexes);
|
||||
gameList = data.games || [];
|
||||
} else {
|
||||
console.error('Failed to fetch game dexes');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching game dexes:', error);
|
||||
} finally {
|
||||
loadingDexes = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Validation
|
||||
$: hasAtLeastOneType =
|
||||
pokedex.isLivingDex || pokedex.isShinyDex || pokedex.isOriginDex || pokedex.isFormDex;
|
||||
$: hasDexScope = !pokedex.gameScope || (pokedex.dexScopes && pokedex.dexScopes.length > 0);
|
||||
$: canSubmit = pokedex.name && pokedex.name.trim() !== '' && hasAtLeastOneType && hasDexScope;
|
||||
|
||||
$: if (pokedex.gameScope !== lastGameScope) {
|
||||
const shouldResetDexes = mode === 'create' || hasSeenGameScope;
|
||||
lastGameScope = pokedex.gameScope || null;
|
||||
dexScopesInitialized = false;
|
||||
if (shouldResetDexes) {
|
||||
pokedex.dexScopes = [];
|
||||
}
|
||||
if (!pokedex.gameScope) {
|
||||
availableDexes = [];
|
||||
}
|
||||
hasSeenGameScope = true;
|
||||
}
|
||||
|
||||
$: if (!loadingDexes && pokedex.gameScope) {
|
||||
availableDexes = gameDexes[pokedex.gameScope] || [];
|
||||
if (!dexScopesInitialized) {
|
||||
if (!pokedex.dexScopes || pokedex.dexScopes.length === 0) {
|
||||
pokedex.dexScopes = availableDexes.map((dex) => dex.id);
|
||||
}
|
||||
dexScopesInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (canSubmit) {
|
||||
onSubmit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="pokedex-name">
|
||||
<span class="label-text">Name</span>
|
||||
</label>
|
||||
<input
|
||||
id="pokedex-name"
|
||||
type="text"
|
||||
placeholder="My Living Dex"
|
||||
class="input input-bordered w-full"
|
||||
bind:value={pokedex.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="pokedex-description">
|
||||
<span class="label-text">Description (optional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="pokedex-description"
|
||||
class="textarea textarea-bordered h-24"
|
||||
placeholder="Describe your pokédex..."
|
||||
bind:value={pokedex.description}
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-control w-full">
|
||||
<fieldset class="w-full">
|
||||
<legend class="label">
|
||||
<span class="label-text">Type(s)</span>
|
||||
<span class="label-text-alt text-error"
|
||||
>{!hasAtLeastOneType ? 'At least one type required' : ''}</span
|
||||
>
|
||||
</legend>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" bind:checked={pokedex.isLivingDex} />
|
||||
<span class="label-text">Living Dex (one of each Pokémon)</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" bind:checked={pokedex.isShinyDex} />
|
||||
<span class="label-text">Shiny Dex (shiny variants only)</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" bind:checked={pokedex.isOriginDex} />
|
||||
<span class="label-text">Origin Dex (caught in native regions)</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" bind:checked={pokedex.isFormDex} />
|
||||
<span class="label-text">Form Dex (all forms included)</span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="game-scope">
|
||||
<span class="label-text">Game Scope</span>
|
||||
</label>
|
||||
<select
|
||||
id="game-scope"
|
||||
class="select select-bordered"
|
||||
bind:value={pokedex.gameScope}
|
||||
disabled={loadingDexes}
|
||||
>
|
||||
<option value={null}>All Games</option>
|
||||
{#if loadingDexes}
|
||||
<option disabled>Loading games...</option>
|
||||
{:else if gameList.length > 0}
|
||||
{#each gameList as game}
|
||||
<option value={game.displayName}>{game.displayName}</option>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each gameOrder.length > 0 ? gameOrder : Object.keys(gameDexes) as game}
|
||||
<option value={game}>{game}</option>
|
||||
{/each}
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{#if pokedex.gameScope}
|
||||
<div class="form-control w-full">
|
||||
<fieldset class="w-full">
|
||||
<legend class="label">
|
||||
<span class="label-text">Dex Scope</span>
|
||||
<span class="label-text-alt text-error"
|
||||
>{!hasDexScope ? 'Select at least one dex' : ''}</span
|
||||
>
|
||||
</legend>
|
||||
{#if availableDexes.length === 0}
|
||||
<p class="text-sm text-error">No dexes found for this game.</p>
|
||||
{:else}
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each availableDexes as dex}
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
value={dex.id}
|
||||
bind:group={pokedex.dexScopes}
|
||||
/>
|
||||
<span class="label-text">{dex.displayName}</span>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn btn-ghost" type="button" on:click={onCancel}>Cancel</button>
|
||||
<button class="btn btn-primary" type="button" on:click={handleSubmit} disabled={!canSubmit}>
|
||||
{mode === 'create' ? 'Create' : 'Save'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,142 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let isOpen: boolean;
|
||||
export let onClose: () => void;
|
||||
|
||||
function handleBackdropClick() {
|
||||
onClose();
|
||||
}
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape' && isOpen) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
|
||||
let dialog: HTMLDivElement;
|
||||
onMount(() => {
|
||||
const previous = document.activeElement as HTMLElement | null;
|
||||
const close = dialog.querySelector<HTMLButtonElement>('.close-button');
|
||||
close?.focus();
|
||||
const trapFocus = (event: KeyboardEvent) => {
|
||||
handleKeyDown(event);
|
||||
if (event.key !== 'Tab') return;
|
||||
const nodes = Array.from(
|
||||
dialog.querySelectorAll<HTMLElement>(
|
||||
'button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), a[href]'
|
||||
)
|
||||
).filter((node) => node.getClientRects().length);
|
||||
const first = nodes[0],
|
||||
last = nodes.at(-1);
|
||||
if (event.shiftKey && document.activeElement === first) {
|
||||
event.preventDefault();
|
||||
last?.focus();
|
||||
} else if (!event.shiftKey && document.activeElement === last) {
|
||||
event.preventDefault();
|
||||
first?.focus();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', trapFocus);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', trapFocus);
|
||||
if (previous?.isConnected) previous.focus();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if isOpen}
|
||||
<div
|
||||
bind:this={dialog}
|
||||
class="modal modal-open"
|
||||
role="dialog"
|
||||
aria-label="Pokémon details"
|
||||
aria-modal="true"
|
||||
>
|
||||
<div class="modal-box-custom bg-primary text-primary-content">
|
||||
<button data-offline-action class="close-button" on:click={onClose} aria-label="Close">
|
||||
✕
|
||||
</button>
|
||||
<div class="modal-content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
data-offline-action
|
||||
type="button"
|
||||
class="modal-backdrop bg-black/50"
|
||||
aria-label="Close modal"
|
||||
on:click={handleBackdropClick}
|
||||
></button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modal-box-custom {
|
||||
max-width: 72rem;
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
border: none;
|
||||
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5);
|
||||
outline: none;
|
||||
position: relative;
|
||||
border-radius: 1rem;
|
||||
padding: 0.5rem;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 1rem;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
color: white;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
z-index: 10;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Remove margin and background from card inside modal */
|
||||
.modal-content :global(.dex-entry) {
|
||||
margin-bottom: 0;
|
||||
background-color: transparent !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.modal-box-custom {
|
||||
width: 91.666667%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,129 @@
|
||||
<script lang="ts">
|
||||
import Pagination from '$lib/components/Pagination.svelte';
|
||||
|
||||
export let viewAsBoxes = false;
|
||||
export let currentPage = 1;
|
||||
export let itemsPerPage = 20;
|
||||
export let totalPages = 0;
|
||||
export let showForms = true;
|
||||
export let showOrigins = true;
|
||||
export let showShiny = false;
|
||||
export let catchRegion = '';
|
||||
export let catchGame = '';
|
||||
export let toggleForms = () => {};
|
||||
export let toggleOrigins = () => {};
|
||||
export let toggleShiny = () => {};
|
||||
export let getData = () => {};
|
||||
export let toggleViewAsBoxes = () => {};
|
||||
</script>
|
||||
|
||||
<aside
|
||||
class="menu bg-gray-800 text-white min-h-full w-64 p-4 lg:fixed xs:top-0 lg:left-0 h-full lg:h-5/6"
|
||||
>
|
||||
<h2 class="text-2xl font-semibold mb-4">Views</h2>
|
||||
<ul>
|
||||
<li class="mb-2">
|
||||
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleViewAsBoxes}>
|
||||
{viewAsBoxes ? 'Switch to List View' : 'Switch to Box View'}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 class="text-2xl font-semibold mb-4">Filters</h2>
|
||||
<ul>
|
||||
<li class="mb-2">
|
||||
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleForms}>
|
||||
Toggle Forms (Currently {showForms ? 'On' : 'Off'})
|
||||
</button>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleOrigins}>
|
||||
Toggle Origins (Currently {showOrigins ? 'On' : 'Off'})
|
||||
</button>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleShiny}>
|
||||
Toggle Shiny (Currently {showShiny ? 'On' : 'Off'})
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mb-4">
|
||||
<label for="catchRegionSelect">Region to catch in:</label>
|
||||
<select
|
||||
id="catchRegionSelect"
|
||||
class="select select-bordered w-full max-w-xs text-black"
|
||||
bind:value={catchRegion}
|
||||
on:change={getData}
|
||||
>
|
||||
<option value="">All</option>
|
||||
<option value="Kanto">Kanto (Red, Blue, Yellow, LG: Pikachu, LG: Eevee)</option>
|
||||
<option value="Johto">Johto (Gold, Silver, Crystal, HG, SS)</option>
|
||||
<option value="Hoenn">Hoenn (Ruby, Sapphire, Emerald, OR, AS)</option>
|
||||
<option value="Sinnoh">Sinnoh (Diamond, Pearl, Platinum, BD, SP)</option>
|
||||
<option value="Unova">Unova (Black, White, Black2, White2, Dream Radar)</option>
|
||||
<option value="Kalos">Kalos (X, Y)</option>
|
||||
<option value="Alola">Alola (Sun, Moon, Moon Demo, US, UM)</option>
|
||||
<option value="Galar">Galar (Sword, Shield)</option>
|
||||
<option value="Hisui">Hisui (PLA)</option>
|
||||
<option value="Paldea">Paldea (Scarlet, Violet)</option>
|
||||
<option value="Unknown">Unknown (Home, Go)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="catchGameSelect">Game to catch in: {catchGame}</label>
|
||||
<select
|
||||
id="catchGameSelect"
|
||||
class="select select-bordered w-full max-w-xs text-black"
|
||||
bind:value={catchGame}
|
||||
on:change={getData}
|
||||
>
|
||||
<option value="">All</option>
|
||||
<option value="Red">Red (Kanto)</option>
|
||||
<option value="Blue">Blue (Kanto)</option>
|
||||
<option value="Yellow">Yellow (Kanto)</option>
|
||||
<option value="LG: Pikachu">LG: Pikachu (Kanto)</option>
|
||||
<option value="LG: Eevee">LG: Eevee (Kanto)</option>
|
||||
<option value="Gold">Gold (Johto)</option>
|
||||
<option value="Silver">Silver (Johto)</option>
|
||||
<option value="Crystal">Crystal (Johto)</option>
|
||||
<option value="HG">HG (Johto)</option>
|
||||
<option value="SS">SS (Johto)</option>
|
||||
<option value="Ruby">Ruby (Hoenn)</option>
|
||||
<option value="Sapphire">Sapphire (Hoenn)</option>
|
||||
<option value="Emerald">Emerald (Hoenn)</option>
|
||||
<option value="OR">OR (Hoenn)</option>
|
||||
<option value="AS">AS (Hoenn)</option>
|
||||
<option value="Diamond">Diamond (Sinnoh)</option>
|
||||
<option value="Pearl">Pearl (Sinnoh)</option>
|
||||
<option value="Platinum">Platinum (Sinnoh)</option>
|
||||
<option value="BD">BD (Sinnoh)</option>
|
||||
<option value="SP">SP (Sinnoh)</option>
|
||||
<option value="Black">Black (Unova)</option>
|
||||
<option value="White">White (Unova)</option>
|
||||
<option value="Black2">Black2 (Unova)</option>
|
||||
<option value="White2">White2 (Unova)</option>
|
||||
<option value="Dream Radar">Dream Radar (Unova)</option>
|
||||
<option value="X">X (Kalos)</option>
|
||||
<option value="Y">Y (Kalos)</option>
|
||||
<option value="Sun">Sun (Alola)</option>
|
||||
<option value="Moon">Moon (Alola)</option>
|
||||
<option value="Moon Demo">Moon Demo (Alola)</option>
|
||||
<option value="US">US (Alola)</option>
|
||||
<option value="UM">UM (Alola)</option>
|
||||
<option value="Sword">Sword (Galar)</option>
|
||||
<option value="Shield">Shield (Galar)</option>
|
||||
<option value="PLA">PLA (Hisui)</option>
|
||||
<option value="Scarlet">Scarlet (Paldea)</option>
|
||||
<option value="Violet">Violet (Paldea)</option>
|
||||
<option value="Home">Home (Unknown)</option>
|
||||
<option value="Go">Go (Unknown)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{#if !viewAsBoxes}
|
||||
<h2 class="text-2xl font-semibold mb-4">Paging</h2>
|
||||
<div>
|
||||
<Pagination bind:currentPage bind:itemsPerPage bind:totalPages />
|
||||
</div>
|
||||
{/if}
|
||||
</aside>
|
||||
@@ -1,710 +1,118 @@
|
||||
<script lang="ts">
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { calculateBoxPlacement } from '$lib/utils/boxPlacement';
|
||||
import type { CatchRecord } from '$lib/models/CatchRecord';
|
||||
import type { CombinedData } from '$lib/models/CombinedData';
|
||||
import type { PokedexEntry } from '$lib/models/PokedexEntry';
|
||||
import PokemonSprite from '$lib/components/PokemonSprite.svelte';
|
||||
import type { SharedCombinedData } from '$lib/models/SharedPokedex';
|
||||
import type { PokedexGridRow } from '$lib/models/PokedexGridRow';
|
||||
import { markGridInteractive } from '$lib/utils/criticalPageWork';
|
||||
|
||||
export let virtualize = false;
|
||||
export let retryLoad: (() => void) | null = null;
|
||||
let renderAll = false;
|
||||
let visibleBoxes = new Set([1, 2, 3, 4]);
|
||||
let focusedBox: number | null = null;
|
||||
let grid: HTMLDivElement;
|
||||
const shells = new Map<number, HTMLElement>();
|
||||
let viewportFrame = 0;
|
||||
let mounted = false;
|
||||
export let gridKey = '';
|
||||
let markedKey: string | null = null;
|
||||
let scrollAnchor: { number: number; top: number } | null = null;
|
||||
|
||||
function measureViewport() {
|
||||
viewportFrame = 0;
|
||||
const next = new Set<number>();
|
||||
scrollAnchor = null;
|
||||
for (const [number, node] of shells) {
|
||||
const rect = node.getBoundingClientRect();
|
||||
if (!scrollAnchor && rect.bottom > 0) scrollAnchor = { number, top: rect.top };
|
||||
const overscan = rect.height + 16;
|
||||
if (rect.bottom >= -overscan && rect.top <= window.innerHeight + overscan) next.add(number);
|
||||
}
|
||||
visibleBoxes = next;
|
||||
}
|
||||
function resizeViewport() {
|
||||
if (scrollAnchor && window.scrollY > 0) {
|
||||
const node = shells.get(scrollAnchor.number);
|
||||
if (node) window.scrollBy(0, node.getBoundingClientRect().top - scrollAnchor.top);
|
||||
}
|
||||
scheduleViewport();
|
||||
}
|
||||
function trackFocus(event: FocusEvent) {
|
||||
const target = event.target instanceof Element ? event.target : null;
|
||||
if (!target?.closest('[data-box-number], [role="dialog"]')) focusedBox = null;
|
||||
}
|
||||
function scheduleViewport() {
|
||||
if (!viewportFrame) viewportFrame = requestAnimationFrame(measureViewport);
|
||||
}
|
||||
function boxShell(node: HTMLElement, number: number) {
|
||||
shells.set(number, node);
|
||||
scheduleViewport();
|
||||
return {
|
||||
destroy() {
|
||||
shells.delete(number);
|
||||
}
|
||||
};
|
||||
}
|
||||
async function focusEntry(index: number) {
|
||||
if (!combinedData || index < 0 || index >= combinedData.length) return;
|
||||
focusedBox = Math.floor(index / 30) + 1;
|
||||
await tick();
|
||||
grid.querySelector<HTMLElement>(`[data-entry-index="${index}"]`)?.focus();
|
||||
}
|
||||
function navigateEntry(event: KeyboardEvent, index: number) {
|
||||
const offset = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -6, ArrowDown: 6 }[event.key];
|
||||
if (offset !== undefined) {
|
||||
event.preventDefault();
|
||||
void focusEntry(index + offset);
|
||||
} else if (event.key === 'Tab') {
|
||||
const next = index + (event.shiftKey ? -1 : 1);
|
||||
if (
|
||||
next >= 0 &&
|
||||
next < (combinedData?.length ?? 0) &&
|
||||
!grid.querySelector(`[data-entry-index="${next}"]`)
|
||||
) {
|
||||
event.preventDefault();
|
||||
void focusEntry(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
$: if (mounted && combinedData && gridKey !== markedKey) {
|
||||
markedKey = gridKey;
|
||||
grid?.removeAttribute('data-grid-interactive');
|
||||
void tick().then(() => {
|
||||
if (virtualize) markGridInteractive();
|
||||
});
|
||||
}
|
||||
onMount(() => {
|
||||
mounted = true;
|
||||
const observer = new ResizeObserver(resizeViewport);
|
||||
if (grid) observer.observe(grid);
|
||||
window.addEventListener('scroll', scheduleViewport, { passive: true });
|
||||
window.addEventListener('resize', resizeViewport);
|
||||
window.addEventListener('focusin', trackFocus);
|
||||
measureViewport();
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
cancelAnimationFrame(viewportFrame);
|
||||
window.removeEventListener('scroll', scheduleViewport);
|
||||
window.removeEventListener('resize', resizeViewport);
|
||||
window.removeEventListener('focusin', trackFocus);
|
||||
};
|
||||
});
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
|
||||
export let showShiny = false;
|
||||
type DisplayData = PokedexGridRow | SharedCombinedData;
|
||||
type DisplayStatus = DisplayData['catchRecord'];
|
||||
|
||||
export let combinedData: DisplayData[] | null;
|
||||
export let readOnly = false;
|
||||
export let boxNumbers: number[] = [];
|
||||
export let combinedData: CombinedData[] | null;
|
||||
export let boxNumbers = Array<number>;
|
||||
export let currentPlacement = 'boxPlacementForms';
|
||||
export let creatingRecords = false;
|
||||
export let totalRecordsCreated = 0;
|
||||
export let failedToLoad = false;
|
||||
export let markBoxAsNotCaught: (boxNumber: number) => void = () => {};
|
||||
export let markBoxAsCaught: (boxNumber: number) => void = () => {};
|
||||
export let markBoxAsNeedsToEvolve: (boxNumber: number) => void = () => {};
|
||||
export let markBoxAsInHome: (boxNumber: number) => void = () => {};
|
||||
export let markBoxAsNotInHome: (boxNumber: number) => void = () => {};
|
||||
export let markBoxAsNotCaught = (boxNumber: number) => {};
|
||||
export let markBoxAsCaught = (boxNumber: number) => {};
|
||||
export let markBoxAsNeedsToEvolve = (boxNumber: number) => {};
|
||||
export let markBoxAsInHome = (boxNumber: number) => {};
|
||||
export let markBoxAsNotInHome = (boxNumber: number) => {};
|
||||
export let createCatchRecords = () => {};
|
||||
export let onPokemonClick: (pokemon: DisplayData) => void = () => {};
|
||||
|
||||
let filterNotCaught = false;
|
||||
let filterNeedsToEvolve = false;
|
||||
let filterInHome = false;
|
||||
let filterNotInHome = false;
|
||||
|
||||
// Bulk actions menu state (one open menu at a time)
|
||||
let openBulkMenuForBox: number | null = null;
|
||||
|
||||
onMount(() => {
|
||||
const close = () => {
|
||||
openBulkMenuForBox = null;
|
||||
};
|
||||
window.addEventListener('click', close);
|
||||
return () => window.removeEventListener('click', close);
|
||||
});
|
||||
|
||||
let filteredCombinedData: DisplayData[] = [];
|
||||
let filteredTotal = 0;
|
||||
let overallTotal = 0;
|
||||
let overallCaughtCount = 0;
|
||||
let overallNotCaughtCount = 0;
|
||||
let overallNeedsToEvolveCount = 0;
|
||||
let overallInHomeCount = 0;
|
||||
let overallNotInHomeCount = 0;
|
||||
let filtersActive = false;
|
||||
let filtersKey = '';
|
||||
|
||||
function normalizedStatus(catchRecord: DisplayStatus) {
|
||||
return {
|
||||
caught: !!catchRecord?.caught,
|
||||
needsToEvolve: !!catchRecord?.haveToEvolve,
|
||||
inHome: !!catchRecord?.inHome
|
||||
};
|
||||
}
|
||||
|
||||
function matchesFilters(catchRecord: DisplayStatus) {
|
||||
const status = normalizedStatus(catchRecord);
|
||||
if (!filtersActive) return true;
|
||||
|
||||
const isCaught = status.caught || status.needsToEvolve;
|
||||
const isNotCaught = !isCaught;
|
||||
const isNeedsToEvolve = status.needsToEvolve;
|
||||
const isInHome = status.inHome;
|
||||
const isNotInHome = !status.inHome;
|
||||
|
||||
// OR within each group, AND between groups.
|
||||
const progressGroupActive = filterNotCaught || filterNeedsToEvolve;
|
||||
const homeGroupActive = filterInHome || filterNotInHome;
|
||||
|
||||
const progressMatch =
|
||||
!progressGroupActive ||
|
||||
(filterNotCaught && isNotCaught) ||
|
||||
(filterNeedsToEvolve && isNeedsToEvolve);
|
||||
|
||||
const homeMatch =
|
||||
!homeGroupActive || (filterInHome && isInHome) || (filterNotInHome && isNotInHome);
|
||||
|
||||
return progressMatch && homeMatch;
|
||||
}
|
||||
|
||||
$: filtersActive = filterNotCaught || filterNeedsToEvolve || filterInHome || filterNotInHome;
|
||||
$: filtersKey = `${filterNotCaught}-${filterNeedsToEvolve}-${filterInHome}-${filterNotInHome}`;
|
||||
|
||||
$: {
|
||||
// Ensure this recalculates when any filter changes (Svelte doesn't track function internals).
|
||||
filtersKey;
|
||||
filteredCombinedData = combinedData
|
||||
? combinedData.filter(({ catchRecord }) => matchesFilters(catchRecord))
|
||||
: [];
|
||||
}
|
||||
|
||||
$: filteredTotal = filteredCombinedData.length;
|
||||
$: overallTotal = combinedData?.length ?? 0;
|
||||
|
||||
$: overallCaughtCount = (combinedData ?? []).reduce((acc, { catchRecord }) => {
|
||||
const status = normalizedStatus(catchRecord);
|
||||
// Treat "needs to evolve" as a subset of "caught" (it is caught, just not finished).
|
||||
return acc + (status.caught || status.needsToEvolve ? 1 : 0);
|
||||
}, 0);
|
||||
|
||||
$: overallNotCaughtCount = overallTotal - overallCaughtCount;
|
||||
|
||||
$: overallNeedsToEvolveCount = (combinedData ?? []).reduce((acc, { catchRecord }) => {
|
||||
return acc + (normalizedStatus(catchRecord).needsToEvolve ? 1 : 0);
|
||||
}, 0);
|
||||
|
||||
$: overallInHomeCount = (combinedData ?? []).reduce((acc, { catchRecord }) => {
|
||||
return acc + (normalizedStatus(catchRecord).inHome ? 1 : 0);
|
||||
}, 0);
|
||||
|
||||
$: overallNotInHomeCount = overallTotal - overallInHomeCount;
|
||||
|
||||
const BOX_VIEW_LAYOUT_STORAGE_KEY = 'livingdex:boxViewLayout:v1';
|
||||
type BoxViewLayout = 'comfortable' | 'compact' | 'ultra';
|
||||
export let initialLayout: BoxViewLayout = 'comfortable';
|
||||
let boxViewLayout: BoxViewLayout = initialLayout;
|
||||
|
||||
function persistBoxViewLayout(next: BoxViewLayout) {
|
||||
const anchor = [...shells.entries()].find(
|
||||
([, node]) => node.getBoundingClientRect().bottom > 0
|
||||
);
|
||||
const top = anchor?.[1].getBoundingClientRect().top;
|
||||
boxViewLayout = next;
|
||||
document.cookie = `boxViewLayout=${next};path=/;max-age=31536000;SameSite=Lax`;
|
||||
void tick().then(() => {
|
||||
if (anchor && top !== undefined && window.scrollY > 0)
|
||||
window.scrollBy(0, anchor[1].getBoundingClientRect().top - top);
|
||||
measureViewport();
|
||||
});
|
||||
try {
|
||||
localStorage.setItem(BOX_VIEW_LAYOUT_STORAGE_KEY, next);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function onBoxViewLayoutChange(event: Event) {
|
||||
const next = (event.currentTarget as HTMLSelectElement).value;
|
||||
if (next === 'comfortable' || next === 'compact' || next === 'ultra') {
|
||||
persistBoxViewLayout(next);
|
||||
}
|
||||
}
|
||||
|
||||
$: boxesPerRow = boxViewLayout === 'comfortable' ? 2 : boxViewLayout === 'compact' ? 3 : 4;
|
||||
$: cellPaddingRem =
|
||||
boxViewLayout === 'comfortable' ? 1 : boxViewLayout === 'compact' ? 0.6 : 0.45;
|
||||
$: spriteSizePx = boxViewLayout === 'comfortable' ? 64 : boxViewLayout === 'compact' ? 52 : 44;
|
||||
|
||||
function cellStatusClasses(catchRecord: DisplayStatus) {
|
||||
// Keep borders/layout unchanged; rely on clearer fills + badges instead.
|
||||
if (catchRecord?.caught) {
|
||||
// Match legend (green-600) while keeping sprites readable.
|
||||
return 'bg-green-600/15';
|
||||
}
|
||||
if (catchRecord?.haveToEvolve) {
|
||||
// Match legend (yellow-500) while keeping sprites readable.
|
||||
return 'bg-yellow-500/20';
|
||||
}
|
||||
function cellBackgroundColourClass(catchRecord: CatchRecord) {
|
||||
if (catchRecord.caught) {
|
||||
return 'bg-green-100/50';
|
||||
} else if (catchRecord.haveToEvolve) {
|
||||
return 'bg-yellow-100/50';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
function statusLabel(catchRecord: DisplayStatus) {
|
||||
const parts: string[] = [];
|
||||
if (catchRecord?.caught) parts.push('Caught');
|
||||
if (catchRecord?.haveToEvolve) parts.push('Needs to evolve');
|
||||
if (catchRecord?.inHome) parts.push('In HOME');
|
||||
return parts.length ? parts.join(', ') : 'Not caught';
|
||||
}
|
||||
|
||||
function cellBackgroundColourStyle(index: number, catchRecord: DisplayStatus) {
|
||||
if (catchRecord?.caught || catchRecord?.haveToEvolve) {
|
||||
function cellBackgroundColourStyle(pokedexEntry: PokedexEntry, catchRecord: CatchRecord) {
|
||||
if (catchRecord.caught || catchRecord.haveToEvolve) {
|
||||
return '';
|
||||
} else {
|
||||
const placement = calculateBoxPlacement(index);
|
||||
if (placement.column % 2 === 0) {
|
||||
return 'background-color: var(--ld-box-bg-even);';
|
||||
if (pokedexEntry[currentPlacement].column % 2 === 0) {
|
||||
return 'background-color: #ffffff';
|
||||
} else {
|
||||
return 'background-color: var(--ld-box-bg-odd);';
|
||||
return 'background-color: #f9f9f9;';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onInHomeFilterChange(event: Event) {
|
||||
const checked = (event.currentTarget as HTMLInputElement).checked;
|
||||
if (checked) filterNotInHome = false;
|
||||
}
|
||||
|
||||
function onNotInHomeFilterChange(event: Event) {
|
||||
const checked = (event.currentTarget as HTMLInputElement).checked;
|
||||
if (checked) filterInHome = false;
|
||||
}
|
||||
|
||||
const POKEMON_PER_BOX = 30;
|
||||
const BOX_POSITIONS = Array.from({ length: POKEMON_PER_BOX }, (_, i) => i);
|
||||
</script>
|
||||
|
||||
<main class="flex-1 p-4 w-full">
|
||||
<div class="max-w-[1440px] w-full mx-auto">
|
||||
<div class="max-w-fit mx-auto">
|
||||
{#if combinedData && combinedData.length > 0}
|
||||
<div class="container mx-auto">
|
||||
<div class="card bg-base-100 shadow mb-4">
|
||||
<div class="card-body p-4 flex flex-col gap-3">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<label class="label p-0" for="box-view-layout">
|
||||
<span class="label-text font-semibold">Box view layout</span>
|
||||
</label>
|
||||
<select
|
||||
data-offline-action
|
||||
id="box-view-layout"
|
||||
class="select select-bordered select-sm"
|
||||
bind:value={boxViewLayout}
|
||||
on:change={onBoxViewLayoutChange}
|
||||
aria-label="Choose box view layout density"
|
||||
>
|
||||
<option value="comfortable">Comfortable (2 boxes/row)</option>
|
||||
<option value="compact">Compact (3 boxes/row)</option>
|
||||
<option value="ultra">Ultra (4 boxes/row)</option>
|
||||
</select>
|
||||
{#if virtualize}
|
||||
<label class="label cursor-pointer gap-2"
|
||||
><input
|
||||
data-offline-action
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm"
|
||||
bind:checked={renderAll}
|
||||
/>Render all boxes</label
|
||||
>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2 text-sm">
|
||||
<span class="font-semibold">Legend:</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full border border-green-700 bg-green-600 px-2 py-0.5 text-white"
|
||||
>
|
||||
<span class="status-badge status-badge--caught" aria-hidden="true">
|
||||
<svg
|
||||
class="status-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#ffffff"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Caught</span>
|
||||
</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full border border-yellow-700 bg-yellow-500 px-2 py-0.5 text-white"
|
||||
>
|
||||
<span class="status-badge status-badge--evolve" aria-hidden="true">
|
||||
<svg
|
||||
class="status-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#ffffff"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 19V5" />
|
||||
<path d="M5 12l7-7 7 7" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Caught but needs to evolve</span>
|
||||
</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full border border-sky-700 bg-sky-600 px-2 py-0.5 text-white"
|
||||
>
|
||||
<span class="status-badge status-badge--home" aria-hidden="true">
|
||||
<svg
|
||||
class="status-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="#ffffff"
|
||||
stroke="#ffffff"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 3 3 10.5V21a1 1 0 0 0 1 1h5v-6h6v6h5a1 1 0 0 0 1-1V10.5L12 3Z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>In Home</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<span class="font-semibold">Filters:</span>
|
||||
<label class="label cursor-pointer gap-2 p-0">
|
||||
<input
|
||||
data-offline-action
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm"
|
||||
bind:checked={filterNotCaught}
|
||||
/>
|
||||
<span class="label-text">Not caught</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer gap-2 p-0">
|
||||
<input
|
||||
data-offline-action
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm"
|
||||
bind:checked={filterNeedsToEvolve}
|
||||
/>
|
||||
<span class="label-text">Needs to evolve</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer gap-2 p-0">
|
||||
<input
|
||||
data-offline-action
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm"
|
||||
bind:checked={filterInHome}
|
||||
on:change={onInHomeFilterChange}
|
||||
/>
|
||||
<span class="label-text">In HOME</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer gap-2 p-0">
|
||||
<input
|
||||
data-offline-action
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm"
|
||||
bind:checked={filterNotInHome}
|
||||
on:change={onNotInHomeFilterChange}
|
||||
/>
|
||||
<span class="label-text">Not in HOME</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-between gap-2 rounded-box bg-base-200/60 px-3 py-2"
|
||||
>
|
||||
<div class="text-sm text-base-content/70">
|
||||
<span class="font-semibold text-base-content">Caught</span>
|
||||
{overallCaughtCount}
|
||||
<span class="mx-2">•</span>
|
||||
<span class="font-semibold text-base-content">Not caught</span>
|
||||
{overallNotCaughtCount}
|
||||
<span class="mx-2">•</span>
|
||||
<span class="font-semibold text-base-content">Needs to evolve</span>
|
||||
{overallNeedsToEvolveCount}
|
||||
<span class="mx-2">•</span>
|
||||
<span class="font-semibold text-base-content">In HOME</span>
|
||||
{overallInHomeCount}
|
||||
<span class="mx-2">•</span>
|
||||
<span class="font-semibold text-base-content">Not in HOME</span>
|
||||
{overallNotInHomeCount}
|
||||
</div>
|
||||
<div class="badge badge-outline">
|
||||
Showing {filteredTotal} of {overallTotal}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
bind:this={grid}
|
||||
class="boxes-grid"
|
||||
style="--boxes-per-row: {boxesPerRow}; --cell-padding: {cellPaddingRem}rem; --sprite-size: {spriteSizePx}px;"
|
||||
>
|
||||
{#each boxNumbers as boxNumber (boxNumber)}
|
||||
{@const bulkMenuId = `box-${boxNumber}-bulk-menu`}
|
||||
<div class="box-shell" use:boxShell={boxNumber} data-box-number={boxNumber}>
|
||||
{#if !virtualize || renderAll || visibleBoxes.has(boxNumber) || focusedBox === boxNumber}
|
||||
<div class="box-content">
|
||||
<div
|
||||
class="box-heading flex items-center justify-between gap-3 mb-4 relative z-20"
|
||||
>
|
||||
<h2 class="text-xl font-bold">Box {boxNumber}</h2>
|
||||
{#if !readOnly}<div class="relative">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline relative z-[210]"
|
||||
aria-label="Open bulk actions menu"
|
||||
aria-haspopup="menu"
|
||||
aria-controls={bulkMenuId}
|
||||
aria-expanded={openBulkMenuForBox === boxNumber}
|
||||
on:click={(event) => {
|
||||
event.stopPropagation();
|
||||
openBulkMenuForBox =
|
||||
openBulkMenuForBox === boxNumber ? null : boxNumber;
|
||||
}}
|
||||
on:keydown={(event) => {
|
||||
if (event.key === 'Escape') openBulkMenuForBox = null;
|
||||
}}
|
||||
>
|
||||
⋯
|
||||
<div class="flex flex-wrap -mx-2">
|
||||
{#each boxNumbers as boxNumber}
|
||||
<div class="mb-8 md:w-1/2 px-2">
|
||||
<h2 class="text-xl font-bold mb-4">Box {boxNumber}</h2>
|
||||
<button class="btn" on:click={markBoxAsNotCaught(boxNumber)}>
|
||||
Mark box as not 'caught'
|
||||
</button>
|
||||
|
||||
{#if openBulkMenuForBox === boxNumber}
|
||||
<ul
|
||||
id={bulkMenuId}
|
||||
class="menu bg-base-100 rounded-box absolute right-0 mt-2 z-[220] w-56 p-2 shadow border border-base-300"
|
||||
>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
on:click|stopPropagation={() => {
|
||||
markBoxAsNotCaught(boxNumber);
|
||||
openBulkMenuForBox = null;
|
||||
}}
|
||||
>
|
||||
Mark box as Not caught
|
||||
<button class="btn" on:click={markBoxAsCaught(boxNumber)}>
|
||||
Mark box as 'caught'
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
on:click|stopPropagation={() => {
|
||||
markBoxAsCaught(boxNumber);
|
||||
openBulkMenuForBox = null;
|
||||
}}
|
||||
<button class="btn" on:click={markBoxAsNeedsToEvolve(boxNumber)}
|
||||
>Mark box as 'needs to evolve'</button
|
||||
>
|
||||
Mark box as Caught
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
on:click|stopPropagation={() => {
|
||||
markBoxAsNeedsToEvolve(boxNumber);
|
||||
openBulkMenuForBox = null;
|
||||
}}
|
||||
<button class="btn" on:click={markBoxAsInHome(boxNumber)}
|
||||
>Mark box as 'in Home'</button
|
||||
>
|
||||
Mark box as Needs to evolve
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
on:click|stopPropagation={() => {
|
||||
markBoxAsInHome(boxNumber);
|
||||
openBulkMenuForBox = null;
|
||||
}}
|
||||
<button class="btn" on:click={markBoxAsNotInHome(boxNumber)}
|
||||
>Mark box as not 'in Home'</button
|
||||
>
|
||||
Mark box as In HOME
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
on:click|stopPropagation={() => {
|
||||
markBoxAsNotInHome(boxNumber);
|
||||
openBulkMenuForBox = null;
|
||||
}}
|
||||
>
|
||||
Mark box as Not in HOME
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</div>{/if}
|
||||
</div>
|
||||
<div class="grid grid-cols-6">
|
||||
{#each BOX_POSITIONS as positionInBox}
|
||||
{@const globalIndex = (boxNumber - 1) * POKEMON_PER_BOX + positionInBox}
|
||||
{@const placement = calculateBoxPlacement(globalIndex)}
|
||||
{@const entry = combinedData?.[globalIndex]}
|
||||
{@const pokedexEntry = entry?.pokedexEntry}
|
||||
{@const catchRecord = entry?.catchRecord ?? null}
|
||||
{@const isFilteredOut =
|
||||
!!entry && filtersActive && !!filtersKey && !matchesFilters(catchRecord)}
|
||||
{#if entry && pokedexEntry}
|
||||
<button
|
||||
type="button"
|
||||
class="pokemon-box {cellStatusClasses(catchRecord)} {isFilteredOut
|
||||
? 'pokemon-box--filtered-out'
|
||||
: 'hover:scale-105 hover:shadow-lg hover:z-50'} transition-all cursor-pointer relative"
|
||||
style="grid-column-start: {placement.column}; grid-row-start: {placement.row};
|
||||
{cellBackgroundColourStyle(globalIndex, catchRecord)}"
|
||||
data-offline-action
|
||||
data-entry-index={globalIndex}
|
||||
data-entry-id={pokedexEntry._id}
|
||||
on:focus={() => (focusedBox = boxNumber)}
|
||||
on:keydown={(event) => navigateEntry(event, globalIndex)}
|
||||
aria-disabled={isFilteredOut}
|
||||
on:click={() => {
|
||||
if (!isFilteredOut) onPokemonClick(entry);
|
||||
}}
|
||||
aria-label="View details for {pokedexEntry.pokemon}{pokedexEntry.form
|
||||
? ` (${pokedexEntry.form})`
|
||||
: ''}. Status: {statusLabel(catchRecord)}"
|
||||
{#each combinedData as { pokedexEntry, catchRecord }}
|
||||
{#if pokedexEntry[currentPlacement].box === boxNumber}
|
||||
<div
|
||||
class="pokemon-box {cellBackgroundColourClass(catchRecord)}"
|
||||
style="grid-column-start: {pokedexEntry[currentPlacement]
|
||||
.column}; grid-row-start: {pokedexEntry[currentPlacement].row};
|
||||
{cellBackgroundColourStyle(pokedexEntry, catchRecord)}"
|
||||
>
|
||||
<span class="cell-tooltip">
|
||||
<span class="block w-full h-full">
|
||||
{#if catchRecord?.caught}
|
||||
<Tooltip>
|
||||
<div slot="hover-target">
|
||||
{#if catchRecord.inHome}
|
||||
<span
|
||||
class="status-badge status-badge--caught absolute left-0.5 status-badge-top z-10"
|
||||
title="Caught"
|
||||
class="absolute -top-5 -right-4 z-2 p-1 text-secondary text-lg font-extrabold"
|
||||
>✓</span
|
||||
>
|
||||
<svg
|
||||
class="status-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<span class="sr-only">Caught</span>
|
||||
</span>
|
||||
{:else if catchRecord?.haveToEvolve}
|
||||
<span
|
||||
class="status-badge status-badge--evolve absolute left-0.5 status-badge-top z-10"
|
||||
title="Caught but needs to evolve"
|
||||
>
|
||||
<svg
|
||||
class="status-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M12 19V5" />
|
||||
<path d="M5 12l7-7 7 7" />
|
||||
</svg>
|
||||
<span class="sr-only">Caught but needs to evolve</span>
|
||||
</span>
|
||||
{/if}
|
||||
{#if catchRecord?.inHome}
|
||||
<span
|
||||
class="status-badge status-badge--home absolute right-0.5 status-badge-top z-10"
|
||||
title="In Pokémon HOME"
|
||||
>
|
||||
<svg
|
||||
class="status-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M12 3 3 10.5V21a1 1 0 0 0 1 1h5v-6h6v6h5a1 1 0 0 0 1-1V10.5L12 3Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">In HOME</span>
|
||||
</span>
|
||||
{/if}
|
||||
<div class="pokemon-box-inner">
|
||||
<PokemonSprite
|
||||
pokemonName={pokedexEntry.pokemon}
|
||||
pokedexNumber={pokedexEntry.pokedexNumber}
|
||||
form={pokedexEntry.form}
|
||||
spriteKey={pokedexEntry.spriteKey}
|
||||
shiny={showShiny}
|
||||
variant="grid"
|
||||
/>
|
||||
<span class="md:hidden">ⓘ</span>
|
||||
</div>
|
||||
</span>
|
||||
<span class="cell-tooltip-text" role="tooltip">
|
||||
<div slot="tooltip">
|
||||
<div class="font-bold">
|
||||
{pokedexEntry.pokemon}
|
||||
{pokedexEntry.form ? `(${pokedexEntry.form})` : ''}
|
||||
</div>
|
||||
<div>{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</div>
|
||||
<div>
|
||||
Caught: {catchRecord?.caught ? 'Yes' : 'No'} <br />
|
||||
Caught but needs to Evolve: {catchRecord?.haveToEvolve
|
||||
? 'Yes'
|
||||
: 'No'}
|
||||
<br />
|
||||
In Home: {catchRecord?.inHome ? 'Yes' : 'No'}
|
||||
Caught: {catchRecord.caught ? 'Yes' : 'No'} <br />
|
||||
Needs to Evolve: {catchRecord.haveToEvolve ? 'Yes' : 'No'} <br />
|
||||
In Home: {catchRecord.inHome ? 'Yes' : 'No'}
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="pokemon-box pokemon-box--empty"
|
||||
disabled
|
||||
style="grid-column-start: {placement.column}; grid-row-start: {placement.row};
|
||||
{cellBackgroundColourStyle(globalIndex, null)}"
|
||||
aria-label="Empty box slot"
|
||||
>
|
||||
<div class="pokemon-box-inner" aria-hidden="true">
|
||||
<span class="sprite-placeholder" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else if failedToLoad}
|
||||
{#if retryLoad}
|
||||
<p role="alert">Unable to load Pokédex.</p>
|
||||
<button class="btn" on:click={retryLoad}>Retry loading Pokédex</button>
|
||||
{:else if creatingRecords && totalRecordsCreated > 0}
|
||||
{#if creatingRecords && totalRecordsCreated > 0}
|
||||
<p>Processed {totalRecordsCreated} Pokédex entries so far...</p>
|
||||
<p>Please be patient, this may take some time.</p>
|
||||
{:else if creatingRecords}
|
||||
@@ -716,12 +124,8 @@
|
||||
If you're seeing this, you probably haven't created your Pokédex data yet. Please do so by
|
||||
clicking this button.
|
||||
</p>
|
||||
{#if !readOnly}
|
||||
<button class="btn" on:click={createCatchRecords}>Create Pokédex data</button>
|
||||
{/if}
|
||||
{/if}
|
||||
{:else if combinedData}
|
||||
<p>No entries match this Pokédex.</p>
|
||||
{:else}
|
||||
<div class="min-w-max mx-auto">
|
||||
<h1>Loading Pokédex</h1>
|
||||
@@ -732,187 +136,8 @@
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.box-shell {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
}
|
||||
.box-shell::before {
|
||||
content: '';
|
||||
display: block;
|
||||
padding-top: calc(83.333333% + 80px);
|
||||
}
|
||||
.box-content {
|
||||
position: absolute;
|
||||
inset: 0 0 32px;
|
||||
}
|
||||
.box-heading {
|
||||
height: 32px;
|
||||
}
|
||||
.cell-tooltip {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.cell-tooltip-text {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
pointer-events: none;
|
||||
background: #1f2937;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
width: 13rem;
|
||||
}
|
||||
.pokemon-box:hover .cell-tooltip-text,
|
||||
.pokemon-box:focus-visible .cell-tooltip-text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
Theme-aware backgrounds for non-caught box slots.
|
||||
- Light mode (`pokeball`) keeps the original exact colors.
|
||||
- Dark mode maps to DaisyUI theme base tokens so it stays consistent with the active theme.
|
||||
*/
|
||||
:global(:root) {
|
||||
--ld-box-bg-even: var(--fallback-b1, oklch(var(--b1) / 1));
|
||||
--ld-box-bg-odd: var(--fallback-b3, oklch(var(--b3) / 1));
|
||||
}
|
||||
|
||||
:global([data-theme='pokeball']) {
|
||||
--ld-box-bg-even: #ffffff;
|
||||
--ld-box-bg-odd: #f9f9f9;
|
||||
}
|
||||
|
||||
.boxes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.boxes-grid {
|
||||
grid-template-columns: repeat(var(--boxes-per-row), minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.pokemon-box {
|
||||
border: 1px solid #ddd;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
/*
|
||||
On small screens the grid columns get narrow; without an explicit ratio,
|
||||
the button height becomes content-driven (padding + sprite) and you end up
|
||||
with rectangular cells. Force each slot to be square.
|
||||
*/
|
||||
aspect-ratio: 1 / 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pokemon-box--filtered-out {
|
||||
opacity: 0.25;
|
||||
filter: grayscale(1);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.pokemon-box--empty {
|
||||
opacity: 0.55;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.pokemon-box-inner {
|
||||
padding: var(--cell-padding, 1rem);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pokemon-box :global(img) {
|
||||
width: var(--sprite-size, 64px);
|
||||
height: var(--sprite-size, 64px);
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.sprite-placeholder {
|
||||
width: var(--sprite-size, 64px);
|
||||
height: var(--sprite-size, 64px);
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Mobile: keep 6 columns but prevent sprite/padding from forcing tall cells */
|
||||
@media (max-width: 767px) {
|
||||
.pokemon-box-inner {
|
||||
/* cap padding so the sprite can fit inside small squares */
|
||||
padding: min(var(--cell-padding, 1rem), 0.35rem);
|
||||
}
|
||||
|
||||
.pokemon-box :global(img) {
|
||||
/* allow the sprite to shrink with the square cell */
|
||||
width: min(var(--sprite-size, 64px), 100%);
|
||||
height: min(var(--sprite-size, 64px), 100%);
|
||||
}
|
||||
|
||||
.sprite-placeholder {
|
||||
width: min(var(--sprite-size, 64px), 100%);
|
||||
height: min(var(--sprite-size, 64px), 100%);
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
width: 0.95rem;
|
||||
height: 0.95rem;
|
||||
}
|
||||
|
||||
.status-badge-top {
|
||||
top: 0.35rem;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
width: 0.85rem;
|
||||
height: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.15rem;
|
||||
height: 1.15rem;
|
||||
border-radius: 0.35rem;
|
||||
font-weight: 900;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.status-badge-top {
|
||||
top: 0.7rem;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.status-badge--caught {
|
||||
color: #16a34a; /* green-600 */
|
||||
}
|
||||
|
||||
.status-badge--evolve {
|
||||
color: #eab308; /* yellow-500 */
|
||||
}
|
||||
|
||||
.status-badge--home {
|
||||
color: #0284c7; /* sky-600 */
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import PokedexEntryCatchRecord from './PokedexEntryCatchRecord.svelte';
|
||||
import type { CombinedData } from '$lib/models/CombinedData';
|
||||
|
||||
export let showOrigins = true;
|
||||
export let showForms = true;
|
||||
export let showShiny = false;
|
||||
export let combinedData: CombinedData[] | null;
|
||||
export let creatingRecords = false;
|
||||
export let totalRecordsCreated = 0;
|
||||
export let failedToLoad = false;
|
||||
export let updateACatch = (event: any) => {};
|
||||
export let createCatchRecords = () => {};
|
||||
</script>
|
||||
|
||||
<main class="flex-1 p-4 w-full">
|
||||
<div class="max-w-min mx-auto">
|
||||
{#if combinedData && combinedData.length > 0}
|
||||
{#each combinedData as { pokedexEntry, catchRecord }}
|
||||
<PokedexEntryCatchRecord
|
||||
{pokedexEntry}
|
||||
{showOrigins}
|
||||
{showForms}
|
||||
{showShiny}
|
||||
bind:catchRecord
|
||||
on:updateCatch={updateACatch}
|
||||
/>
|
||||
{/each}
|
||||
{:else if failedToLoad}
|
||||
{#if creatingRecords && totalRecordsCreated > 0}
|
||||
<p>Processed {totalRecordsCreated} Pokédex entries so far...</p>
|
||||
<p>Please be patient, this may take some time.</p>
|
||||
{:else if creatingRecords}
|
||||
<p>Processing...</p>
|
||||
<p>Please be patient, this may take some time.</p>
|
||||
{:else}
|
||||
<h1>Failed to load</h1>
|
||||
<p>
|
||||
If you're seeing this, you probably haven't created your Pokédex data yet. Please do so by
|
||||
clicking this button.
|
||||
</p>
|
||||
<button class="btn" on:click={createCatchRecords}>Create Pokédex data</button>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="min-w-max mx-auto">
|
||||
<h1>Loading Pokédex</h1>
|
||||
<span class="loading loading-spinner loading-xl"></span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
||||
@@ -1,9 +1,8 @@
|
||||
// Supabase-based CatchRecord interface
|
||||
export interface CatchRecord {
|
||||
_id: string; // Maps to Supabase 'id' field for frontend compatibility
|
||||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface CatchRecord extends Document {
|
||||
userId: string;
|
||||
pokemonId: string;
|
||||
pokedexId: string;
|
||||
pokedexEntryId: Types.ObjectId;
|
||||
haveToEvolve: boolean;
|
||||
caught: boolean;
|
||||
inHome: boolean;
|
||||
@@ -11,18 +10,15 @@ export interface CatchRecord {
|
||||
personalNotes: string;
|
||||
}
|
||||
|
||||
// Database record type for Supabase
|
||||
export interface CatchRecordDB {
|
||||
id: string;
|
||||
userId: string;
|
||||
/** Numeric foreign key in database (references `pokemon.id`) */
|
||||
pokemonId: number;
|
||||
pokedexId: string; // NEW: Foreign key to pokedexes table
|
||||
haveToEvolve: boolean;
|
||||
caught: boolean;
|
||||
inHome: boolean;
|
||||
hasGigantamaxed: boolean;
|
||||
personalNotes: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
const catchRecordSchema = new Schema<CatchRecord>({
|
||||
userId: String,
|
||||
pokedexEntryId: { type: Schema.Types.ObjectId, ref: 'PokedexEntry', required: true },
|
||||
haveToEvolve: { type: Boolean, default: false },
|
||||
caught: { type: Boolean, default: false },
|
||||
inHome: { type: Boolean, default: false },
|
||||
hasGigantamaxed: { type: Boolean, default: false },
|
||||
personalNotes: String
|
||||
});
|
||||
|
||||
const CatchRecordModel = mongoose.model<CatchRecord>('CatchRecord', catchRecordSchema);
|
||||
export default CatchRecordModel;
|
||||
|
||||
@@ -3,5 +3,5 @@ import { type CatchRecord } from './CatchRecord';
|
||||
|
||||
export interface CombinedData {
|
||||
pokedexEntry: PokedexEntry;
|
||||
catchRecord: CatchRecord | null;
|
||||
catchRecord: CatchRecord;
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { CombinedData } from './CombinedData';
|
||||
import type { Pokedex } from './Pokedex';
|
||||
|
||||
export const OFFLINE_SNAPSHOT_VERSION = 1;
|
||||
|
||||
export type OfflinePokedexSnapshot = {
|
||||
pokedex: Pokedex;
|
||||
entries: CombinedData[];
|
||||
};
|
||||
|
||||
export type OfflineSnapshot = {
|
||||
version: typeof OFFLINE_SNAPSHOT_VERSION;
|
||||
generatedAt: string;
|
||||
userId: string;
|
||||
pokedexes: OfflinePokedexSnapshot[];
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
export interface Pokedex {
|
||||
_id: string;
|
||||
shareToken: string;
|
||||
userId: string;
|
||||
name: string;
|
||||
description: string;
|
||||
isLivingDex: boolean;
|
||||
isShinyDex: boolean;
|
||||
isOriginDex: boolean;
|
||||
isFormDex: boolean;
|
||||
gameScope: string | null;
|
||||
dexScopes: string[];
|
||||
}
|
||||
|
||||
export interface PokedexDB {
|
||||
id: string;
|
||||
shareToken: string;
|
||||
userId: string;
|
||||
name: string;
|
||||
description: string;
|
||||
isLivingDex: boolean;
|
||||
isShinyDex: boolean;
|
||||
isOriginDex: boolean;
|
||||
isFormDex: boolean;
|
||||
gameScope: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
@@ -1,42 +1,51 @@
|
||||
// Supabase-based PokedexEntry interface
|
||||
export type CatchInformationItem = {
|
||||
game: string;
|
||||
location: string;
|
||||
notes: string;
|
||||
};
|
||||
import mongoose, { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface PokedexEntry {
|
||||
_id: string; // Maps to Supabase 'id' field for frontend compatibility
|
||||
export interface PokedexEntry extends Document {
|
||||
pokedexNumber: number;
|
||||
boxPlacement: { box: number; row: number; column: number };
|
||||
boxPlacementForms: { box: number; row: number; column: number };
|
||||
pokemon: string;
|
||||
form: string;
|
||||
spriteKey: string;
|
||||
canGigantamax: boolean;
|
||||
regionToCatchIn: string;
|
||||
gamesToCatchIn: string[];
|
||||
regionToEvolveIn: string;
|
||||
evolutionInformation: string;
|
||||
catchInformation: Array<string | CatchInformationItem>;
|
||||
catchInformation: [
|
||||
{
|
||||
game: string;
|
||||
location: string;
|
||||
notes: string;
|
||||
// Note: Regional dex numbers stored in separate regional_dex_numbers table
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// Database record type for Supabase
|
||||
export interface PokedexEntryDB {
|
||||
id: number;
|
||||
pokedexNumber: number;
|
||||
pokemon: string;
|
||||
form: string | null;
|
||||
spriteKey: string | null;
|
||||
canGigantamax: boolean;
|
||||
isDefaultForm: boolean;
|
||||
regionToCatchIn: string | null;
|
||||
gamesToCatchIn: string[] | null;
|
||||
regionToEvolveIn: string | null;
|
||||
evolutionInformation: string | null;
|
||||
catchInformation: string[] | null;
|
||||
notes: string | null;
|
||||
// Note: Regional dex numbers stored in separate regional_dex_numbers table
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
const pokedexEntrySchema = new Schema<PokedexEntry>({
|
||||
pokedexNumber: Number,
|
||||
boxPlacement: {
|
||||
box: Number,
|
||||
row: Number,
|
||||
column: Number
|
||||
},
|
||||
boxPlacementForms: {
|
||||
box: Number,
|
||||
row: Number,
|
||||
column: Number
|
||||
},
|
||||
pokemon: String,
|
||||
form: String,
|
||||
canGigantamax: Boolean,
|
||||
regionToCatchIn: String,
|
||||
gamesToCatchIn: Array<string>,
|
||||
regionToEvolveIn: String,
|
||||
evolutionInformation: String,
|
||||
catchInformation: [
|
||||
{
|
||||
game: String,
|
||||
location: String,
|
||||
notes: String
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
export default mongoose.model<PokedexEntry>('PokedexEntry', pokedexEntrySchema);
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
export type ExportProvider = 'google_drive' | 'dropbox';
|
||||
|
||||
export interface PokedexExportIntegration {
|
||||
_id: string;
|
||||
userId: string;
|
||||
pokedexId: string | null;
|
||||
provider: ExportProvider;
|
||||
enabled: boolean;
|
||||
fileName: string | null;
|
||||
folderId: string | null;
|
||||
path: string | null;
|
||||
accessToken: string;
|
||||
refreshToken: string | null;
|
||||
accessTokenExpiresAt: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
lastExportedAt: string | null;
|
||||
lastError: string | null;
|
||||
/** Set by a database trigger on every write, so it doubles as the row's version. */
|
||||
updatedAt: string | null;
|
||||
}
|
||||
|
||||
export interface PokedexExportIntegrationDB {
|
||||
id: string;
|
||||
userId: string;
|
||||
pokedexId: string | null;
|
||||
provider: ExportProvider;
|
||||
enabled: boolean;
|
||||
fileName: string | null;
|
||||
folderId: string | null;
|
||||
path: string | null;
|
||||
accessToken: string;
|
||||
refreshToken: string | null;
|
||||
accessTokenExpiresAt: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
lastExportedAt: string | null;
|
||||
lastError: string | null;
|
||||
updatedAt: string | null;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import type { PokedexEntry } from './PokedexEntry';
|
||||
import type { CatchRecord } from './CatchRecord';
|
||||
|
||||
/** Complete ordering/status data, without detail text or repeated ownership fields. */
|
||||
export type PokedexGridRow = {
|
||||
pokedexEntry: Pick<
|
||||
PokedexEntry,
|
||||
'_id' | 'pokemon' | 'pokedexNumber' | 'form' | 'spriteKey' | 'canGigantamax'
|
||||
>;
|
||||
catchRecord: Pick<
|
||||
CatchRecord,
|
||||
'_id' | 'caught' | 'haveToEvolve' | 'inHome' | 'hasGigantamaxed'
|
||||
> | null;
|
||||
};
|
||||
export type CatchRecordPatch = Partial<CatchRecord> &
|
||||
Pick<CatchRecord, 'userId' | 'pokedexId' | 'pokemonId'>;
|
||||
|
||||
/** Version 1 transport rows: avoid repeating field names 1,000+ times. IDs remain available. */
|
||||
export type PackedGridRow = [
|
||||
entryId: string,
|
||||
number: number,
|
||||
name: string,
|
||||
form: string,
|
||||
spriteKey: string,
|
||||
canGigantamax: boolean,
|
||||
catchId: string | null,
|
||||
status: number
|
||||
];
|
||||
export function packGrid(rows: PokedexGridRow[]): PackedGridRow[] {
|
||||
return rows.map(({ pokedexEntry: e, catchRecord: c }) => [
|
||||
e._id,
|
||||
e.pokedexNumber,
|
||||
e.pokemon,
|
||||
e.form,
|
||||
e.spriteKey,
|
||||
e.canGigantamax,
|
||||
c?._id ?? null,
|
||||
(c?.caught ? 1 : 0) |
|
||||
(c?.haveToEvolve ? 2 : 0) |
|
||||
(c?.inHome ? 4 : 0) |
|
||||
(c?.hasGigantamaxed ? 8 : 0)
|
||||
]);
|
||||
}
|
||||
export function unpackGrid(rows: PackedGridRow[]): PokedexGridRow[] {
|
||||
return rows.map(([id, number, name, form, spriteKey, canGigantamax, catchId, status]) => ({
|
||||
pokedexEntry: { _id: id, pokedexNumber: number, pokemon: name, form, spriteKey, canGigantamax },
|
||||
catchRecord:
|
||||
catchId === null
|
||||
? null
|
||||
: {
|
||||
_id: catchId,
|
||||
caught: !!(status & 1),
|
||||
haveToEvolve: !!(status & 2),
|
||||
inHome: !!(status & 4),
|
||||
hasGigantamaxed: !!(status & 8)
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import mongoose, { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface PokedexMetadata extends Document {
|
||||
lastModified: Date;
|
||||
}
|
||||
|
||||
const pokedexMetadataSchema = new Schema<PokedexMetadata>({
|
||||
lastModified: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
}
|
||||
});
|
||||
|
||||
export default mongoose.model<PokedexMetadata>('PokedexMetadata', pokedexMetadataSchema);
|
||||
@@ -1,13 +1,13 @@
|
||||
// Supabase-based RegionGameMapping interface
|
||||
export interface RegionGameMapping {
|
||||
id?: number;
|
||||
import mongoose, { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface RegionGameMapping extends Document {
|
||||
region: string;
|
||||
game: string; // Note: Different from MongoDB version which had 'games' array
|
||||
games: string[];
|
||||
}
|
||||
|
||||
// Database record type for Supabase
|
||||
export interface RegionGameMappingDB {
|
||||
id: number;
|
||||
region: string;
|
||||
game: string;
|
||||
}
|
||||
const regionGameMappingSchema = new Schema<RegionGameMapping>({
|
||||
region: String,
|
||||
games: Array<string>
|
||||
});
|
||||
|
||||
export default mongoose.model<RegionGameMapping>('RegionGameMapping', regionGameMappingSchema);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user