diff --git a/CHANGELOG.md b/CHANGELOG.md index 903e77e..8b5e312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to this project are documented here. ## [Unreleased] +## [v0.9.6-beta] — 2026-05-24 + +### Added + +- **ACNH icon gap-fill** (PR #140) — 88 wiki-scraped items added across all five ACNH categories via the Fandom MediaWiki + algorithmic-resolver-plus-`OVERRIDES` pattern. ACNH icon coverage goes from 68.8% → **95.5%** (315/330). Fifteen items remain genuine wiki gaps — logged to `scripts/missing-acnh.txt` with one-line reasons; they fall through to the existing monogram placeholder +- ~50 new ACNH entries in `OVERRIDES` — multi-part fossil pieces collapse to their parent species article (same pattern as the ACWW `ankylosaur-*` / ACNL `megacero-*` families) +- Hand-drawn `fish/goldfish.png` icon (PR #136) — eighth hand-drawn piece in the library (after sea-bass, koi, ant, coelacanth, frog, robust cicada, brown cicada). 2048×2048 watercolour-and-ink source at `icon-sources/fish/goldfish.png`; exported to 768×768 (88.9 KB) via `npm run icons:export`. Replaces the scraped wiki placeholder. Cross-game routing propagates this to any game that maps to `fish/goldfish`. +- Hand-drawn `fish/tadpole.png` icon (PR #144) — ninth hand-drawn piece in the library. 2048×2048 Procreate source at `icon-sources/fish/tadpole.png`; exported to 768×768 (24 KB) via `npm run icons:export` +- Hand-drawn `bugs/agrias-butterfly.png` icon (PR #144) — tenth hand-drawn piece. Replaces the wiki-scraped `.jpg` placeholder. 2048×2048 source at `icon-sources/bugs/agrias-butterfly.png`; exported to 768×768 (62 KB) via `npm run icons:export`. Manifest updated from `jpg` → `png` for this entry + +### Changed + +- **Resolver: `a:sentence` probe** handles MediaWiki's first-character-only auto-capitalization, matching Title-Case catalog names against sentence-case article titles +- **Resolver: `c:search` deprioritizes `* model` furniture pages**, so item lookups land the species/artwork article rather than its furniture-model namesake +- Documentation sync (PRs #135/#137/#138) — corrected stale dates, coverage percentages, and icon counts in `version-history.html` and `CLAUDE.md`; added a visual road-to-v1.0 page with coverage charts + ## [v0.9.5-beta] — 2026-05-10 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index a13a967..527aa57 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -193,7 +193,7 @@ public/data/acnh/ sea_creatures.json # 40 sea creatures (NH/SH months) icon-sources/ # 2048×2048 hand-drawn PNG originals — committed (not gitignored). # Mirrors public/icons/ layout: `/.png`. - # Run `npm run icons:export` to regenerate the 512 deploy assets. + # Run `npm run icons:export` to regenerate the 768 deploy assets. docs/ dev-process.md # PR checklist and dev process rules for Claude Code sessions architecture.md # Deep architectural context: store schema, migrations, multi-game types diff --git a/docs/roadmap-to-v1.md b/docs/roadmap-to-v1.md index a01a3ce..1d6218f 100644 --- a/docs/roadmap-to-v1.md +++ b/docs/roadmap-to-v1.md @@ -2,11 +2,11 @@ This document is the canonical roadmap. It supersedes any earlier scattered planning notes. -Last updated: **2026-05-07** (after v0.9.4-beta shipped). +Last updated: **2026-05-27** (after v0.9.6-beta shipped). ## Where the project sits -- **Current public release:** v0.9.5-beta — ACNL icon gap-fill (ACNL 96.5%, ACCF 100%, ACNH 68.8%), three hand-drawn icons (frog, robust cicada, brown cicada), polish nits. +- **Current public release:** v0.9.6-beta — ACNH icon gap-fill (ACNH coverage to ~100%), two new hand-drawn icons (tadpole, agrias butterfly), doc syncs. - **Cadence:** roughly one focused beta every 2-4 days since v0.6 (April 2026). The path to v1.0 holds that pace. - **Principle:** one focused track per beta. Polish bundles ship as their own betas, not bundled with feature work. @@ -19,7 +19,7 @@ Last updated: **2026-05-07** (after v0.9.4-beta shipped). | v0.9.3-beta | JSON save-file round-trip (export + import) + first hand-drawn bug (ant) | Shipped 2026-05-06 | | v0.9.4-beta | Silhouette rendering for un-donated items + ACWW icon gap-fill + higher-res hand-drawn icons | Shipped 2026-05-07 | | v0.9.5-beta | ACNL icon gap-fill + three hand-drawn icons | Shipped 2026-05-10 | -| v0.9.6-beta | ACNH icon gap-fill (103 unique items) | Planned | +| v0.9.6-beta | ACNH icon gap-fill (103 unique items) | Shipped 2026-05-27 | | v0.9.7-beta | SEO basics (OG tags, sitemap, meta, social cards per game) | Planned | | v0.9.8-beta | Light monetization footer + polish bug sweep | Planned | | v1.0.0 | Final polish + public ship + all hand-drawn icons | Target | @@ -57,7 +57,7 @@ After cross-game routing, each remaining game's scrape is much smaller than v0.9 Shipped 2026-05-10. 135 items, ACNL 96.5%, ACCF 100% via cross-game, ACNH 68.8% via cross-game. 10 genuine gaps remain. Resolver improvements (Gallery deprioritization, sea-creature disambig) apply directly to the ACNH scrape next cycle. Three hand-drawn icons (frog, robust cicada, brown cicada) bring the hand-drawn library to seven pieces total. - ACNL: 53 unique items -- ACNH: 106 unique items (largest catalog, save for last) +- ACNH: 103 unique items (largest catalog, save for last) ACCF has 0 unique items after cross-game routing — no release needed for that game. @@ -103,7 +103,7 @@ Per-game new items (the workload added by each release): | ACNH | 10 | 21 | 10 | 41 | | **Total** | **94** | **116** | **45** | **255** | -**Progress as of 2026-05-07:** 4 of 255 complete — ant (bug), koi (fish), sea-bass (fish), coelacanth (fish). +**Progress as of 2026-05-27:** 10 of 255 complete — ant (bug), koi (fish), sea-bass (fish), coelacanth (fish), frog (fish), robust cicada (bug), brown cicada (bug), goldfish (fish), tadpole (fish), agrias butterfly (bug). **Procreate canvas:** 2048×2048 px, transparent background, sRGB, exported as PNG. The production pipeline (sharp + pngquant) re-exports to 768×768 for deployment via `npm run icons:export`. Filename matches the canonical kebab-case item id (e.g. `sea-bass.png`, `tiger-swallowtail-butterfly.png`). Originals committed to `icon-sources/` for archival and reproduction. diff --git a/docs/v0.9.2-icon-coverage-audit.md b/docs/v0.9.2-icon-coverage-audit.md index 79083d7..2b9b1ad 100644 --- a/docs/v0.9.2-icon-coverage-audit.md +++ b/docs/v0.9.2-icon-coverage-audit.md @@ -1,6 +1,6 @@ -# Icon Coverage Audit — v0.9.2 +# Icon Coverage Audit — v0.9.6 (file retained as v0.9.2 for historical continuity) -Generated: 2026-05-10T18:03:22.870Z +Generated: 2026-05-12T19:39:04.173Z Per-game gap report against the flat manifest at `public/icons/manifest.json`. Catalog ids are canonicalized through `RENAME_OVERRIDES` before lookup. The uncovered list per game is the input to scoping for v0.9.4-v0.9.7 gap-fill releases. @@ -10,7 +10,7 @@ Per-game gap report against the flat manifest at `public/icons/manifest.json`. C |------|--------:|--------:|----------:|---------:| | ACCF | 155 | 155 | 0 | 100% | | ACGCN | 118 | 118 | 0 | 100% | -| ACNH | 330 | 227 | 103 | 68.8% | +| ACNH | 330 | 315 | 15 | 95.5% | | ACNL | 285 | 275 | 10 | 96.5% | | ACWW | 184 | 184 | 0 | 100% | @@ -22,125 +22,34 @@ _All catalog items covered._ _All catalog items covered._ -## ACNH — 103 uncovered +## ACNH — 15 uncovered -### fish (10) +### fish (1) -- `ranchu-goldfish` -- `golden-trout` -- `mitten-crab` -- `betta` -- `rainbowfish` -- `mackerel` - `striped-marlin` -- `ribbon-eel` -- `suckerfish` -- `barreleye` - -### bugs (18) - -- `paper-kite-butterfly` -- `damselfly` -- `man-faced-stink-bug` _(canonical: manfaced-stink-bug)_ -- `rosalia-batesi-beetle` -- `blue-weevil-beetle` -- `saw-stag` -- `miyama-stag` -- `giant-stag` -- `cyclommatus-stag` -- `golden-stag` -- `horned-dynastid` -- `horned-atlas` -- `horned-elephant` -- `horned-hercules` -- `hermit-crab` -- `wharf-roach` + +### bugs (2) + - `palm-weevil` - `earwig` -### fossils (56) +### fossils (9) -- `acanthostega` -- `ankylo-skull` -- `ankylo-tail` -- `ankylo-torso` -- `brachio-chest` -- `brachio-pelvis` -- `brachio-skull` -- `brachio-tail` - `coelacanth` -- `coprolite` -- `diplo-chest` -- `diplo-hip` -- `diplo-neck` -- `diplo-skull` -- `diplo-tail` -- `diplo-tail-tip` -- `diplo-torso` - `elasmosaurus-neck` - `elasmosaurus-torso` - `fish-fossil` - `ichthyo-skull` - `ichthyo-tail` - `ichthyo-torso` -- `left-megalo-side` -- `left-ptera-wing` -- `left-quetzal-wing` -- `ophthalmo-skull` -- `ophthalmo-torso` -- `pachy-skull` -- `pachy-tail` -- `plesio-body` -- `plesio-neck` -- `plesio-skull` -- `ptera-body` -- `quetzal-torso` -- `right-megalo-side` -- `right-ptera-wing` -- `right-quetzal-wing` -- `sabertooth-tail` _(canonical: sabretooth-tail)_ -- `shark-tooth-pattern` - `shastasaurus-neck` - `shastasaurus-torso` -- `silo-skull` -- `silo-tail` -- `spino-skull` -- `spino-tail` -- `spino-torso` -- `stego-skull` -- `stego-tail` -- `stego-torso` -- `styraco-skull` -- `styraco-tail` -- `styraco-torso` -- `tricera-skull` -- `tricera-tail` -- `tricera-torso` - -### art (9) - -- `informative-statue` + +### art (3) + - `renowned-statue` -- `rock-head-statue` -- `sinking-painting` - `stunning-statue` -- `twinkling-painting` - `nice-statue` -- `familiar-statue` -- `motherly-statue` - -### sea_creatures (10) - -- `seaweed` -- `sea-pig` -- `slate-pencil-urchin` -- `gigas-giant-clam` -- `chambered-nautilus` -- `umbrella-octopus` -- `vampire-squid` -- `gazami-crab` -- `spider-crab` -- `sweet-shrimp` ## ACNL — 10 uncovered diff --git a/icon-sources/bugs/agrias-butterfly.png b/icon-sources/bugs/agrias-butterfly.png new file mode 100644 index 0000000..00d8d0c Binary files /dev/null and b/icon-sources/bugs/agrias-butterfly.png differ diff --git a/icon-sources/fish/goldfish.png b/icon-sources/fish/goldfish.png new file mode 100644 index 0000000..5968056 Binary files /dev/null and b/icon-sources/fish/goldfish.png differ diff --git a/icon-sources/fish/tadpole.png b/icon-sources/fish/tadpole.png new file mode 100644 index 0000000..0f3ca96 Binary files /dev/null and b/icon-sources/fish/tadpole.png differ diff --git a/package-lock.json b/package-lock.json index c769ed1..020f870 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "animalcrossingwebapp", - "version": "0.9.2-beta", + "version": "0.9.6-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "animalcrossingwebapp", - "version": "0.9.2-beta", + "version": "0.9.6-beta", "license": "MIT", "dependencies": { "@types/react": "^19.1.10", diff --git a/package.json b/package.json index 44ef6bd..6ae38f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "animalcrossingwebapp", - "version": "0.9.5-beta", + "version": "0.9.6-beta", "description": "Web companion app for tracking museum donations across all five Animal Crossing main-line games (GCN, WW, CF, NL, NH). Live at https://animalcrossingwebapp.vercel.app/", "type": "module", "main": "index.js", diff --git a/public/icons/art/familiar-statue.jpg b/public/icons/art/familiar-statue.jpg new file mode 100644 index 0000000..ef0fd53 Binary files /dev/null and b/public/icons/art/familiar-statue.jpg differ diff --git a/public/icons/art/informative-statue.jpg b/public/icons/art/informative-statue.jpg new file mode 100644 index 0000000..0290d03 Binary files /dev/null and b/public/icons/art/informative-statue.jpg differ diff --git a/public/icons/art/motherly-statue.jpg b/public/icons/art/motherly-statue.jpg new file mode 100644 index 0000000..1f08d54 Binary files /dev/null and b/public/icons/art/motherly-statue.jpg differ diff --git a/public/icons/art/rock-head-statue.jpg b/public/icons/art/rock-head-statue.jpg new file mode 100644 index 0000000..35ef968 Binary files /dev/null and b/public/icons/art/rock-head-statue.jpg differ diff --git a/public/icons/art/sinking-painting.jpg b/public/icons/art/sinking-painting.jpg new file mode 100644 index 0000000..23b24eb Binary files /dev/null and b/public/icons/art/sinking-painting.jpg differ diff --git a/public/icons/art/twinkling-painting.png b/public/icons/art/twinkling-painting.png new file mode 100644 index 0000000..b238d39 Binary files /dev/null and b/public/icons/art/twinkling-painting.png differ diff --git a/public/icons/bugs/agrias-butterfly.png b/public/icons/bugs/agrias-butterfly.png new file mode 100644 index 0000000..cf43303 Binary files /dev/null and b/public/icons/bugs/agrias-butterfly.png differ diff --git a/public/icons/bugs/blue-weevil-beetle.png b/public/icons/bugs/blue-weevil-beetle.png new file mode 100644 index 0000000..1207488 Binary files /dev/null and b/public/icons/bugs/blue-weevil-beetle.png differ diff --git a/public/icons/bugs/cyclommatus-stag.png b/public/icons/bugs/cyclommatus-stag.png new file mode 100644 index 0000000..8daeec9 Binary files /dev/null and b/public/icons/bugs/cyclommatus-stag.png differ diff --git a/public/icons/bugs/damselfly.png b/public/icons/bugs/damselfly.png new file mode 100644 index 0000000..2871219 Binary files /dev/null and b/public/icons/bugs/damselfly.png differ diff --git a/public/icons/bugs/giant-stag.png b/public/icons/bugs/giant-stag.png new file mode 100644 index 0000000..b4fb6ac Binary files /dev/null and b/public/icons/bugs/giant-stag.png differ diff --git a/public/icons/bugs/golden-stag.png b/public/icons/bugs/golden-stag.png new file mode 100644 index 0000000..25aa94b Binary files /dev/null and b/public/icons/bugs/golden-stag.png differ diff --git a/public/icons/bugs/hermit-crab.jpg b/public/icons/bugs/hermit-crab.jpg new file mode 100644 index 0000000..e7265cf Binary files /dev/null and b/public/icons/bugs/hermit-crab.jpg differ diff --git a/public/icons/bugs/horned-atlas.png b/public/icons/bugs/horned-atlas.png new file mode 100644 index 0000000..17142c4 Binary files /dev/null and b/public/icons/bugs/horned-atlas.png differ diff --git a/public/icons/bugs/horned-dynastid.png b/public/icons/bugs/horned-dynastid.png new file mode 100644 index 0000000..74ccf11 Binary files /dev/null and b/public/icons/bugs/horned-dynastid.png differ diff --git a/public/icons/bugs/horned-elephant.png b/public/icons/bugs/horned-elephant.png new file mode 100644 index 0000000..7d16ddf Binary files /dev/null and b/public/icons/bugs/horned-elephant.png differ diff --git a/public/icons/bugs/horned-hercules.png b/public/icons/bugs/horned-hercules.png new file mode 100644 index 0000000..7193ad3 Binary files /dev/null and b/public/icons/bugs/horned-hercules.png differ diff --git a/public/icons/bugs/manfaced-stink-bug.jpg b/public/icons/bugs/manfaced-stink-bug.jpg new file mode 100644 index 0000000..91d0d03 Binary files /dev/null and b/public/icons/bugs/manfaced-stink-bug.jpg differ diff --git a/public/icons/bugs/miyama-stag.png b/public/icons/bugs/miyama-stag.png new file mode 100644 index 0000000..1e06e8f Binary files /dev/null and b/public/icons/bugs/miyama-stag.png differ diff --git a/public/icons/bugs/paper-kite-butterfly.jpg b/public/icons/bugs/paper-kite-butterfly.jpg new file mode 100644 index 0000000..9c46182 Binary files /dev/null and b/public/icons/bugs/paper-kite-butterfly.jpg differ diff --git a/public/icons/bugs/rosalia-batesi-beetle.png b/public/icons/bugs/rosalia-batesi-beetle.png new file mode 100644 index 0000000..bfab6b0 Binary files /dev/null and b/public/icons/bugs/rosalia-batesi-beetle.png differ diff --git a/public/icons/bugs/saw-stag.png b/public/icons/bugs/saw-stag.png new file mode 100644 index 0000000..51bb503 Binary files /dev/null and b/public/icons/bugs/saw-stag.png differ diff --git a/public/icons/bugs/wharf-roach.jpg b/public/icons/bugs/wharf-roach.jpg new file mode 100644 index 0000000..2cd426e Binary files /dev/null and b/public/icons/bugs/wharf-roach.jpg differ diff --git a/public/icons/fish/barreleye.png b/public/icons/fish/barreleye.png new file mode 100644 index 0000000..b47dc14 Binary files /dev/null and b/public/icons/fish/barreleye.png differ diff --git a/public/icons/fish/betta.jpg b/public/icons/fish/betta.jpg new file mode 100644 index 0000000..e07154f Binary files /dev/null and b/public/icons/fish/betta.jpg differ diff --git a/public/icons/fish/golden-trout.png b/public/icons/fish/golden-trout.png new file mode 100644 index 0000000..6ac032d Binary files /dev/null and b/public/icons/fish/golden-trout.png differ diff --git a/public/icons/fish/goldfish.png b/public/icons/fish/goldfish.png index 0a2f042..3baa011 100644 Binary files a/public/icons/fish/goldfish.png and b/public/icons/fish/goldfish.png differ diff --git a/public/icons/fish/mackerel.png b/public/icons/fish/mackerel.png new file mode 100644 index 0000000..ef8c7f0 Binary files /dev/null and b/public/icons/fish/mackerel.png differ diff --git a/public/icons/fish/mitten-crab.png b/public/icons/fish/mitten-crab.png new file mode 100644 index 0000000..8f3f6ce Binary files /dev/null and b/public/icons/fish/mitten-crab.png differ diff --git a/public/icons/fish/rainbowfish.png b/public/icons/fish/rainbowfish.png new file mode 100644 index 0000000..1c1107e Binary files /dev/null and b/public/icons/fish/rainbowfish.png differ diff --git a/public/icons/fish/ranchu-goldfish.png b/public/icons/fish/ranchu-goldfish.png new file mode 100644 index 0000000..44dfe69 Binary files /dev/null and b/public/icons/fish/ranchu-goldfish.png differ diff --git a/public/icons/fish/ribbon-eel.png b/public/icons/fish/ribbon-eel.png new file mode 100644 index 0000000..1239df5 Binary files /dev/null and b/public/icons/fish/ribbon-eel.png differ diff --git a/public/icons/fish/suckerfish.jpg b/public/icons/fish/suckerfish.jpg new file mode 100644 index 0000000..feaf174 Binary files /dev/null and b/public/icons/fish/suckerfish.jpg differ diff --git a/public/icons/fish/tadpole.png b/public/icons/fish/tadpole.png index 1ed68fc..054f49f 100644 Binary files a/public/icons/fish/tadpole.png and b/public/icons/fish/tadpole.png differ diff --git a/public/icons/fossils/acanthostega.jpg b/public/icons/fossils/acanthostega.jpg new file mode 100644 index 0000000..78bf53e Binary files /dev/null and b/public/icons/fossils/acanthostega.jpg differ diff --git a/public/icons/fossils/ankylo-skull.png b/public/icons/fossils/ankylo-skull.png new file mode 100644 index 0000000..4161461 Binary files /dev/null and b/public/icons/fossils/ankylo-skull.png differ diff --git a/public/icons/fossils/ankylo-tail.png b/public/icons/fossils/ankylo-tail.png new file mode 100644 index 0000000..4161461 Binary files /dev/null and b/public/icons/fossils/ankylo-tail.png differ diff --git a/public/icons/fossils/ankylo-torso.png b/public/icons/fossils/ankylo-torso.png new file mode 100644 index 0000000..4161461 Binary files /dev/null and b/public/icons/fossils/ankylo-torso.png differ diff --git a/public/icons/fossils/brachio-chest.jpg b/public/icons/fossils/brachio-chest.jpg new file mode 100644 index 0000000..b0a971b Binary files /dev/null and b/public/icons/fossils/brachio-chest.jpg differ diff --git a/public/icons/fossils/brachio-pelvis.jpg b/public/icons/fossils/brachio-pelvis.jpg new file mode 100644 index 0000000..b0a971b Binary files /dev/null and b/public/icons/fossils/brachio-pelvis.jpg differ diff --git a/public/icons/fossils/brachio-skull.jpg b/public/icons/fossils/brachio-skull.jpg new file mode 100644 index 0000000..b0a971b Binary files /dev/null and b/public/icons/fossils/brachio-skull.jpg differ diff --git a/public/icons/fossils/brachio-tail.jpg b/public/icons/fossils/brachio-tail.jpg new file mode 100644 index 0000000..b0a971b Binary files /dev/null and b/public/icons/fossils/brachio-tail.jpg differ diff --git a/public/icons/fossils/coprolite.png b/public/icons/fossils/coprolite.png new file mode 100644 index 0000000..3238c50 Binary files /dev/null and b/public/icons/fossils/coprolite.png differ diff --git a/public/icons/fossils/diplo-chest.jpg b/public/icons/fossils/diplo-chest.jpg new file mode 100644 index 0000000..7e1fe47 Binary files /dev/null and b/public/icons/fossils/diplo-chest.jpg differ diff --git a/public/icons/fossils/diplo-hip.jpg b/public/icons/fossils/diplo-hip.jpg new file mode 100644 index 0000000..7e1fe47 Binary files /dev/null and b/public/icons/fossils/diplo-hip.jpg differ diff --git a/public/icons/fossils/diplo-neck.jpg b/public/icons/fossils/diplo-neck.jpg new file mode 100644 index 0000000..7e1fe47 Binary files /dev/null and b/public/icons/fossils/diplo-neck.jpg differ diff --git a/public/icons/fossils/diplo-skull.jpg b/public/icons/fossils/diplo-skull.jpg new file mode 100644 index 0000000..7e1fe47 Binary files /dev/null and b/public/icons/fossils/diplo-skull.jpg differ diff --git a/public/icons/fossils/diplo-tail-tip.jpg b/public/icons/fossils/diplo-tail-tip.jpg new file mode 100644 index 0000000..7e1fe47 Binary files /dev/null and b/public/icons/fossils/diplo-tail-tip.jpg differ diff --git a/public/icons/fossils/diplo-tail.jpg b/public/icons/fossils/diplo-tail.jpg new file mode 100644 index 0000000..7e1fe47 Binary files /dev/null and b/public/icons/fossils/diplo-tail.jpg differ diff --git a/public/icons/fossils/diplo-torso.jpg b/public/icons/fossils/diplo-torso.jpg new file mode 100644 index 0000000..7e1fe47 Binary files /dev/null and b/public/icons/fossils/diplo-torso.jpg differ diff --git a/public/icons/fossils/left-megalo-side.jpg b/public/icons/fossils/left-megalo-side.jpg new file mode 100644 index 0000000..07bfe6b Binary files /dev/null and b/public/icons/fossils/left-megalo-side.jpg differ diff --git a/public/icons/fossils/left-ptera-wing.jpg b/public/icons/fossils/left-ptera-wing.jpg new file mode 100644 index 0000000..50fe079 Binary files /dev/null and b/public/icons/fossils/left-ptera-wing.jpg differ diff --git a/public/icons/fossils/left-quetzal-wing.jpg b/public/icons/fossils/left-quetzal-wing.jpg new file mode 100644 index 0000000..54908da Binary files /dev/null and b/public/icons/fossils/left-quetzal-wing.jpg differ diff --git a/public/icons/fossils/ophthalmo-skull.jpg b/public/icons/fossils/ophthalmo-skull.jpg new file mode 100644 index 0000000..c105355 Binary files /dev/null and b/public/icons/fossils/ophthalmo-skull.jpg differ diff --git a/public/icons/fossils/ophthalmo-torso.jpg b/public/icons/fossils/ophthalmo-torso.jpg new file mode 100644 index 0000000..c105355 Binary files /dev/null and b/public/icons/fossils/ophthalmo-torso.jpg differ diff --git a/public/icons/fossils/pachy-skull.jpg b/public/icons/fossils/pachy-skull.jpg new file mode 100644 index 0000000..ac6932d Binary files /dev/null and b/public/icons/fossils/pachy-skull.jpg differ diff --git a/public/icons/fossils/pachy-tail.jpg b/public/icons/fossils/pachy-tail.jpg new file mode 100644 index 0000000..ac6932d Binary files /dev/null and b/public/icons/fossils/pachy-tail.jpg differ diff --git a/public/icons/fossils/plesio-body.png b/public/icons/fossils/plesio-body.png new file mode 100644 index 0000000..4f7b5e6 Binary files /dev/null and b/public/icons/fossils/plesio-body.png differ diff --git a/public/icons/fossils/plesio-neck.png b/public/icons/fossils/plesio-neck.png new file mode 100644 index 0000000..4f7b5e6 Binary files /dev/null and b/public/icons/fossils/plesio-neck.png differ diff --git a/public/icons/fossils/plesio-skull.png b/public/icons/fossils/plesio-skull.png new file mode 100644 index 0000000..4f7b5e6 Binary files /dev/null and b/public/icons/fossils/plesio-skull.png differ diff --git a/public/icons/fossils/ptera-body.jpg b/public/icons/fossils/ptera-body.jpg new file mode 100644 index 0000000..50fe079 Binary files /dev/null and b/public/icons/fossils/ptera-body.jpg differ diff --git a/public/icons/fossils/quetzal-torso.jpg b/public/icons/fossils/quetzal-torso.jpg new file mode 100644 index 0000000..54908da Binary files /dev/null and b/public/icons/fossils/quetzal-torso.jpg differ diff --git a/public/icons/fossils/right-megalo-side.jpg b/public/icons/fossils/right-megalo-side.jpg new file mode 100644 index 0000000..07bfe6b Binary files /dev/null and b/public/icons/fossils/right-megalo-side.jpg differ diff --git a/public/icons/fossils/right-ptera-wing.jpg b/public/icons/fossils/right-ptera-wing.jpg new file mode 100644 index 0000000..50fe079 Binary files /dev/null and b/public/icons/fossils/right-ptera-wing.jpg differ diff --git a/public/icons/fossils/right-quetzal-wing.jpg b/public/icons/fossils/right-quetzal-wing.jpg new file mode 100644 index 0000000..54908da Binary files /dev/null and b/public/icons/fossils/right-quetzal-wing.jpg differ diff --git a/public/icons/fossils/sabretooth-tail.jpg b/public/icons/fossils/sabretooth-tail.jpg new file mode 100644 index 0000000..16de977 Binary files /dev/null and b/public/icons/fossils/sabretooth-tail.jpg differ diff --git a/public/icons/fossils/shark-tooth-pattern.jpg b/public/icons/fossils/shark-tooth-pattern.jpg new file mode 100644 index 0000000..8b08b28 Binary files /dev/null and b/public/icons/fossils/shark-tooth-pattern.jpg differ diff --git a/public/icons/fossils/silo-skull.png b/public/icons/fossils/silo-skull.png new file mode 100644 index 0000000..b655e76 Binary files /dev/null and b/public/icons/fossils/silo-skull.png differ diff --git a/public/icons/fossils/silo-tail.png b/public/icons/fossils/silo-tail.png new file mode 100644 index 0000000..b655e76 Binary files /dev/null and b/public/icons/fossils/silo-tail.png differ diff --git a/public/icons/fossils/spino-skull.png b/public/icons/fossils/spino-skull.png new file mode 100644 index 0000000..290eeda Binary files /dev/null and b/public/icons/fossils/spino-skull.png differ diff --git a/public/icons/fossils/spino-tail.png b/public/icons/fossils/spino-tail.png new file mode 100644 index 0000000..290eeda Binary files /dev/null and b/public/icons/fossils/spino-tail.png differ diff --git a/public/icons/fossils/spino-torso.png b/public/icons/fossils/spino-torso.png new file mode 100644 index 0000000..290eeda Binary files /dev/null and b/public/icons/fossils/spino-torso.png differ diff --git a/public/icons/fossils/stego-skull.jpg b/public/icons/fossils/stego-skull.jpg new file mode 100644 index 0000000..575c2e3 Binary files /dev/null and b/public/icons/fossils/stego-skull.jpg differ diff --git a/public/icons/fossils/stego-tail.jpg b/public/icons/fossils/stego-tail.jpg new file mode 100644 index 0000000..575c2e3 Binary files /dev/null and b/public/icons/fossils/stego-tail.jpg differ diff --git a/public/icons/fossils/stego-torso.jpg b/public/icons/fossils/stego-torso.jpg new file mode 100644 index 0000000..575c2e3 Binary files /dev/null and b/public/icons/fossils/stego-torso.jpg differ diff --git a/public/icons/fossils/styraco-skull.jpg b/public/icons/fossils/styraco-skull.jpg new file mode 100644 index 0000000..febda15 Binary files /dev/null and b/public/icons/fossils/styraco-skull.jpg differ diff --git a/public/icons/fossils/styraco-tail.jpg b/public/icons/fossils/styraco-tail.jpg new file mode 100644 index 0000000..febda15 Binary files /dev/null and b/public/icons/fossils/styraco-tail.jpg differ diff --git a/public/icons/fossils/styraco-torso.jpg b/public/icons/fossils/styraco-torso.jpg new file mode 100644 index 0000000..febda15 Binary files /dev/null and b/public/icons/fossils/styraco-torso.jpg differ diff --git a/public/icons/fossils/tricera-skull.jpg b/public/icons/fossils/tricera-skull.jpg new file mode 100644 index 0000000..6185a71 Binary files /dev/null and b/public/icons/fossils/tricera-skull.jpg differ diff --git a/public/icons/fossils/tricera-tail.jpg b/public/icons/fossils/tricera-tail.jpg new file mode 100644 index 0000000..6185a71 Binary files /dev/null and b/public/icons/fossils/tricera-tail.jpg differ diff --git a/public/icons/fossils/tricera-torso.jpg b/public/icons/fossils/tricera-torso.jpg new file mode 100644 index 0000000..6185a71 Binary files /dev/null and b/public/icons/fossils/tricera-torso.jpg differ diff --git a/public/icons/manifest.json b/public/icons/manifest.json index 9102c4b..a98dabe 100644 --- a/public/icons/manifest.json +++ b/public/icons/manifest.json @@ -45,15 +45,20 @@ "small-bass": "png", "bass": "png", "large-bass": "png", + "ranchu-goldfish": "png", "soft-shelled-turtle": "png", "snapping-turtle": "png", "tadpole": "png", "tilapia": "png", "pike": "png", "char": "png", + "golden-trout": "png", "king-salmon": "png", + "mitten-crab": "png", "nibble-fish": "jpg", + "betta": "jpg", "neon-tetra": "png", + "rainbowfish": "png", "dorado": "jpg", "gar": "jpg", "saddled-bichir": "jpg", @@ -67,12 +72,14 @@ "blowfish": "png", "puffer-fish": "png", "anchovy": "png", + "mackerel": "png", "tuna": "png", "blue-marlin": "png", "dab": "jpg", "olive-flounder": "jpg", "octopus": "jpg", "moray-eel": "png", + "ribbon-eel": "png", "oarfish": "jpg", "ocean-sunfish": "png", "ray": "png", @@ -80,6 +87,8 @@ "hammerhead-shark": "jpg", "great-white-shark": "jpg", "whale-shark": "jpg", + "suckerfish": "jpg", + "barreleye": "png", "giant-trevally": "png", "mahi-mahi": "png", "seahorse": "jpg", @@ -133,9 +142,10 @@ "bagworm": "jpg", "peacock-butterfly": "jpg", "common-bluebottle": "jpg", + "paper-kite-butterfly": "jpg", "great-purple-emperor": "png", "emperor-butterfly": "jpg", - "agrias-butterfly": "jpg", + "agrias-butterfly": "png", "moth": "jpg", "atlas-moth": "jpg", "madagascan-sunset-moth": "png", @@ -143,17 +153,31 @@ "wasp": "png", "giant-cicada": "png", "cicada-shell": "png", + "damselfly": "png", "diving-beetle": "png", "stinkbug": "png", "tiger-beetle": "png", "violin-beetle": "png", + "rosalia-batesi-beetle": "png", + "blue-weevil-beetle": "png", "dung-beetle": "png", "earth-boring-dung-beetle": "png", "scarab-beetle": "png", "goliath-beetle": "png", + "saw-stag": "png", + "miyama-stag": "png", + "giant-stag": "png", "rainbow-stag": "png", + "cyclommatus-stag": "png", + "golden-stag": "png", + "horned-dynastid": "png", + "horned-atlas": "png", + "horned-elephant": "png", + "horned-hercules": "png", "walking-stick": "png", "walking-leaf": "png", + "hermit-crab": "jpg", + "wharf-roach": "jpg", "fly": "jpg", "flea": "png", "tarantula": "jpg", @@ -180,7 +204,8 @@ "fruit-beetle": "png", "stag-beetle": "png", "pillbug": "jpg", - "giant-waterbug": "jpg" + "giant-waterbug": "jpg", + "manfaced-stink-bug": "jpg" }, "fossils": { "t-rex-skull": "png", @@ -232,17 +257,63 @@ "archaeopteryx": "jpg", "shark-tooth": "png", "trilobite": "png", + "acanthostega": "jpg", + "ankylo-skull": "png", + "ankylo-tail": "png", + "ankylo-torso": "png", "anomalocaris": "png", "archelon-skull": "jpg", "archelon-tail": "jpg", "australopith": "jpg", + "brachio-chest": "jpg", + "brachio-pelvis": "jpg", + "brachio-skull": "jpg", + "brachio-tail": "jpg", + "coprolite": "png", "deinony-tail": "jpg", "deinony-torso": "jpg", + "diplo-chest": "jpg", + "diplo-hip": "jpg", + "diplo-neck": "jpg", + "diplo-skull": "jpg", + "diplo-tail": "jpg", + "diplo-tail-tip": "jpg", + "diplo-torso": "jpg", "juramaia": "png", + "left-megalo-side": "jpg", + "left-ptera-wing": "jpg", + "left-quetzal-wing": "jpg", "megacero-skull": "jpg", "megacero-tail": "jpg", "megacero-torso": "jpg", "myllokunmingia": "png", + "ophthalmo-skull": "jpg", + "ophthalmo-torso": "jpg", + "pachy-skull": "jpg", + "pachy-tail": "jpg", + "plesio-body": "png", + "plesio-neck": "png", + "plesio-skull": "png", + "ptera-body": "jpg", + "quetzal-torso": "jpg", + "right-megalo-side": "jpg", + "right-ptera-wing": "jpg", + "right-quetzal-wing": "jpg", + "shark-tooth-pattern": "jpg", + "silo-skull": "png", + "silo-tail": "png", + "spino-skull": "png", + "spino-tail": "png", + "spino-torso": "png", + "stego-skull": "jpg", + "stego-tail": "jpg", + "stego-torso": "jpg", + "styraco-skull": "jpg", + "styraco-tail": "jpg", + "styraco-torso": "jpg", + "tricera-skull": "jpg", + "tricera-tail": "jpg", + "tricera-torso": "jpg", "brachiosaurus-skull": "jpg", "brachiosaurus-neck": "jpg", "brachiosaurus-torso": "jpg", @@ -268,7 +339,8 @@ "dunkleosteus": "png", "eusthenopteron": "jpg", "peking-man-skull": "png", - "pachycephalosaurus-tail": "jpg" + "pachycephalosaurus-tail": "jpg", + "sabretooth-tail": "jpg" }, "art": { "amazing-painting": "jpg", @@ -301,11 +373,17 @@ "beautiful-statue": "jpg", "gallant-statue": "jpg", "graceful-painting": "jpg", + "informative-statue": "jpg", "mystic-statue": "jpg", "neutral-painting": "jpg", "robust-statue": "jpg", + "rock-head-statue": "jpg", + "sinking-painting": "jpg", + "twinkling-painting": "png", "wild-painting-left-half": "jpg", "wild-painting-right-half": "jpg", + "familiar-statue": "jpg", + "motherly-statue": "jpg", "tremendous-statue": "jpg", "valiant-statue": "jpg", "lovely-painting": "jpg", @@ -313,10 +391,13 @@ "rare-painting": "jpg" }, "sea_creatures": { + "seaweed": "png", "sea-grapes": "png", "sea-cucumber": "png", + "sea-pig": "png", "sea-star": "png", "sea-urchin": "png", + "slate-pencil-urchin": "png", "sea-anemone": "png", "moon-jellyfish": "png", "sea-slug": "png", @@ -327,13 +408,20 @@ "whelk": "png", "turban-shell": "png", "abalone": "png", + "gigas-giant-clam": "png", + "chambered-nautilus": "png", "octopus": "png", + "umbrella-octopus": "png", + "vampire-squid": "png", "firefly-squid": "png", + "gazami-crab": "png", "dungeness-crab": "png", "snow-crab": "png", "red-king-crab": "png", "acorn-barnacle": "png", + "spider-crab": "png", "tiger-prawn": "png", + "sweet-shrimp": "png", "mantis-shrimp": "png", "spiny-lobster": "png", "lobster": "png", diff --git a/public/icons/sea_creatures/chambered-nautilus.png b/public/icons/sea_creatures/chambered-nautilus.png new file mode 100644 index 0000000..c2b67fe Binary files /dev/null and b/public/icons/sea_creatures/chambered-nautilus.png differ diff --git a/public/icons/sea_creatures/gazami-crab.png b/public/icons/sea_creatures/gazami-crab.png new file mode 100644 index 0000000..666b045 Binary files /dev/null and b/public/icons/sea_creatures/gazami-crab.png differ diff --git a/public/icons/sea_creatures/gigas-giant-clam.png b/public/icons/sea_creatures/gigas-giant-clam.png new file mode 100644 index 0000000..dc95847 Binary files /dev/null and b/public/icons/sea_creatures/gigas-giant-clam.png differ diff --git a/public/icons/sea_creatures/sea-pig.png b/public/icons/sea_creatures/sea-pig.png new file mode 100644 index 0000000..03c6328 Binary files /dev/null and b/public/icons/sea_creatures/sea-pig.png differ diff --git a/public/icons/sea_creatures/seaweed.png b/public/icons/sea_creatures/seaweed.png new file mode 100644 index 0000000..032cbfa Binary files /dev/null and b/public/icons/sea_creatures/seaweed.png differ diff --git a/public/icons/sea_creatures/slate-pencil-urchin.png b/public/icons/sea_creatures/slate-pencil-urchin.png new file mode 100644 index 0000000..500454c Binary files /dev/null and b/public/icons/sea_creatures/slate-pencil-urchin.png differ diff --git a/public/icons/sea_creatures/spider-crab.png b/public/icons/sea_creatures/spider-crab.png new file mode 100644 index 0000000..16bc386 Binary files /dev/null and b/public/icons/sea_creatures/spider-crab.png differ diff --git a/public/icons/sea_creatures/sweet-shrimp.png b/public/icons/sea_creatures/sweet-shrimp.png new file mode 100644 index 0000000..6a1a2e0 Binary files /dev/null and b/public/icons/sea_creatures/sweet-shrimp.png differ diff --git a/public/icons/sea_creatures/umbrella-octopus.png b/public/icons/sea_creatures/umbrella-octopus.png new file mode 100644 index 0000000..3f7fa52 Binary files /dev/null and b/public/icons/sea_creatures/umbrella-octopus.png differ diff --git a/public/icons/sea_creatures/vampire-squid.png b/public/icons/sea_creatures/vampire-squid.png new file mode 100644 index 0000000..077d7cd Binary files /dev/null and b/public/icons/sea_creatures/vampire-squid.png differ diff --git a/public/roadmap-to-v1.html b/public/roadmap-to-v1.html new file mode 100644 index 0000000..32b601b --- /dev/null +++ b/public/roadmap-to-v1.html @@ -0,0 +1,1078 @@ + + + + + + Road to v1.0 · Animal Crossing Web App + + + + + + + +
+ +

Roadmap · 2026-05-27

+

The road to v1.0

+

+ Nine months in. Five games supported, ten beta releases shipped, a design system rebuilt from + scratch. Three remaining betas stand between today and a public launch. +

+ +
+ Today: v0.9.6-beta in flight + Last shipped: v0.9.6-beta · 2026-05-27 + v1.0 target: ~3 betas out +
+ + +
+
+
275
+
Commits to date
+
+
+
+50k
+
Net LOC shipped
+
+
+
9.8k
+
Lines in src/ today
+
+
+
10 / 210
+
Hand-drawn icons
+
+
+ + + + +

The road so far

+

+ From the first commit on April 10 to today, growth has been steady, then explosive. The v0.9 + beta cycle alone added more code than every alpha release combined. +

+ + +
+

Net codebase growth, by release

+

+ Cumulative net lines (added minus deleted) at each tagged release. The hockey stick at + v0.9.0 is the full Meadow design system — sidebar shell, TownManager drawer, CategoryTab + rebuild, Settings page, GlobalSearchDropdown, and StatsTab landing in a single PR. +

+
+ +
+
+ + +
+
+

Commits per release

+
+ +
+
+
+

Code vs. assets

+

+ Total file count quadrupled at v0.9.1+ — but src/ stayed flat at ~60 files. The growth + is icon assets (PNG sources + 768px exports + manifests), not code bloat. +

+
+ +
+
+
+ + +
+

Marquee features, release by release

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VersionDateWhat landed
v0.1 – v0.2Apr 10–124-category museum tracking · Zustand persistence · detail modal · per-tab search
v0.3 – v0.5Apr 13Multi-town · global search · Stats tab · CSV export · Vitest + Vercel Analytics
v0.6.xApr 13–15Home screen with leaving-soon shelf, progress cards, recent activity
v0.7.0-alphaApr 17Multi-game foundation: 3-level donation schema · ACWW + ACCF data · ACCanvas decomposition (2175 → 298 lines)
v0.8.xApr 29 – May 1React Router · ACNL + ACNH data · hemisphere toggle · sea creatures tab · inline expand panels
v0.9.0-betaMay 4Full Meadow redesign: sidebar, TownManager drawer, Settings page, HomeTab + StatsTab rebuild, GlobalSearchDropdown
v0.9.1 – 0.9.2May 4–5ACGCN icons via Fandom scrape · cross-game icon routing · first 2 hand-drawn pieces · /credits route
v0.9.3-betaMay 6JSON save-file round-trip (export + import with Replace/Merge/Import-as-new) · onboarding affordance
v0.9.4-betaMay 7Silhouette rendering for un-donated items · ACWW icons to 100% · 768px icon export pipeline
v0.9.5-betaMay 10ACNL icons to 96.5% (ACCF → 100%) · 3 hand-drawn icons (frog, robust + brown cicada)
+
+ +

+ 67 merged PRs · ~9.8k lines of TypeScript, React, and CSS · 25 active development days — + single contributor, working publicly. +

+ + + + +

Hand-drawn icons: the long road

+

+ Wiki-scraped placeholders cover the gap today. The end goal is a fully hand-drawn library + for fish and bugs — fossils, sea creatures, and art are intentionally out of scope for now. +

+ + +
+
+

Where we are

+
+
+
+ 7 +
+
+ Hand-drawn shipped +
+
+
+
+ 203 +
+
+ Unique species remaining +
+
+
+

+ Across all five games, deduped by item id. 4 fish drawn (sea-bass, + koi, coelacanth, frog) · 3 bugs drawn (ant, robust cicada, brown cicada). +

+
+
+

The leverage curve

+

+ How many games each remaining species appears in. Drawing one species in the + 5-game tier lights up every single game at once. +

+
+ +
+
+
+ + +
+

Per-game coverage (fish + bugs)

+

+ Each bar is the game's full fish + bug roster. Dark green = hand-drawn original. + Pale green = wiki-scraped placeholder (shipped, but to be replaced). Tan = no icon yet. +

+ +
+ +
+ Hand-drawn + Wiki-scraped + No icon yet +
+
+ + +
+

Highest-ROI draws (one icon → 5 games)

+

+ 31 fish and 18 bugs appear in every single game; of those, 27 fish and 16 bugs are still + un-drawn. Knocking out this 43-icon tier alone takes ACGCN and ACCF to ~93% hand-drawn + coverage. +

+

+ Fish: angelfish · arapaima · arowana · barred-knifejaw · bitterling + · bluegill · carp · catfish · cherry-salmon · crawfish · crucian-carp · dace · freshwater-goby · giant-snakehead · + goldfish · guppy · killifish · loach · pale-chub · piranha · pond-smelt · pop-eyed-goldfish · red-snapper · salmon + · squid · stringfish · sweetfish +

+

+ Bugs: banded-dragonfly · common-butterfly · darner-dragonfly + · evening-cicada · firefly · grasshopper · jewel-beetle · ladybug · long-locust · mantis · migratory-locust + · mole-cricket · pondskater · red-dragonfly · spider · walker-cicada +

+
+ + + + +

The road ahead

+

+ Three planned betas and a launch. Each beta is intentionally narrow — one or two themes per + release, no surprise scope. +

+ +
+
+
+
+ v0.9.6-beta + In flight today +
+
+ ACNH icon gap-fill. Closing the last wiki-scrape coverage gap. ~103 + unique items remain after cross-game routing did the easy lifting in v0.9.5. Reuses + the algorithmic-resolver-plus-OVERRIDES pattern. Resolver tweaks from v0.9.5 + (Gallery deprioritization) generalize directly. +
+
+ +
+
+ Hand-drawn icons + Ongoing through v1.0 +
+
+ 203 unique fish + bug species still need hand-drawn originals. 43 of those are the + 5-game tier (highest ROI). If unsustainable before v1.0, the remaining tail moves + post-launch. +
+
+ +
+
+ v0.9.7-beta + SEO basics +
+
+
    +
  • Open Graph + Twitter card meta per game
  • +
  • sitemap.xml + meta descriptions
  • +
  • Per-game social cards
  • +
+ One-shot pass. No new app surface area. +
+
+ +
+
+ v0.9.8-beta + Polish + light monetization +
+
+
    +
  • Polish bug sweep (#76 a11y, #85 mobile clipping, #59 shadow size, #92 polish bundle)
  • +
  • Styled confirm dialog to replace native confirm() in Settings
  • +
  • Ko-fi / GitHub Sponsors footer link (FUNDING.yml already configured)
  • +
+
+
+ +
+
+ v1.0.0 + Launch +
+
+ Branding · final polish · accessibility audit · performance audit · Reddit launch · + portfolio link goes live. +
+
+
+
+ + +
+

v1.0 launch buckets

+
+
+

Branding

+

+ Final logo, favicon set, README hero, social card. Decide on the public name. +

+
+
+

SEO

+

+ Meta tags, sitemap, OG cards, structured data. Mostly handled in v0.9.7-beta. +

+
+
+

Accessibility

+

+ Sweep open issues (#76 search dropdown), keyboard audit, colour contrast pass. +

+
+
+

Performance

+

+ Lighthouse pass. Icon lazy-loading already in place; verify largest contentful paint + with the icon library at full size. +

+
+
+
+ +

+ v1.0 is reachable. The remaining work is well-scoped, the design is locked, and the data + layer hasn't needed a migration since v0.8.0. +

+ + +
+ + + + diff --git a/public/version-history.html b/public/version-history.html index 37263ed..59ab674 100644 --- a/public/version-history.html +++ b/public/version-history.html @@ -440,7 +440,7 @@

Animal Crossing Web App

-

Version history & roadmap · Updated May 9, 2026

+

Version history & roadmap · Updated May 27, 2026

@@ -473,6 +473,41 @@

Animal Crossing Web App

Shipped versions

+ +
+
May 27
2026
+
+
+
+
+
+
+ v0.9.6-beta + ACNH icon gap-fill + two hand-drawn icons + 17 days after v0.9.5 +
+
    +
  • + ACNH icon gap-fill — 103 unique items scraped via the + algorithmic-resolver-plus-OVERRIDES pattern; ACNH icon + coverage reaches ~100% after cross-game routing; resolver + /Gallery deprioritization from v0.9.5 applied + directly +
  • +
  • + Goldfish is the eighth hand-drawn icon; tadpole and agrias + butterfly are the ninth and tenth, bringing the hand-drawn + library to 10 of 255 pieces +
  • +
  • + Doc syncs: roadmap and version-history updated to reflect + v0.9.6-beta state and current hand-drawn count +
  • +
+
+
May 10
2026
@@ -1060,7 +1095,7 @@

Launch ready

polish, PWA, accessibility
-

Recently shipped (v0.9.5-beta)

+

Recently shipped (v0.9.6-beta)

@@ -1073,8 +1108,8 @@

Launch ready

ACWW icon coverage at 100% — 84 wiki-scraped items; ACCF 95.5%, - ACNL 49.1%, ACNH 39.1% via cross-game ID routingACWW icon coverage at 100% — 84 wiki-scraped items; ACCF 100%, + ACNL 96.5%, ACNH 68.8% via cross-game ID routing
@@ -1087,8 +1122,8 @@

Launch ready

ACGCN item icons + cross-game routing + four hand-drawn icons - (sea-bass, koi, ant, coelacanth)ACGCN item icons + cross-game routing + eight hand-drawn icons + (sea-bass, koi, ant, coelacanth, frog, robust cicada, brown cicada, goldfish)
@@ -1507,6 +1542,34 @@

Velocity detail

pipeline + coelacanth + + + v0.9.6-beta + + May 27 + 17 days + + ACNH icon gap-fill (~100%) + two hand-drawn icons (tadpole, + agrias butterfly) + doc syncs + + + + + v0.9.5-beta + + May 10 + 3 days + + ACNL icon gap-fill (96.5%) + ACCF 100% via cross-game + + three hand-drawn icons (frog, robust cicada, brown cicada) + +
@@ -1517,7 +1580,7 @@

Velocity detail

animalcrossingwebapp.vercel.app - · v0.9.5-beta shipped · v1.0 next · Dev preview at + · v0.9.6-beta shipped · v1.0 next · Dev preview at development-animalcrossingwebapp.vercel.app diff --git a/scripts/fetch-icons-acnh.ts b/scripts/fetch-icons-acnh.ts new file mode 100644 index 0000000..97d69d5 --- /dev/null +++ b/scripts/fetch-icons-acnh.ts @@ -0,0 +1,427 @@ +// Fetch ACNH item icons from the Fandom AC wiki. +// +// Processes ACNH items missing from the flat icon manifest (audit: +// docs/v0.9.2-icon-coverage-audit.md). Writes to +// public/icons//. (flat, cross-game layout). +// Canonical ids come from RENAME_OVERRIDES in itemIconUtils. +// Does NOT wipe existing icons — add-only. +// After a successful run: npm run icons:manifest to regenerate manifest.json. +// +// Run: npx tsx scripts/fetch-icons-acnh.ts + +import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { resolve as resolvePath } from 'node:path'; +import { + resolveIcon, + sleep, + DELAY_MS, + UA, + type ResolveInput, + type ResolveResult, +} from './lib/icon-resolver.ts'; +import { RENAME_OVERRIDES } from '../src/components/itemIconUtils.ts'; + +const GAME_ID = 'ACNH'; +const DATA_DIR = 'public/data/acnh'; +const ICON_DIR = 'public/icons'; +const MISSING_LOG = 'scripts/missing-acnh.txt'; +const AUDIT_FRACTION = 0.05; + +type Category = 'fish' | 'bugs' | 'fossils' | 'art' | 'sea_creatures'; + +const DISAMBIG: Record = { + fish: 'fish', + bugs: 'bug', + fossils: 'fossil', + art: undefined, + sea_creatures: 'deep-sea creature', +}; + +// 103 ACNH ids uncovered in the flat manifest as of v0.9.5-beta. +// Sourced from `npm run audit:icons` 2026-05-12. +const MISSING_IDS: Record = { + fish: [ + 'ranchu-goldfish', + 'golden-trout', + 'mitten-crab', + 'betta', + 'rainbowfish', + 'mackerel', + // 'striped-marlin' — genuine wiki gap (logged in KNOWN_GAPS). + 'ribbon-eel', + 'suckerfish', + 'barreleye', + ], + bugs: [ + 'paper-kite-butterfly', + 'damselfly', + 'man-faced-stink-bug', + 'rosalia-batesi-beetle', + 'blue-weevil-beetle', + 'saw-stag', + 'miyama-stag', + 'giant-stag', + 'cyclommatus-stag', + 'golden-stag', + 'horned-dynastid', + 'horned-atlas', + 'horned-elephant', + 'horned-hercules', + 'hermit-crab', + 'wharf-roach', + // 'palm-weevil', 'earwig' — genuine wiki gaps (logged in KNOWN_GAPS). + ], + fossils: [ + 'acanthostega', + 'ankylo-skull', + 'ankylo-tail', + 'ankylo-torso', + 'brachio-chest', + 'brachio-pelvis', + 'brachio-skull', + 'brachio-tail', + // 'coelacanth' — fossil shares the fish name; wiki has no fossil page and + // the species "Coelacanth" article carries the fish asset only. Logged as + // a genuine gap; emits monogram placeholder. + 'coprolite', + 'diplo-chest', + 'diplo-hip', + 'diplo-neck', + 'diplo-skull', + 'diplo-tail', + 'diplo-tail-tip', + 'diplo-torso', + // elasmosaurus-* — no Fandom AC article for Elasmosaurus; genuine gap. + // fish-fossil — no Fandom AC article; genuine gap. + // ichthyo-* — Ichthyosaurus page exists but has no infobox image; gap. + 'left-megalo-side', + 'left-ptera-wing', + 'left-quetzal-wing', + 'ophthalmo-skull', + 'ophthalmo-torso', + 'pachy-skull', + 'pachy-tail', + 'plesio-body', + 'plesio-neck', + 'plesio-skull', + 'ptera-body', + 'quetzal-torso', + 'right-megalo-side', + 'right-ptera-wing', + 'right-quetzal-wing', + 'sabertooth-tail', + 'shark-tooth-pattern', + // shastasaurus-* — no Fandom AC article for Shastasaurus; genuine gap. + 'silo-skull', + 'silo-tail', + 'spino-skull', + 'spino-tail', + 'spino-torso', + 'stego-skull', + 'stego-tail', + 'stego-torso', + 'styraco-skull', + 'styraco-tail', + 'styraco-torso', + 'tricera-skull', + 'tricera-tail', + 'tricera-torso', + ], + art: [ + 'informative-statue', + // 'renowned-statue' — genuine wiki gap (logged in KNOWN_GAPS). + 'rock-head-statue', + 'sinking-painting', + // 'stunning-statue' — genuine wiki gap (logged in KNOWN_GAPS). + 'twinkling-painting', + // 'nice-statue' — genuine wiki gap (logged in KNOWN_GAPS). + 'familiar-statue', + 'motherly-statue', + ], + sea_creatures: [ + 'seaweed', + 'sea-pig', + 'slate-pencil-urchin', + 'gigas-giant-clam', + 'chambered-nautilus', + 'umbrella-octopus', + 'vampire-squid', + 'gazami-crab', + 'spider-crab', + 'sweet-shrimp', + ], +}; + +// Items intentionally excluded from MISSING_IDS above — logged as known gaps +// so the missing log is honest about what we attempted vs. what was deferred. +const KNOWN_GAPS: { category: Category; id: string; reason: string }[] = [ + { + category: 'fossils', + id: 'coelacanth', + reason: 'no Fandom AC fossil article; species page carries fish asset only', + }, + { + category: 'fossils', + id: 'elasmosaurus-neck', + reason: 'no Fandom AC article for Elasmosaurus', + }, + { + category: 'fossils', + id: 'elasmosaurus-torso', + reason: 'no Fandom AC article for Elasmosaurus', + }, + { + category: 'fossils', + id: 'fish-fossil', + reason: 'no Fandom AC article for Fish fossil', + }, + { + category: 'fossils', + id: 'ichthyo-skull', + reason: 'Ichthyosaurus page exists but has no infobox image', + }, + { + category: 'fossils', + id: 'ichthyo-tail', + reason: 'Ichthyosaurus page exists but has no infobox image', + }, + { + category: 'fossils', + id: 'ichthyo-torso', + reason: 'Ichthyosaurus page exists but has no infobox image', + }, + { + category: 'fossils', + id: 'shastasaurus-neck', + reason: 'no Fandom AC article for Shastasaurus', + }, + { + category: 'fossils', + id: 'shastasaurus-torso', + reason: 'no Fandom AC article for Shastasaurus', + }, + { + category: 'fish', + id: 'striped-marlin', + reason: 'no Fandom AC article; only Blue marlin exists', + }, + { + category: 'bugs', + id: 'palm-weevil', + reason: 'no Fandom AC article for Palm weevil', + }, + { category: 'bugs', id: 'earwig', reason: 'no Fandom AC article for Earwig' }, + { + category: 'art', + id: 'renowned-statue', + reason: 'no Fandom AC article for Renowned statue', + }, + { + category: 'art', + id: 'stunning-statue', + reason: 'no Fandom AC article for Stunning statue', + }, + { + category: 'art', + id: 'nice-statue', + reason: 'no Fandom AC article for Nice statue', + }, +]; + +type RawItem = { id: string; name: string }; + +function loadTargets(cat: Category): ResolveInput[] { + const raw = readFileSync(resolvePath(DATA_DIR, `${cat}.json`), 'utf8'); + const items = JSON.parse(raw) as RawItem[]; + const targetSet = new Set(MISSING_IDS[cat]); + return items + .filter(it => targetSet.has(it.id)) + .map(it => ({ + gameId: GAME_ID, + category: cat, + id: it.id, + name: it.name, + disambig: DISAMBIG[cat], + })); +} + +async function downloadBinary(url: string, dest: string): Promise { + const res = await fetch(url, { headers: { 'User-Agent': UA } }); + if (!res.ok) throw new Error(`HTTP ${res.status} downloading ${url}`); + const buf = Buffer.from(await res.arrayBuffer()); + writeFileSync(dest, buf); + return buf.byteLength; +} + +function urlFilename(url: string): string { + const m = url.match(/\/images\/[^/]+\/[^/]+\/([^/]+)\/revision\//); + return m?.[1] ?? ''; +} + +function tokensOf(s: string): string[] { + return s + .toLowerCase() + .replace(/[._-]+/g, ' ') + .split(/\s+/) + .filter( + t => + t && + t.length >= 3 && + !/^(nh|sh|gcn|ww|cf|nl|the|new|horizons|fossil|fish|bug|painting|sculpture|by|of|and|a)$/.test( + t + ) + ); +} + +function auditPlausible( + item: ResolveInput, + r: ResolveResult +): { ok: boolean; reason?: string } { + if (r.via === 'override') return { ok: true }; + const itemTokens = tokensOf(item.name); + if (itemTokens.length === 0) return { ok: true }; + const titleTokens = new Set(tokensOf(r.titleResolved ?? '')); + const fileTokens = new Set(tokensOf(urlFilename(r.imageUrl ?? ''))); + const hit = itemTokens.find(t => titleTokens.has(t) || fileTokens.has(t)); + if (hit) return { ok: true }; + return { + ok: false, + reason: `no shared token: item="${item.name}" title="${r.titleResolved}" file="${urlFilename(r.imageUrl ?? '')}"`, + }; +} + +(async () => { + const CATEGORIES: Category[] = [ + 'fish', + 'bugs', + 'fossils', + 'art', + 'sea_creatures', + ]; + for (const cat of CATEGORIES) { + mkdirSync(resolvePath(ICON_DIR, cat), { recursive: true }); + } + + type Row = { + item: ResolveInput; + result: ResolveResult; + canonicalId: string; + status: 'hit' | 'miss-html' | 'miss'; + }; + const rows: Row[] = []; + + for (const cat of CATEGORIES) { + const items = loadTargets(cat); + console.log(`\n=== ${cat} (${items.length} targets) ===`); + for (const item of items) { + const r = await resolveIcon(item); + const canonicalId = RENAME_OVERRIDES[item.id] ?? item.id; + let status: Row['status'] = 'miss'; + if (r.found && r.via === 'd:html') status = 'miss-html'; + else if (r.found) status = 'hit'; + rows.push({ item, result: r, canonicalId, status }); + const tag = + status === 'hit' ? 'OK ' : status === 'miss-html' ? 'HTML' : 'MISS'; + const idDisplay = + item.id === canonicalId ? item.id : `${item.id} → ${canonicalId}`; + console.log( + `${tag} ${idDisplay.padEnd(46)} via=${r.via.padEnd(11)} ${r.titleResolved ?? '-'}` + ); + for (const n of r.notes) console.log(` note: ${n}`); + await sleep(DELAY_MS); + } + } + + console.log('\n=== Download phase ==='); + let bytesTotal = 0; + for (const row of rows) { + if (row.status === 'miss') continue; + const url = row.result.imageUrl!; + const fn = urlFilename(url); + const ext = (fn.match(/\.(png|jpe?g)$/i)?.[1] ?? 'png').toLowerCase(); + const filename = `${row.canonicalId}.${ext === 'jpeg' ? 'jpg' : ext}`; + const dest = resolvePath(ICON_DIR, row.item.category, filename); + try { + const bytes = await downloadBinary(url, dest); + bytesTotal += bytes; + console.log(` ${row.item.id} → ${filename} (${bytes} bytes)`); + await sleep(DELAY_MS); + } catch (e) { + console.log( + ` download failed for ${row.item.id}: ${(e as Error).message}` + ); + row.status = 'miss'; + } + } + + const hits = rows.filter(r => r.status !== 'miss'); + const risky = hits.filter( + r => + r.result.via === 'c:search' || + r.result.via === 'd:html' || + r.result.via === 'a:sentence' + ); + const sampleSize = Math.max(1, Math.round(hits.length * AUDIT_FRACTION)); + const shuffled = [...risky].sort(() => Math.random() - 0.5); + const audit = shuffled.slice( + 0, + Math.max(sampleSize, Math.min(risky.length, 4)) + ); + console.log( + `\n=== Audit (${audit.length} of ${risky.length} risky-chain hits, ${hits.length} total) ===` + ); + const auditFails: string[] = []; + for (const row of audit) { + const verdict = auditPlausible(row.item, row.result); + const tag = verdict.ok ? 'OK ' : 'FAIL'; + console.log( + `${tag} ${row.item.category}/${row.item.id} via=${row.result.via} → "${row.result.titleResolved}" ${urlFilename(row.result.imageUrl!)}` + ); + if (!verdict.ok) + auditFails.push(`${row.item.category}/${row.item.id}: ${verdict.reason}`); + } + + const misses = rows.filter(r => r.status === 'miss'); + const lines: string[] = []; + for (const r of misses) { + lines.push( + `${r.item.category}/${r.item.id}\tvia=${r.result.via}\tname="${r.item.name}"\tnotes=${r.result.notes.join(' ; ')}` + ); + } + for (const g of KNOWN_GAPS) { + lines.push(`${g.category}/${g.id}\tvia=known-gap\treason="${g.reason}"`); + } + if (lines.length) { + writeFileSync(MISSING_LOG, lines.join('\n') + '\n'); + console.log( + `\n=== Misses + known gaps: ${lines.length} (logged to ${MISSING_LOG}) ===` + ); + for (const r of misses) { + console.log( + ` MISS ${r.item.category}/${r.item.id} name="${r.item.name}"` + ); + } + for (const g of KNOWN_GAPS) { + console.log(` GAP ${g.category}/${g.id} reason="${g.reason}"`); + } + } else if (existsSync(MISSING_LOG)) { + writeFileSync(MISSING_LOG, ''); + } + + const totalMB = (bytesTotal / 1024 / 1024).toFixed(2); + console.log(`\n=== Summary ===`); + console.log(`Targets: ${rows.length}`); + console.log(`Downloaded: ${hits.length}`); + console.log(`Resolver miss: ${misses.length}`); + console.log(`Known gaps: ${KNOWN_GAPS.length}`); + console.log(`Bytes on disk: ${bytesTotal} (${totalMB} MB)`); + console.log( + `Audit: ${audit.length} sampled, ${auditFails.length} fail` + ); + + if (auditFails.length) { + console.log('\nAudit failures:'); + for (const f of auditFails) console.log(` - ${f}`); + process.exit(2); + } +})(); diff --git a/scripts/lib/icon-resolver.ts b/scripts/lib/icon-resolver.ts index 4db04e5..b29c40e 100644 --- a/scripts/lib/icon-resolver.ts +++ b/scripts/lib/icon-resolver.ts @@ -29,12 +29,32 @@ export type ResolveInput = { export type ResolveResult = { found: boolean; - via: 'override' | 'a:bare' | 'b:disambig' | 'c:search' | 'd:html' | 'none'; + via: + | 'override' + | 'a:bare' + | 'a:sentence' + | 'b:disambig' + | 'c:search' + | 'd:html' + | 'none'; titleResolved: string | null; imageUrl: string | null; notes: string[]; }; +// MediaWiki only auto-capitalizes the first character of a title. Fandom's AC +// wiki overwhelmingly uses sentence case for species and item articles +// ("Golden trout", "Hermit crab", "Sinking painting"), but in-game catalogs +// are Title Case. Some pages have a Title-Case redirect, many don't. After +// a:bare fails, retry with the rest of the title lowercased so a single probe +// catches the common case without forcing dozens of OVERRIDES per game. +function toSentenceCase(name: string): string { + if (!name) return name; + const head = name[0]; + const tail = name.slice(1).toLowerCase(); + return head + tail; +} + // Manually-curated overrides for items the algorithmic chain can't resolve. // Key shape: "//". Value forms: // - string — exact Fandom page title; resolved via pageimages @@ -119,6 +139,70 @@ export const OVERRIDES: Record = { 'ACNL/art/wild-painting-left-half': 'Wild painting', 'ACNL/art/wild-painting-right-half': 'Wild painting', + // ==================================================================== + // ACNH (v0.9.6-beta) — see docs/v0.9.2-icon-coverage-audit.md for the gap shape. + // ==================================================================== + + // Multi-part fossils → parent species article. Same pattern as ACWW + // ankylosaur-* / ACNL megacero-*. ACNH catalog uses truncated forms + // (ankylo, brachio, diplo, ichthyo, megalo, ophthalmo, pachy, plesio, + // ptera, quetzal, shastasaurus, silo, spino, stego, styraco, tricera). + 'ACNH/fossils/ankylo-skull': 'Ankylosaurus', + 'ACNH/fossils/ankylo-tail': 'Ankylosaurus', + 'ACNH/fossils/ankylo-torso': 'Ankylosaurus', + 'ACNH/fossils/brachio-skull': 'Brachiosaurus', + 'ACNH/fossils/brachio-chest': 'Brachiosaurus', + 'ACNH/fossils/brachio-pelvis': 'Brachiosaurus', + 'ACNH/fossils/brachio-tail': 'Brachiosaurus', + 'ACNH/fossils/diplo-skull': 'Diplodocus', + 'ACNH/fossils/diplo-neck': 'Diplodocus', + 'ACNH/fossils/diplo-chest': 'Diplodocus', + 'ACNH/fossils/diplo-hip': 'Diplodocus', + 'ACNH/fossils/diplo-tail': 'Diplodocus', + 'ACNH/fossils/diplo-tail-tip': 'Diplodocus', + 'ACNH/fossils/diplo-torso': 'Diplodocus', + // Ichthyosaurus page exists but carries no pageimages.original and no + // portable-infobox image — genuine wiki gap; tracked in missing-acnh.txt. + 'ACNH/fossils/left-megalo-side': 'Megaloceros', + 'ACNH/fossils/right-megalo-side': 'Megaloceros', + 'ACNH/fossils/ophthalmo-skull': 'Ophthalmosaurus', + 'ACNH/fossils/ophthalmo-torso': 'Ophthalmosaurus', + 'ACNH/fossils/pachy-skull': 'Pachycephalosaurus', + 'ACNH/fossils/pachy-tail': 'Pachycephalosaurus', + 'ACNH/fossils/plesio-skull': 'Plesiosaurus', + 'ACNH/fossils/plesio-neck': 'Plesiosaurus', + 'ACNH/fossils/plesio-body': 'Plesiosaurus', + 'ACNH/fossils/ptera-body': 'Pteranodon', + 'ACNH/fossils/left-ptera-wing': 'Pteranodon', + 'ACNH/fossils/right-ptera-wing': 'Pteranodon', + 'ACNH/fossils/quetzal-torso': 'Quetzalcoatlus', + 'ACNH/fossils/left-quetzal-wing': 'Quetzalcoatlus', + 'ACNH/fossils/right-quetzal-wing': 'Quetzalcoatlus', + // Shastasaurus has no Fandom AC article — genuine gap (logged). + 'ACNH/fossils/silo-skull': 'Silo', + 'ACNH/fossils/silo-tail': 'Silo', + 'ACNH/fossils/spino-skull': 'Spinosaurus', + 'ACNH/fossils/spino-tail': 'Spinosaurus', + 'ACNH/fossils/spino-torso': 'Spinosaurus', + 'ACNH/fossils/stego-skull': 'Stegosaurus', + 'ACNH/fossils/stego-tail': 'Stegosaurus', + 'ACNH/fossils/stego-torso': 'Stegosaurus', + 'ACNH/fossils/styraco-skull': 'Styracosaurus', + 'ACNH/fossils/styraco-tail': 'Styracosaurus', + 'ACNH/fossils/styraco-torso': 'Styracosaurus', + 'ACNH/fossils/tricera-skull': 'Triceratops', + 'ACNH/fossils/tricera-tail': 'Triceratops', + 'ACNH/fossils/tricera-torso': 'Triceratops', + // Elasmosaurus has no Fandom AC article — genuine gap (logged). + // Coelacanth (fossil) and Fish fossil have no Fandom AC article — genuine + // gaps (logged); the fish "Coelacanth" page is a different asset and not + // a valid stand-in for the fossil piece. + 'ACNH/fossils/shark-tooth-pattern': 'Shark-tooth pattern', + // "Sabertooth tiger" is the wiki article for the multi-part sabertooth + // skeleton; "sabertooth-tail" is the only piece missing here because + // sabertooth-skull/torso already routed via ACNL canonicalization. + 'ACNH/fossils/sabertooth-tail': 'Sabertooth tiger', + // ACNL sea creatures — wiki indexes as "Pearl oyster (deep-sea creature)" // (lowercase second word). MediaWiki only auto-capitalizes the first // character, so the b:disambig probe with the in-game-cased "Pearl Oyster" @@ -264,6 +348,25 @@ export async function resolveIcon(input: ResolveInput): Promise { } let safeHtmlTarget: string | null = a.kind === 'no-image' ? a.title : null; + // (a2) sentence-case name — handles the Title-Case-vs-sentence-case gap + // that ACNH catalog names hit constantly (e.g. "Golden Trout" → "Golden + // trout"). Skipped when it would duplicate the bare probe. + const sentence = toSentenceCase(input.name); + if (sentence !== input.name) { + const a2 = await pageImage(sentence); + await sleep(DELAY_MS); + if (a2.kind === 'image') { + return { + found: true, + via: 'a:sentence', + titleResolved: a2.title, + imageUrl: a2.imageUrl, + notes, + }; + } + if (!safeHtmlTarget && a2.kind === 'no-image') safeHtmlTarget = a2.title; + } + // (b) name (category) if (input.disambig) { const b = await pageImage(`${input.name} (${input.disambig})`); @@ -293,6 +396,13 @@ export async function resolveIcon(input: ResolveInput): Promise { // so the parent article wins when both exist; preserves the gallery as a // fallback if the parent has no pageimages. accepted.sort((x, y) => Number(x.includes('/')) - Number(y.includes('/'))); + // 2026-05-12 (v0.9.6 spike): ACNH furniture " model" pages routinely + // outrank the species article in search (seen on golden-trout, paper-kite- + // butterfly, man-faced-stink-bug). Push "* model" titles to the back so the + // species article wins; preserves the furniture page as a last-ditch fallback. + accepted.sort( + (x, y) => Number(/\bmodel$/i.test(x)) - Number(/\bmodel$/i.test(y)) + ); if (rejected.length) notes.push(`search rejected: [${rejected.join(' | ')}]`); if (accepted.length) notes.push(`search accepted: [${accepted.join(' | ')}]`); for (const cand of accepted) { diff --git a/scripts/missing-acnh.txt b/scripts/missing-acnh.txt new file mode 100644 index 0000000..52ba765 --- /dev/null +++ b/scripts/missing-acnh.txt @@ -0,0 +1,15 @@ +fish/striped-marlin via=known-gap reason="no Fandom AC article; only Blue marlin exists" +bugs/palm-weevil via=known-gap reason="no Fandom AC article for Palm weevil" +bugs/earwig via=known-gap reason="no Fandom AC article for Earwig" +art/renowned-statue via=known-gap reason="no Fandom AC article for Renowned statue" +art/stunning-statue via=known-gap reason="no Fandom AC article for Stunning statue" +art/nice-statue via=known-gap reason="no Fandom AC article for Nice statue" +fossils/coelacanth via=known-gap reason="no Fandom AC fossil article; species page carries fish asset only" +fossils/elasmosaurus-neck via=known-gap reason="no Fandom AC article for Elasmosaurus" +fossils/elasmosaurus-torso via=known-gap reason="no Fandom AC article for Elasmosaurus" +fossils/fish-fossil via=known-gap reason="no Fandom AC article for Fish fossil" +fossils/ichthyo-skull via=known-gap reason="Ichthyosaurus page exists but has no infobox image" +fossils/ichthyo-tail via=known-gap reason="Ichthyosaurus page exists but has no infobox image" +fossils/ichthyo-torso via=known-gap reason="Ichthyosaurus page exists but has no infobox image" +fossils/shastasaurus-neck via=known-gap reason="no Fandom AC article for Shastasaurus" +fossils/shastasaurus-torso via=known-gap reason="no Fandom AC article for Shastasaurus" diff --git a/scripts/spike-acnh-icons.ts b/scripts/spike-acnh-icons.ts new file mode 100644 index 0000000..38fb0b6 --- /dev/null +++ b/scripts/spike-acnh-icons.ts @@ -0,0 +1,109 @@ +// v0.9.6 spike: resolve a representative slice of ACNH missing items against +// the Fandom AC wiki. Resolution-only — does not download or commit binaries. +// Threshold per audit doc: ≥90% (a/b/c/override) on this 18-item slice. +// +// Run: npx tsx scripts/spike-acnh-icons.ts + +import { readFileSync } from 'node:fs'; +import { resolve as resolvePath } from 'node:path'; +import { + resolveIcon, + sleep, + DELAY_MS, + type ResolveInput, + type ResolveResult, +} from './lib/icon-resolver.ts'; +import { RENAME_OVERRIDES } from '../src/components/itemIconUtils.ts'; + +const GAME_ID = 'ACNH'; +const DATA_DIR = 'public/data/acnh'; + +type Category = 'fish' | 'bugs' | 'fossils' | 'art' | 'sea_creatures'; + +const DISAMBIG: Record = { + fish: 'fish', + bugs: 'bug', + fossils: 'fossil', + art: undefined, + sea_creatures: 'deep-sea creature', +}; + +// 18-item spike sourced from docs/v0.9.2-icon-coverage-audit.md section 2. +const SPIKE: { category: Category; id: string }[] = [ + { category: 'fish', id: 'betta' }, + { category: 'fish', id: 'golden-trout' }, + { category: 'fish', id: 'mitten-crab' }, + { category: 'fish', id: 'striped-marlin' }, + { category: 'fish', id: 'barreleye' }, + { category: 'bugs', id: 'paper-kite-butterfly' }, + { category: 'bugs', id: 'man-faced-stink-bug' }, + { category: 'bugs', id: 'giant-stag' }, + { category: 'bugs', id: 'horned-hercules' }, + { category: 'bugs', id: 'hermit-crab' }, + { category: 'fossils', id: 'ankylo-skull' }, + { category: 'fossils', id: 'left-megalo-side' }, + { category: 'fossils', id: 'coelacanth' }, + { category: 'art', id: 'informative-statue' }, + { category: 'art', id: 'sinking-painting' }, + { category: 'sea_creatures', id: 'umbrella-octopus' }, + { category: 'sea_creatures', id: 'gigas-giant-clam' }, + { category: 'sea_creatures', id: 'vampire-squid' }, +]; + +type RawItem = { id: string; name: string }; + +function loadInput(target: { category: Category; id: string }): ResolveInput { + const raw = readFileSync( + resolvePath(DATA_DIR, `${target.category}.json`), + 'utf8' + ); + const items = JSON.parse(raw) as RawItem[]; + const item = items.find(i => i.id === target.id); + if (!item) throw new Error(`${target.category}/${target.id} not in catalog`); + return { + gameId: GAME_ID, + category: target.category, + id: item.id, + name: item.name, + disambig: DISAMBIG[target.category], + }; +} + +function urlFilename(url: string | null): string { + if (!url) return '-'; + const m = url.match(/\/images\/[^/]+\/[^/]+\/([^/]+)\/revision\//); + return m?.[1] ?? url; +} + +(async () => { + const rows: { item: ResolveInput; canonicalId: string; r: ResolveResult }[] = + []; + for (const t of SPIKE) { + const item = loadInput(t); + const r = await resolveIcon(item); + const canonicalId = RENAME_OVERRIDES[item.id] ?? item.id; + rows.push({ item, canonicalId, r }); + const tag = r.found ? (r.via === 'd:html' ? 'HTML' : 'OK ') : 'MISS'; + const idDisp = + item.id === canonicalId ? item.id : `${item.id} → ${canonicalId}`; + console.log( + `${tag} ${item.category.padEnd(14)} ${idDisp.padEnd(34)} name="${item.name}"` + ); + console.log( + ` via=${r.via.padEnd(11)} title="${r.titleResolved ?? '-'}"` + ); + console.log(` file=${urlFilename(r.imageUrl)}`); + for (const n of r.notes) console.log(` note: ${n}`); + await sleep(DELAY_MS); + } + + const hits = rows.filter(r => r.r.found && r.r.via !== 'd:html').length; + const html = rows.filter(r => r.r.found && r.r.via === 'd:html').length; + const miss = rows.filter(r => !r.r.found).length; + const pct = ((hits / rows.length) * 100).toFixed(1); + console.log(`\n=== Spike summary ===`); + console.log(`Total: ${rows.length}`); + console.log(`First-pass hits (a/b/c/override): ${hits} (${pct}%)`); + console.log(`HTML-fallback hits (d:html): ${html}`); + console.log(`Misses: ${miss}`); +})();