release: v0.9.3-beta#112
Merged
Merged
Conversation
Replaces the wiki-scraped bugs/ant.png with a 2048×2048 hand-drawn original, exported through the v0.9.2 sharp + pngquant pipeline (947 KB → 31 KB, 96.7% reduction). Third hand-drawn icon after sea-bass and koi. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(icons): hand-drawn ant (ACGCN bug)
Locked the six §12 open questions in the v0.9.3 CSV import scoping doc and switched the save-file format from CSV (option B) to JSON. Added a concrete JSON shape, updated the import entry-point to cover both Settings and the onboarding empty state, and shifted the filename convention to ac-save-<town>-<date>.json. Report CSV survives as a separate "Download report" button. Replace remains confirmation-only (two-step, gated on data presence) with no undo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaced the placeholder town name in the §4 JSON example so the doc keeps to single-author / past-tense declarative voice without naming the maintainer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs: v0.9.3 CSV/JSON import scoping plan
Adds the round-trip-capable save-file artifact alongside the existing human-readable CSV report. New `Export save` button in the sidebar foot writes `ac-save-<town>-<date>.json` per the schema locked in docs/v0.9.3-csv-import-plan.md §4. CSV stays as `Download report`. Importer follows in PR 2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-format feat(export): JSON save-file export (v0.9.3 PR 1/2)
Round-trip companion to the JSON save-file export shipped in PR #106. - src/lib/saveFileImport.ts — parser + validator. Hard-rejects on schemaVersion != 1, wrong app, missing/malformed required fields, unknown gameId, ACNH-without-hemisphere. Tolerates malformed donation rows by dropping them with a count. - src/lib/saveFileReconcile.ts — pure reconciliation. Matches existing towns by (name, gameId); produces create | replace | merge plans. Drops unknown itemIds (vs loaded data files) with a count. - src/lib/store.ts — atomic applyImportedSave(plan) action. - src/components/ImportSaveModal.tsx — file picker -> preview -> mode selector -> heavy two-step gate (only when destructive Replace would wipe non-empty data) -> apply. Mounted at App layout level. - Sidebar gets an Import save button next to Export save; TownManager empty state gets an Import save instead affordance. Auto-open of the forced-create TownManager is gated on !importSaveOpen so the two modals never stack. - 24 new unit tests across saveFileImport and saveFileReconcile, plus the existing saveFile round-trip suite stays green. Decisions per docs/v0.9.3-csv-import-plan.md (locked answers): - Merge tiebreak: earlier donatedAt wins on conflict (Q6/§6 — preserves the user's actual first-donation timestamp). - Replace UX: heavy two-step confirm only when the matched town has donations to lose. Empty town -> single light confirm. No undo (Q5). - Single-town import only (Q3); multi-town deferred. - File extension/MIME .json / application/json (Q6/locked). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
feat(import): JSON save-file import (v0.9.3 PR 2/2)
…ing flow App.tsx calls openTownManager(forceCreate=true) on first load, which immediately sets creating=true via the useEffect. This collapsed showEmptyState to false before first paint, so the empty-state import button never rendered — violating locked Q2 in v0.9.3-csv-import-plan.md. Adds a secondary "or import an existing save" link below NewTownForm, visible when creating && towns.length === 0. Reuses the same close() + openImportSave() handler as the existing empty-state button. The empty-state button is unchanged. Adds .ac-tm-import-link CSS (muted, full-width, hover underline). New Vitest tests cover: link renders on forceCreate+no-towns, does not render when towns exist, click fires close+openImportSave, existing empty-state button unaffected. Refs PR #108. Closes locked Q2 in docs/v0.9.3-csv-import-plan.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract handleOpenImportSave in TownManager to deduplicate the close()+openImportSave() inline handlers shared by the empty-state button and the new onboarding footer link - Fix test: 'does NOT render import link when towns already exist' was using townManagerForceCreate:true with a populated towns array, an impossible UI state; corrected to forceCreate:false to reflect real app behavior Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(import): show import-save affordance during forced-create onboarding flow
… open On first load with no towns, App.tsx opens TownManager in forceCreate mode, rendering a backdrop blur. The canvas-level "Create a town..." EmptyState rendered behind that blur — redundant and visually broken. Gate the EmptyState on `!townManagerOpen` so the drawer is the sole onboarding surface whenever it's open. Closes #107. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous fix fell through to <FullView /> when noTowns && townManagerOpen, showing a stale populated HomeTab behind the backdrop blur — worse than the original bug. Simplify to `noTowns ? null : <FullView />`: the drawer is the sole onboarding surface and the full view must never mount without an active town. Removes now-unused EmptyState + useUIStore imports from ACCanvas. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ind-blur fix(onboarding): hide canvas empty-state message while TownManager is open
Bump package.json to 0.9.3-beta, convert CHANGELOG [Unreleased] to [0.9.3-beta] — 2026-05-06, add a v0.9.3-beta entry to public/version-history.html (timeline card + status table + footer), and refresh README/CLAUDE/architecture version refs.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR cuts the v0.9.3-beta release and (despite the PR description) includes the functional implementation of the JSON save-file export + import round-trip, along with onboarding/UI updates and documentation/version bumps.
Changes:
- Add JSON save-file export/import flow (schema, parser/validator, reconcile, atomic store apply, modal UI, and entry points).
- Update onboarding empty-state flow to surface “Import save” during first-run/forced-create scenarios.
- Bump version + update changelog/version-history/architecture docs to v0.9.3-beta.
Reviewed changes
Copilot reviewed 26 out of 28 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/uiStore.ts | Adds UI state/actions for opening/closing the import-save modal. |
| src/lib/uiStore.test.ts | Adds UI store tests (includes a canvas gate assertion). |
| src/lib/store.ts | Adds applyImportedSave(plan) for atomic import application. |
| src/lib/saveFile.ts | Implements JSON save-file export schema + filename + download helper. |
| src/lib/saveFile.test.ts | Unit tests for save-file export shape, sorting, filename, round-trip JSON. |
| src/lib/saveFileImport.ts | Implements JSON save-file parser/validator + formatted error strings. |
| src/lib/saveFileImport.test.ts | Unit tests for parser acceptance/rejection and tolerance behavior. |
| src/lib/saveFileReconcile.ts | Pure reconcile logic producing ImportPlan (create/replace/merge). |
| src/lib/saveFileReconcile.test.ts | Unit tests for matching/reconcile behavior and round-trip set preservation. |
| src/components/ImportSaveModal.tsx | Modal UI for file pick/preview/mode selection/confirm + apply + navigate. |
| src/components/Sidebar.tsx | Sidebar footer now exposes Export save / Import save / Download report actions. |
| src/components/SettingsRoute.tsx | Wires sidebar export to JSON save export + adds Download report handler. |
| src/components/CreditsRoute.tsx | Wires sidebar export to JSON save export + adds Download report handler. |
| src/components/ACCanvas.tsx | Switches export behavior to JSON save; keeps CSV as “Download report”; suppresses canvas empty-state when no towns. |
| src/components/TownManager.tsx | Adds empty-state import button + forced-create secondary import link below NewTownForm. |
| src/components/TownManager.test.tsx | Tests onboarding import link rendering/click behavior. |
| src/index.css | Adds ImportSaveModal styling + onboarding import-link/button styling. |
| src/App.tsx | Mounts ImportSaveModal and prevents auto-opening TownManager while import modal is open. |
| docs/v0.9.3-csv-import-plan.md | Adds scoping doc describing the (now JSON) save-file format and import decisions. |
| CHANGELOG.md | Adds v0.9.3-beta entry describing save-file round trip + onboarding fixes. |
| public/version-history.html | Adds v0.9.3-beta timeline card/velocity row/footer version bump. |
| README.md | Updates “Current release” blurb to v0.9.3-beta. |
| docs/architecture.md | Updates architecture doc header version to v0.9.3-beta. |
| .claude/rules/architecture.md | Updates Claude rules architecture header version to v0.9.3-beta. |
| CLAUDE.md | Updates current release blurb and adds v0.9.3-beta shipped section. |
| package.json | Bumps package version to 0.9.3-beta. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+38
to
+39
| const hasTowns = false; | ||
| expect(hasTowns ? 'null' : 'fullView').toBe('fullView'); |
Comment on lines
+82
to
+86
| if (e.key === 'Escape') close(); | ||
| }; | ||
| window.addEventListener('keydown', onKey); | ||
| return () => window.removeEventListener('keydown', onKey); | ||
| }, [open, close]); |
Comment on lines
+172
to
+176
| applyImportedSave: plan => { | ||
| let resultTownId = ''; | ||
| set(state => { | ||
| if (plan.kind === 'create') { | ||
| const id = generateId(); |
Comment on lines
1
to
6
| { | ||
| "name": "animalcrossingwebapp", | ||
| "version": "0.9.2-beta", | ||
| "version": "0.9.3-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", |
|
|
||
| All notable changes to this project are documented here. | ||
|
|
||
| ## [0.9.3-beta] — 2026-05-06 |
Comment on lines
+80
to
+83
| { "itemId": "fish-sea_bass", "category": "fish", "donatedAt": "2026-04-15T18:42:11.000Z" }, | ||
| { "itemId": "bug-tarantula", "category": "bugs", "donatedAt": "2026-04-16T02:11:08.000Z" }, | ||
| { "itemId": "fossil-trex_skull","category": "fossils","donatedAt": "2026-04-17T..." }, | ||
| { "itemId": "art-mona_lisa", "category": "art", "donatedAt": "2026-04-18T..." } |
| <li>JSON save-file export — new <code>Export save</code> button writes a versioned, lossless <code>ac-save-<town>-<date>.json</code> snapshot of one town and its donations</li> | ||
| <li>JSON save-file import — round-trip companion with Replace / Merge / Import-as-new modes, <code>(name, gameId)</code> matching, and a heavy two-step confirmation when Replace would overwrite existing donations</li> | ||
| <li>Sidebar foot now has two distinct buttons: <code>Export save</code> (the new JSON) and <code>Download report</code> (the existing human-readable CSV, unchanged)</li> | ||
| <li>Onboarding fix: <code>Import save instead</code> link is visible in the empty-state TownManager so a fresh-device user can restore from a save file without first creating a placeholder town (closes #107)</li> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cuts v0.9.3-beta — one day after v0.9.2-beta. Headline is the JSON save-file round trip (export + import), so users on a fresh device can restore from a save without re-checking 80+ items by hand. Also lands a third hand-drawn icon and tightens the empty-state onboarding flow.
What shipped to
developmentsince v0.9.2-beta (6 PRs)icon-sources/bugs/ant.png, exported through the v0.9.2 pipeline. Third hand-drawn replacement after sea-bass + koi.docs/v0.9.3-csv-import-plan.md— scoping doc for the import work; 6 questions locked (JSON not CSV,(name, gameId)matching, three reconcile modes, no undo, etc.).Export savebutton in the sidebar foot writesac-save-<safeTown>-<YYYY-MM-DD>.json, a versioned, lossless format (schemaVersion,app,appVersion,exportedAt, full town metadata, donations keyed by store-nativeitemId). The existing CSV is preserved asDownload reportfor spreadsheet users. 18 unit tests.(name, gameId). Replace into a non-empty town gates behind a heavy two-step "YOU CAN NOT GO BACK" confirmation; into an empty town it skips the second step. Items missing from loaded data files are dropped silently with a count surfaced. Single atomicapplyImportedSave(plan)action.NewTownFormin the forced-create TownManager flow, so a fresh-device user can restore from a save file without first creating a placeholder town.dd07d20follow-up rendersnullrather than the full view whennoTowns, plus 41 newuiStore.test.tstests.Decisions worth surfacing
(name, gameId)matching. Town id is internal; users identify a save by its town name + game. Conflict-free by construction since game is immutable post-create.What this PR does
package.jsonfrom0.9.2-beta→0.9.3-beta[Unreleased]→[0.9.3-beta] — 2026-05-06public/version-history.html(timeline card + status table row + footer)README.md,CLAUDE.md,docs/architecture.md,.claude/rules/architecture.mdNo code changes. Open issue carried into v0.9.4: #111 — render-based ACCanvas tests (test-depth hardening from Copilot review on #110).
Test plan
npm run build— clean (301 KB JS / 43.5 KB CSS gzipped 93.5 / 8.2)npm test— 129 tests pass in this worktree