From fc7c9e978f60f57784b1ae6be3848805b847a2ce Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Tue, 16 Jun 2026 20:31:42 -0400 Subject: [PATCH] feat(mix-visualizer): gate knob controls with Blazor @if in TopContent band; drop CSS collapse, glass, and TopRowCenter slot --- .../Controls/MixVisualizerControls.razor | 26 +++----- .../Controls/MixVisualizerControls.razor.css | 61 ++----------------- .../Controls/ReleaseDetailScaffold.razor | 14 ++--- .../Controls/ReleaseDetailScaffold.razor.cs | 16 ++--- .../Controls/ReleaseDetailScaffold.razor.css | 9 --- DeepDrftPublic.Client/Pages/MixDetail.razor | 30 ++++----- 6 files changed, 39 insertions(+), 117 deletions(-) diff --git a/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor b/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor index ad48340..7392d20 100644 --- a/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor +++ b/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor @@ -2,12 +2,12 @@ @using DeepDrftPublic.Client.Services @inject MixVisualizerControlState ControlState -@* The Mix visualizer controls (lava reframe Wave R4). SEVEN continuous RadialKnobs — scroll speed, - gradient rotation speed, lava gravity, lava heat, blob density, collision strength, waveform width — - each its own dedicated control with a Material-icon caption (no more R2 temp-remapping: no knob - caption misrepresents its function). The bar lives INLINE in the mix-detail controls area and - ANIMATES open/closed in place via CSS transition off the @Expanded flag — it reads as the controls - collapsing/expanding, NOT a floating popover/drawer (§7b). +@* The Mix visualizer controls. SEVEN continuous RadialKnobs — scroll speed, gradient rotation speed, + lava gravity, lava heat, blob density, collision strength, waveform width — each its own dedicated + control with a Material-icon caption. Visibility is controlled by Blazor, not CSS: the host page + renders this component only while the lava-lamp toggle is on (@if-guarded), so when off it is not in + the DOM at all. There is no collapse/expand animation and no glass surface — the knobs simply appear + in their own transparent band and disappear when un-rendered. It owns NO JS interop: it mutates the shared, session-scoped MixVisualizerControlState and raises its Changed event. The backdrop bridge (MixWaveformVisualizer) subscribes to that event and pushes the @@ -17,13 +17,9 @@ RadialKnob has no icon slot (its Label renders as SVG text) and no aria attribute-capture, so each control's Material icon rides beside its knob as an adjacent MudIcon caption and the accessible name rides on the wrapping group div (§7d). HoldValue stays false so the knobs are live — ValueChanged - fires continuously during drag, preserving the Changed/NotifyChanged seam. + fires continuously during drag, preserving the Changed/NotifyChanged seam. *@ - Aesthetic: the bar matches the session-hero NowPlaying overlay (§7e) — a translucent dark glass - surface with overlay-label captions and Color.Secondary accents, so it reads as of-a-piece with the - hero rather than a generic MudBlazor panel. *@ - -
+
@code { - /// - /// Whether the knob bar is expanded. Owned by the host page (the lava-lamp toggle button flips it); - /// drives the CSS open/close transition. When false the bar collapses to zero size in place. - /// - [Parameter] public bool Expanded { get; set; } - // Each handler mutates its own dedicated property then raises Changed — the bridge re-reads and // pushes the affected dial. All values are already normalized [0,1]; the bridge maps scroll speed // to a visible time-span and routes the rest straight to the lava/colour dials. diff --git a/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor.css b/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor.css index ed994f8..98ef2fd 100644 --- a/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor.css +++ b/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor.css @@ -1,63 +1,14 @@ -/* The seven-knob container lives IN-FLOW in the scaffold's top-row center zone, between the back link - and the lava-lamp toggle, and grows open/closed in place (lava reframe §7b) — NOT a popover, drawer, - or floating overlay. Collapsed, it has zero footprint and is fully transparent; the @Expanded flag - (mirrored to the .is-expanded class) transitions it open by growing its width. The PRIMARY animated - axis is horizontal width — the controls area opening between the back link and the lamp; max-height is - a secondary axis for the wrap case. Closed state is pointer-events:none + visibility:hidden so - collapsed knobs are not focusable or hit-testable. */ +/* The seven-knob band. Blazor gates its presence (the host renders this component only while the + lava-lamp is on), so this is purely a layout rule for the visible state — no collapse machinery, no + transitions, no glass surface. A plain transparent horizontal flex row of the seven knobs that wraps + to a second line only if the band is genuinely too narrow. */ .mix-visualizer-controls-bar { display: flex; flex-wrap: wrap; align-items: flex-start; - justify-content: flex-start; + justify-content: center; gap: 0.85rem 1rem; - - /* Collapsed: zero horizontal footprint, transparent, no layout space. */ - max-width: 0; - max-height: 0; - opacity: 0; - overflow: hidden; - visibility: hidden; - pointer-events: none; - - /* NowPlaying glass surface (§7e): translucent dark shim, soft blur, rounded, secondary-tinted - hairline — matches the session-hero overlay family. Padding animates in with the size. */ - padding: 0; - border-radius: 10px; - background: rgba(13, 27, 42, 0.55); - border: 1px solid color-mix(in srgb, var(--mud-palette-secondary) 22%, transparent); - backdrop-filter: blur(10px); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35); - - transition: - max-width 0.32s cubic-bezier(0.22, 0.61, 0.36, 1), - max-height 0.32s cubic-bezier(0.22, 0.61, 0.36, 1), - opacity 0.24s ease, - padding 0.32s cubic-bezier(0.22, 0.61, 0.36, 1); -} - -/* Expanded: grows horizontally in the row's flow. Wide enough to hold all seven 64px knobs (with - captions and gaps) on one line where the row has room; on a narrower center zone the knobs flex-wrap - to a second in-flow line and max-height absorbs the taller stack — never clipped, never floating. */ -.mix-visualizer-controls-bar.is-expanded { - max-width: 720px; - max-height: 420px; - opacity: 1; - visibility: visible; - pointer-events: auto; - padding: 0.85rem 1rem; -} - -/* Narrow rows: the controls container can't sit beside the back link and lamp on one line, so it takes - the full row width and the scaffold's flex-wrapped top row drops it to its own in-flow line below the - back/lamp pair (§7b-responsive). Still fully in-flow — never floats, never clips. The seven knobs get - the row's full width and wrap within it. */ -@media (max-width: 959.98px) { - .mix-visualizer-controls-bar.is-expanded { - flex-basis: 100%; - max-width: 100%; - justify-content: center; - } + margin: 0.5rem 0; } /* One control: a RadialKnob with its Material icon caption underneath. RadialKnob has no icon slot, so diff --git a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor index 49de5d1..d882b10 100644 --- a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor +++ b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor @@ -7,20 +7,16 @@
- @* Three-zone top row: back link (left) | optional center affordance | optional action (right), on - one SpaceBetween row. Both the center and action slots stay null for media that don't supply them - (Track/Cut/Session), so SpaceBetween collapses to the back link alone at the left edge. The Mix - detail page fills the center with its in-flow visualizer-controls container (§7b). The native - wrapper lets the row flex-wrap the center zone to its own line on narrow widths, keeping the - controls in-flow rather than clipping. *@ + @* Two-end top row: back link (left) | optional action (right), on one SpaceBetween row. The action + slot stays null for media that don't supply it (Track/Cut/Session), so SpaceBetween collapses to + the back link alone at the left edge. The Mix detail page fills the action with its lava-lamp + toggle and renders its knob band below via TopContent. *@
- + ← @BackLabel - @TopRowCenter - @TopRightAction
diff --git a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.cs b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.cs index 6cc9447..c785dc0 100644 --- a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.cs +++ b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.cs @@ -25,21 +25,13 @@ public partial class ReleaseDetailScaffold : ComponentBase [Parameter] public string BackLabel { get; set; } = "Archive"; /// - /// Optional medium-specific content rendered between the back link and the masthead — the "below - /// the back button, above the details" band. The Mix detail page uses it for the visualizer - /// controls row; other media leave it null. + /// Optional medium-specific content rendered as its own full-width band between the back/action top + /// row and the masthead — the "below the back button, above the details" band. The Mix detail page + /// uses it for the visualizer knob row (gated by Blazor on the lava-lamp toggle); other media leave + /// it null. /// [Parameter] public RenderFragment? TopContent { get; set; } - /// - /// Optional affordance rendered in the center of the top row, between the back link (left) and - /// (right). The Mix detail page uses it for the in-flow visualizer- - /// controls container that grows in place between the back link and the lava-lamp toggle (§7b); - /// other media leave it null, so the SpaceBetween row collapses the center and renders the back - /// link alone. Variance rides the slot, never a flag (Phase 9 §5.3). - /// - [Parameter] public RenderFragment? TopRowCenter { get; set; } - /// /// Optional action rendered at the top-right of the container, on the same SpaceBetween row as the /// back link (back link left, action right). The Mix detail page uses it for the lava-lamp diff --git a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.css b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.css index 41e3445..2c5ff4f 100644 --- a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.css +++ b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.css @@ -3,12 +3,3 @@ justify-content: center; margin-top: 1.5rem; } - -/* The three-zone top row: back link | center affordance | action. The center zone (the Mix - visualizer-controls container) grows in-flow between the two pinned ends. On narrow widths the row - flex-wraps so the center zone drops to its own line below the back/action pair — keeping the seven - knobs in-flow with the full row width rather than clipping. The MudStack output is a child Razor - component's native div, so ::deep is required to reach it. */ -::deep .deepdrft-track-detail-top-row-stack { - flex-wrap: wrap; -} diff --git a/DeepDrftPublic.Client/Pages/MixDetail.razor b/DeepDrftPublic.Client/Pages/MixDetail.razor index f60a8df..386b5fd 100644 --- a/DeepDrftPublic.Client/Pages/MixDetail.razor +++ b/DeepDrftPublic.Client/Pages/MixDetail.razor @@ -48,18 +48,20 @@ else BackLabel="All mixes" ShowMeta="@(hasGenre || hasDate)" ShowShareRow="false"> - - @* In-flow seven-knob control container, between the back link and the lava-lamp toggle. - It grows in place (width/opacity transition) when expanded and collapses to zero - footprint when closed — never a popover, drawer, or floating overlay (§7b). The - container mutates the shared MixVisualizerControlState; the backdrop bridge pushes the - dials. A knob drag does not collapse it — the toggle flips only on the lamp's click. *@ - - + + @* The seven-knob band lives in its own full-width area below the back/lamp top row. + Blazor — not CSS — controls its visibility: it is rendered only while the lava-lamp is + on, so when off it is not in the DOM at all. No background, no animation, no reflow of + the row above. The band mutates the shared MixVisualizerControlState; the backdrop + bridge pushes the dials. A knob drag does not toggle it — the lamp's click does. *@ + @if (_controlsExpanded) + { + + } + - @* Lava-lamp button top-right, across from the back link. Toggles the in-flow control - container in the center zone. The icon swaps to its FILLED variant while the - container is expanded (§7f / Part B). *@ + @* Lava-lamp button top-right, across from the back link. Toggles the knob band below the + row. The icon swaps to its FILLED variant while the band is shown (§7f / Part B). *@ "mix-detail"; - // Lava-lamp inline knob-bar expanded state. Pure presentation over MixVisualizerControlState — the - // bar discloses the seven knobs and animates open/closed; toggling it touches no control value or - // bridge push. The lava-lamp button's filled/outline glyph is driven off this same flag. + // Lava-lamp knob-band visibility. Pure presentation over MixVisualizerControlState — gates whether + // the seven-knob MixVisualizerControls is rendered into the TopContent band; toggling it touches no + // control value or bridge push. The lava-lamp button's filled/outline glyph is driven off this flag. private bool _controlsExpanded; private void ToggleSettings() => _controlsExpanded = !_controlsExpanded;