feat(identity): prototype car identity catalog + manual picker#40
Merged
Conversation
Revive real Hot Wheels car identity in the garage without touching the shipped `cars` table. A bundled, wiki-sourced catalog gives castings real names + photos, and a manual picker lets the user identify a car keyed off the stable Mattel id the app already decodes. Protocol: - Decode the NFC NDEF `mattelId` on the modern MPID path and surface it on CarDetectedEvent; thread it into portalStore's CurrentCar. - Add castingKeyFromMattelId() (model-id bytes [2..6), base64url-aware, defensive raw-id fallback) + KAT tests. Identity (isolated module, append-only v6 migration: car_links, car_identifications): - identityStore: uid->castingKey links (learned on detection) and castingKey->catalogId identifications (user pick); catalogIdForUid selector. Identifying one copy names every copy of a casting. - Repository interface + in-memory + sqlite impls; bootstrap hydrate + write-through sink + learnIdentity() in seed and subscribe paths. Catalog + UI: - catalog.json (146 cars, 136 with art) via stdlib-only Fandom scraper. - catalog.ts typed surface + searchCatalog ranking; useCarIdentity hook. - Shared CarPhoto component; garage rows + detail show catalog name/photo; Identify/Change modal grid picker (app/identify.tsx). Docs: ADR-0013 + ROADMAP Phase 5 status. Caveats (see ADR-0013): catalog images hot-link the CC-BY-SA wiki CDN (licensing/attribution is a pre-release follow-up); the model-id byte offset is reverse-engineered, with raw-id fallback to keep grouping safe. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make the Car Identity & Collection surfaces feel less utilitarian while staying inside the documented design language (night-track dark, flame accent, soft accent glow for "alive" states over heavy shadows). - tokens: add surfaceRaised + accentSoft/accentBlueSoft washes and a reusable `elevation` preset (card ambient lift, accentGlow, blueGlow). - CarPhoto: move to expo-image (fade transition, disk cache), flexible sizing (size | width/height/aspectRatio) and an optional accent ring. - garage list: summary line (N identified · on portal), richer rows with 56px photo, identified ring, on-portal accent glow, card depth. - garage detail: imagery-forward hero photo banner with Change badge when identified; identity-prompt card only when unidentified; accent glow on the best-speed hero; depth on stat cards. - identify: search row with leading magnify glyph; casting cards become square photo tiles with a selected check-badge + ring. - home: subtle card depth on the speed stat tiles for cohesion. - docs: document the new elevation/glow + raised-surface tokens in the design language (§4). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
What this does
Revives real Hot Wheels car identity in the garage — castings get their real names + photos, and you can manually identify a car with a tap. Identity is keyed off the stable Mattel id the app already decodes, so identifying one copy names every copy of that casting you scan.
Crucially, this is an isolated module — the shipped
carsSQLite table is untouched. Two small maps live in a new append-only v6 migration (car_links,car_identifications), so it's safe to iterate on or rip out.What you'll see
How it works
mattelIdon the modern MPID path, surface it onCarDetectedEvent→portalStore'sCurrentCar. NewcastingKeyFromMattelId()(model-id bytes[2..6), base64url-aware, defensive raw-id fallback) + KAT tests.uid → castingKeylinks (learned automatically) andcastingKey → catalogIdidentifications (user pick);catalogIdForUidselector. Repository interface + in-memory + sqlite impls; bootstrap hydrate + write-through sink +learnIdentity()in seed and subscribe paths.catalog.jsonvia a stdlib-only Fandom scraper; typedcatalog.tssurface + rankedsearchCatalog;useCarIdentityhook; sharedCarPhotocomponent;app/identify.tsxmodal picker.Caveats (tracked in ADR-0013)
Testing
npm run typecheckclean.casting,catalog,identityStore, andidentityRepositorysuites.🤖 Generated with GitHub Copilot CLI