feat(visualizer): Phase 15 control-deck rework

Centered tinted MudOverlay (NowPlayingCard chrome) replaces the anchored popover; eight dials become a deterministic three-row LAVA/WAVE layout; lava + waveform lamp toggles drive a genuine per-subsystem draw-skip; scroll/zoom becomes a slider; playful tooltips; green=interactive/light=static.
This commit is contained in:
daniel-c-harvey
2026-06-17 14:28:15 -04:00
parent fe481d0417
commit dd4f8ddded
8 changed files with 465 additions and 184 deletions
@@ -1,8 +1,8 @@
namespace DeepDrftPublic.Client.Services;
/// <summary>
/// Holds the waveform visualizer's eight continuous-control positions for the lifetime of the WASM app
/// instance. Scoped in DI, so it lives across SPA navigations within one listening session — open a
/// Holds the waveform visualizer's eight continuous-control positions plus two subsystem on/off
/// toggles for the lifetime of the WASM app instance. Scoped in DI, so it lives across SPA navigations within one listening session — open a
/// second mix and the knobs keep where you left them — but a fresh page load (F5) constructs a new
/// instance, resetting to defaults. That matches the spec's "persist within session, reset on fresh
/// load" without any cookie/localStorage round-trip (lava reframe §7c).
@@ -84,6 +84,19 @@ public sealed class WaveformVisualizerControlState
/// </summary>
public const double DefaultWaveformWidth = 0.5;
/// <summary>
/// Default lava-subsystem on-state. <c>true</c> so the lava field is on out of the box — the
/// current behavior. Backs the row-1 lava lamp toggle (Phase 15 §6). Has no TS-side anchor: the
/// bridge pushes it as an enable/disable, not a tuning dial.
/// </summary>
public const bool DefaultLavaEnabled = true;
/// <summary>
/// Default waveform-subsystem on-state. <c>true</c> so the waveform ribbon is on out of the box.
/// Backs the row-1 waveform lamp toggle (Phase 15 §6).
/// </summary>
public const bool DefaultWaveformEnabled = true;
/// <summary>Apparent bottom-to-top scroll rate, normalized [0,1]. Bridge maps it to a visible
/// time-span via <see cref="WaveformZoomMapping"/>; the standalone resolution/zoom control is gone.</summary>
public double ScrollSpeed { get; set; } = DefaultScrollSpeed;
@@ -110,6 +123,20 @@ public sealed class WaveformVisualizerControlState
/// <summary>Waveform-band horizontal extent, normalized [0,1]. Narrowing clears room for the lava.</summary>
public double WaveformWidth { get; set; } = DefaultWaveformWidth;
/// <summary>
/// Whether the lava field is drawn. When <c>false</c> the lava subsystem is genuinely not rendered
/// (the bridge skips its physics + uploads no blobs — no render cost, Phase 15 §6/§10.1), not dimmed.
/// Also gates the row-1/row-2 control visibility (§3).
/// </summary>
public bool LavaEnabled { get; set; } = DefaultLavaEnabled;
/// <summary>
/// Whether the waveform ribbon is drawn. When <c>false</c> the ribbon subsystem is genuinely not
/// rendered (the bridge disables the ribbon SDF + drops its collision boundary — no render cost,
/// Phase 15 §6/§10.1), not dimmed. Also gates the row-1/row-3 control visibility (§3).
/// </summary>
public bool WaveformEnabled { get; set; } = DefaultWaveformEnabled;
/// <summary>
/// Raised whenever any control value changes. The visualizer bridge subscribes to push the
/// affected dial(s). Mutators set the property then raise this; subscribers re-read the values.