Skip to content

feat: add a trainer card with a team you pick - #331

Open
DyRize wants to merge 3 commits into
chattymin:mainfrom
DyRize:feat/trainer-card
Open

DyRize wants to merge 3 commits into
chattymin:mainfrom
DyRize:feat/trainer-card

Conversation

@DyRize

@DyRize DyRize commented Sep 18, 2026

Copy link
Copy Markdown

Summary

Adds a trainer card to the Collection tab, built entirely from what the save already holds — no new tracking, no cost figures, nothing leaving the Mac.

Part of #260 (point 3).

Front: trainer name (optional, empty by default), a 5-digit number drawn once on first open, lifetime tokens, Pokédex count over the 649 species the app can hatch, shinies, graduations, duplicates, first catch, and the rarity split. Back: a team of six.

The team asks nothing of the user until they want it. With no picks it fills itself — shinies first, then the rarest, then the newest. The first edit freezes what is on screen and edits from there, so nobody starts from an empty card. "Automatic team" is a toggle rather than a reset: the picked line-up is kept aside and comes back when it is turned off, because losing six deliberate picks to one stray click is not a fair trade.

Lifecycle cases are handled: the Pokémon being raised can join and keeps its slot through graduation (the team key is its profile id, which its graduated entry keeps), released individuals leave the team, and unknown, duplicate or out-of-range ids in a hand-edited save are reconciled on load and on import.

The card frames the pinned representative species and falls back to the Pokémon being raised, so a fresh egg does not take the card over. Released individuals count in no figure except the Pokédex line, which folds owned species whatever became of the individual — that rule is the dex's, not the card's.

Privacy: no cost in dollars, no account names, no emails, an empty trainer name by default and a toggle to hide the token count. Export copies a PNG to the clipboard or saves it through NSSavePanel; the floating pet's right-click menu copies it too and confirms with a callout.

Species beyond the animated range (Sylveon at #700, reachable from Eevee) stay out of the Pokédex count so it can never read 650 / 649.

State added to CompanionState: teamEntryIDs, teamIsAutomatic, trainerID, trainerName, all classified as progress for save transfer and lenient-decoded like the rest.

If #265 (box) lands, the team could pick from the box as well — happy to follow up.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation
  • Other:

UI changes

Before After
Collection tab: a segmented picker for Pokédex / Catch log, filling the width. Catch log rows show the sprite, name, evolution line, rarity, nature and date. The floating pet's right-click menu has Open and Hide. A card button sits next to the picker (person.text.rectangle) and opens the card screen inside the popover: the two card sides, a name field, a "Show tokens" checkbox, Copy image, Save… and an "Automatic team" checkbox. Catch log rows gain a TEAM badge and a right-click entry to add or remove. The floating pet's menu gains "Copy trainer card".

Screenshots (English, rendered from this branch):

pr331-carte-ecran pr331-carte-export pr331-journal-badge

The card itself is always rendered light so the exported image looks the same in light and dark mode. A shiny companion gets a holo frame on both sides.

Checklist

  • swift build and swift test pass locally
  • PR title and description are written in English
  • UI changes are described above (before/after — images optional)
  • No copyrighted assets, secrets, or private tooling references are committed (see CONTRIBUTING)
  • Tests were added or updated for this change

scripts/test-gate.sh: 1105 tests, 11 skipped, 0 failures, logic-core line coverage 92.93%. 27 tests cover the card: automatic order, the first edit, add/replace/move/limit, the automatic toggle keeping the picks, graduation and release, load reconciliation, trainer id and name, stats and rows, released individuals counting nowhere but the Pokédex, save transfer, rendering in all seven languages, and the holo frame on both sides. New branches were checked with llvm-cov --show-regions rather than the coverage number, and the team logic was verified by injecting defects one at a time to confirm the tests fail.

A two-sided card in the Collection tab, built from what the save already holds.
Front: trainer name and number, lifetime tokens, Pokédex count over the 649 the
app can hatch, shinies, graduations, duplicates, first catch, and the rarity
split. Back: a team of six.

The team asks nothing of the user until they want it: it fills itself with the
rarest and shiniest graduates, and the first edit freezes what is on screen and
takes it from there. "Automatic team" is a toggle, not a reset, so the picked
line-up is kept aside and comes back. The raised Pokémon can join and keeps its
slot through graduation, released individuals leave it, and a hand-edited save
is reconciled on load.

The card frames the pinned representative species, falling back to the raised
Pokémon, so a fresh egg does not take it over. Released individuals count in no
figure except the Pokédex, which folds owned species whatever became of them.

Export copies the image to the clipboard or saves a PNG, with a toggle to hide
the token count. The floating pet's menu copies it too and says so with a
callout. Nothing leaves the Mac.
XCTest's setUp and tearDown are nonisolated, so a @mainactor test case cannot
touch a mutable stored property from them. Swift 6.2 lets it through, the CI
toolchain (Swift 6.0 on macos-15) rejects it.
The card was branched before Unown forms landed, so its sprites fell back to the
default A and its names dropped the letter. The pinned form reaches the frame
through the representative subject, and team slots and the picker rows read the
form off the entry like the catch log does.

This branch has not been deployed

No deployments
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.

1 participant