Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da3e5423dc | |||
| a1864271de |
+102
-130
@@ -1,101 +1,85 @@
|
|||||||
# Solitaire Quest — Session Handoff
|
# Solitaire Quest — Session Handoff
|
||||||
|
|
||||||
**Last updated:** 2026-05-08 — **v0.21.5 cut and tagged at
|
**Last updated:** 2026-05-08 — **v0.21.6 cut and tagged at
|
||||||
`a2432df`**, working tree clean, all post-tag work pushed to
|
`f63db76`**, working tree clean, all post-tag work pushed to
|
||||||
origin.
|
origin.
|
||||||
|
|
||||||
v0.21.5 is a patch release with one through-line:
|
v0.21.6 is a patch release with through-line:
|
||||||
**replay-overlay scrubbing affordances + accessibility**.
|
**Move Log panel + scrub-UX polish**. v0.21.5 closed out the
|
||||||
v0.21.4 shipped pause / resume / step + the WIN MOVE marker as
|
keyboard-accelerator surface (Space / Esc / ← / →) and the
|
||||||
the first scrubbing-shaped additions to the replay overlay;
|
keybind footer; v0.21.6 builds on that with two parallel
|
||||||
v0.21.5 fills out the rest of the scrubbing UX so the player
|
threads — accessibility + scrub-on-hold polish for the v0.21.5
|
||||||
has both visual anchor points (notches + labels) and a complete
|
surfaces, plus a brand-new Move Log panel anchored to the
|
||||||
keyboard control surface (Space / Esc / ← / →) for navigating a
|
viewport's bottom edge that gives players a 5-row recent-and-
|
||||||
paused replay.
|
upcoming move history alongside the existing top-edge banner.
|
||||||
|
|
||||||
|
The Move Log panel is the first replay-overlay surface that
|
||||||
|
*isn't* attached to the banner — it lives at a separate screen
|
||||||
|
anchor (`bottom: 0`) with its own spawn/despawn lifecycle.
|
||||||
|
Establishes the "multi-anchor replay UI" pattern that the
|
||||||
|
remaining B-2 sub-piece (mini-tableau preview) will inherit.
|
||||||
|
|
||||||
Six commits on the B-2 replay screen-takeover redesign arc land
|
Six commits on the B-2 replay screen-takeover redesign arc land
|
||||||
here. Two of them are layout-changing — banner height grew
|
here, bringing the post-v0.21.4 total to 12. The remaining B-2
|
||||||
60 → 76 → 92 px to make room for the notch labels and keybind
|
piece — mini-tableau preview that dims the gameplay tableau
|
||||||
footer. Banner geometry was fixed for every prior B-2 commit;
|
during replay — is the only major sub-piece still open.
|
||||||
this release establishes the "grow the container, add a
|
|
||||||
flex-column child" pattern that the remaining B-2 sub-pieces
|
|
||||||
(move-log scroller, mini-tableau preview) will inherit when
|
|
||||||
they land.
|
|
||||||
|
|
||||||
Full v0.21.5 detail lives in `CHANGELOG.md` § [0.21.5]. This
|
Full v0.21.6 detail lives in `CHANGELOG.md` § [0.21.6]. This
|
||||||
file from here on focuses on what's *open* post-cut and how to
|
file from here on focuses on what's *open* post-cut and how to
|
||||||
resume.
|
resume.
|
||||||
|
|
||||||
## Status at pause
|
## Status at pause
|
||||||
|
|
||||||
- **HEAD locally:** see `git rev-parse HEAD`. The cut commit is
|
- **HEAD locally:** see `git rev-parse HEAD`. The cut commit is
|
||||||
`a2432df`; any post-cut docs edits ride on top of that.
|
`f63db76`; any post-cut docs edits ride on top of that.
|
||||||
- **HEAD on origin:** matches local. v0.21.5 is fully on origin.
|
- **HEAD on origin:** matches local. v0.21.6 is fully on origin.
|
||||||
- **Working tree:** clean. No WIP outstanding.
|
- **Working tree:** clean. No WIP outstanding.
|
||||||
- **`artwork/` directory:** still untracked. Intentional.
|
- **`artwork/` directory:** still untracked. Intentional.
|
||||||
- **Build:** `cargo clippy --workspace --all-targets -- -D warnings`
|
- **Build:** `cargo clippy --workspace --all-targets -- -D warnings`
|
||||||
clean.
|
clean.
|
||||||
- **Tests:** **1254 passing / 0 failing** across the workspace
|
- **Tests:** **1273 passing / 0 failing** across the workspace.
|
||||||
(1250 in v0.21.5 + 2 from `d3cb1a5`'s HC-marker tests + 2
|
Detail in `CHANGELOG.md` § [0.21.6] § Stats.
|
||||||
from `2e25476`'s continuous-scrub tests). The
|
- **Tags on origin:** `v0.9.0` through `v0.21.6`. v0.21.6 is on
|
||||||
time-dependent `daily_challenge` flake noted in v0.21.5's
|
`f63db76`; v0.21.5 stays on `a2432df`; v0.21.4 stays on
|
||||||
CHANGELOG passes again (UTC clock has moved past the
|
`23ff62c`; v0.21.3 stays on `3d92a91`; v0.21.2 stays on
|
||||||
trigger window). Detail in `CHANGELOG.md` § [0.21.5] § Stats
|
`f23df3b`; v0.21.1 stays on `daa655a`; v0.21.0 stays on
|
||||||
for the v0.21.5 baseline; post-cut delta tracked in this
|
`04f9bf9`; v0.20.0 stays on `41a009a`.
|
||||||
file's Since-cut log.
|
|
||||||
- **Tags on origin:** `v0.9.0` through `v0.21.5`. v0.21.5 is on
|
|
||||||
`a2432df`; v0.21.4 stays on `23ff62c`; v0.21.3 stays on
|
|
||||||
`3d92a91`; v0.21.2 stays on `f23df3b`; v0.21.1 stays on
|
|
||||||
`daa655a`; v0.21.0 stays on `04f9bf9`; v0.20.0 stays on
|
|
||||||
`41a009a`.
|
|
||||||
|
|
||||||
## Since the v0.21.5 cut
|
## Since the v0.21.6 cut
|
||||||
|
|
||||||
- **`d3cb1a5` — `feat(replay): HC-mode coverage for scrub
|
No threads in flight. Working tree clean as of 2026-05-08. New
|
||||||
track + notches`.** Adds a parallel primitive to ui_theme
|
work since the cut would land here as commit narratives; for
|
||||||
(`HighContrastBackground` marker carrying `default_color`)
|
the v0.21.6 contents themselves, see `CHANGELOG.md` § [0.21.6].
|
||||||
and a paint system in settings_plugin
|
|
||||||
(`update_high_contrast_backgrounds`) that mirrors the
|
|
||||||
existing border-marker pattern but targets `BackgroundColor`
|
|
||||||
instead of `BorderColor`. Tags the 1 px scrub track Node and
|
|
||||||
all five quarter-mark notch ticks with the new marker so
|
|
||||||
they bump from `BORDER_SUBTLE` (#505050) → `BORDER_SUBTLE_HC`
|
|
||||||
(#a0a0a0) under HC mode. Scrub fill (ACCENT_PRIMARY) and
|
|
||||||
WIN MOVE marker (STATE_SUCCESS) don't get the marker —
|
|
||||||
accent and state colours are already saturated. 2 new tests;
|
|
||||||
1250 → 1252.
|
|
||||||
- **`2e25476` — `feat(replay): continuous scrub on key-held
|
|
||||||
arrow keys`.** Holding ← or → now triggers continuous step
|
|
||||||
at 100 ms cadence (10 steps/sec) — matches the mockup's
|
|
||||||
`[← →] scrub` terminology while keeping single-press =
|
|
||||||
single-step semantics. Per-key accumulators in a new
|
|
||||||
`ReplayScrubKeyHold` resource; `just_pressed` events bypass
|
|
||||||
the accumulator and fire immediately. Release resets to 0
|
|
||||||
so the next fresh press fires immediately rather than at
|
|
||||||
half-interval. Footer text unchanged (`[← →] step`) —
|
|
||||||
held-key scrub is a discoverable enhancement to the same
|
|
||||||
keybind, not a new keybind. 2 new tests using
|
|
||||||
`TimeUpdateStrategy::ManualDuration`; 1252 → 1254.
|
|
||||||
|
|
||||||
Open next-step menu (B-2 keyboard accelerator coverage +
|
Open next-step menu (Move Log + scrub-UX + keyboard accelerator
|
||||||
accessibility + scrub UX are all complete):
|
coverage + accessibility are all complete):
|
||||||
1. **Move-log scroller / mini-tableau preview** — both need
|
1. **Mini-tableau preview** — the only remaining major B-2
|
||||||
a much larger banner-height grow (effectively the takeover
|
sub-piece. Mockup shows a 240 px-tall band at 50 % opacity
|
||||||
container itself). Multi-session arcs that close B-2.
|
showing the gameplay tableau peeking through the replay
|
||||||
Mockup at `docs/ui-mockups/replay-overlay-mobile.html`.
|
chrome. Implementation needs to add a settings-aware dim
|
||||||
2. **Polish: notch label centering.** Bevy 0.18 lacks a clean
|
overlay or alpha modulation on the tableau cards during
|
||||||
`translate-x: -50%` primitive so middle three labels sit
|
replay. Architectural — touches `card_plugin` rendering.
|
||||||
slightly right-of-notch. Could use a child Text wrapper
|
Multi-session.
|
||||||
with computed left-margin compensation. Tiny commit.
|
2. **Move Log auto-scroll** — only relevant if the panel's
|
||||||
3. **Polish: WIN MOVE marker HC bump.** Currently the marker
|
row count grows beyond the current 5-row fixed window.
|
||||||
uses `STATE_SUCCESS` lime which stays visible under HC,
|
Currently the prev-2 / active / next-2 layout fits all
|
||||||
but a slight saturation / contrast bump under HC would
|
visible content, so auto-scroll is unneeded. Becomes
|
||||||
make the marker even more legible alongside the bumped
|
relevant if a future commit expands the panel's row
|
||||||
notches. Optional.
|
capacity (e.g. 10-row scrolling list).
|
||||||
|
3. **Polish: notch label centering.** Bevy 0.18 lacks a
|
||||||
|
clean `translate-x: -50%` primitive so middle three
|
||||||
|
labels sit slightly right-of-notch. Could use a child
|
||||||
|
Text wrapper with computed left-margin compensation.
|
||||||
|
Tiny commit, requires visual review.
|
||||||
|
4. **Polish: WIN MOVE marker HC bump.** Currently uses
|
||||||
|
`STATE_SUCCESS` lime which stays visible under HC, but a
|
||||||
|
contrast bump under HC would make it even more legible
|
||||||
|
alongside the bumped notches. Optional.
|
||||||
|
|
||||||
Recommended order: option 2 (notch label centering) is the
|
Recommended order: option 1 (mini-tableau preview) is the
|
||||||
smallest concrete next-step. Option 1 is the multi-session
|
big remaining piece that closes B-2 — best tackled in a
|
||||||
arc that closes B-2 — natural place to start a fresh session.
|
fresh session because it crosses into `card_plugin`. Options
|
||||||
|
3 and 4 are visual polish that benefit from user review.
|
||||||
|
|
||||||
## Open punch list
|
## Open punch list
|
||||||
|
|
||||||
@@ -138,21 +122,19 @@ palette refresh all shipped in v0.20.0 + v0.21.0. What stays open:
|
|||||||
(UI). Playback controls (pause / resume / step + Space
|
(UI). Playback controls (pause / resume / step + Space
|
||||||
accelerator) shipped post-v0.21.3 in `fbe48ac`. v0.21.5
|
accelerator) shipped post-v0.21.3 in `fbe48ac`. v0.21.5
|
||||||
bundled six more commits under "replay-overlay scrubbing
|
bundled six more commits under "replay-overlay scrubbing
|
||||||
affordances + accessibility" — scrub notches + percentage
|
affordances + accessibility" (scrub notches + labels +
|
||||||
labels + keybind-hint footer + ESC and ← / → accelerators
|
keybind footer + ESC and ← / → accelerators + HC border).
|
||||||
+ HC marker for the footer top border. Banner height grew
|
v0.21.6 bundled six more under "Move Log panel + scrub-UX
|
||||||
60 → 76 → 92 px across two layout-changing commits in
|
polish" — bottom-edge Move Log panel with prev/active/next
|
||||||
v0.21.5; banner geometry is now mutable. Full per-commit
|
rows + active highlight, HC-mode coverage for the scrub
|
||||||
detail in `CHANGELOG.md` § [0.21.5]. Keyboard accelerator
|
track + notches, continuous scrub on key-held arrows. Banner
|
||||||
coverage is complete. What still needs to land: HC-mode
|
height grew 60 → 76 → 92 px across two layout-changing
|
||||||
coverage for the scrub-track / notches / WIN MOVE marker
|
commits in v0.21.5; Move Log panel grew 56 → 84 → 112 px
|
||||||
(they render via `BackgroundColor` so the
|
across the v0.21.6 move-log commits. Per-commit detail in
|
||||||
`HighContrastBorder` marker doesn't apply — needs a
|
`CHANGELOG.md` § [0.21.5] and § [0.21.6]. The only major
|
||||||
settings-aware paint), continuous scrub on key-held ← / →
|
B-2 piece left is the mini-tableau preview — the mockup's
|
||||||
(vs single-step), then the bigger pieces — a move-log
|
"Game Peek Band" at 50 % opacity. Architectural; touches
|
||||||
scroller and a mini-tableau preview — both screen-
|
`card_plugin` rendering.
|
||||||
takeover-only pieces that need a much larger banner height
|
|
||||||
grow (effectively the takeover container itself).
|
|
||||||
Multi-session.
|
Multi-session.
|
||||||
- *Floating `MOVE N/M` chip above the focused card during
|
- *Floating `MOVE N/M` chip above the focused card during
|
||||||
playback — closed 2026-05-08 by `2fb2d63`.* World-space
|
playback — closed 2026-05-08 by `2fb2d63`.* World-space
|
||||||
@@ -296,25 +278,23 @@ into a v0.21.1 / v0.22.0 cut.
|
|||||||
```
|
```
|
||||||
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.21.5 is tagged at a2432df (cut 2026-05-08, a
|
Branch: master. v0.21.6 is tagged at f63db76 (cut 2026-05-08, a
|
||||||
patch release rolling up replay-overlay scrubbing affordances +
|
patch release rolling up Move Log panel + scrub-UX polish:
|
||||||
accessibility: scrub-bar notches with percentage labels, keybind-
|
brand-new bottom-edge Move Log panel with prev / active / next
|
||||||
hint footer, ESC + ← / → keyboard accelerators, and HC-mode
|
row context + active-row highlight, plus HC-mode coverage for
|
||||||
coverage for the footer top border). v0.21.4 stays at 23ff62c,
|
scrub track + notches and continuous scrub on key-held arrow
|
||||||
v0.21.3 at 3d92a91, v0.21.2 at f23df3b, v0.21.1 at daa655a,
|
keys). v0.21.5 stays at a2432df, v0.21.4 at 23ff62c, v0.21.3
|
||||||
v0.21.0 at 04f9bf9. Working tree clean. See CHANGELOG.md §
|
at 3d92a91, v0.21.2 at f23df3b, v0.21.1 at daa655a, v0.21.0 at
|
||||||
[0.21.5] for full detail.
|
04f9bf9. Working tree clean. See CHANGELOG.md § [0.21.6] for
|
||||||
|
full detail.
|
||||||
|
|
||||||
State: HEAD locally — see `git rev-parse HEAD`. The cut commit
|
State: HEAD locally — see `git rev-parse HEAD`. The cut commit
|
||||||
is a2432df; any post-cut docs edits ride on top of that.
|
is f63db76; any post-cut docs edits ride on top of that.
|
||||||
Workspace tests: 1250 total / 1249 passing / 1 pre-existing
|
Workspace tests: 1273 passing / 0 failing. Clippy clean.
|
||||||
time-dependent flake (`daily_challenge` warning, fails when UTC
|
|
||||||
is within 30 min of midnight; verified not introduced by recent
|
|
||||||
work). Clippy clean.
|
|
||||||
|
|
||||||
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 — [0.21.5] section is the most recent cut
|
2. CHANGELOG.md — [0.21.6] 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
|
||||||
@@ -334,30 +314,22 @@ DECISION TO ASK THE PLAYER FIRST:
|
|||||||
tests can't catch. Likely surfaces JNI ClipboardManager
|
tests can't catch. Likely surfaces JNI ClipboardManager
|
||||||
and Android Keystore stubs that need real bridges. Larger
|
and Android Keystore stubs that need real bridges. Larger
|
||||||
scope; needs an Android device or emulator running.
|
scope; needs an Android device or emulator running.
|
||||||
B. Replay-overlay screen-takeover redesign — multi-session
|
B. Replay-overlay screen-takeover redesign — nearly complete
|
||||||
work. v0.21.4 shipped WIN MOVE marker, pause / resume /
|
after 12 commits across v0.21.4-6. Scrub bar with notches
|
||||||
step + Space accelerator, plus the floating-MOVE-chip
|
+ labels + WIN MOVE marker, pause / resume / step / stop
|
||||||
piece from v0.21.2 (`2fb2d63`). v0.21.5 shipped scrub
|
buttons, Space + Esc + ← / → keyboard accelerators with
|
||||||
notches + percentage labels + keybind-hint footer + ESC
|
continuous scrub on hold, keybind-hint footer, full HC-mode
|
||||||
and ← / → accelerators + HC marker for the footer top
|
coverage on the banner pieces, and a brand-new bottom-edge
|
||||||
border (six commits across CHANGELOG § [0.21.5]). Banner
|
Move Log panel with a 5-row prev/active/next window all
|
||||||
height grew 60 → 76 → 92 px across two layout-changing
|
ship. The only remaining major B-2 sub-piece is the
|
||||||
commits in v0.21.5; geometry is now mutable. Keyboard
|
**mini-tableau preview** — the mockup's "Game Peek Band"
|
||||||
accelerator coverage is complete. Natural next finite
|
at 50 % opacity showing the tableau through the replay
|
||||||
steps:
|
chrome. Implementation needs a settings-aware dim overlay
|
||||||
1. **HC-mode coverage** for the scrub-track / notches /
|
or alpha modulation on the tableau cards during replay.
|
||||||
WIN MOVE marker (render via `BackgroundColor` not
|
Architectural — touches `card_plugin` rendering. Best
|
||||||
`BorderColor`, so `HighContrastBorder` doesn't apply
|
tackled in a fresh session because it crosses into a
|
||||||
— needs a settings-aware paint, precedent
|
plugin the recent B-2 work hasn't touched. Mockup at
|
||||||
`radial_rim_outline`). Smallest next commit.
|
`docs/ui-mockups/replay-overlay-mobile.html`.
|
||||||
2. **Continuous scrub on key-held ← / →** instead of
|
|
||||||
single-step. Needs a key-held event source. Matches
|
|
||||||
the mockup's `[← →] scrub` terminology.
|
|
||||||
3. **Move-log scroller / mini-tableau preview** — both
|
|
||||||
need a much larger banner-height grow (effectively
|
|
||||||
the takeover container itself). Multi-session arcs
|
|
||||||
that close B-2.
|
|
||||||
Mockup at `docs/ui-mockups/replay-overlay-mobile.html`.
|
|
||||||
C. Phase 8 (sync) — local storage scaffolding, self-hosted
|
C. Phase 8 (sync) — local storage scaffolding, self-hosted
|
||||||
Axum server, `SolitaireServerClient` impl, GPGS stub
|
Axum server, `SolitaireServerClient` impl, GPGS stub
|
||||||
wired into Settings. The biggest open arc by scope; rolls
|
wired into Settings. The biggest open arc by scope; rolls
|
||||||
|
|||||||
@@ -60,6 +60,23 @@ use crate::ui_theme::{
|
|||||||
/// we materialise a separate constant rather than reuse the `f32` value.
|
/// we materialise a separate constant rather than reuse the `f32` value.
|
||||||
pub const Z_REPLAY_OVERLAY: i32 = Z_DROP_OVERLAY as i32 + 5;
|
pub const Z_REPLAY_OVERLAY: i32 = Z_DROP_OVERLAY as i32 + 5;
|
||||||
|
|
||||||
|
/// `bevy::ui` `ZIndex` for the full-screen tableau dim layer.
|
||||||
|
///
|
||||||
|
/// One rung below [`Z_REPLAY_OVERLAY`] (= 54) so the replay chrome
|
||||||
|
/// (banner + move-log panel) renders clearly on top while the dim scrim
|
||||||
|
/// darkens the card world beneath it. World-space sprites (cards,
|
||||||
|
/// badges, drop-target overlays) are always below any UI node regardless
|
||||||
|
/// of their Transform.z — the dim layer doesn't need to know their z
|
||||||
|
/// values.
|
||||||
|
const Z_REPLAY_DIM: i32 = Z_REPLAY_OVERLAY - 1;
|
||||||
|
|
||||||
|
/// Alpha for the tableau dim layer — 50 % opacity black. Dark enough
|
||||||
|
/// to visually separate the gameplay scene from the replay chrome
|
||||||
|
/// above it; light enough that card positions remain legible through
|
||||||
|
/// the scrim. Matches the mockup's "Game Peek Band at 50 % opacity"
|
||||||
|
/// spec in `docs/ui-mockups/replay-overlay-mobile.html`.
|
||||||
|
const TABLEAU_DIM_ALPHA: f32 = 0.5;
|
||||||
|
|
||||||
/// Total height of the banner in pixels. Thin enough to leave the
|
/// Total height of the banner in pixels. Thin enough to leave the
|
||||||
/// gameplay surface visible underneath, tall enough to comfortably fit
|
/// gameplay surface visible underneath, tall enough to comfortably fit
|
||||||
/// the headline-sized "▌ replay" label stacked above the
|
/// the headline-sized "▌ replay" label stacked above the
|
||||||
@@ -189,6 +206,18 @@ pub struct ReplayPauseButton;
|
|||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug)]
|
||||||
pub struct ReplayStepButton;
|
pub struct ReplayStepButton;
|
||||||
|
|
||||||
|
/// Marker on the full-screen tableau dim layer spawned at the start of
|
||||||
|
/// every replay. The dim layer is a 100 % × 100 % `Node` at
|
||||||
|
/// [`Z_REPLAY_DIM`] (= `Z_REPLAY_OVERLAY - 1`) with a semi-transparent
|
||||||
|
/// black `BackgroundColor`. It darkens the card world so the replay
|
||||||
|
/// chrome reads clearly against it without obscuring card positions.
|
||||||
|
///
|
||||||
|
/// Carries no [`Interaction`] component — purely visual; pointer events
|
||||||
|
/// pass through to the underlying UI and world-space systems.
|
||||||
|
/// Despawned by `react_to_state_change` when the replay ends.
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct ReplayTableauDimLayer;
|
||||||
|
|
||||||
/// Marker on the small caption sitting below the "▌ replay"
|
/// Marker on the small caption sitting below the "▌ replay"
|
||||||
/// headline. Carries `GAME #YYYY-DDD` (year + chrono ordinal) while a
|
/// headline. Carries `GAME #YYYY-DDD` (year + chrono ordinal) while a
|
||||||
/// replay is playing — a compact, monotonically-increasing identifier
|
/// replay is playing — a compact, monotonically-increasing identifier
|
||||||
@@ -435,6 +464,7 @@ fn react_to_state_change(
|
|||||||
existing: Query<Entity, With<ReplayOverlayRoot>>,
|
existing: Query<Entity, With<ReplayOverlayRoot>>,
|
||||||
floating_chips: Query<Entity, With<ReplayFloatingProgressChip>>,
|
floating_chips: Query<Entity, With<ReplayFloatingProgressChip>>,
|
||||||
move_log_panels: Query<Entity, With<ReplayOverlayMoveLogPanel>>,
|
move_log_panels: Query<Entity, With<ReplayOverlayMoveLogPanel>>,
|
||||||
|
dim_layers: Query<Entity, With<ReplayTableauDimLayer>>,
|
||||||
font_res: Option<Res<FontResource>>,
|
font_res: Option<Res<FontResource>>,
|
||||||
) {
|
) {
|
||||||
if !state.is_changed() {
|
if !state.is_changed() {
|
||||||
@@ -463,6 +493,11 @@ fn react_to_state_change(
|
|||||||
for entity in &move_log_panels {
|
for entity in &move_log_panels {
|
||||||
commands.entity(entity).despawn();
|
commands.entity(entity).despawn();
|
||||||
}
|
}
|
||||||
|
// Tableau dim layer is also a separate root entity — same
|
||||||
|
// pattern as the move-log panel.
|
||||||
|
for entity in &dim_layers {
|
||||||
|
commands.entity(entity).despawn();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// The `should_be_visible && already_spawned` branch is a no-op here —
|
// The `should_be_visible && already_spawned` branch is a no-op here —
|
||||||
// the per-frame text update systems below repaint the banner label
|
// the per-frame text update systems below repaint the banner label
|
||||||
@@ -504,6 +539,27 @@ fn spawn_overlay(
|
|||||||
};
|
};
|
||||||
let progress_label = format_progress(state);
|
let progress_label = format_progress(state);
|
||||||
|
|
||||||
|
// Tableau dim layer — full-screen scrim at z = Z_REPLAY_DIM (= 54).
|
||||||
|
// Spawned first so it sits behind the banner (z=55) and move-log (z=55)
|
||||||
|
// in the UI stacking context. World-space sprites (cards, badges) are
|
||||||
|
// always below any UI node, so the dim layer darkens the entire
|
||||||
|
// gameplay scene without needing to touch card_plugin. No Interaction
|
||||||
|
// component — purely visual.
|
||||||
|
commands.spawn((
|
||||||
|
ReplayTableauDimLayer,
|
||||||
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
left: Val::Px(0.0),
|
||||||
|
top: Val::Px(0.0),
|
||||||
|
width: Val::Percent(100.0),
|
||||||
|
height: Val::Percent(100.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, TABLEAU_DIM_ALPHA)),
|
||||||
|
ZIndex(Z_REPLAY_DIM),
|
||||||
|
GlobalZIndex(Z_REPLAY_DIM),
|
||||||
|
));
|
||||||
|
|
||||||
let banner_bg = Color::srgba(
|
let banner_bg = Color::srgba(
|
||||||
BG_ELEVATED_HI.to_srgba().red,
|
BG_ELEVATED_HI.to_srgba().red,
|
||||||
BG_ELEVATED_HI.to_srgba().green,
|
BG_ELEVATED_HI.to_srgba().green,
|
||||||
@@ -3798,4 +3854,65 @@ mod tests {
|
|||||||
other => panic!("expected Playing, got {other:?}"),
|
other => panic!("expected Playing, got {other:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The tableau dim layer spawns alongside the banner when playback
|
||||||
|
/// starts and despawns when the replay ends. Mirrors
|
||||||
|
/// `floating_chip_spawns_and_despawns_with_overlay` for the dim layer.
|
||||||
|
#[test]
|
||||||
|
fn dim_layer_spawns_and_despawns_with_overlay() {
|
||||||
|
let mut app = headless_app();
|
||||||
|
|
||||||
|
// Inactive → no dim layer yet.
|
||||||
|
app.update();
|
||||||
|
assert_eq!(
|
||||||
|
app.world_mut()
|
||||||
|
.query::<&ReplayTableauDimLayer>()
|
||||||
|
.iter(app.world())
|
||||||
|
.count(),
|
||||||
|
0,
|
||||||
|
"no dim layer while playback is Inactive",
|
||||||
|
);
|
||||||
|
|
||||||
|
set_state(
|
||||||
|
&mut app,
|
||||||
|
ReplayPlaybackState::Playing {
|
||||||
|
replay: synthetic_replay(5),
|
||||||
|
cursor: 0,
|
||||||
|
secs_to_next: 0.5,
|
||||||
|
paused: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
app.update();
|
||||||
|
assert_eq!(
|
||||||
|
app.world_mut()
|
||||||
|
.query::<&ReplayTableauDimLayer>()
|
||||||
|
.iter(app.world())
|
||||||
|
.count(),
|
||||||
|
1,
|
||||||
|
"dim layer must spawn when playback starts",
|
||||||
|
);
|
||||||
|
|
||||||
|
set_state(&mut app, ReplayPlaybackState::Inactive);
|
||||||
|
app.update();
|
||||||
|
assert_eq!(
|
||||||
|
app.world_mut()
|
||||||
|
.query::<&ReplayTableauDimLayer>()
|
||||||
|
.iter(app.world())
|
||||||
|
.count(),
|
||||||
|
0,
|
||||||
|
"dim layer must despawn when playback ends",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The dim layer is a full-screen node (100 % × 100 %) at a lower
|
||||||
|
/// z-index than the replay chrome (z = Z_REPLAY_DIM < Z_REPLAY_OVERLAY).
|
||||||
|
/// Lock the z-ordering so a future refactor of the z constants can't
|
||||||
|
/// silently flip the intended stacking.
|
||||||
|
#[test]
|
||||||
|
fn dim_layer_z_is_below_replay_chrome() {
|
||||||
|
assert!(
|
||||||
|
Z_REPLAY_DIM < Z_REPLAY_OVERLAY,
|
||||||
|
"dim layer (z={Z_REPLAY_DIM}) must be below replay chrome (z={Z_REPLAY_OVERLAY})",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user