fix(p10-reframe-w1): clip visualizer to minimized FAB height; replace LavaLamp icon with SVG Repo glyph

This commit is contained in:
daniel-c-harvey
2026-06-16 11:53:47 -04:00
parent bef1e3adfb
commit 2c4bd3a394
4 changed files with 39 additions and 37 deletions
@@ -1,6 +1,6 @@
@if (_isMinimized)
{
<div class="minimized-dock">
<div class="minimized-dock" @ref="_miniDock">
<LevelMeterFab OnClick="@ToggleMinimized" />
</div>
}
@@ -27,7 +27,15 @@ public partial class AudioPlayerBar : ComponentBase, IAsyncDisposable
// var via a ResizeObserver (see Interop/layout/spacer.ts) rather than a static
// value, because the player reflows across breakpoints and grows with the
// error banner.
//
// _miniDock is the minimized FAB container. We observe it in minimized state so
// --player-height stays non-zero (the FAB's actual height) and the MixWaveformVisualizer
// clips to the top of the FAB rather than extending to the viewport bottom (fix §1).
// The player-spacer's .minimized class uses a hardcoded 60px and ignores the var,
// so publishing the FAB height here does not regress the spacer.
private ElementReference _playerRoot;
private ElementReference _miniDock;
private ElementReference _lastObservedElement;
private IJSObjectReference? _spacerModule;
private bool _spacerObserved;
@@ -114,22 +122,27 @@ public partial class AudioPlayerBar : ComponentBase, IAsyncDisposable
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Only the docked, expanded shape needs a spacer: the Fixed embed is
// already in normal flow, and the minimized FAB floats clear of content.
// Toggle the observer on the minimized/expanded transition only — the
// ResizeObserver itself handles every size change in between.
var shouldObserve = !_isMinimized && !Fixed;
if (shouldObserve == _spacerObserved) return;
// The Fixed embed is already in normal flow — no spacer/clip needed.
// For the docked player: we observe in BOTH expanded and minimized states
// so --player-height always reflects the live height of whichever element
// is visible. This keeps the MixWaveformVisualizer clipped to the top of
// the footer in both states (fix §1).
// expanded → observe _playerRoot (full player bar, reflows across breakpoints)
// minimized → observe _miniDock (floating FAB container, ~5660px)
// The player-spacer's .minimized class uses a hardcoded height and ignores
// the var, so publishing the FAB height here does not regress the spacer.
if (Fixed) return;
var elementToObserve = _isMinimized ? _miniDock : _playerRoot;
var alreadyOnThisElement = _spacerObserved && elementToObserve.Id == _lastObservedElement.Id;
if (alreadyOnThisElement) return;
var module = await GetSpacerModuleAsync();
if (module is null) return;
if (shouldObserve)
await module.InvokeVoidAsync("observe", _playerRoot);
else
await module.InvokeVoidAsync("unobserve");
_spacerObserved = shouldObserve;
await module.InvokeVoidAsync("observe", elementToObserve);
_spacerObserved = true;
_lastObservedElement = elementToObserve;
}
private async Task<IJSObjectReference?> GetSpacerModuleAsync()
@@ -3,13 +3,13 @@
Footer clip (Phase 10 W1, spec §2c): the backdrop must stop cleanly ABOVE the audio player bar so
no lava/waveform pixel paints over or under it. `overflow: hidden` clips the canvas to this box, and
`bottom` is inset by the player bar's LIVE height — `--player-height`, the custom property the player
already publishes on :root via its ResizeObserver (AudioPlayerBar + Interop/layout/spacer.ts). That
var tracks the expanded bar's border-box height across breakpoints/error-banner reflow, and resets to
0 when the bar is minimized — so the clip line follows the bar's actual current height with no extra
coupling: when minimized the var is 0 and the backdrop reaches the viewport bottom (the floating FAB,
z-index 1300, simply sits over it — there is no full-width bar to clip to), matching spec §2c. The
0px fallback keeps the backdrop full-height on any page that doesn't host the player. */
`bottom` is inset by `--player-height`, which AudioPlayerBar publishes on :root via its ResizeObserver
(Interop/layout/spacer.ts). The observer now points at whichever element is live:
expanded → the full player dock (tracks breakpoint reflow + error-banner growth)
minimized the minimized-dock FAB container (~5660 px)
so --player-height is always non-zero while the player is mounted and the clip line follows the bar in
BOTH states (fix §1 / p10-reframe-w1-fix). The 0px fallback keeps the backdrop full-height on any
page that does not host the player. */
.mix-waveform-bg {
position: fixed;
inset: 0;