feat: in-game theme store — server catalog + verified downloads + install UI #154
Reference in New Issue
Block a user
Delete Branch "feat/theme-store"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 1+2 of the theme-store roadmap: a free catalog served by
solitaire_serverand an in-app browse/install flow. This makes custom themes installable on Android for the first time — the manual drop-a-zip flow can never reach the app-private themes dir there.Server — drop theme zips (+ optional
<id>.pngpreview) intoTHEME_STORE_DIR(compose mounts./theme_store), restart; catalog scanned once at startup with meta-only manifest parse, sha256, 20 MiB cap, best-effort skip of bad archives. Public endpoints:GET /api/themes,/api/themes/{id}/download,/api/themes/{id}/preview.Client —
ThemeStoreClient(solitaire_data) verifies size + sha256 before releasing bytes; the engine plugin then writes the zip atomically intouser_theme_dirand reuses the existing hardenedimport_themepipeline (zip-slip/size/manifest validation) plus in-place registry refresh.UI — Settings → Cosmetic → "Browse theme store" (uses the configured sync server; warns if none). Modal mirrors the leaderboard panel lifecycle.
Wire types — new additive
solitaire_sync::theme_storemodule;SyncPayload/SyncProvider/merge untouched. New deps:sha2(workspace→server+data),serde_jsonas solitaire_sync dev-dep.Tests: 8 server (scan unit + router integration), 3 data↔server round-trip over TCP, 4 client verification units, 6 engine headless UI, 2 DTO round-trips. Workspace tests, clippy
-D warnings, and fmt all green.⚠️ Based on
fix/review-2026-07-07(#153) — merge that first; this diff then shrinks to the theme-store commits only.🤖 Generated with Claude Code
Phase 1+2 of the theme-store roadmap: a free catalog served by solitaire_server and an in-app browse/install flow, making custom themes installable on Android for the first time (the manual drop-a-zip flow can't reach the app-private themes dir there). - solitaire_sync: ThemeCatalogEntry/ThemeCatalogResponse wire types (additive module; SyncPayload and SyncProvider untouched) - solitaire_server: THEME_STORE_DIR scan at startup (meta-only theme.ron parse, sha256, 20 MiB cap, best-effort per archive); public GET /api/themes, /api/themes/{id}/download, /{id}/preview; compose volume + README_SERVER docs - solitaire_data: ThemeStoreClient — catalog fetch + download with mandatory size/sha256 verification before bytes are released - solitaire_engine: ThemeStorePlugin — 'Browse theme store' button in Settings → Cosmetic, modal catalog (leaderboard-style rebuild), download on AsyncComputeTaskPool, atomic .tmp+rename write into user_theme_dir, then the existing hardened import_theme pipeline and an in-place registry refresh New deps: sha2 (workspace, server+data); ron/zip reused in server; serde_json added to solitaire_sync dev-deps for DTO round-trip tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>