Skip to content

release: v0.9.3-beta#112

Merged
jacuzzicoding merged 18 commits into
mainfrom
release/v0.9.3-beta
May 6, 2026
Merged

release: v0.9.3-beta#112
jacuzzicoding merged 18 commits into
mainfrom
release/v0.9.3-beta

Conversation

@jacuzzicoding
Copy link
Copy Markdown
Owner

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 development since v0.9.2-beta (6 PRs)

Decisions worth surfacing

  • JSON, not CSV, for the save format. CSV stays as the human-readable report; JSON is the round-trip artifact. Decided in docs: v0.9.3 CSV/JSON import scoping plan #105 because CSV can't carry hemisphere, schema version, or game id without ambiguity.
  • No undo on destructive Replace. Mitigated by the two-step confirmation — locked in feat(import): JSON save-file import (v0.9.3 PR 2/2) #108 to keep the store action atomic and the mental model simple.
  • (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

  • Bumps package.json from 0.9.2-beta0.9.3-beta
  • Converts CHANGELOG [Unreleased][0.9.3-beta] — 2026-05-06
  • Adds a v0.9.3-beta entry to public/version-history.html (timeline card + status table row + footer)
  • Refreshes version references in README.md, CLAUDE.md, docs/architecture.md, .claude/rules/architecture.md

No 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
  • Vercel preview check on the PR
  • Manual smoke on preview: sidebar foot shows both Export save + Download report; empty-state import link visible

jacuzzicoding and others added 18 commits May 5, 2026 15:04
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>
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.
Copilot AI review requested due to automatic review settings May 6, 2026 20:23
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
animalcrossingwebapp Ready Ready Preview, Comment May 6, 2026 8:23pm

@jacuzzicoding jacuzzicoding merged commit 5aad43b into main May 6, 2026
5 checks passed
@jacuzzicoding jacuzzicoding deleted the release/v0.9.3-beta branch May 6, 2026 20:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 thread src/lib/uiStore.test.ts
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 thread src/lib/store.ts
Comment on lines +172 to +176
applyImportedSave: plan => {
let resultTownId = '';
set(state => {
if (plan.kind === 'create') {
const id = generateId();
Comment thread package.json
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",
Comment thread CHANGELOG.md

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-&lt;town&gt;-&lt;date&gt;.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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(onboarding): empty-state message renders behind TownManager backdrop on first load

2 participants