docs(handoff): record splash pulse + scanline; mark Option B closed

Bookkeeping pass after 29136d8 (cursor pulse) and a27cf5a (scanline
overlay) shipped — both halves of the splash polish arc deferred in
cacb19c are now done.

Changes:
- "Since the v0.20.0 cut": added per-commit narratives for 29136d8
  and a27cf5a with the implementation notes worth preserving past
  the commit log:
  - The "multiply, don't override" pattern that resolved the
    cursor-pulse / fade-timeline conflict (and generalises to any
    two ECS systems writing the same per-frame component).
  - The texture-α × tint-α GPU-composite trick that integrates the
    scanline with the fade without a new "multiplicative fadable"
    abstraction.
  - Two Bevy 0.18 API surprises (RenderAssetUsages module move;
    pixel_size() returning Result) — pinned for next time we touch
    runtime-generated images.
  - The defensive period <= 0.0 guard on cursor_pulse_factor — a
    cheap NaN-prevention pattern worth mirroring on every trig
    helper.
- "Open punch list" → "Visual-identity follow-ups": collapsed the
  two splash-polish bullets into closed pointers.
- Resume prompt → Option B: marked closed with "no further splash
  work pending unless a new mockup detail surfaces" so a future
  session knows it's a finished arc, not an in-flight one.

Three options now closed (A, B, C); D / E / F remain — all three
have a real blocker (D = multi-session, E = artwork PNGs missing,
F = Android hardware/AVD) so the next session starts with a
genuine commitment-vs-blocker decision rather than picking the
smallest piece.
This commit is contained in:
funman300
2026-05-07 22:45:46 -07:00
parent a27cf5a020
commit 73ac67d76b
+82 -15
View File
@@ -195,6 +195,77 @@ chip-above-focused-card and the full screen-takeover redesign
remain — both data-layer or cross-plugin and intentionally still
open.
### `29136d8` `feat(engine): add pulsing trailing cursor to splash "▌ ready_" line`
Closes the cursor-pulse half of the splash polish arc deferred in
`cacb19c`. The "▌ ready_" line now ends with a 6×12 px cyan Node
that pulses on a 1 s sine cadence, multiplied with the global
splash fade timeline so the cursor never reaches full alpha while
the rest of the splash is still fading in.
**The "multiply, don't override" pattern.** Two systems write the
same `BackgroundColor` per frame: `advance_splash` writes the
global-fade alpha, `pulse_splash_cursor` overwrites with
`global_alpha × pulse_factor`. Both derive from `SplashAge` on the
root, so the writes are commensurate — the second one isn't
"fighting" the first, just refining it. This is the cleanest fix
for the "fight the global fade timeline" warning the original
`cacb19c` skip note flagged.
**Defensive division guard.** `cursor_pulse_factor(age, period, min)`
short-circuits to `1.0` when `period <= 0.0` so a future
misconfiguration produces a steady cursor rather than NaN
propagation (NaN in alpha = invisible UI, hard to debug). Worth
mirroring on every trig/division helper, not just this one.
One new test (1182 → 1183): `cursor_pulse_factor_corners` pins the
peak (factor = 1 at age = period / 4), trough (factor = min at age =
period × 3 / 4), and the zero/negative-period guard.
### `a27cf5a` `feat(engine): add tiled scanline overlay to splash`
Closes the scanline half of the splash polish arc. A fullscreen
`ImageNode` tiles a runtime-generated 2×2 RGBA8 texture over the
splash content — top row transparent, bottom row `#1a1a1a` at
~30 % alpha — producing the 1 px-pitch horizontal scanline pattern
called for in `docs/ui-mockups/splash-mobile.html`.
**Texture-α × tint-α composite for fade integration.** The 30 %
alpha is baked into the texture pixels, not the `ImageNode.color`
tint. `advance_splash`'s new third query writes
`(1, 1, 1, global_alpha)` into the tint each tick; the GPU
multiplies texture-α by tint-α, so the visible composite is
`0.3 × global_alpha`. Cleaner than building a "multiplicative
fadable" abstraction in the ECS — the GPU already does this
multiplication for free.
**Bevy 0.18 API surprises (worth pinning):**
- `RenderAssetUsages` re-exports under `bevy::asset::`, not
`bevy::render::render_asset::`. Type name unchanged; module
path moved.
- `TextureFormat::pixel_size()` returns `Result<usize, _>` rather
than the bare `usize` you'd expect for a static format query.
Annoying enough that the `debug_assert_eq!` against the buffer
length just hard-codes the `2 × 2 × 4 = 16` literal.
Headless test fixture now also `init_resource::<Assets<Image>>()`
since `MinimalPlugins` doesn't pull `AssetPlugin` — same pattern
`settings_plugin::tests` already used. Without it, the
`Option<ResMut<Assets<Image>>>` parameter on `spawn_splash` would
fall through and the scanline overlay would silently skip,
defeating the new tests.
Two new tests (1183 → 1185):
`build_scanline_image_has_expected_2x2_rgba_bytes` locks the
texture pixels literally so a future tweak can't drift the
appearance silently; `scanline_overlay_spawns_and_fades_with_splash`
asserts spawn placement under `SplashRoot` and the new
fade-images branch's correctness end-to-end.
This pair (`29136d8` + `a27cf5a`) closes Option B from the
SESSION_HANDOFF Resume prompt — both splash polish pieces now
shipped.
## What shipped in v0.20.0 (frozen at `41a009a`)
### Terminal visual-identity port
@@ -295,16 +366,14 @@ reads from it, so swapping the palette is now a one-file edit:
intentionally unmigrated and should swap in lockstep with the
artwork. Largest visible payoff remaining in the visual-
identity arc.
- **Splash boot-loader scanline overlay.** `cacb19c` shipped the
rest of the boot screen but skipped the scanline overlay
(1px lines at 2 px pitch in `#1a1a1a` over the whole splash,
30 % opacity). Needs a tiled-pattern asset (a 2 × 2 px PNG) or
a custom shader. Pure aesthetic, no behaviour change.
- **Splash cursor pulse.** The "ready_" line's mockup pulses a
cyan 6 × 12 px block at the end of the text. `cacb19c`
skipped this because a per-element pulse fights the global
`SplashFadable` fade timeline. Either layer the pulse on top
of the fade (multiply alphas) or accept the static cursor.
- *Splash boot-loader scanline overlay — closed by `a27cf5a`.*
Runtime-generated 2 × 2 RGBA8 texture tiled via
`NodeImageMode::Tiled`; per-pixel alpha × tint alpha gives
multiplicative fade integration without new abstractions.
- *Splash cursor pulse — closed by `29136d8`.* Trailing 6 × 12 px
cyan Node, sine-pulsed, multiplied with the global splash fade
(the "multiply, don't override" pattern that resolves the
original `cacb19c` skip-rationale).
- **Replay-overlay enrichments beyond the scrub bar.** Banner-local
pieces of the mockup (`docs/ui-mockups/replay-overlay-mobile.html`)
all shipped: scrub bar (`c84d9f4`), `▌ replay` cursor-block label
@@ -451,11 +520,9 @@ DECISION TO ASK THE PLAYER FIRST:
A. Push the post-cut commits to origin. Either as-is on master
or rolled into a v0.20.1 cut (CHANGELOG entry + tag).
Mechanical, but local master diverges from origin until done.
B. Splash polish — scanline overlay + cursor pulse. The two
pieces of the mockup `cacb19c` skipped (scanline needs a
tiled-pattern asset or shader; pulse needs to layer on top
of the SplashFadable timeline). Pure polish; no behaviour
change.
B. *Closed by `29136d8` + `a27cf5a`.* Both splash polish
pieces shipped (cursor pulse + scanline overlay). No further
splash work pending unless a new mockup detail surfaces.
C. *Closed by `54005d5` + `e080b49`.* Banner-local replay-overlay
pieces all shipped (scrub bar, ▌ label, GAME caption, MOVE
chip). Remaining are cross-plugin (floating MOVE chip above