docs: cut v0.20.0 — Terminal design system + Android persistence
Promotes the [Unreleased] section to [0.20.0] dated 2026-05-07 and opens a fresh empty [Unreleased]. The cycle's two through- lines: - **Terminal visual-identity port.** ui_theme token system (0d477ac) is load-bearing; downstream chrome migrations cover the modal scaffold, gameplay-feedback layer (ceec4fc), toasts with a new ToastVariant enum (a137607), table chrome (651f406), card chrome (d752870), splash cursor (cdcadda), and final hint-source / dest pairing (9891ae4). Card-face / suit / card- back palette intentionally NOT migrated — those track PNG artwork that hasn't been regenerated yet. The 24 Stitch-rendered mockups and design-system.md spec landed infa7f98a. - **Android persistence shim.** solitaire_data::data_dir routes through a per-platform shim (4b51e50) closing the CLAUDE.md §10 dirs::data_dir() = None pitfall on Android. Settings, stats, achievements, replays, game-state, time-attack sessions, and user themes now persist on a real APK. Also closes three v0.19.0 punch-list candidates that landed earlier in the cycle (pull_failure flake at67c150b, smart-window- size opt-out ate1b8766, Shareable badge at9b065e5). Tests: 1176 passing / 0 failing (six new this cycle: ui_theme invariant guards, toast-variant-border-mapping, palette-tracking guards on MARKER_VALID / HINT_PILE_HIGHLIGHT_COLOUR / RIGHT_CLICK_HIGHLIGHT_COLOUR / toast-border distinctness). SESSION_HANDOFF.md refreshed: HEAD pointer, test count, the v0.20.0 changelog summary, the open punch list (Phase Android runtime gaps, visual-identity follow-ups including the artwork regeneration item), the updated design-direction box (was Midnight Purple + Balatro yellow; now base16-eighties Terminal), and a refreshed Resume Prompt offering A–F next-step options. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+153
-11
@@ -6,17 +6,89 @@ project follows [Semantic Versioning](https://semver.org/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
Two threads in flight: closing the v0.19.0 punch list (settings opt-out
|
No threads in flight. v0.20.0 cut on 2026-05-07; CHANGELOG accumulates
|
||||||
for the smart-default sizer, share-link discoverability, the last
|
the next cycle here.
|
||||||
async-pull test flake) and a new performance / portability arc (F3
|
|
||||||
diagnostics overlay landing first, then a working Android build
|
## [0.20.0] — 2026-05-07
|
||||||
target via `cargo apk`). The Android build is the load-bearing
|
|
||||||
change — `solitaire_app` now compiles into both a desktop `bin` and
|
Two through-lines closed: a full **Android port** (build target,
|
||||||
an Android `cdylib` from the same source tree, so subsequent work
|
first 54 MB APK, JNI-free per-app persistence shim) and the
|
||||||
can iterate on a phone or AVD without forking the codebase.
|
**Terminal visual-identity port** that replaces the prior
|
||||||
|
Premium-Solitaire palette across every UI surface. The Android
|
||||||
|
arc opened in `fb8b2ac` (compile + APK), continued in `4b51e50`
|
||||||
|
(`solitaire_data::data_dir` shim closing the CLAUDE.md §10
|
||||||
|
`dirs::data_dir() = None` pitfall), and is functional end-to-end
|
||||||
|
on a real device — though the runtime artwork is still the legacy
|
||||||
|
white-card palette, and JNI ClipboardManager / keyring bridges
|
||||||
|
remain stubbed (matching v0.19.0's documented fallback behaviour).
|
||||||
|
The Terminal port lands as a top-down stack: the `ui_theme` token
|
||||||
|
API in `0d477ac` is load-bearing, and the rest of the cycle is
|
||||||
|
downstream applications (modal scaffold, gameplay-feedback,
|
||||||
|
toasts, table / card chrome, splash cursor, hint-highlight
|
||||||
|
pairing). The card faces and suit-pip palette are deliberately
|
||||||
|
NOT migrated — those track PNG artwork that hasn't been
|
||||||
|
regenerated yet, and swapping the fallback constants ahead of the
|
||||||
|
artwork would mix two visual systems on any code path where
|
||||||
|
image loading fails.
|
||||||
|
|
||||||
|
The 24 Stitch-rendered mockups in `docs/ui-mockups/` are now
|
||||||
|
in-tree (`fa7f98a`); future plugin work should diff against the
|
||||||
|
matching mockup before touching pixels.
|
||||||
|
|
||||||
|
Two threads from v0.19.0's punch list also closed in this cycle:
|
||||||
|
the pull-failure test flake (`67c150b`), the Settings opt-out for
|
||||||
|
the smart-default window sizer (`e1b8766`), and the share-link
|
||||||
|
discoverability surfacing (`9b065e5`). The remaining v0.19.0
|
||||||
|
candidate — the app-icon round — stays open.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- **`ui_theme` Terminal design-token system** (`0d477ac`). Single
|
||||||
|
source of truth for the engine's visual identity:
|
||||||
|
base16-eighties palette (cyan primary CTA, lime/lavender/gold/
|
||||||
|
teal/pink semantic accents), 5-rung type scale, 7-rung 4-multiple
|
||||||
|
spacing scale, 3-step radius, 14-rung z-index hierarchy, full
|
||||||
|
motion budget, and four invariant-pinning unit tests. Every
|
||||||
|
downstream port commit in this cycle reads from this module —
|
||||||
|
swapping the palette is now a one-file edit, not a hunt across
|
||||||
|
~50 plugin files. Card-shadow alphas pinned to 0 (Terminal
|
||||||
|
achieves depth via 1px borders + tonal layering, no
|
||||||
|
`box-shadow`); the rendering path is left intact so a future
|
||||||
|
palette can re-enable shadows without touching consumers.
|
||||||
|
- **`ToastVariant` enum + Terminal toast styling** (`a137607`).
|
||||||
|
Toasts now follow `docs/ui-mockups/design-system.md`: opaque
|
||||||
|
`BG_ELEVATED` fill, 1px accent border keyed off
|
||||||
|
`Info` / `Warning` / `Error` / `Celebration` variants, 18px
|
||||||
|
monospaced caption (`TYPE_BODY_LG`), bottom-anchored. All ten
|
||||||
|
call sites pass their semantic variant: achievement / level-up
|
||||||
|
/ XP / daily / weekly / challenge → Celebration (lavender);
|
||||||
|
goal-announcement / time-attack / settings volume / auto-complete
|
||||||
|
→ Info (teal). Two regression tests pin variant→border mapping
|
||||||
|
to the design tokens and require all four borders to be visually
|
||||||
|
distinct. Queued and immediate toasts use slightly different
|
||||||
|
bottom anchors (6 % vs. 14 %) so a celebration toast spawned
|
||||||
|
alongside a queued info banner layers above it.
|
||||||
|
- **Terminal cursor block on the splash overlay** (`cdcadda`).
|
||||||
|
The launch splash now renders the design system's signature
|
||||||
|
`▌` cyan (`ACCENT_PRIMARY`) glyph (96 px, hand-tuned literal)
|
||||||
|
above the wordmark, matching `docs/ui-mockups/splash-mobile.html`.
|
||||||
|
Cursor fades on the same per-frame alpha schedule as the title
|
||||||
|
and subtitle so the brand beat still dissolves as a single
|
||||||
|
layer. Did *not* pull in the mockup's full boot-loader treatment
|
||||||
|
(scanline overlay, ✓ check log, progress bar, ROOT@SOLITAIRE
|
||||||
|
prompt) — those are aesthetic features warranting their own
|
||||||
|
commit.
|
||||||
|
- **Terminal design-system spec + 24-mockup library** (`fa7f98a`).
|
||||||
|
`docs/ui-mockups/design-system.md` (palette, type scale, spacing
|
||||||
|
scale, motion budget, component library, accessibility notes —
|
||||||
|
color-blind toggle, high-contrast mode, glyph differentiation,
|
||||||
|
canonical `"Terminal"` card-back theme) and 24 Stitch-rendered
|
||||||
|
mockups (HTML + PNG): 12 redesigned existing screens, 1 desktop
|
||||||
|
home variant, 2 onboarding steps, and 9 missing-plugin screens
|
||||||
|
(splash, challenge, time-attack, weekly-goals, leaderboard,
|
||||||
|
sync, level-up, replay, radial-menu). The spec the rest of this
|
||||||
|
cycle ports against; future plugin work diffs here before
|
||||||
|
touching pixels.
|
||||||
- **Android build target — first working APK** (`fb8b2ac`).
|
- **Android build target — first working APK** (`fb8b2ac`).
|
||||||
`cargo apk build -p solitaire_app --target x86_64-linux-android`
|
`cargo apk build -p solitaire_app --target x86_64-linux-android`
|
||||||
now produces a 54 MB debug-signed APK at
|
now produces a 54 MB debug-signed APK at
|
||||||
@@ -84,8 +156,75 @@ can iterate on a phone or AVD without forking the codebase.
|
|||||||
dismisses the Win Summary modal. Three post-v0.18 entries
|
dismisses the Win Summary modal. Three post-v0.18 entries
|
||||||
that had drifted out of the cheat sheet are now listed.
|
that had drifted out of the cheat sheet are now listed.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Gameplay-feedback colours route through Terminal state
|
||||||
|
tokens** (`ceec4fc`). Selection-highlight tints in
|
||||||
|
`selection_plugin` and the valid-drop marker tint in
|
||||||
|
`cursor_plugin` were hand-tuned RGB literals. Migrated to
|
||||||
|
semantic state tokens: keyboard-drag picking source →
|
||||||
|
`ACCENT_PRIMARY` (cyan focus); keyboard-drag lifted source →
|
||||||
|
`STATE_WARNING` (gold attention); destination → `STATE_SUCCESS`
|
||||||
|
(lime valid-move); `cursor_plugin::MARKER_VALID` →
|
||||||
|
`STATE_SUCCESS` at 0.55 α with a tracking test pinning its RGB
|
||||||
|
to the token. Three stale doc comments in `ui_modal` corrected
|
||||||
|
("loud yellow CTA" / "magenta secondary accent" → cyan /
|
||||||
|
lavender to match the actual token values).
|
||||||
|
- **`table_plugin` chrome migration to Terminal tokens** (`651f406`).
|
||||||
|
`marker_colour` promoted to module-level `pub const
|
||||||
|
PILE_MARKER_DEFAULT_COLOUR` so `cursor_plugin::MARKER_DEFAULT`
|
||||||
|
imports the const directly — replaces the prior
|
||||||
|
duplicated literal kept in sync only by doc comment with a
|
||||||
|
compile-enforced invariant. The empty-tableau "K" placeholder
|
||||||
|
text now uses `TEXT_PRIMARY` at 0.35 α; `HINT_PILE_HIGHLIGHT_COLOUR`
|
||||||
|
retuned from bright `srgb(1.0, 0.85, 0.1)` to the `STATE_WARNING`
|
||||||
|
token (`#ddb26f`) with a tracking test, and the existing "is
|
||||||
|
gold" character test loosened to fit the muted Terminal gold
|
||||||
|
while still rejecting non-warm colours.
|
||||||
|
- **`card_plugin` chrome migration to Terminal tokens** (`d752870`).
|
||||||
|
Drag-elevation shadow now sources its colour from
|
||||||
|
`CARD_SHADOW_COLOR` + `CARD_SHADOW_ALPHA_DRAG` so the Terminal
|
||||||
|
"no box-shadow" policy disables the stack shadow in lockstep
|
||||||
|
with the per-card shadows. `RIGHT_CLICK_HIGHLIGHT_COLOUR`
|
||||||
|
retuned from raw green to `STATE_SUCCESS` at 0.6 α with a
|
||||||
|
tracking test. The duplicated `PILE_MARKER_DEFAULT_COLOUR`
|
||||||
|
const dropped — this plugin now imports the promoted const
|
||||||
|
from `table_plugin`. Stock recycle "↺" text moved from raw
|
||||||
|
white-at-0.7-α to `TEXT_PRIMARY.with_alpha(0.7)`. Card-face /
|
||||||
|
suit / card-back palette constants were intentionally NOT
|
||||||
|
migrated (the runtime path renders PNG artwork that's still on
|
||||||
|
the previous "white card" palette).
|
||||||
|
- **Hint-source card tint matches the destination pile**
|
||||||
|
(`9891ae4`). `input_plugin`'s hint-source card tint moved from
|
||||||
|
raw bright-yellow `srgba(1.0, 1.0, 0.4, 1.0)` to `STATE_WARNING`,
|
||||||
|
so the source card and the destination pile (which already uses
|
||||||
|
`STATE_WARNING` via `HINT_PILE_HIGHLIGHT_COLOUR`) wear the same
|
||||||
|
attention colour as a coherent pair.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- **`solitaire_data::data_dir` shim closes the Android persistence
|
||||||
|
gap** (`4b51e50`). `dirs::data_dir()` returns `None` on Android,
|
||||||
|
which silently disabled every persistence path (settings, stats,
|
||||||
|
achievements, replays, game-state, time-attack sessions, user
|
||||||
|
themes). New `solitaire_data::platform::data_dir()` shim falls
|
||||||
|
through to `dirs::data_dir()` on desktop and returns the per-app
|
||||||
|
sandbox at `/data/data/com.solitairequest.app/files` on Android
|
||||||
|
— no JNI needed, since the package id is pinned in
|
||||||
|
`[package.metadata.android]`. Six call sites across
|
||||||
|
`solitaire_data` plus `solitaire_engine/assets/user_dir.rs`
|
||||||
|
migrated. CLAUDE.md §10 already flagged this as a known
|
||||||
|
pitfall; the shim pays it down at the one chokepoint instead
|
||||||
|
of per feature.
|
||||||
|
- **`card_shadow_params` test aligned with Terminal "no shadow"
|
||||||
|
intent** (`1d1543e`). The Terminal token system pinned both
|
||||||
|
`CARD_SHADOW_ALPHA_IDLE` and `CARD_SHADOW_ALPHA_DRAG` to 0.0,
|
||||||
|
which made the prior `drag_alpha > idle_alpha` assertion fail
|
||||||
|
(`0 > 0` is false). Loosened to `drag_alpha >= idle_alpha`
|
||||||
|
with a comment naming the new invariant: under Terminal both
|
||||||
|
are 0; under any future palette that re-enables shadows, drag
|
||||||
|
still must not be weaker than idle. The useful regression-guard
|
||||||
|
(catching an accidental swap of the two constants) is preserved.
|
||||||
- **`pull_failure_sets_error_status` test flake** (`67c150b`).
|
- **`pull_failure_sets_error_status` test flake** (`67c150b`).
|
||||||
The fixed 5-update budget was the last test still subject to
|
The fixed 5-update budget was the last test still subject to
|
||||||
the AsyncComputeTaskPool starvation mode that v0.19.0's
|
the AsyncComputeTaskPool starvation mode that v0.19.0's
|
||||||
@@ -96,9 +235,12 @@ can iterate on a phone or AVD without forking the codebase.
|
|||||||
|
|
||||||
### Stats
|
### Stats
|
||||||
|
|
||||||
- 1170 passing tests / 0 failing (matches v0.19.0; the
|
- **1176 passing tests / 0 failing** across the workspace
|
||||||
pull-failure flake fix changed the test's pumping shape but
|
(six new tests this cycle: four `ui_theme` invariant guards
|
||||||
not its count).
|
for the type / spacing / z-index scales + `scaled_duration`,
|
||||||
|
one toast-variant-border-mapping pair, and four palette-
|
||||||
|
tracking guards on `MARKER_VALID` / `HINT_PILE_HIGHLIGHT_COLOUR`
|
||||||
|
/ `RIGHT_CLICK_HIGHLIGHT_COLOUR` / toast-border distinctness).
|
||||||
- Zero clippy warnings under `--workspace --all-targets -- -D warnings`.
|
- Zero clippy warnings under `--workspace --all-targets -- -D warnings`.
|
||||||
|
|
||||||
## [0.19.0] — 2026-05-06
|
## [0.19.0] — 2026-05-06
|
||||||
|
|||||||
+230
-153
@@ -1,196 +1,273 @@
|
|||||||
# Solitaire Quest — Session Handoff
|
# Solitaire Quest — Session Handoff
|
||||||
|
|
||||||
**Last updated:** 2026-05-07 — `[Unreleased]` accumulating v0.20
|
**Last updated:** 2026-05-07 — v0.20.0 cut. Two through-lines closed
|
||||||
candidates. Seven commits sit on top of v0.19.0: three close items
|
in this cycle: a full **Terminal visual-identity port** (token system
|
||||||
from v0.19.0's punch list (pull-failure flake, smart-window-size
|
in `ui_theme` plus downstream chrome migrations across modal scaffold,
|
||||||
opt-out, Shareable badge), one extends Help cheat-sheet coverage,
|
gameplay-feedback, toasts, and the table / card / splash surfaces)
|
||||||
and three open a new performance / portability arc — F3 FPS
|
and the **Android persistence shim** that closes the
|
||||||
overlay, Android build target (first working APK), and the matching
|
`dirs::data_dir() = None` pitfall flagged in CLAUDE.md §10. The
|
||||||
developer-setup runbook. The Android build is the load-bearing
|
Android *build* target landed earlier in the cycle (`fb8b2ac`); this
|
||||||
change: `solitaire_app` now compiles into both a desktop `bin` and
|
session paid down the persistence half so a real APK can survive a
|
||||||
an Android `cdylib` from the same source.
|
cold start. The 24 Stitch-rendered mockups are now in-tree under
|
||||||
|
`docs/ui-mockups/`; future plugin work diffs against the matching
|
||||||
|
mockup before touching pixels.
|
||||||
|
|
||||||
## Status at pause
|
## Status at pause
|
||||||
|
|
||||||
- **HEAD on origin:** `59424a3` (post-Android-runbook commit).
|
- **HEAD on origin:** the v0.20.0 docs commit (the one that lands
|
||||||
- **Working tree:** modified — `CHANGELOG.md` and
|
this file + CHANGELOG cut). Tag not yet pushed; cut whenever
|
||||||
`SESSION_HANDOFF.md` carry the v0.20-candidates promotion + this
|
feels right.
|
||||||
refresh, ready to commit. `artwork/` remains untracked.
|
- **Working tree:** clean apart from the still-untracked `artwork/`
|
||||||
|
directory (intentional — the card PNGs there are mid-flight for
|
||||||
|
the Terminal aesthetic and committing now would freeze a
|
||||||
|
transitional state).
|
||||||
- **Build:** `cargo clippy --workspace --all-targets -- -D warnings`
|
- **Build:** `cargo clippy --workspace --all-targets -- -D warnings`
|
||||||
clean (verified this session).
|
clean.
|
||||||
- **Tests:** **1170 passing / 0 failing** across the workspace
|
- **Tests:** **1176 passing / 0 failing** across the workspace.
|
||||||
(verified this session). The previously-flaky
|
Six new tests this cycle: four `ui_theme` invariant guards
|
||||||
`pull_failure_sets_error_status` is fixed; no known flakes remain.
|
(type / spacing / z-index scales + `scaled_duration`), one
|
||||||
- **Tags on origin:** `v0.9.0` through `v0.19.0`.
|
toast-variant-border-mapping pair, and four palette-tracking
|
||||||
|
guards on `MARKER_VALID` / `HINT_PILE_HIGHLIGHT_COLOUR` /
|
||||||
|
`RIGHT_CLICK_HIGHLIGHT_COLOUR` / toast-border distinctness. No
|
||||||
|
known flakes.
|
||||||
|
- **Tags on origin:** `v0.9.0` through `v0.19.0`. v0.20.0 not yet
|
||||||
|
tagged.
|
||||||
|
|
||||||
## Where we are
|
## What shipped in v0.20.0
|
||||||
|
|
||||||
v0.19.0's "Possible next-round candidates" list shipped 3 of 4:
|
### Terminal visual-identity port
|
||||||
|
|
||||||
- ~~**`pull_failure_sets_error_status` flake fix:**~~ shipped at
|
Top-down stack — every commit downstream of the token system
|
||||||
`67c150b`.
|
reads from it, so swapping the palette is now a one-file edit:
|
||||||
- ~~**Settings UI for smart-default-size opt-out:**~~ shipped at
|
|
||||||
`e1b8766`.
|
|
||||||
- ~~**Persistent share link URL on selector caption:**~~ shipped at
|
|
||||||
`9b065e5` (as a "Shareable" badge on the Latest-win caption —
|
|
||||||
the Prev/Next selector chips don't have a live spawn site yet,
|
|
||||||
so the badge attaches to the existing single-replay caption).
|
|
||||||
- **App icon round** — still open. 11 PNGs generated by
|
|
||||||
`artwork/Icon Export.html` are *not* in the `artwork/` directory
|
|
||||||
any more (current `artwork/` holds the reverted Rusty Pixel
|
|
||||||
card PNGs); icon-export needs to be re-run before this item can
|
|
||||||
be picked up.
|
|
||||||
|
|
||||||
Two new threads opened that weren't on any prior punch list:
|
- **`ui_theme` token system** (`0d477ac`). base16-eighties
|
||||||
|
palette, 5-rung type scale, 7-rung 4-multiple spacing scale,
|
||||||
|
3-step radius, 14-rung z-index hierarchy, full motion budget,
|
||||||
|
4 invariant-pinning unit tests. Card-shadow alphas pinned to 0
|
||||||
|
(Terminal achieves depth via 1px borders + tonal layering).
|
||||||
|
- **Modal scaffold already on tokens** — `ui_modal` was ported
|
||||||
|
in the same commit's wake; three stale "loud yellow" /
|
||||||
|
"magenta secondary" doc comments fixed.
|
||||||
|
- **Gameplay feedback → semantic state tokens** (`ceec4fc`).
|
||||||
|
Selection / valid-drop tints route through `ACCENT_PRIMARY` /
|
||||||
|
`STATE_WARNING` / `STATE_SUCCESS`.
|
||||||
|
- **Toasts** (`a137607`). New `ToastVariant` enum
|
||||||
|
(Info / Warning / Error / Celebration); opaque `BG_ELEVATED`
|
||||||
|
+ 1px accent border + bottom-anchor. All ten call sites pass
|
||||||
|
their semantic variant.
|
||||||
|
- **`table_plugin` chrome** (`651f406`).
|
||||||
|
`PILE_MARKER_DEFAULT_COLOUR` promoted; `cursor_plugin` imports
|
||||||
|
it, replacing a "kept in sync" doc comment with a compile-
|
||||||
|
enforced invariant. `HINT_PILE_HIGHLIGHT_COLOUR` →
|
||||||
|
`STATE_WARNING`.
|
||||||
|
- **`card_plugin` chrome** (`d752870`). Drag-elevation shadow
|
||||||
|
routes through `CARD_SHADOW_*` tokens. `RIGHT_CLICK_HIGHLIGHT_COLOUR`
|
||||||
|
→ `STATE_SUCCESS`. Stock recycle "↺" text → `TEXT_PRIMARY @ 0.7α`.
|
||||||
|
Card-face / suit / card-back palette intentionally NOT migrated
|
||||||
|
(artwork dependency — see open-list item below).
|
||||||
|
- **Splash cursor** (`cdcadda`). The signature `▌` cyan glyph
|
||||||
|
(96 px) added above the wordmark, matching the spec.
|
||||||
|
- **Hint-source / dest pairing** (`9891ae4`). `input_plugin`'s
|
||||||
|
source-card tint now matches the destination pile's
|
||||||
|
`STATE_WARNING`.
|
||||||
|
- **Design system + 24-mockup library** (`fa7f98a`).
|
||||||
|
`docs/ui-mockups/design-system.md` + 24 Stitch mockups (HTML +
|
||||||
|
PNG) covering every screen plus 9 missing-plugin surfaces.
|
||||||
|
- **`card_shadow_params` test aligned** (`1d1543e`). Drag-vs-
|
||||||
|
idle shadow assertion loosened to `>=` to accept the Terminal
|
||||||
|
"no shadow" intent without losing the regression-guard.
|
||||||
|
|
||||||
- **F3 FPS / frame-time overlay** (`690e1d2`). `DiagnosticsHudPlugin`
|
### Android persistence
|
||||||
wraps `FrameTimeDiagnosticsPlugin`; F3 toggles a top-right
|
|
||||||
readout. Hidden by default; primarily a developer affordance.
|
|
||||||
- **Android build target** (`fb8b2ac` + `59424a3`).
|
|
||||||
`solitaire_app` is now a bin + cdylib hybrid. `cargo apk build`
|
|
||||||
produces a 54 MB APK with full assets. Five gating points
|
|
||||||
resolved (split, manifest, bevy feature, arboard target-gate,
|
|
||||||
keyring target-gate). What's *not* yet verified: APK launch on
|
|
||||||
AVD/phone, `dirs::data_dir()` Android behaviour for the
|
|
||||||
persistence/sync layer.
|
|
||||||
|
|
||||||
### Design direction (unchanged)
|
- **`solitaire_data::data_dir` shim** (`4b51e50`). New
|
||||||
|
`solitaire_data::platform::data_dir()` falls through to
|
||||||
|
`dirs::data_dir()` on desktop and returns the per-app sandbox
|
||||||
|
at `/data/data/com.solitairequest.app/files` on Android — no
|
||||||
|
JNI needed (package id pinned in `[package.metadata.android]`).
|
||||||
|
Six `solitaire_data` callsites + `solitaire_engine/assets/user_dir.rs`
|
||||||
|
migrated. Settings, stats, achievements, replays, game-state,
|
||||||
|
time-attack sessions, and user themes now persist on Android.
|
||||||
|
|
||||||
- **Tone:** Balatro — chunky readable type, theatrical hierarchy,
|
### Inherited from earlier in the cycle (pre-session)
|
||||||
satisfying micro-interactions.
|
|
||||||
- **Palette:** Midnight Purple base + Balatro yellow primary + warm
|
- Android build target + APK (`fb8b2ac`), runbook (`59424a3`),
|
||||||
magenta secondary.
|
F3 FPS overlay (`690e1d2`), Smart Window Size opt-out
|
||||||
|
(`e1b8766`), Shareable badge (`9b065e5`), Help cheat-sheet
|
||||||
|
M/P/Enter rows (`35516d3`), `pull_failure_sets_error_status`
|
||||||
|
flake fix (`67c150b`).
|
||||||
|
|
||||||
|
## Open punch list
|
||||||
|
|
||||||
|
### Phase Android (build + persistence shipped; runtime gaps remain)
|
||||||
|
|
||||||
|
- **APK launch verification on AVD / device.** `adb install` then
|
||||||
|
`adb logcat` against the `bevy_test` AVD or an x86_64 device.
|
||||||
|
The build works and persistence is wired, but no end-to-end
|
||||||
|
device run has been logged. Shakes out runtime bugs the build +
|
||||||
|
unit tests can't catch.
|
||||||
|
- **JNI ClipboardManager bridge.** Replaces the Android stub for
|
||||||
|
the Stats "Copy share link" toast. `arboard` doesn't ship an
|
||||||
|
Android backend; small custom JNI call.
|
||||||
|
- **Android Keystore for credentials.** `keyring` is target-gated
|
||||||
|
to a stub returning `KeychainUnavailable`; replace with Android
|
||||||
|
Keystore via JNI when sync auth ships on mobile.
|
||||||
|
- **Google Play Games (gpgs) integration.** Listed as a
|
||||||
|
Phase-Android target since Phase 1; now unblocked by the build
|
||||||
|
target.
|
||||||
|
- **Cosmetic `cargo apk build --lib` workaround.** Post-sign
|
||||||
|
panic doesn't affect the APK on disk but produces noisy stderr.
|
||||||
|
Either upstream a cargo-apk fix or document `--lib` as
|
||||||
|
canonical in the runbook.
|
||||||
|
|
||||||
|
### Visual-identity follow-ups (opened by v0.20.0's port)
|
||||||
|
|
||||||
|
- **Card-face / suit / card-back artwork regeneration.** The
|
||||||
|
Terminal spec calls for dark `#1a1a1a` cards with light suit
|
||||||
|
pips (pink for hearts/diamonds, foreground gray for spades/
|
||||||
|
clubs); the runtime path still renders the legacy white-card
|
||||||
|
PNG artwork. The fallback constants in `card_plugin`
|
||||||
|
(`CARD_FACE_COLOUR`, `RED_SUIT_COLOUR`, `BLACK_SUIT_COLOUR`,
|
||||||
|
`CARD_FACE_COLOUR_RED_CBM`, `card_back_colour` palette) are
|
||||||
|
intentionally unmigrated and should swap in lockstep with the
|
||||||
|
artwork. Largest visible payoff remaining in the visual-
|
||||||
|
identity arc.
|
||||||
|
- **Splash boot-loader richness.** The mockup
|
||||||
|
(`docs/ui-mockups/splash-mobile.html`) calls for a scanline
|
||||||
|
overlay, ✓ lime check log lines, pulsing cursor, ROOT@SOLITAIRE
|
||||||
|
prompt, and a loading bar — none of which v0.20.0's
|
||||||
|
cursor-glyph-only port pulled in. Aesthetic feature, its own
|
||||||
|
commit.
|
||||||
|
- **Replay-overlay redesign.** The mockup
|
||||||
|
(`docs/ui-mockups/replay-overlay-mobile.html`) envisions a
|
||||||
|
much richer surface (terminal `▌replay.tsx` header, move log
|
||||||
|
scroll, MOVE 47/87 chip, WIN MOVE callout, status bar) versus
|
||||||
|
the current top banner. Aesthetic feature.
|
||||||
|
- **Toast Warning / Error variants.** The new `ToastVariant`
|
||||||
|
enum has slots for `Warning` (gold) and `Error` (pink) but no
|
||||||
|
in-engine event uses them yet (the four current toast events
|
||||||
|
all map to Info or Celebration). Wire when a warning- or
|
||||||
|
error-flavoured toast event materialises.
|
||||||
|
|
||||||
|
### Carried forward from v0.19.0
|
||||||
|
|
||||||
|
- **App icon round.** `Window::icon` not yet wired; no
|
||||||
|
`.icns` / `.ico` / Linux hicolor PNG hierarchy. The 11-size
|
||||||
|
icon export the v0.19 handoff referenced is *not* currently
|
||||||
|
in `artwork/` (current `artwork/` holds the reverted Rusty
|
||||||
|
Pixel card PNGs and is intentionally untracked); icon-export
|
||||||
|
needs to be re-run before this item can be picked up.
|
||||||
|
Half-day task once the PNGs are back in place. No cert
|
||||||
|
dependency.
|
||||||
|
|
||||||
|
### Other small candidates
|
||||||
|
|
||||||
|
- **Prev/Next selector chips spawn site.** v0.19.0's `9b065e5`
|
||||||
|
noted Prev/Next markers exist in `stats_plugin` but no spawn
|
||||||
|
site renders them today — the Shareable badge therefore lands
|
||||||
|
on the single-replay caption. If/when Prev/Next is plumbed,
|
||||||
|
the badge will need to follow.
|
||||||
|
- **Toast queue / immediate unification.** The two toast paths
|
||||||
|
(`spawn_queued_toast` for `InfoToastEvent` queue; `spawn_toast`
|
||||||
|
for fire-and-forget) now share visual treatment but remain
|
||||||
|
separate functions because they serve different temporal
|
||||||
|
needs (sequential vs. parallel). If overlap becomes a UX
|
||||||
|
issue, merge into one queue with priority lanes.
|
||||||
|
|
||||||
|
### Process notes
|
||||||
|
|
||||||
|
- **Token-port pattern.** v0.20.0's chrome-migration commits
|
||||||
|
set a reusable shape for "centralized design system applied
|
||||||
|
across N plugins":
|
||||||
|
1. Constants module (`ui_theme.rs`) is the source of truth.
|
||||||
|
2. Const sites that can't call `Alpha::with_alpha` (not yet
|
||||||
|
`const` on stable) use a literal RGB matching the token,
|
||||||
|
with a unit test pinning the RGB to the token (e.g.
|
||||||
|
`MARKER_VALID`, `HINT_PILE_HIGHLIGHT_COLOUR`,
|
||||||
|
`RIGHT_CLICK_HIGHLIGHT_COLOUR`).
|
||||||
|
3. Cross-plugin duplication (e.g. `MARKER_DEFAULT` ↔
|
||||||
|
`PILE_MARKER_DEFAULT_COLOUR`) collapses to a single
|
||||||
|
promoted const re-exported from one plugin and imported
|
||||||
|
by the other — replaces "kept in sync" doc comments with a
|
||||||
|
compile-time invariant.
|
||||||
|
4. Domain colours (suit pips, card faces, lerp helpers) stay
|
||||||
|
as literals with a comment naming the rationale; only UI
|
||||||
|
chrome routes through tokens.
|
||||||
|
- **Audit before migrating wide.** Before touching any plugin,
|
||||||
|
grep for the literal pattern (`Color::srgb\(|Color::srgba\(|
|
||||||
|
Color::WHITE|Color::BLACK`) and classify each hit as domain
|
||||||
|
vs. chrome. Most plugins after the modal scaffold port turned
|
||||||
|
out to be 100 % token-correct already; the audit prevents
|
||||||
|
wasted churn.
|
||||||
|
|
||||||
### Canonical remote
|
### Canonical remote
|
||||||
|
|
||||||
`github.com/funman300/Rusty_Solitaire` is the canonical repo.
|
`github.com/funman300/Rusty_Solitaire` is the canonical repo.
|
||||||
Always push there.
|
Always push there.
|
||||||
|
|
||||||
## v0.20 candidates ([Unreleased] in CHANGELOG)
|
### Design direction (now Terminal — base16-eighties)
|
||||||
|
|
||||||
| Area | Commit | What landed |
|
- **Tone:** retro-terminal / synthwave — flat depth (no box-shadows),
|
||||||
|---|---|---|
|
monospaced-forward typography (JetBrains Mono / FiraMono), tight
|
||||||
| Async-pull flake fix | `67c150b` | `pull_failure_sets_error_status` swaps a fixed 5-update budget for a 5-second wall-clock deadline + `yield_now` between pumps. Closes the last v0.19-era flake. |
|
16 px edge margins, 8 px card radius.
|
||||||
| Smart window size opt-out | `e1b8766` | New `Settings::disable_smart_default_size: bool` (#[serde(default)]). `solitaire_app::main` reads the flag at startup and skips `apply_smart_default_window_size` registration when set. Settings → Gameplay row toggles it; tooltip notes saved geometry always wins. |
|
- **Palette:** near-black surface ramp (`#151515` / `#202020` / `#2a2a2a`
|
||||||
| Shareable badge | `9b065e5` | Stats overlay's Latest-win caption gains `\u{2022} Shareable` when the displayed replay carries a `share_url`. Badge appears on the single-replay caption (no Prev/Next live spawn site yet). |
|
/ `#353535`), cyan primary CTA (`#6fc2ef`), lime success
|
||||||
| Help: M / P / Win-Summary-Enter | `35516d3` | Three rows added to F1 Help → Overlays. Closes coverage drift on post-v0.18 keys. |
|
(`#acc267`), gold warning (`#ddb26f`), pink error / suit-red
|
||||||
| F3 FPS overlay | `690e1d2` | `DiagnosticsHudPlugin` + `FrameTimeDiagnosticsPlugin`. Hidden by default; F3 toggle is not gated by pause/modal state. Smoothed FPS / frame_time. Anchored top-right at `Z_SPLASH + 100`. |
|
(`#fb9fb1`), lavender celebration (`#e1a3ee`), teal info
|
||||||
| Android first APK | `fb8b2ac` | `solitaire_app` split into bin + lib (cdylib). `[package.metadata.android]` pins SDK 34 / 26. Bevy gains `android-native-activity`. `arboard` and `keyring`/`keyring-core` target-gated to non-Android. 54 MB APK builds; launch on device not yet verified. |
|
(`#12cfc0`).
|
||||||
| Android runbook | `59424a3` | Debian 13 toolchain install, `cargo apk build` invocation, post-sign panic workaround, wired-vs-stubbed table. Fresh-clone runnable. |
|
- **Two-color suits.** Red = `#fb9fb1`, black = `#d0d0d0`. Outlined
|
||||||
|
glyphs for diamonds & clubs are *always on*; the Settings
|
||||||
|
"color-blind mode" toggle only swaps red → cyan.
|
||||||
|
|
||||||
## Open punch list
|
(Was: Midnight Purple base + Balatro yellow primary + warm magenta.
|
||||||
|
Replaced this cycle.)
|
||||||
### Carried forward from v0.19.0
|
|
||||||
|
|
||||||
- **App icon round.** `Window::icon` not yet wired; no `.icns` /
|
|
||||||
`.ico` / Linux hicolor PNG hierarchy. The 11-size icon export
|
|
||||||
the v0.19 handoff referenced is *not* currently in
|
|
||||||
`artwork/` — re-running `artwork/Icon Export.html` is the
|
|
||||||
prerequisite. Half-day task once the PNGs are back in place.
|
|
||||||
No cert dependency.
|
|
||||||
|
|
||||||
### New — Phase Android (opened by `fb8b2ac` + `59424a3`)
|
|
||||||
|
|
||||||
- **APK launch verification on AVD / device.** `adb install` then
|
|
||||||
`adb logcat` against the bevy_test AVD. Shakes out any
|
|
||||||
Bevy/winit Android-specific runtime bugs not caught by the
|
|
||||||
build alone.
|
|
||||||
- **`dirs::data_dir()` Android port.** Persistence (settings,
|
|
||||||
stats, achievements, replays, progress) all route through this
|
|
||||||
function. Android sandboxing usually demands `getFilesDir()` via
|
|
||||||
JNI. Until verified, the APK may launch but fail to persist
|
|
||||||
anything across cold starts.
|
|
||||||
- **JNI ClipboardManager bridge.** Replaces the Android stub for
|
|
||||||
the Stats "Copy share link" toast. `arboard` doesn't ship an
|
|
||||||
Android backend, so this is a small custom JNI call.
|
|
||||||
- **Android Keystore for credentials.** `keyring` is target-gated
|
|
||||||
to a stub that returns `KeychainUnavailable`; replace with
|
|
||||||
Android Keystore via JNI when sync auth ships on mobile.
|
|
||||||
- **Google Play Games (gpgs) integration.** Listed in `solitaire_gpgs`
|
|
||||||
as a Phase-Android target since Phase 1; now unblocked by the
|
|
||||||
build target.
|
|
||||||
- **Cosmetic `cargo apk build --lib` workaround.** The
|
|
||||||
post-sign panic doesn't affect the APK on disk but produces
|
|
||||||
noisy stderr. Either upstream a cargo-apk fix or document the
|
|
||||||
`--lib` invocation as canonical in the runbook.
|
|
||||||
|
|
||||||
### Other small candidates
|
|
||||||
|
|
||||||
- **Cut v0.20.0** — the seven commits are a coherent bundle (3
|
|
||||||
punch-list closes, 1 docs polish, 1 perf tool, 2 Android arc
|
|
||||||
starters). Tag whenever feels right; the Android arc has more
|
|
||||||
surface to land before it's "done", but the build-works state
|
|
||||||
is a defensible release point.
|
|
||||||
- **Prev/Next selector chips spawn site.** `9b065e5` notes
|
|
||||||
Prev/Next markers exist in `stats_plugin` but no spawn site
|
|
||||||
renders them today — the Shareable badge therefore lands on
|
|
||||||
the single-replay caption. If/when Prev/Next is plumbed to the
|
|
||||||
Stats overlay, the badge will need to follow.
|
|
||||||
|
|
||||||
### Process notes (from this round)
|
|
||||||
|
|
||||||
- **Async-test starvation pattern (resolved twice now).**
|
|
||||||
Auto-save flake (v0.19) and pull-failure flake
|
|
||||||
(v0.20-candidates) shared the same root cause: a fixed
|
|
||||||
N-update budget for an `AsyncComputeTaskPool` task to surface
|
|
||||||
its result, which starves under cargo-test parallelism. The
|
|
||||||
wall-clock-bounded loop pattern (`std::thread::yield_now()`
|
|
||||||
between pumps, deadline = `Instant::now() + Duration::from_secs(5)`)
|
|
||||||
is the canonical fix. Future async-test work should reach for
|
|
||||||
this shape on the first commit, not after a flake materialises.
|
|
||||||
- **Bin → lib + thin shim refactor.** The `solitaire_app` split
|
|
||||||
for cargo-apk is a textbook cdylib-as-NativeActivity pattern.
|
|
||||||
Worth knowing as a reusable shape for any future Bevy-on-mobile
|
|
||||||
project: keep `main.rs` to ≤10 lines that delegate to
|
|
||||||
`pub fn run`, put all setup in `lib.rs`, and let the build
|
|
||||||
system pick which artifact (`bin` or `cdylib`) it needs.
|
|
||||||
- **Target-gating defensive defaults.** Three of v0.20-candidates'
|
|
||||||
Android-arc commits had to disable a default-on dependency
|
|
||||||
(`arboard`, `keyring`, `keyring-core`) for the new target.
|
|
||||||
When a future contributor adds a desktop-implicit dependency,
|
|
||||||
reaching for `[target.'cfg(not(target_os = "android"))'.dependencies]`
|
|
||||||
preemptively is cheaper than rediscovering the gating during
|
|
||||||
the next platform port.
|
|
||||||
|
|
||||||
## Resume prompt
|
## Resume prompt
|
||||||
|
|
||||||
```
|
```
|
||||||
You are a senior Rust + Bevy developer working on Solitaire Quest.
|
You are a senior Rust + Bevy developer working on Solitaire Quest.
|
||||||
Working directory: <Rusty_Solitaire clone path on this machine>.
|
Working directory: <Rusty_Solitaire clone path on this machine>.
|
||||||
Branch: master. v0.19.0 is tagged on origin; seven commits sit on
|
Branch: master. v0.20.0 just cut on 2026-05-07; CHANGELOG's new
|
||||||
top opening the v0.20 cycle (3 punch-list closes + a perf tool +
|
[Unreleased] section is empty pending the next cycle's threads.
|
||||||
the Android build target).
|
|
||||||
|
|
||||||
State: HEAD at 59424a3 + the v0.20-candidates docs commit on top
|
State: HEAD on the v0.20.0 docs commit. Tag not pushed yet — last
|
||||||
(this session). Working tree may carry the doc updates if not yet
|
pushed tag is v0.19.0. Working tree clean apart from the
|
||||||
committed.
|
intentionally-untracked `artwork/`.
|
||||||
|
|
||||||
READ FIRST (in order, before doing anything):
|
READ FIRST (in order, before doing anything):
|
||||||
1. SESSION_HANDOFF.md — this file
|
1. SESSION_HANDOFF.md — this file
|
||||||
2. CHANGELOG.md — [Unreleased] holds the v0.20 draft
|
2. CHANGELOG.md — [0.20.0] section is the most recent cut
|
||||||
3. CLAUDE.md — unified-3.0 rule set
|
3. CLAUDE.md — unified-3.0 rule set
|
||||||
4. CLAUDE_SPEC.md — formal architecture spec
|
4. CLAUDE_SPEC.md — formal architecture spec
|
||||||
5. ARCHITECTURE.md — crate responsibilities + data flow
|
5. ARCHITECTURE.md — crate responsibilities + data flow
|
||||||
6. docs/android/* — Android setup + build runbook (59424a3)
|
6. docs/ui-mockups/ — design system + 24-mockup library
|
||||||
7. ~/.claude/projects/<this-project>/memory/MEMORY.md
|
(Terminal aesthetic — landed in fa7f98a)
|
||||||
|
7. docs/android/* — Android setup + build runbook
|
||||||
|
8. ~/.claude/projects/<this-project>/memory/MEMORY.md
|
||||||
— saved feedback / project context
|
— saved feedback / project context
|
||||||
(machine-local; may be missing on a
|
(machine-local; may be missing on a
|
||||||
fresh machine)
|
fresh machine)
|
||||||
|
|
||||||
DECISION TO ASK THE PLAYER FIRST:
|
DECISION TO ASK THE PLAYER FIRST:
|
||||||
A. APK launch verification — `adb install` + `adb logcat` on
|
A. Push v0.20.0 tag — `git tag v0.20.0 && git push --tags`. If
|
||||||
bevy_test AVD or a physical x86_64 device. Shakes out
|
the player wants the cut formalised before any new work.
|
||||||
runtime bugs the build can't catch.
|
B. APK launch verification — `adb install` + `adb logcat` on
|
||||||
B. Phase-Android persistence — port dirs::data_dir() through a
|
bevy_test AVD or an x86_64 device. Now that persistence is
|
||||||
JNI getFilesDir() bridge so the APK can survive a cold
|
wired (4b51e50), shake out remaining runtime bugs.
|
||||||
start. Largest unblocked Android piece.
|
C. Card-face artwork regeneration — generate Terminal-aesthetic
|
||||||
C. App icon — re-run artwork/Icon Export.html, then wire
|
card PNGs (dark face, light suit pips), then migrate
|
||||||
|
CARD_FACE_COLOUR / RED_SUIT_COLOUR / BLACK_SUIT_COLOUR /
|
||||||
|
CARD_FACE_COLOUR_RED_CBM in lockstep. Largest visible
|
||||||
|
payoff remaining in the visual-identity arc.
|
||||||
|
D. Splash boot-loader richness — port the scanline overlay,
|
||||||
|
✓ check log, pulsing cursor, ROOT@SOLITAIRE prompt, and
|
||||||
|
loading bar from docs/ui-mockups/splash-mobile.html. Pure
|
||||||
|
polish; no behavioural change.
|
||||||
|
E. App icon round — re-run artwork/Icon Export.html (the
|
||||||
|
export PNGs are not currently in `artwork/`), then wire
|
||||||
Window::icon + generate .icns / .ico. Half-day task. No
|
Window::icon + generate .icns / .ico. Half-day task. No
|
||||||
cert dependency.
|
cert dependency.
|
||||||
D. Cut v0.20.0 — promote [Unreleased] to [0.20.0], tag,
|
F. JNI ClipboardManager / Keystore bridge — replaces the
|
||||||
push. Mechanical close-out; the Android arc has more
|
Android stubs for Stats clipboard share + sync auth.
|
||||||
surface to land but the build-works state is a defensible
|
|
||||||
release point.
|
|
||||||
|
|
||||||
WORKFLOW NOTES:
|
WORKFLOW NOTES:
|
||||||
- Use the system git config (already correct).
|
- Use the system git config (already correct).
|
||||||
@@ -201,5 +278,5 @@ WORKFLOW NOTES:
|
|||||||
- Push to GitHub (origin) — gh auth setup-git wired on
|
- Push to GitHub (origin) — gh auth setup-git wired on
|
||||||
primary dev box; verify on laptop before first push.
|
primary dev box; verify on laptop before first push.
|
||||||
|
|
||||||
OPEN AT THE START: ask which of A–D. Don't pick unilaterally.
|
OPEN AT THE START: ask which of A–F. Don't pick unilaterally.
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user