Pictuary: replaced search with type filter chips. Each of the 10 types
shows its emoji icon + count. Click to filter, click again to clear.
All/Mine toggle preserved. Horizontal scroll for the chips.
ProgressBar: compute user stats (total_rarity, discovery_count) from
the dex by filtering discoverer_username — no longer depends on
myUser which could be null. Shows ⭐ rarity 🔍 count for signed-in
users, falls back to global 📷 👥 for anonymous.
ScanResult: drop emoji + subtitle, show headline only
Pictuary: 'Mine' filter now simply filters dex entries where
discoverer_username matches the signed-in user's preferred_username.
No myUser.discoveries dependency — works immediately, no hack.
Scanner: replaced file upload with CameraCapture component using
getUserMedia({ facingMode: 'environment' }). Live camera view with
a capture button and framing reticle. No arbitrary image upload.
Camera stream cleaned up on unmount.
Reverted the optimisticInsert myUser-creation hack.
Fix Mine tab: create myUser on first scan if null
optimisticInsert now creates a minimal UserRecord from the auth store
when myUser is null (e.g. getUser returned 404 or refreshAll hasn't
completed). Previously, if myUser was null, the discovery was added
to the dex but never to the user's discoveries list, so the Mine
filter showed nothing.
Also added console.error logging to the getUser catch block so 404s
and other failures are visible in the browser console.
Pictuary: default to Mine when signed in, fix disabled button
- showMineOnly defaults to true via $effect when isAuthenticated
- Removed the broken isSignedIn derived (was checking userInfo?.sub
which wasn't reactive); removed disabled attribute on Mine button
- Empty state no longer depends on isSignedIn
PicletCard: type-colored details section with white name text
- Drop the anonymous warning banner; scanning now works without sign-in
(backend falls back to its own token for AI quota + attribution)
- Scanner auth gate removed; token passed as empty string when anon
- Top bar: drop progress bar, just show ⭐ rarity 🔍 count (or global
stats for anonymous)
- PicletDetail: display key with spaces instead of underscores
PicletCard: square top corners (image flush with borders), full-width
image with aspect-ratio: 1, add ★rarity to tier banner.
Leaderboard: Recent Discoveries now uses full PicletCards (same as
Pictuary) instead of small thumbnails. Cleaned up unused imports.
PicletDetail: show the monster's canonical key in the metadata list
(monospace, word-break).
The upload endpoint is under the /gradio_api/ prefix, not at the root.
Also wraps the file data in {meta: {_type: 'gradio.FileData'}} per the
Gradio 5.x REST API format that @gradio/client uses internally.
Fix CORS: bypass @gradio/client, use direct REST API with credentials: 'omit'
The Gradio client fetches /config with credentials: 'include', which the
backend Space doesn't allow. Rewrote scan.ts to call the Gradio REST API
directly (upload → POST /gradio_api/call/scan → SSE result stream), with
credentials: 'omit' on every fetch to avoid the CORS check entirely.
Scanner.svelte simplified: no more getBackendClient, just scanPhoto().
- Rework App.svelte: lazy Gradio connection, no boot-time AI clients
- New Scanner: upload → downscale → /scan → render result
- New services: dataset.ts (CDN reads), scan.ts (lazy backend client + 90s timeout)
- New stores/data.ts: shared reactive cache + optimistic insert (no refetch)
- New utils: image.ts (downscale), rarity.ts (rarity→tier)
- New types for Monster, ScanResult, LeaderboardEntry, UserRecord, Stats
- EXPLANATION.md: full codebase architecture doc
- SIMPLIFICATION_PLAN.md: remove/keep/add plan for the gut