docs: update all project docs to reflect Phase 8 + Android work
- CLAUDE.md unified-3.1 → unified-4.0: narrowed error policy, relaxed ECS/embed/API rules, added Android pitfalls, modal conventions (§14), Android build guide (§15), context injection system (§16), auto-hide HUD chrome exception in UI-first rule - ARCHITECTURE.md: Android → Active platform; add Android to sync table; add SafeAreaInsets + HudVisibility to Key Resources; add solitaire_wasm - CLAUDE_SPEC.md: add solitaire_wasm crate; communication: events → events and resources - CLAUDE_PROMPT_PACK.md: fix §8 typo; narrow dep rule to core/sync only - SESSION_HANDOFF.md: add §5b Android UX punch list; resume prompt unified-4.0 - docs/android/PLAYABILITY_TODO.md: add P5 section (UX-1/UX-5b/UX-7/BUG-3) - docs/SESSION_HANDOFF.md: mark as archived (Phase 2 era) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+8
-1
@@ -43,6 +43,7 @@ Solitaire Quest is a cross-platform Klondike Solitaire game written in Rust, tar
|
|||||||
| macOS | Self-hosted server | Full feature set |
|
| macOS | Self-hosted server | Full feature set |
|
||||||
| Windows | Self-hosted server | Full feature set |
|
| Windows | Self-hosted server | Full feature set |
|
||||||
| Linux | Self-hosted server | Full feature set |
|
| Linux | Self-hosted server | Full feature set |
|
||||||
|
| Android | Self-hosted server | Touch input; safe-area insets via JNI; `cargo-apk` build |
|
||||||
|
|
||||||
### Design Principles
|
### Design Principles
|
||||||
|
|
||||||
@@ -322,6 +323,12 @@ struct FontResource(Handle<Font>);
|
|||||||
struct BackgroundImageSet {
|
struct BackgroundImageSet {
|
||||||
handles: Vec<Handle<Image>>, // indices 0–4 match selected_background setting
|
handles: Vec<Handle<Image>>, // indices 0–4 match selected_background setting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OS-reserved edge insets (physical px); zero on desktop
|
||||||
|
struct SafeAreaInsets { top: f32, bottom: f32, left: f32, right: f32 }
|
||||||
|
|
||||||
|
// Whether the HUD band is visible (auto-hide chrome feature)
|
||||||
|
enum HudVisibility { Visible, Hidden }
|
||||||
```
|
```
|
||||||
|
|
||||||
### Key Bevy Events
|
### Key Bevy Events
|
||||||
@@ -900,7 +907,7 @@ All sound effect WAV files are embedded at compile time via `include_bytes!()` i
|
|||||||
| macOS | Primary | Self-hosted server | x86_64 + Apple Silicon (universal binary via `cargo-lipo`) |
|
| macOS | Primary | Self-hosted server | x86_64 + Apple Silicon (universal binary via `cargo-lipo`) |
|
||||||
| Windows | Primary | Self-hosted server | x86_64, MSVC toolchain |
|
| Windows | Primary | Self-hosted server | x86_64, MSVC toolchain |
|
||||||
| Linux | Primary | Self-hosted server | x86_64, tested on Ubuntu 22.04+ and Fedora 39+ |
|
| Linux | Primary | Self-hosted server | x86_64, tested on Ubuntu 22.04+ and Fedora 39+ |
|
||||||
| Android | Stretch | Self-hosted server | `cargo-mobile2`, touch input |
|
| Android | Active | Self-hosted server | `cargo-apk`; touch + long-press + double-tap; safe-area JNI; portrait layout |
|
||||||
| iOS | Stretch | Self-hosted server | `cargo-mobile2`, touch input |
|
| iOS | Stretch | Self-hosted server | `cargo-mobile2`, touch input |
|
||||||
|
|
||||||
Minimum Bevy window size enforced: 800×600. Desktop windows are freely resizable; layout recomputes on `WindowResized`.
|
Minimum Bevy window size enforced: 800×600. Desktop windows are freely resizable; layout recomputes on `WindowResized`.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
version: unified-3.0
|
version: unified-4.0
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -29,8 +29,9 @@ solitaire_sync/ # Shared API + merge logic
|
|||||||
solitaire_data/ # Persistence + sync client
|
solitaire_data/ # Persistence + sync client
|
||||||
solitaire_engine/ # Bevy ECS + UI + gameplay orchestration
|
solitaire_engine/ # Bevy ECS + UI + gameplay orchestration
|
||||||
solitaire_server/ # Axum backend (optional sync layer)
|
solitaire_server/ # Axum backend (optional sync layer)
|
||||||
|
solitaire_wasm/ # WASM bindings for browser-side replay player
|
||||||
solitaire_app/ # Entry binary
|
solitaire_app/ # Entry binary
|
||||||
assets/ # Runtime assets (except audio)
|
assets/ # Runtime assets (except audio + default theme)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -72,12 +73,16 @@ These override all other instructions.
|
|||||||
|
|
||||||
* NO `unwrap()`
|
* NO `unwrap()`
|
||||||
* NO `panic!()` in runtime/game logic
|
* NO `panic!()` in runtime/game logic
|
||||||
* All state transitions:
|
* Core game state mutations MUST return:
|
||||||
|
|
||||||
```rust id="err_model"
|
```rust id="err_model"
|
||||||
Result<T, MoveError>
|
Result<T, MoveError>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Engine / UI state changes follow ECS patterns (Resources, Events) —
|
||||||
|
they do not return `MoveError`
|
||||||
|
* Use `thiserror`-derived types for any new error enums outside `solitaire_core`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2.4 Threading Rules
|
## 2.4 Threading Rules
|
||||||
@@ -126,10 +131,15 @@ trait SyncProvider
|
|||||||
## 3.1 ECS Design
|
## 3.1 ECS Design
|
||||||
|
|
||||||
* systems = single responsibility
|
* systems = single responsibility
|
||||||
* communication = Events only
|
* cross-system communication = Events (fire-and-forget triggers)
|
||||||
* shared state = Resources only
|
* persistent shared state = Resources (polled every frame or on change)
|
||||||
* per-entity state = Components only
|
* per-entity state = Components only
|
||||||
|
|
||||||
|
Events and Resources are both valid communication paths — use Events when
|
||||||
|
the receiver needs to react once; use Resources when the receiver polls
|
||||||
|
or when multiple systems read the same value (e.g. `SafeAreaInsets`,
|
||||||
|
`HudVisibility`, `LayoutResource`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3.2 Game State Authority
|
## 3.2 Game State Authority
|
||||||
@@ -149,11 +159,22 @@ Every player action MUST:
|
|||||||
Keyboard shortcuts are:
|
Keyboard shortcuts are:
|
||||||
→ optional accelerators only
|
→ optional accelerators only
|
||||||
|
|
||||||
|
**Exception — UI chrome gestures:**
|
||||||
|
Tap-to-toggle visibility of UI chrome (e.g. auto-hiding HUD band) is
|
||||||
|
permitted without a visible button. The gesture MUST:
|
||||||
|
* affect only chrome visibility, never game state
|
||||||
|
* restore chrome automatically when any modal opens
|
||||||
|
* be purely additive (game remains fully playable with chrome always visible)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3.4 Layout System
|
## 3.4 Layout System
|
||||||
|
|
||||||
* recompute on `WindowResized`
|
* recompute on `WindowResized`
|
||||||
|
* recompute on `SafeAreaInsets` changed
|
||||||
|
* recompute on `HudVisibility` changed
|
||||||
|
* `compute_layout` MUST accept `hud_visible: bool`; pass `HUD_BAND_HEIGHT`
|
||||||
|
when `true`, `0.0` when `false`
|
||||||
* no fixed resolution assumptions
|
* no fixed resolution assumptions
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -178,11 +199,18 @@ Includes:
|
|||||||
|
|
||||||
## 4.2 Embedded Assets
|
## 4.2 Embedded Assets
|
||||||
|
|
||||||
Only audio:
|
Embed via `include_bytes!()` only when ALL of the following are true:
|
||||||
|
|
||||||
```text id="audio_rule"
|
* the asset is small (< 500 KB uncompressed)
|
||||||
include_bytes!()
|
* it changes rarely (not user-customisable)
|
||||||
```
|
* a missing file would be a hard crash, not a graceful degradation
|
||||||
|
|
||||||
|
Currently embedded:
|
||||||
|
* **Audio** — all `.wav` files in `audio_plugin.rs`
|
||||||
|
* **Default card theme** — shipped via `embedded://` scheme in `ThemePlugin`
|
||||||
|
|
||||||
|
Do NOT embed card face PNGs, background images, or user fonts —
|
||||||
|
these are loaded via `AssetServer` so art can be swapped without recompile.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -210,7 +238,9 @@ Must degrade gracefully under `MinimalPlugins`.
|
|||||||
## 5.2 Public API Rules
|
## 5.2 Public API Rules
|
||||||
|
|
||||||
* prefer `Into<T>` over concrete types
|
* prefer `Into<T>` over concrete types
|
||||||
* all public items require doc comments
|
* publicly exported functions, traits, and non-trivial types require doc comments
|
||||||
|
* simple marker components, newtype wrappers, and internal `pub` items
|
||||||
|
used only within the same crate are exempt from doc comment requirements
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -276,11 +306,13 @@ NEVER commit otherwise
|
|||||||
|
|
||||||
Claude must request confirmation before:
|
Claude must request confirmation before:
|
||||||
|
|
||||||
* adding dependencies
|
* adding dependencies to `solitaire_core` or `solitaire_sync`
|
||||||
* modifying `solitaire_sync`
|
(engine/server crates may add deps without confirmation)
|
||||||
* changing DB schema
|
* modifying `solitaire_sync` types or the `SyncProvider` trait
|
||||||
|
* changing DB schema (migrations are append-only)
|
||||||
* introducing `unsafe`
|
* introducing `unsafe`
|
||||||
* changing merge strategy
|
* changing the merge strategy in `solitaire_sync::merge`
|
||||||
|
* changing the `SyncPayload` wire format (breaking change for existing servers)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -304,10 +336,29 @@ Core is always the source of truth.
|
|||||||
|
|
||||||
Must always be handled explicitly:
|
Must always be handled explicitly:
|
||||||
|
|
||||||
|
**All platforms**
|
||||||
* Bevy `Time` uses `f32`
|
* Bevy `Time` uses `f32`
|
||||||
* `sqlx::migrate!()` path is crate-relative
|
* `sqlx::migrate!()` path is crate-relative
|
||||||
* `dirs::data_dir()` may return `None`
|
* `dirs::data_dir()` may return `None`
|
||||||
* Linux may lack keyring backend
|
* Linux may lack keyring backend — handle `keyring::Error` gracefully
|
||||||
|
|
||||||
|
**Android (active target — not stretch)**
|
||||||
|
* Safe-area insets arrive in frames 1–3 via JNI polling, not at startup;
|
||||||
|
UI that depends on them must handle the zero-inset initial state
|
||||||
|
* Physical pixels ≠ logical pixels: `SafeAreaInsets` values are physical
|
||||||
|
(from `WindowInsets` API); divide by `window.scale_factor()` before
|
||||||
|
passing to Bevy `Val::Px`
|
||||||
|
* `adb shell input tap` uses physical pixel coordinates
|
||||||
|
* FiraMono (bundled font) covers: ASCII, card suits U+2660–2666,
|
||||||
|
Arrows U+2190–21FF. It does NOT cover Geometric Shapes (U+25xx) —
|
||||||
|
those render as missing-glyph rectangles on Android
|
||||||
|
* The gesture/navigation bar at the bottom (≈132px physical on common
|
||||||
|
devices) is inside the Bevy viewport; use `SafeAreaInsets.bottom` to
|
||||||
|
avoid placing interactive elements in that zone
|
||||||
|
* `HUD_BAND_HEIGHT` is 128px on Android (two-row wrap) vs 64px on desktop;
|
||||||
|
layout constants are `#[cfg(target_os = "android")]` gated
|
||||||
|
* JNI calls must use `attach_current_thread_permanently` — not
|
||||||
|
`attach_current_thread` — to avoid detach-on-drop panics
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -318,6 +369,12 @@ Must always be handled explicitly:
|
|||||||
* blocking async calls in ECS
|
* blocking async calls in ECS
|
||||||
* insecure credential storage
|
* insecure credential storage
|
||||||
* bypassing core logic layer
|
* bypassing core logic layer
|
||||||
|
* hardcoded pixel coordinates in layout — always derive from `compute_layout`
|
||||||
|
* Unicode Geometric Shapes block (U+25xx) in UI text — not in FiraMono
|
||||||
|
* spawning a second `ModalScrim` while one already exists without first
|
||||||
|
dismissing the existing one (use `scrims.is_empty()` guard)
|
||||||
|
* reading `SafeAreaInsets` physical values directly into `Val::Px` without
|
||||||
|
dividing by `window.scale_factor()`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -345,9 +402,74 @@ If unclear:
|
|||||||
| Both combined | full system understanding |
|
| Both combined | full system understanding |
|
||||||
|
|
||||||
---
|
---
|
||||||
# 14. Context Injection System (AUTOMATIC SCOPE FILTER)
|
# 14. Modal System Conventions
|
||||||
|
|
||||||
## 14.1 Purpose
|
All full-screen overlay panels MUST use the `spawn_modal` / `ModalScrim` pattern
|
||||||
|
from `solitaire_engine::ui_modal`.
|
||||||
|
|
||||||
|
## 14.1 Spawn pattern
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let scrim = spawn_modal(commands, MyScreenMarker, Z_MODAL_PANEL, |card| {
|
||||||
|
spawn_modal_header(card, "Title", font_res);
|
||||||
|
// ... body nodes ...
|
||||||
|
spawn_modal_actions(card, |actions| {
|
||||||
|
spawn_modal_button(actions, MyCloseButton, "Done", None,
|
||||||
|
ButtonVariant::Primary, font_res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Optional: allow clicking the scrim outside the card to dismiss
|
||||||
|
commands.entity(scrim).insert(ScrimDismissible);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 14.2 Guard rule
|
||||||
|
|
||||||
|
Before spawning a new modal, check `scrims: Query<(), With<ModalScrim>>`
|
||||||
|
and return early if `!scrims.is_empty()` — unless the new modal is
|
||||||
|
explicitly replacing the current one (despawn first, then spawn).
|
||||||
|
|
||||||
|
## 14.3 Safe area
|
||||||
|
|
||||||
|
Every `ModalScrim` automatically receives `padding.bottom` equal to the
|
||||||
|
logical gesture-bar height via `apply_safe_area_to_modal_scrims` in
|
||||||
|
`SafeAreaInsetsPlugin`. Do not manually add bottom padding to scrim nodes.
|
||||||
|
|
||||||
|
## 14.4 Z-ordering
|
||||||
|
|
||||||
|
Use `Z_MODAL_PANEL` from `ui_theme` for all modal scrims. Do not use
|
||||||
|
raw `z_index` values — they drift and cause ordering bugs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 15. Android Build & Verification
|
||||||
|
|
||||||
|
## 15.1 Build command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo apk build --package solitaire_app --lib
|
||||||
|
adb install -r target/debug/apk/solitaire-quest.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
## 15.2 Coordinate system reminder
|
||||||
|
|
||||||
|
Device physical: 1080×2400. Bevy logical: 900×2000. Scale factor: 1.20.
|
||||||
|
`adb shell input tap X Y` takes PHYSICAL coordinates.
|
||||||
|
To convert from what you see on screen (logical): multiply by 1.20.
|
||||||
|
|
||||||
|
## 15.3 Android-specific test checklist
|
||||||
|
|
||||||
|
Before shipping any Android build:
|
||||||
|
- [ ] Safe area insets arrive and shift HUD correctly (check after 3s)
|
||||||
|
- [ ] All modal Done buttons are above the gesture bar
|
||||||
|
- [ ] No Geometric Shapes glyphs in UI text
|
||||||
|
- [ ] HUD band does not overlap the top status bar
|
||||||
|
- [ ] Touch drag-and-drop works on all pile types
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 16. Context Injection System (AUTOMATIC SCOPE FILTER)
|
||||||
|
|
||||||
|
## 16.1 Purpose
|
||||||
|
|
||||||
Before generating any response, Claude MUST construct a **minimal relevant context set**.
|
Before generating any response, Claude MUST construct a **minimal relevant context set**.
|
||||||
|
|
||||||
@@ -360,7 +482,7 @@ This prevents:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.2 Input Classification Step (MANDATORY)
|
## 16.2 Input Classification Step (MANDATORY)
|
||||||
|
|
||||||
Every request MUST be classified into exactly one task type:
|
Every request MUST be classified into exactly one task type:
|
||||||
|
|
||||||
@@ -381,13 +503,13 @@ If uncertain → ask clarification.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.3 Context Selection Engine
|
## 16.3 Context Selection Engine
|
||||||
|
|
||||||
After classification, Claude MUST include ONLY the relevant sections below.
|
After classification, Claude MUST include ONLY the relevant sections below.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.4 Context Map (CORE RULESET)
|
## 16.4 Context Map (CORE RULESET)
|
||||||
|
|
||||||
### feature
|
### feature
|
||||||
|
|
||||||
@@ -495,7 +617,7 @@ Include:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.5 Context Compression Rules
|
## 16.5 Context Compression Rules
|
||||||
|
|
||||||
Claude MUST obey:
|
Claude MUST obey:
|
||||||
|
|
||||||
@@ -506,7 +628,7 @@ Claude MUST obey:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.6 Context Priority Order
|
## 16.6 Context Priority Order
|
||||||
|
|
||||||
When space is limited:
|
When space is limited:
|
||||||
|
|
||||||
@@ -517,7 +639,7 @@ When space is limited:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.7 “No Context Pollution” Rule
|
## 16.7 “No Context Pollution” Rule
|
||||||
|
|
||||||
Claude must NOT include:
|
Claude must NOT include:
|
||||||
|
|
||||||
@@ -529,7 +651,7 @@ Claude must NOT include:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.8 Self-Check Before Execution
|
## 16.8 Self-Check Before Execution
|
||||||
|
|
||||||
Before writing code, Claude MUST verify:
|
Before writing code, Claude MUST verify:
|
||||||
|
|
||||||
@@ -542,7 +664,7 @@ If any fail → revise context selection.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.9 Injection Output Format (Internal Model)
|
## 16.9 Injection Output Format (Internal Model)
|
||||||
|
|
||||||
Claude should behave as if it constructed:
|
Claude should behave as if it constructed:
|
||||||
|
|
||||||
@@ -560,7 +682,7 @@ Claude should behave as if it constructed:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14.10 Relationship to ARCHITECTURE.md
|
## 16.10 Relationship to ARCHITECTURE.md
|
||||||
|
|
||||||
* ARCHITECTURE.md = source of truth
|
* ARCHITECTURE.md = source of truth
|
||||||
* CLAUDE.md = execution constraints
|
* CLAUDE.md = execution constraints
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ You must follow CLAUDE_SPEC.md strictly.
|
|||||||
Rules:
|
Rules:
|
||||||
- Do not expand scope beyond what is defined
|
- Do not expand scope beyond what is defined
|
||||||
- Do not refactor unrelated code
|
- Do not refactor unrelated code
|
||||||
- Do not introduce new dependencies
|
- Do not introduce new dependencies to solitaire_core or solitaire_sync without confirmation
|
||||||
- Prefer minimal, surgical changes
|
- Prefer minimal, surgical changes
|
||||||
- Use existing patterns in the codebase
|
- Use existing patterns in the codebase
|
||||||
- Return minimal diffs or changed functions only
|
- Return minimal diffs or changed functions only
|
||||||
@@ -360,7 +360,7 @@ notes:
|
|||||||
target:
|
target:
|
||||||
"<what is slow>"
|
"<what is slow>"
|
||||||
|
|
||||||
constraints:CLAUDE_WORKFLOW.md
|
constraints:
|
||||||
- no behavior change
|
- no behavior change
|
||||||
- no architecture change
|
- no architecture change
|
||||||
- minimal code changes
|
- minimal code changes
|
||||||
|
|||||||
+5
-1
@@ -41,6 +41,10 @@ solitaire_server:
|
|||||||
depends_on: [solitaire_sync, axum, sqlx, jsonwebtoken]
|
depends_on: [solitaire_sync, axum, sqlx, jsonwebtoken]
|
||||||
role: "backend"
|
role: "backend"
|
||||||
|
|
||||||
|
solitaire_wasm:
|
||||||
|
depends_on: [solitaire_core, wasm-bindgen, serde-wasm-bindgen]
|
||||||
|
role: "wasm_replay_player"
|
||||||
|
|
||||||
solitaire_app:
|
solitaire_app:
|
||||||
depends_on: [solitaire_engine]
|
depends_on: [solitaire_engine]
|
||||||
role: "entrypoint"
|
role: "entrypoint"
|
||||||
@@ -180,7 +184,7 @@ threading:
|
|||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
pattern: "feature_isolation"
|
pattern: "feature_isolation"
|
||||||
communication: "events"
|
communication: "events and resources"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+24
-2
@@ -89,6 +89,22 @@ Also shipped (pre-Phase 8 but post-v0.22.0, already in CHANGELOG):
|
|||||||
subcommand reads new password from stdin, bcrypt-hashes it, invalidates all
|
subcommand reads new password from stdin, bcrypt-hashes it, invalidates all
|
||||||
active sessions for the user.
|
active sessions for the user.
|
||||||
|
|
||||||
|
### 5b. Android UX polish (2026-05-12)
|
||||||
|
|
||||||
|
- [x] **UX-1 — Modal Done button in gesture zone.** `apply_safe_area_to_modal_scrims` system
|
||||||
|
added to `SafeAreaInsetsPlugin` (`safe_area.rs`). Pads every `ModalScrim` bottom by
|
||||||
|
`insets.bottom / scale`. Fires on resource change + `Added<ModalScrim>`. Verified on device.
|
||||||
|
- [x] **UX-5b — Home mode glyph corruption.** Geometric Shapes (U+25xx, absent from FiraMono)
|
||||||
|
replaced with card suits U+2660–2666 in `home_plugin.rs`. Affects Zen/Challenge/Daily mode
|
||||||
|
selector buttons at level 5+.
|
||||||
|
- [x] **UX-7 — Help text wrap.** Android HUD entry shortened to
|
||||||
|
`"Open menu (Stats, Settings, Profile...)"` in `help_plugin.rs` — fits one line.
|
||||||
|
- [x] **BUG-3 — Multi-modal stacking.** `handle_menu_button` now checks
|
||||||
|
`scrims: Query<(), With<ModalScrim>>` and guards `spawn_menu_popover` with `scrims.is_empty()`.
|
||||||
|
Verified on device: ≡ tap while Stats open does nothing.
|
||||||
|
|
||||||
|
**Note:** These 4 fixes are implemented and verified but not yet committed.
|
||||||
|
|
||||||
### 6. Testing gaps
|
### 6. Testing gaps
|
||||||
- [x] **Server 401 → refresh → retry path.** Done (`198df75`): both
|
- [x] **Server 401 → refresh → retry path.** Done (`198df75`): both
|
||||||
`jwt_refresh_on_401_succeeds` (pull) and
|
`jwt_refresh_on_401_succeeds` (pull) and
|
||||||
@@ -141,7 +157,7 @@ Branch: master. v0.23.0 is the current version (HEAD: 03be4fc). Fully pushed.
|
|||||||
READ FIRST (in order):
|
READ FIRST (in order):
|
||||||
1. SESSION_HANDOFF.md — this file
|
1. SESSION_HANDOFF.md — this file
|
||||||
2. CHANGELOG.md — [0.23.0] section has full Phase 8 detail
|
2. CHANGELOG.md — [0.23.0] section has full Phase 8 detail
|
||||||
3. CLAUDE.md — unified-3.0 rule set
|
3. CLAUDE.md — unified-4.0 rule set
|
||||||
4. ARCHITECTURE.md — v1.3, fully up to date
|
4. ARCHITECTURE.md — v1.3, fully up to date
|
||||||
5. docs/ui-mockups/ — design system + mockup library
|
5. docs/ui-mockups/ — design system + mockup library
|
||||||
6. docs/android/ — Android setup + build runbook
|
6. docs/android/ — Android setup + build runbook
|
||||||
@@ -151,5 +167,11 @@ OPEN WORK:
|
|||||||
Phase 8 punch list is fully closed. All items verified complete.
|
Phase 8 punch list is fully closed. All items verified complete.
|
||||||
Remaining nuisance: `cargo apk build --lib` noisy stderr (cosmetic, non-blocking).
|
Remaining nuisance: `cargo apk build --lib` noisy stderr (cosmetic, non-blocking).
|
||||||
|
|
||||||
Suggest starting Phase 9 planning — ask what the next arc should be.
|
4 Android UX fixes are implemented and verified but NOT YET COMMITTED:
|
||||||
|
- BUG-3 (hud_plugin.rs): multi-modal stacking guard
|
||||||
|
- UX-7 (help_plugin.rs): help text wrap on Android
|
||||||
|
- UX-5b (home_plugin.rs): FiraMono glyph corruption in mode selector
|
||||||
|
- UX-1 (safe_area.rs): modal Done button in gesture zone
|
||||||
|
|
||||||
|
Commit those first, then suggest Phase 9 planning.
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
# Solitaire Quest — Session Handoff
|
# Solitaire Quest — Session Handoff (ARCHIVED)
|
||||||
|
|
||||||
|
> **This file is from Phase 2 (2026-04-25, 242 tests). It is kept for historical
|
||||||
|
> reference only. The authoritative session handoff is at the repo root:
|
||||||
|
> `SESSION_HANDOFF.md`.**
|
||||||
|
|
||||||
> Last updated: 2026-04-25
|
> Last updated: 2026-04-25
|
||||||
> Branch: `master` — pushed to https://github.com/funman300/Rusty_Solitaire.git
|
> Branch: `master` — pushed to https://github.com/funman300/Rusty_Solitaire.git
|
||||||
|
|||||||
@@ -230,6 +230,31 @@ rewrites required.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## P5 — UX polish (2026-05-12)
|
||||||
|
|
||||||
|
- [x] **UX-1 — Modal Done button unreachable in gesture zone.** *Closed
|
||||||
|
2026-05-12.* New `apply_safe_area_to_modal_scrims` system in
|
||||||
|
`safe_area.rs` pads every `ModalScrim` bottom by `insets.bottom /
|
||||||
|
window.scale_factor()` (logical pixels). Fires when `SafeAreaInsets`
|
||||||
|
changes AND when a new `ModalScrim` is spawned (`Added<ModalScrim>`
|
||||||
|
filter). Verified on device: Settings Done button reachable at physical
|
||||||
|
y ≈ 1800–2000 (was y ≈ 2232+, inside gesture zone).
|
||||||
|
- [x] **UX-5b — Home mode selector glyph corruption.** *Closed
|
||||||
|
2026-05-12.* `home_plugin.rs` mode glyphs changed from Geometric Shapes
|
||||||
|
block (U+25xx — absent from FiraMono, renders as rectangles) to card
|
||||||
|
suits U+2660 ♠ / U+2665 ♥ / U+2666 ♦. Affects Zen, Challenge, and
|
||||||
|
Daily mode selector buttons shown at level 5+.
|
||||||
|
- [x] **UX-7 — Help screen HUD button entry wraps to two lines.** *Closed
|
||||||
|
2026-05-12.* Android `CONTROL_SECTIONS` entry for ≡ button shortened
|
||||||
|
from `"Menu: Stats, Settings, Profile, Achievements"` to
|
||||||
|
`"Open menu (Stats, Settings, Profile...)"` in `help_plugin.rs`.
|
||||||
|
Fits on one line at 360 dp.
|
||||||
|
- [x] **BUG-3 — Multi-modal stacking (Stats + Profile simultaneously).** *Closed
|
||||||
|
2026-05-12.* `handle_menu_button` in `hud_plugin.rs` now checks
|
||||||
|
`scrims: Query<(), With<ModalScrim>>` and only calls
|
||||||
|
`spawn_menu_popover` when `scrims.is_empty()`. Tapping ≡ while any
|
||||||
|
modal is open is a no-op. Verified on device.
|
||||||
|
|
||||||
## Notes / decisions
|
## Notes / decisions
|
||||||
|
|
||||||
* This list is screenshot-driven; expect more items to surface once
|
* This list is screenshot-driven; expect more items to surface once
|
||||||
|
|||||||
Reference in New Issue
Block a user