docs(phase-12): fold popover-hosted controls into spec + plan
Controls move from an inline per-page knob bar to a single popover-hosted panel triggered by the lava-lamp icon, placed identically on every host (Mix, Cut, Session, NowPlaying card). Dissolves the NowPlaying-controls question — full parity via the popover. Adds the popover panel wave, panel styling from theme tokens, and a popover-anchor open item.
This commit is contained in:
@@ -278,13 +278,27 @@ endpoint" — it is a content + upload + CMS + backfill + fetch slice (split int
|
|||||||
**Three hosting modes of the one engine (Daniel corrected "backdrop").** *"backdrop?? MIXES doesn't really
|
**Three hosting modes of the one engine (Daniel corrected "backdrop").** *"backdrop?? MIXES doesn't really
|
||||||
have a backdrop?"* — right: on Mix the visualizer is the full-bleed **centerpiece that IS the page**, not
|
have a backdrop?"* — right: on Mix the visualizer is the full-bleed **centerpiece that IS the page**, not
|
||||||
something behind content. The one engine is hosted three ways (spec §3f): **mode A — visualizer-is-the-page**
|
something behind content. The one engine is hosted three ways (spec §3f): **mode A — visualizer-is-the-page**
|
||||||
(Mix detail, full-bleed, seven-knob bar); **mode B — ambient environment** (Cut/Session detail, living
|
(Mix detail, full-bleed centerpiece); **mode B — ambient environment** (Cut/Session detail, living
|
||||||
texture *behind* the hero+content — this is the only mode that is genuinely a "backdrop"); **mode C —
|
texture *behind* the hero+content — this is the only mode that is genuinely a "backdrop"); **mode C —
|
||||||
contained live element** (NowPlaying card, a bounded live readout, `Fill`-sized to the card). Same engine,
|
contained live element** (NowPlaying card, a bounded live readout, `Fill`-sized to the card). Same engine,
|
||||||
same datum contract — variance is entirely in hosting composition. **Controls (Daniel, full parity, §8b):**
|
same datum contract — variance is entirely in hosting composition. **Controls (Daniel, full parity, §8b):**
|
||||||
the seven-knob bar + lava-lamp toggle ride **every Release Detail host** — Mix, Cut, **and** Session (modes
|
the lava controls ride **every host** — Mix, Cut, Session, **and** the NowPlaying card — via the single
|
||||||
A and B both carry the controls, not Mix-only); only the contained NowPlaying card (mode C) suppresses them
|
popover-hosted panel (below); controls are no longer a per-mode discriminator.
|
||||||
by default (one open sub-question — §8b-followup).
|
|
||||||
|
**Controls-hosting revision (Daniel, 2026-06-17 — supersedes the inline knob-bar model).** *"We have enough
|
||||||
|
[controls] now that I want to design a panel to be hosted in a popover for the visualizer controls. The
|
||||||
|
lava-lamp toggle should be wired to this popover, so anywhere we can put one Icon we can put the control
|
||||||
|
surface."* The eight knobs no longer ride an inline *bar* per page — they move into a **single
|
||||||
|
popover-hosted panel** triggered by the **lava-lamp icon** (click icon → panel pops over). This is **more
|
||||||
|
DRY than the per-page bar** (one `<icon → popover → panel>` composition reused verbatim, not three-to-four
|
||||||
|
per-host bar layouts) and it **dissolves §8b-followup**: with a popover, the small NowPlaying card places
|
||||||
|
the *same* icon as every other host and the panel floats on demand, so the "is the card too small for the
|
||||||
|
bar?" question evaporates — **full parity on all four surfaces, the popover way**. The SOLID seam: **one
|
||||||
|
panel component (`WaveformVisualizerControls` becomes the panel content), one popover host
|
||||||
|
(`WaveformVisualizerControlPopover`), placed by an icon anywhere.** Panel styled to the **NowPlaying Hero
|
||||||
|
look** — dark-navy ground, green-accent knobs, light icons, muted-navy filler — pulled from the
|
||||||
|
`deepdrft-tokens.css` source of truth (no hardcoded hex; spec §3g). New open item the popover creates: its
|
||||||
|
anchor/positioning per host (§8e) — a layout detail, not a presence decision.
|
||||||
|
|
||||||
**Deliverable 2 — NowPlayingHero overhaul (mode C).** `NowPlayingCard.razor` today animates **20 hardcoded
|
**Deliverable 2 — NowPlayingHero overhaul (mode C).** `NowPlayingCard.razor` today animates **20 hardcoded
|
||||||
CSS-bounce bars** with no audio coupling (the "stochastic" visualizer). Replace them with the *same*
|
CSS-bounce bars** with no audio coupling (the "stochastic" visualizer). Replace them with the *same*
|
||||||
@@ -299,13 +313,15 @@ etc.) — a `Mix`-named component on a Cut page is a lie that cements the wrong
|
|||||||
**composition** (a new optional `Ambient` slot on `ReleaseDetailScaffold` for mode B; Mix keeps its own
|
**composition** (a new optional `Ambient` slot on `ReleaseDetailScaffold` for mode B; Mix keeps its own
|
||||||
mode-A mount; the card is a mode-C contained mount; per-host control suppression), never a `switch (medium)`
|
mode-A mount; the card is a mode-C contained mount; per-host control suppression), never a `switch (medium)`
|
||||||
in the engine (memory *One source, multiple views*; scaffold's "variance rides a slot, never a flag" idiom,
|
in the engine (memory *One source, multiple views*; scaffold's "variance rides a slot, never a flag" idiom,
|
||||||
Phase 9 §5.3). The slot is named `Ambient` not `Backdrop` precisely because Mix doesn't use it. The lava
|
Phase 9 §5.3). The slot is named `Ambient` not `Backdrop` precisely because Mix doesn't use it. **The lava
|
||||||
controls ride **every Release Detail host** (Mix, Cut, Session — full parity, Daniel's §8b call); only the
|
controls are now one popover-hosted panel placed by the lava-lamp icon on every host** (Mix, Cut, Session,
|
||||||
contained NowPlaying card mounts controls-suppressed by default. The seam still supports flipping the card
|
NowPlaying card — full parity, the popover dissolving the old card-suppression sub-question); the panel and
|
||||||
to controls-on later with no engine change (memory *Design for adaptability up front*).
|
its NowPlaying-Hero styling are built once and reused (memory *Design for adaptability up front* — the
|
||||||
|
popover seam makes "place the controls anywhere there's an icon" a zero-cost composition).
|
||||||
|
|
||||||
Sequenced as **five waves**: `12.A → 12.B2 → (12.C ‖ 12.D)`, with **12.B1 a parallel server-side track**
|
Sequenced as **six waves**: `12.A → {12.B1 → 12.B2, 12.E}`, then `(12.B2 ∧ 12.E) → (12.C ‖ 12.D)` —
|
||||||
(`12.B1 → 12.B2`) that can start cold day one.
|
**12.B1 a parallel server-side track** and **12.E (the popover controls panel) a third parallel track**,
|
||||||
|
both startable cold day one off the rename.
|
||||||
|
|
||||||
- **12.A — Rename to the abstraction (mechanical, no behavior change).** `Mix*` → `Waveform*` across the
|
- **12.A — Rename to the abstraction (mechanical, no behavior change).** `Mix*` → `Waveform*` across the
|
||||||
five C#/Razor files + the TS module + its import path + the DI registration. **Load-bearing
|
five C#/Razor files + the TS module + its import path + the DI registration. **Load-bearing
|
||||||
@@ -323,30 +339,43 @@ Sequenced as **five waves**: `12.A → 12.B2 → (12.C ‖ 12.D)`, with **12.B1
|
|||||||
`EntryKey` and re-fetches on **track** change (not release change). **Depends on 12.A + 12.B1.**
|
`EntryKey` and re-fetches on **track** change (not release change). **Depends on 12.A + 12.B1.**
|
||||||
Acceptance: Mix renders the same high-res lava via the track-cardinal fetch; a non-Mix track returns
|
Acceptance: Mix renders the same high-res lava via the track-cardinal fetch; a non-Mix track returns
|
||||||
high-res.
|
high-res.
|
||||||
|
- **12.E — Popover-hosted control panel (the controls revision).** Turn the renamed
|
||||||
|
`WaveformVisualizerControls` into the **panel content** and build `WaveformVisualizerControlPopover`
|
||||||
|
pairing the lava-lamp trigger icon with that panel as overlay content (`MudPopover`). Style the panel to
|
||||||
|
the **NowPlaying Hero look** from `deepdrft-tokens.css` (no hardcoded hex; spec §3g). Make the
|
||||||
|
state-scoping call (one shared `WaveformVisualizerControlState`). **Depends on 12.A only** — no per-track
|
||||||
|
datum needed, so runs **parallel to 12.B**. The unit every host then places. Acceptance: lava-lamp icon
|
||||||
|
opens a Hero-styled popover with all eight knobs; turning a knob drives the visualizer via the unchanged
|
||||||
|
`Changed` seam; one panel reused everywhere.
|
||||||
- **12.C — `Ambient` slot on `ReleaseDetailScaffold` + mount on detail pages (mode B, full parity).**
|
- **12.C — `Ambient` slot on `ReleaseDetailScaffold` + mount on detail pages (mode B, full parity).**
|
||||||
Promote the full-bleed / foreground-stacking / dynamic-footer-clip pattern into the scaffold as an optional
|
Promote the full-bleed / foreground-stacking / dynamic-footer-clip pattern into the scaffold as an optional
|
||||||
`Ambient` slot; Cut mounts the ambient layer **with the full seven-knob bar + lava-lamp toggle** (full
|
`Ambient` slot; Cut mounts the ambient layer **and places the lava-lamp icon → popover** (full parity);
|
||||||
parity); Session mounts directly **also full-parity** (it doesn't compose the scaffold — spec §3e). Mix is
|
Session mounts directly **also full-parity** (it doesn't compose the scaffold — spec §3e). Mix is
|
||||||
**unchanged** (mode A keeps its own mount + controls). Also makes the state-scoping call (recommend one
|
**unchanged as a layer** (mode A keeps its own full-bleed mount); its only controls change is swapping the
|
||||||
shared `WaveformVisualizerControlState`). **Depends on 12.B2.** **§8b resolved (full parity) — no longer
|
inline `TopRowCenter` bar for the lava-lamp icon → popover (12.E's affordance). **Depends on 12.B2 + 12.E.**
|
||||||
gated**; Cut and Session ship with both the ambient layer and the controls.
|
**§8b resolved (full parity) — no longer gated**; Cut and Session ship with both the ambient layer and the
|
||||||
- **12.D — NowPlayingHero rewire (mode C).** Replace the synthetic bars with a contained,
|
popover controls.
|
||||||
controls-suppressed `<WaveformVisualizer>` driven by the live cascaded player, pointed at the current
|
- **12.D — NowPlayingHero rewire (mode C).** Replace the synthetic bars with a contained
|
||||||
track; add the `Fill`/container-sizing mode (spec §6c). **Depends on 12.A + 12.B2; independent of 12.C**
|
`<WaveformVisualizer>` driven by the live cascaded player, pointed at the current track; add the
|
||||||
|
`Fill`/container-sizing mode (spec §6c); **place the lava-lamp icon → popover on the card** (full parity —
|
||||||
|
the popover dissolves the old suppression). **Depends on 12.A + 12.B2 + 12.E; independent of 12.C**
|
||||||
(different host). Acceptance: home card shows the real playing-track high-res waveform, at-rest when
|
(different host). Acceptance: home card shows the real playing-track high-res waveform, at-rest when
|
||||||
nothing plays; no synthetic bars remain.
|
nothing plays, and carries the lava-lamp icon → popover like every other host; no synthetic bars remain.
|
||||||
|
|
||||||
**Resolved by Daniel (2026-06-17), kept visible per file convention:** datum resolution → **Direction B**
|
**Resolved by Daniel (2026-06-17), kept visible per file convention:** datum resolution → **Direction B**
|
||||||
(high-res all media; 512-bucket-fallback "Direction A" declined); multi-track-Cut datum → **dissolved by
|
(high-res all media; 512-bucket-fallback "Direction A" declined); multi-track-Cut datum → **dissolved by
|
||||||
the per-track model** (renders the current track's datum, no album-representative choice); Cut/Session
|
the per-track model** (renders the current track's datum, no album-representative choice); Cut/Session
|
||||||
hosting + controls → **full parity (option 3)**: all three hosting modes ship **and** the seven-knob bar +
|
hosting + controls → **full parity (option 3)**: all three hosting modes ship **and** the lava controls ride
|
||||||
lava-lamp toggle ride every Release Detail host (Mix, Cut, Session), not Mix-only — the three-mode *layout*
|
every host — the three-mode *layout* framing is retained, the change is that controls are no longer
|
||||||
framing is retained, the change is that controls are no longer Mix-suppressed on Cut/Session (the old "mode
|
Mix-suppressed (the old "mode 1 Mix-only" and "controls Mix-only" alternatives are both closed);
|
||||||
1 Mix-only" and "controls Mix-only" alternatives are both closed). **Newly open (created by the full-parity
|
**controls hosting → popover-hosted panel** (2026-06-17 revision): the controls move from an inline knob bar
|
||||||
flip + Direction B + per-track):** (a) **§8b-followup — do full-parity controls extend onto the NowPlaying
|
to a single popover-hosted panel triggered by the lava-lamp icon, placed identically on every host;
|
||||||
home card (mode C)?** Full parity was answered against the *detail* pages; the small contained home card
|
**§8b-followup is dissolved by this** — the NowPlaying card gets the icon → popover like everywhere else, so
|
||||||
stays **controls-suppressed by default** (a seven-knob bar on a compact now-playing card may be awkward) —
|
full parity now spans all four surfaces (Mix, Cut, Session, NowPlaying card). **Open (created by the popover
|
||||||
recommend keeping it suppressed, but flag for Daniel; one-line composition flip in 12.D either way.
|
revision + Direction B + per-track):** (a) **§8e — popover anchor/positioning per host**: where the
|
||||||
|
lava-lamp icon sits and the panel anchors on each host (Mix's `TopRightAction` corner is cleanest; the small
|
||||||
|
NowPlaying card is the tightest case and may look cramped) — recommend one popover with a per-host
|
||||||
|
`AnchorOrigin` parameter, not a fork; staff-engineer-owned layout call, flagged for a glance in review.
|
||||||
(b) **§8a-new — backfill shape + gate**: one-shot migration/script vs. a CMS
|
(b) **§8a-new — backfill shape + gate**: one-shot migration/script vs. a CMS
|
||||||
batch action over the generalized generate action (recommend the CMS action; Daniel-gated to *run* either
|
batch action over the generalized generate action (recommend the CMS action; Daniel-gated to *run* either
|
||||||
way; the fetch 404s gracefully for not-yet-backfilled tracks so it can ship before the backfill completes).
|
way; the fetch 404s gracefully for not-yet-backfilled tracks so it can ship before the backfill completes).
|
||||||
|
|||||||
@@ -3,17 +3,29 @@
|
|||||||
Status: **design-complete, implementation-ready.** Daniel resolved the three §8 open questions on
|
Status: **design-complete, implementation-ready.** Daniel resolved the three §8 open questions on
|
||||||
2026-06-17 — committing **Direction B** (high-res compute for all media), correcting the datum model to
|
2026-06-17 — committing **Direction B** (high-res compute for all media), correcting the datum model to
|
||||||
**per-track, not per-release**, and resolving §8b to **full parity on the lava controls**: the visualizer
|
**per-track, not per-release**, and resolving §8b to **full parity on the lava controls**: the visualizer
|
||||||
rides every Release Detail host (Mix, Cut, Session) **and the seven-knob bar + lava-lamp toggle ride all
|
rides every Release Detail host (Mix, Cut, Session) **and the controls ride all three** — not Mix-only. The
|
||||||
three** — not Mix-only. The three-hosting-mode *layout* framing (visualizer-is-the-page on Mix / ambient
|
three-hosting-mode *layout* framing (visualizer-is-the-page on Mix / ambient environment on Cut/Session /
|
||||||
environment on Cut/Session / contained on NowPlaying) is retained; the change is that controls are now
|
contained on NowPlaying) is retained; the change is that controls are present on all three detail-page hosts,
|
||||||
present on all three detail-page hosts, not suppressed on Cut/Session. The spec below is revised to those
|
not suppressed on Cut/Session.
|
||||||
three. One small sub-question remains open (§8b-followup): whether full-parity controls also extend onto
|
|
||||||
the small NowPlaying home card (default: **no** — the card stays controls-suppressed). Author:
|
**Controls-hosting revision (Daniel, 2026-06-17, supersedes the inline knob-bar model).** *"We have enough
|
||||||
product-designer. Date: 2026-06-17. **No code has been written by this doc.**
|
[controls] now that I want to design a panel to be hosted in a popover for the visualizer controls. The
|
||||||
|
lava-lamp toggle should be wired to this popover, so anywhere we can put one Icon we can put the control
|
||||||
|
surface."* The eight knobs no longer ride an inline *bar* per page. They move into a **single
|
||||||
|
popover-hosted control panel** triggered by the **lava-lamp icon**: click the icon → the panel pops over.
|
||||||
|
This is **more DRY than the per-page inline bar** and it **dissolves §8b-followup** — with a popover, every
|
||||||
|
host (Mix, Cut, Session, *and* the NowPlaying home card) places the *same* lava-lamp icon and gets the
|
||||||
|
*identical* panel; full parity is achieved *through the popover*, not through a bar re-laid-out on each
|
||||||
|
page. §8b-followup is now **answered**: the NowPlaying card gets the icon → popover like everywhere else.
|
||||||
|
The panel adopts the **NowPlaying Hero look** (dark-navy ground, green-accent knobs, light icons, muted-navy
|
||||||
|
filler — §3g). The §3/§6 hosting sections and the wave decomposition (§7, controls work consolidates into a
|
||||||
|
distinct 12.E concern) are revised to this model. One new open question the popover creates — its
|
||||||
|
positioning/anchor per host — is surfaced at §8e. Author: product-designer. Date: 2026-06-17. **No code has
|
||||||
|
been written by this doc.**
|
||||||
|
|
||||||
This phase has **two deliverables that share one engine**:
|
This phase has **two deliverables that share one engine**:
|
||||||
|
|
||||||
1. **Generalize** the landed Mix waveform visualizer (the WebGL2 lava renderer + its seven-knob controls)
|
1. **Generalize** the landed Mix waveform visualizer (the WebGL2 lava renderer + its eight-knob controls)
|
||||||
from a Mix-only treatment into a **track-cardinal visualizer** that every Release Detail page can host
|
from a Mix-only treatment into a **track-cardinal visualizer** that every Release Detail page can host
|
||||||
— Cuts, Sessions, and Mixes alike — rendering the waveform of **whatever track is currently
|
— Cuts, Sessions, and Mixes alike — rendering the waveform of **whatever track is currently
|
||||||
playing/selected** on that page.
|
playing/selected** on that page.
|
||||||
@@ -38,7 +50,7 @@ endpoint shape, and the acceptance criteria below.
|
|||||||
Cross-references (read before implementing):
|
Cross-references (read before implementing):
|
||||||
|
|
||||||
- `product-notes/phase-10-mix-visualizer-lava-reframe.md` — the lava renderer this generalizes. The
|
- `product-notes/phase-10-mix-visualizer-lava-reframe.md` — the lava renderer this generalizes. The
|
||||||
CPU-physics wax-blob model, the OKLab three-color gradient, the seven-knob control model, the bridge
|
CPU-physics wax-blob model, the OKLab three-color gradient, the eight-knob control model, the bridge
|
||||||
contract, and the read-only contract all **carry forward unchanged**. This spec does not re-derive any
|
contract, and the read-only contract all **carry forward unchanged**. This spec does not re-derive any
|
||||||
of them; it changes *where the engine lives*, *what feeds it*, and *who hosts it*.
|
of them; it changes *where the engine lives*, *what feeds it*, and *who hosts it*.
|
||||||
- `product-notes/mix-visualizer-webgl-renderer.md` — the renderer architecture (pipeline, datum-as-texture,
|
- `product-notes/mix-visualizer-webgl-renderer.md` — the renderer architecture (pipeline, datum-as-texture,
|
||||||
@@ -47,8 +59,14 @@ Cross-references (read before implementing):
|
|||||||
keyed on `ReleaseEntryKey` + `TrackId`, not on Mix.** Renamed and re-pointed by this phase (§3, §4).
|
keyed on `ReleaseEntryKey` + `TrackId`, not on Mix.** Renamed and re-pointed by this phase (§3, §4).
|
||||||
- `DeepDrftPublic.Client/Controls/MixVisualizerControls.razor[.cs/.css]`,
|
- `DeepDrftPublic.Client/Controls/MixVisualizerControls.razor[.cs/.css]`,
|
||||||
`DeepDrftPublic.Client/Services/MixVisualizerControlState.cs`,
|
`DeepDrftPublic.Client/Services/MixVisualizerControlState.cs`,
|
||||||
`DeepDrftPublic.Client/Controls/MixZoomMapping.cs` — the controls + state + mapping. Renamed, otherwise
|
`DeepDrftPublic.Client/Controls/MixZoomMapping.cs` — the controls + state + mapping. Renamed; the controls
|
||||||
unchanged.
|
component becomes the **panel content** hosted inside a new popover wrapper (§3d-revised, §3g) rather than
|
||||||
|
an inline `TopRowCenter` bar. State + mapping otherwise unchanged.
|
||||||
|
- **New (this revision):** a popover host component — working name `WaveformVisualizerControlPopover` — that
|
||||||
|
pairs the lava-lamp trigger icon with the `WaveformVisualizerControls` panel as its overlay content. This
|
||||||
|
is the single affordance every host places (§3d-revised). MudBlazor `MudPopover` (already in the dependency
|
||||||
|
set) is the natural substrate. Styled to the NowPlaying Hero look (§3g) — no inline hex; tokens from
|
||||||
|
`DeepDrftShared.Client/wwwroot/styles/deepdrft-tokens.css`.
|
||||||
- `DeepDrftPublic/Interop/visualizer/MixVisualizer.ts` — the WebGL2 renderer module. Renamed; the only
|
- `DeepDrftPublic/Interop/visualizer/MixVisualizer.ts` — the WebGL2 renderer module. Renamed; the only
|
||||||
*logic* change is how the datum's time-mapping is established when no high-res mix datum exists (§5).
|
*logic* change is how the datum's time-mapping is established when no high-res mix datum exists (§5).
|
||||||
- `DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor` — the shared detail chrome. The visualizer
|
- `DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor` — the shared detail chrome. The visualizer
|
||||||
@@ -114,9 +132,10 @@ its controls, mounted in **three hosting modes** (§3f) — *visualizer-is-the-p
|
|||||||
environment* on Cut/Session detail, *contained live element* on the home-page NowPlaying card — each fed by
|
environment* on Cut/Session detail, *contained live element* on the home-page NowPlaying card — each fed by
|
||||||
a **per-track high-res datum** that exists for every track (Direction B, §5). The visualizer always renders
|
a **per-track high-res datum** that exists for every track (Direction B, §5). The visualizer always renders
|
||||||
**the currently playing/selected track's** datum; the release is the host, not the datum's owner. The lava
|
**the currently playing/selected track's** datum; the release is the host, not the datum's owner. The lava
|
||||||
controls ride **every Release Detail host** — Mix, Cut, and Session all get the seven-knob bar + lava-lamp
|
controls ride **every host** — Mix, Cut, Session, *and* the NowPlaying card — via a **single
|
||||||
toggle (full parity, §3d). The NowPlaying card drives the *same* engine off live playback (§6) and stays
|
popover-hosted control panel** triggered by the lava-lamp icon (§3d-revised). Every host places one icon and
|
||||||
**controls-suppressed by default** (the one open sub-question, §8b-followup).
|
gets the identical panel; full parity (including the NowPlaying card) is achieved through the popover, so
|
||||||
|
§8b-followup is closed. The NowPlaying card drives the *same* engine off live playback (§6).
|
||||||
|
|
||||||
**In scope.**
|
**In scope.**
|
||||||
|
|
||||||
@@ -137,9 +156,13 @@ toggle (full parity, §3d). The NowPlaying card drives the *same* engine off liv
|
|||||||
re-implementing the wrapper.
|
re-implementing the wrapper.
|
||||||
- **Rewire the NowPlayingHero** to mount the visualizer driven by the live cascaded player, replacing the
|
- **Rewire the NowPlayingHero** to mount the visualizer driven by the live cascaded player, replacing the
|
||||||
20 hardcoded CSS bars (§6).
|
20 hardcoded CSS bars (§6).
|
||||||
- **Mount the lava controls on every detail-page host** — Mix, Cut, and Session all carry the seven-knob
|
- **Build the popover-hosted control panel** (§3d-revised, §3g). The renamed `WaveformVisualizerControls`
|
||||||
bar + lava-lamp toggle (full parity, §3d). The NowPlaying card mounts **controls-suppressed** by default
|
becomes the **panel content**; a new `WaveformVisualizerControlPopover` host pairs the lava-lamp trigger
|
||||||
(§3d, §8b-followup).
|
icon with that panel as overlay content. One panel, one popover host, placed by an icon anywhere — the
|
||||||
|
SOLID seam (§3d-revised). Style the panel to the **NowPlaying Hero look** (§3g) from real theme tokens.
|
||||||
|
- **Place the lava-lamp icon → popover on every host** — Mix, Cut, Session, *and* the NowPlaying card. Every
|
||||||
|
host's controls affordance is now identical: one icon, one popover, one panel. Full parity on all four
|
||||||
|
surfaces (§3d-revised) — §8b-followup is dissolved by the popover.
|
||||||
|
|
||||||
**Out of scope / unchanged.**
|
**Out of scope / unchanged.**
|
||||||
|
|
||||||
@@ -148,7 +171,9 @@ toggle (full parity, §3d). The NowPlaying card drives the *same* engine off liv
|
|||||||
changes its *input plumbing* and its *compute breadth*, never its art.
|
changes its *input plumbing* and its *compute breadth*, never its art.
|
||||||
- **No bridge redesign.** The single-owner bridge, the idempotent datum guard, the `IsActivePlayer`
|
- **No bridge redesign.** The single-owner bridge, the idempotent datum guard, the `IsActivePlayer`
|
||||||
gating, the `isPlaying`-gated rAF loop — all preserved. Extend the fetch, not the contract.
|
gating, the `isPlaying`-gated rAF loop — all preserved. Extend the fetch, not the contract.
|
||||||
- **No new control model.** The seven knobs and `…ControlState` stay as-is (renamed). No new dials.
|
- **No new control model.** The eight knobs and `…ControlState` stay as-is (renamed). No new dials. The
|
||||||
|
popover changes *where the controls are hosted* (a popover panel instead of an inline bar), not *what they
|
||||||
|
are* — same knobs, same state, same `Changed` seam.
|
||||||
- **No new high-res *algorithm*.** Direction B generalizes the *existing* duration-derived compute to run
|
- **No new high-res *algorithm*.** Direction B generalizes the *existing* duration-derived compute to run
|
||||||
per-track for all media; it does not redesign how the high-res datum is computed (`MixWaveformResolution`
|
per-track for all media; it does not redesign how the high-res datum is computed (`MixWaveformResolution`
|
||||||
carries forward — the same ~333 samples/sec duration-derived model).
|
carries forward — the same ~333 samples/sec duration-derived model).
|
||||||
@@ -171,7 +196,8 @@ every later wave references the generalized names:
|
|||||||
| `MixVisualizerControlState.cs` (+ DI registration) | `WaveformVisualizerControlState.cs` |
|
| `MixVisualizerControlState.cs` (+ DI registration) | `WaveformVisualizerControlState.cs` |
|
||||||
| `MixZoomMapping.cs` | `WaveformZoomMapping.cs` |
|
| `MixZoomMapping.cs` | `WaveformZoomMapping.cs` |
|
||||||
| `MixVisualizer.ts` (+ the `./js/visualizer/MixVisualizer.js` import path) | `WaveformVisualizer.ts` |
|
| `MixVisualizer.ts` (+ the `./js/visualizer/MixVisualizer.js` import path) | `WaveformVisualizer.ts` |
|
||||||
| `DDIcons.LavaLamp` / `LavaLampFilled` | keep (the lava-lamp glyph is the *controls* affordance, now on every detail-page host — §3d) |
|
| `DDIcons.LavaLamp` / `LavaLampFilled` | keep (the lava-lamp glyph is now the **popover trigger** — the single controls affordance, placed on every host — §3d-revised) |
|
||||||
|
| *(new)* | `WaveformVisualizerControlPopover.razor[.cs/.css]` — popover host pairing the lava-lamp trigger icon with the `WaveformVisualizerControls` panel (§3d-revised, §3g). Not a rename; new in 12.E. |
|
||||||
|
|
||||||
The `ReleaseEntryKey` / `TrackId` parameters and the fetch keep working unchanged through the rename.
|
The `ReleaseEntryKey` / `TrackId` parameters and the fetch keep working unchanged through the rename.
|
||||||
The `mix-waveforms` vault name and `MixMetadata.WaveformEntryKey` stay (they are still where the high-res
|
The `mix-waveforms` vault name and `MixMetadata.WaveformEntryKey` stay (they are still where the high-res
|
||||||
@@ -190,10 +216,11 @@ Generalizing does **not** mean flattening every medium to the same look. The cle
|
|||||||
|
|
||||||
- **Shared (the engine):** the renderer, the bridge, the controls component, the state, the datum
|
- **Shared (the engine):** the renderer, the bridge, the controls component, the state, the datum
|
||||||
contract, the playback coupling, the read-only contract. One copy, consumed by all hosts.
|
contract, the playback coupling, the read-only contract. One copy, consumed by all hosts.
|
||||||
- **Per-host (the composition):** *whether* the visualizer is mounted, *whether* the lava controls are
|
- **Per-host (the composition):** *whether* the visualizer is mounted, *where the lava-lamp icon sits* (the
|
||||||
exposed, and *what datum* the host points it at. These ride host composition (slots + parameters),
|
controls affordance is now a single popover-triggering icon — §3d-revised), and *what datum* the host
|
||||||
never a `switch (medium)` inside the engine. A medium that wants no visualizer mounts none; a medium
|
points it at. These ride host composition (slots + parameters), never a `switch (medium)` inside the
|
||||||
that wants the ambient backdrop but no knobs mounts the backdrop with controls suppressed.
|
engine. A medium that wants no visualizer mounts none; a host that wants the controls places the lava-lamp
|
||||||
|
icon and the identical popover panel follows.
|
||||||
|
|
||||||
This is the same "variance rides a slot, never a flag" discipline the scaffold already uses for
|
This is the same "variance rides a slot, never a flag" discipline the scaffold already uses for
|
||||||
`Header`/`Hero`/`TopRightAction` (Phase 9 §5.3) — extended to the ambient mount.
|
`Header`/`Hero`/`TopRightAction` (Phase 9 §5.3) — extended to the ambient mount.
|
||||||
@@ -217,17 +244,21 @@ into the scaffold so it is the default, not a Mix bespoke). A host that supplies
|
|||||||
plain background (Liskov: absent slot = no ambient layer, no regression).
|
plain background (Liskov: absent slot = no ambient layer, no regression).
|
||||||
|
|
||||||
- **Cut** (composes the scaffold) supplies `<WaveformVisualizer ReleaseEntryKey=… TrackId=… />` to
|
- **Cut** (composes the scaffold) supplies `<WaveformVisualizer ReleaseEntryKey=… TrackId=… />` to
|
||||||
`Ambient` **with the full seven-knob bar + lava-lamp toggle exposed** (full parity, §3d) — a living
|
`Ambient` and places the **lava-lamp icon → popover panel** (full parity, §3d-revised) — a living
|
||||||
waveform field behind the album hero + track list, rendering the *currently selected/playing track's*
|
waveform field behind the album hero + track list, rendering the *currently selected/playing track's*
|
||||||
datum, tunable in place. **§8b is resolved: Cut and Session get the ambient layer AND the controls
|
datum, tunable in place via the popover. **§8b is resolved: Cut and Session get the ambient layer AND the
|
||||||
(full parity, not Mix-only).** The ambient *layout* (visualizer behind hero+content) is unchanged; the
|
controls (full parity, not Mix-only).** The ambient *layout* (visualizer behind hero+content) is
|
||||||
difference from the earlier draft is that the controls are no longer suppressed here.
|
unchanged; the difference from the earlier draft is that the controls are present, and (this revision)
|
||||||
|
they live in a popover behind the lava-lamp icon rather than an inline bar.
|
||||||
- **Session** does not compose the scaffold (§3e) — it mounts the ambient visualizer directly behind its
|
- **Session** does not compose the scaffold (§3e) — it mounts the ambient visualizer directly behind its
|
||||||
own hero overlay, the same engine, its own thin full-bleed wrapper.
|
own hero overlay, the same engine, its own thin full-bleed wrapper.
|
||||||
- **Mix does *not* use this slot.** Mix is the *visualizer-is-the-page* mode (§3f) and keeps its existing
|
- **Mix does *not* use this slot.** Mix is the *visualizer-is-the-page* mode (§3f) and keeps its existing
|
||||||
full-bleed mount + `TopRowCenter` knob-bar + lava-lamp toggle exactly as the Phase 10 reframe landed
|
full-bleed mount as the Phase 10 reframe landed it. **The one controls change for Mix in this revision:**
|
||||||
them. The `Ambient` slot is for the *ambient* mode only — folding Mix into it would force the slot to
|
its `TopRowCenter` inline knob-bar is replaced by the lava-lamp icon → popover panel (§3d-revised), the
|
||||||
carry both "the page" and "behind the page," which is the conflation §3f exists to avoid.
|
same affordance every other host now uses. The `TopRightAction` lava-lamp glyph Mix already mounts becomes
|
||||||
|
the popover trigger directly — a small, contained change to Mix, not a redesign. The `Ambient` slot is for
|
||||||
|
the *ambient* mode only — folding Mix into it would force the slot to carry both "the page" and "behind
|
||||||
|
the page," which is the conflation §3f exists to avoid.
|
||||||
|
|
||||||
**Why the scaffold, not each page.** The full-bleed wrapper, the foreground stacking context, and the
|
**Why the scaffold, not each page.** The full-bleed wrapper, the foreground stacking context, and the
|
||||||
footer-clip plumbing (the dynamic-footer overflow clip from the reframe §2c) are all *chrome*, and the
|
footer-clip plumbing (the dynamic-footer overflow clip from the reframe §2c) are all *chrome*, and the
|
||||||
@@ -235,36 +266,51 @@ scaffold is where chrome lives. Putting the ambient layer on the scaffold means
|
|||||||
stacking context, and the mount point are written once. `SessionDetail` is the lone holdout that doesn't
|
stacking context, and the mount point are written once. `SessionDetail` is the lone holdout that doesn't
|
||||||
compose the scaffold today — see §3e.
|
compose the scaffold today — see §3e.
|
||||||
|
|
||||||
### 3d. Where do the lava controls live per host? (the controls boundary — full parity, resolved)
|
### 3d. Where do the lava controls live? (the controls boundary — popover-hosted, full parity, resolved)
|
||||||
|
|
||||||
The seven-knob lava bar is an **expert tuning surface** whose identity is "the lava lamp." **Daniel's §8b
|
**Revised model (Daniel, 2026-06-17): a single popover-hosted control panel, triggered by the lava-lamp
|
||||||
call: full parity on the detail pages.** The controls ride **every Release Detail host**:
|
icon.** *"We have enough [controls] now that I want to design a panel to be hosted in a popover for the
|
||||||
|
visualizer controls. The lava-lamp toggle should be wired to this popover, so anywhere we can put one Icon
|
||||||
|
we can put the control surface."* This supersedes the inline knob-*bar* model (where each detail page laid
|
||||||
|
out its own eight-knob bar). The controls are now **one panel behind one icon**, placed identically on
|
||||||
|
every host.
|
||||||
|
|
||||||
- **Mix, Cut, and Session detail pages all carry the seven-knob bar + lava-lamp toggle.** The
|
The mechanics:
|
||||||
`WaveformVisualizerControls` mount is **no longer Mix-suppressed** — it rides every detail-page mount.
|
|
||||||
On Mix (mode A) the bar sits in `TopRowCenter` over the full-bleed visualizer as the Phase 10 reframe
|
|
||||||
landed it; on Cut and Session (mode B) the same controls bar + lava-lamp toggle ride the ambient mount,
|
|
||||||
letting a visitor tune the living field behind the release. The *layout* still differs per mode (§3f) —
|
|
||||||
visualizer-is-the-page vs. ambient-behind-content — but the **controls presence is uniform across all
|
|
||||||
three detail hosts.** Rationale (Daniel): the lava is a signature affordance of the site; giving every
|
|
||||||
release page the tuning surface makes the whole site feel alive and consistent, not just the Mix page.
|
|
||||||
The shared `WaveformVisualizerControlState` supplies the dial values and is where each mount reads/writes,
|
|
||||||
so tuning behaves identically wherever the bar appears (see the state-scoping note below).
|
|
||||||
- **The NowPlaying home card (mode C) stays controls-suppressed by default.** Full parity was resolved
|
|
||||||
against the Release *Detail* pages; the NowPlaying card is the *contained* mode-C host — a small hero
|
|
||||||
panel, not a full page — where a seven-knob tuning bar may be awkward. Default: the card mounts the
|
|
||||||
ambient/contained field with **no knobs, no lava-lamp button**, rendering at the shared tuned defaults.
|
|
||||||
**This is the one open sub-question — see §8b-followup.** The seam supports controls-on-the-card either
|
|
||||||
way (the controls are a separate component over shared state), so flipping it later is a one-line
|
|
||||||
composition change, not an engine change.
|
|
||||||
|
|
||||||
> **State-scoping note (raised by full parity, staff-engineer's call at 12.C).** With the controls now
|
- **The lava-lamp icon is the single affordance.** Click the icon → the control panel pops over. There is
|
||||||
> present on Mix *and* Cut *and* Session, decide whether `WaveformVisualizerControlState` is **one shared
|
no separate inline bar and no separate toggle; the icon *is* the toggle and the popover *is* the panel.
|
||||||
> tuning** (a knob turned on a Cut page is the same lava everywhere — single source of truth, simplest, and
|
Closed state is just the icon (compact, unobtrusive). Open state floats the panel over the surface.
|
||||||
> consistent with the "one engine" framing) or **per-host/per-mode scoped** (each surface remembers its own
|
- **One panel, one popover host, placed by an icon anywhere.** The renamed `WaveformVisualizerControls`
|
||||||
> dial positions). **Recommend one shared state** — it matches the single-engine model, it is what the
|
becomes the **panel content** (the eight RadialKnobs, unchanged). A new `WaveformVisualizerControlPopover`
|
||||||
> persisted `WaveformVisualizerControlState` already is, and "the lava lamp has one set of dials" is the
|
host pairs the lava-lamp trigger icon with that panel as its overlay content (MudBlazor `MudPopover` the
|
||||||
> simpler mental model. Flag only; not a product blocker.
|
natural substrate). Every host renders exactly this one component; the panel is byte-for-byte identical
|
||||||
|
everywhere. **This is the SOLID seam, named precisely:** *one panel component, one popover host, placed by
|
||||||
|
an icon anywhere.*
|
||||||
|
- **Full parity across all four surfaces, the popover way.** Mix, Cut, Session, **and** the NowPlaying card
|
||||||
|
each place the lava-lamp icon and get the identical popover panel. There is no per-host control divergence
|
||||||
|
left to decide — the variance that *used* to live in "which page shows the bar" is dissolved because every
|
||||||
|
host shows the same icon. The only per-host question that remains is *where the icon sits and where the
|
||||||
|
popover anchors* (§8e), which is positioning, not presence.
|
||||||
|
- **Why this is more DRY than the inline bar.** The earlier model asked each detail page to host the
|
||||||
|
controls bar in its own layout (Mix in `TopRowCenter`, Cut/Session in the ambient composition, the
|
||||||
|
NowPlaying card suppressed because a bar didn't fit). That is three-to-four *different* control
|
||||||
|
compositions over one shared state. The popover collapses them to **one** composition — `<icon → popover →
|
||||||
|
panel>` — reused verbatim. The awkwardness that justified suppressing controls on the small NowPlaying card
|
||||||
|
evaporates: a popover doesn't compete with the card's compact layout the way an inline bar would, because
|
||||||
|
it's closed by default and floats when opened. **That is what dissolves §8b-followup** (below).
|
||||||
|
|
||||||
|
**§8b-followup is dissolved, not deferred.** The old open sub-question — "do the full-parity controls extend
|
||||||
|
onto the small NowPlaying card?" — existed *because* an inline eight-knob bar was awkward on a compact card.
|
||||||
|
With the popover, the card places the same lava-lamp icon as every other host and the panel floats on
|
||||||
|
demand; there is no compact-layout conflict to weigh. **Resolution: the NowPlaying card gets the icon →
|
||||||
|
popover like everywhere else — full parity on all four surfaces.** (See §8b-followup, now marked resolved.)
|
||||||
|
|
||||||
|
> **State-scoping note (staff-engineer's call at 12.E).** With one shared panel surfaced on every host,
|
||||||
|
> `WaveformVisualizerControlState` should be **one shared tuning** — a knob turned in the popover on a Cut
|
||||||
|
> page is the same lava everywhere. This is even more natural under the popover than under the inline-bar
|
||||||
|
> model: there is literally one panel, so "one set of dials" is the only coherent reading. It matches the
|
||||||
|
> single-engine framing and is what the persisted `WaveformVisualizerControlState` already is. Flag only;
|
||||||
|
> not a product blocker.
|
||||||
|
|
||||||
### 3e. The `SessionDetail` scaffold question
|
### 3e. The `SessionDetail` scaffold question
|
||||||
|
|
||||||
@@ -277,9 +323,11 @@ Session gets the ambient layer at all — don't reopen the Session-vs-scaffold d
|
|||||||
compose the scaffold, so Cut gets the ambient layer for free via the slot. This asymmetry is fine: the
|
compose the scaffold, so Cut gets the ambient layer for free via the slot. This asymmetry is fine: the
|
||||||
slot serves scaffold-composing pages; the one non-composing page mounts the shared engine directly. The
|
slot serves scaffold-composing pages; the one non-composing page mounts the shared engine directly. The
|
||||||
*engine* is still single-source either way — only the *mount* differs, which is exactly the per-host
|
*engine* is still single-source either way — only the *mount* differs, which is exactly the per-host
|
||||||
variance §3b sanctions. **Under full parity (§3d), Session's direct mount also carries the seven-knob bar +
|
variance §3b sanctions. **Under full parity (§3d-revised), Session also places the lava-lamp icon → popover
|
||||||
lava-lamp toggle** — the controls bar rides Session's own top-row composition the same way Cut's rides the
|
panel** — the same single affordance every host uses. Because the controls are now one popover behind one
|
||||||
scaffold's, so Session is full-parity even though it doesn't compose the scaffold.
|
icon (not an inline bar threaded into a page's top row), Session's lack of scaffold composition is a
|
||||||
|
non-issue for controls: it just places the icon wherever its own chrome puts it. Session is full-parity even
|
||||||
|
though it doesn't compose the scaffold.
|
||||||
|
|
||||||
### 3f. Three hosting modes of the one engine (the elaboration §8b asked for)
|
### 3f. Three hosting modes of the one engine (the elaboration §8b asked for)
|
||||||
|
|
||||||
@@ -289,37 +337,75 @@ out surface-by-surface, because the slot design and the naming follow from this
|
|||||||
|
|
||||||
| Mode | Surfaces | What the visualizer *is* on screen | Controls | Mount mechanism |
|
| Mode | Surfaces | What the visualizer *is* on screen | Controls | Mount mechanism |
|
||||||
|------|----------|-----------------------------------|----------|-----------------|
|
|------|----------|-----------------------------------|----------|-----------------|
|
||||||
| **A — Visualizer-is-the-page** | **Mix detail** | The full-bleed centerpiece. The lava field **is** the page; the mix details sit *over* it as a thin overlay. You came here to watch the lava. | Seven-knob bar + lava-lamp toggle (`TopRowCenter`) | Mix's own full-bleed mount (unchanged from Phase 10 reframe) — **not** the `Ambient` slot |
|
| **A — Visualizer-is-the-page** | **Mix detail** | The full-bleed centerpiece. The lava field **is** the page; the mix details sit *over* it as a thin overlay. You came here to watch the lava. | Lava-lamp icon → popover panel (§3d-revised) | Mix's own full-bleed mount (unchanged from Phase 10 reframe) — **not** the `Ambient` slot |
|
||||||
| **B — Ambient environment** | **Cut detail, Session detail** | Living texture *behind and around* the hero + content. The album cover / track list / session hero is the subject; the waveform is environment that makes the page feel alive. You came here for the release; the lava is atmosphere. | **Seven-knob bar + lava-lamp toggle (full parity, §3d)** | `ReleaseDetailScaffold.Ambient` slot (Cut) / direct full-bleed mount (Session, §3e) |
|
| **B — Ambient environment** | **Cut detail, Session detail** | Living texture *behind and around* the hero + content. The album cover / track list / session hero is the subject; the waveform is environment that makes the page feel alive. You came here for the release; the lava is atmosphere. | Lava-lamp icon → popover panel (full parity, §3d-revised) | `ReleaseDetailScaffold.Ambient` slot (Cut) / direct full-bleed mount (Session, §3e) |
|
||||||
| **C — Contained live element** | **NowPlaying card** (home) | A small, bounded live panel *inside* the card. Not full-bleed, not "behind" anything — a contained element that shows the real signal of whatever is playing. | Suppressed by default (§8b-followup) | Contained mount, `Fill`-mode canvas sized to the card, not the viewport (§6c) |
|
| **C — Contained live element** | **NowPlaying card** (home) | A small, bounded live panel *inside* the card. Not full-bleed, not "behind" anything — a contained element that shows the real signal of whatever is playing. | Lava-lamp icon → popover panel (full parity — the popover dissolves the old suppression, §3d-revised) | Contained mount, `Fill`-mode canvas sized to the card, not the viewport (§6c) |
|
||||||
|
|
||||||
**Why three modes and not "backdrop everywhere":** the three differ in *what the user's eye treats as the
|
**Why three modes and not "backdrop everywhere":** the three differ in *what the user's eye treats as the
|
||||||
subject*. On Mix the visualizer **is** the subject (mode A). On Cut/Session the *release* is the subject
|
subject*. On Mix the visualizer **is** the subject (mode A). On Cut/Session the *release* is the subject
|
||||||
and the visualizer is environment (mode B — this is the only mode that is genuinely a "backdrop"). On the
|
and the visualizer is environment (mode B — this is the only mode that is genuinely a "backdrop"). On the
|
||||||
home card the visualizer is a *bounded live readout* of current playback (mode C). Same engine, same datum
|
home card the visualizer is a *bounded live readout* of current playback (mode C). Same engine, same datum
|
||||||
contract, same renderer — three compositions. This is the SOLID payoff stated precisely: the variance is
|
contract, same renderer — three compositions. This is the SOLID payoff stated precisely: the variance is
|
||||||
entirely in **hosting composition** (full-bleed vs. slot vs. contained; viewport-sized vs. container-sized;
|
entirely in **hosting composition** (full-bleed vs. slot vs. contained; viewport-sized vs. container-sized),
|
||||||
controls present on all three detail hosts, suppressed-by-default only on the contained home card), never in
|
never in the engine. **The controls are no longer a per-mode discriminator at all** — under the
|
||||||
the engine. **Note the controls are no longer a per-mode discriminator on the detail pages** — modes A and B
|
popover-hosted model (§3d-revised) all four surfaces place the *same* lava-lamp icon → popover panel. The
|
||||||
both carry the full seven-knob bar (§3d, §8b resolved to full parity); only the mode-C home card suppresses
|
only per-mode controls nuance left is *where the icon anchors* (§8e), which is positioning, not presence.
|
||||||
them by default (§8b-followup).
|
|
||||||
|
|
||||||
**What this resolves about the slot.** Because Mix (mode A) keeps its own mount and the NowPlaying card
|
**What this resolves about the slot.** Because Mix (mode A) keeps its own mount and the NowPlaying card
|
||||||
(mode C) is a contained mount, the `ReleaseDetailScaffold` slot serves **mode B only** — and that is why it
|
(mode C) is a contained mount, the `ReleaseDetailScaffold` slot serves **mode B only** — and that is why it
|
||||||
is named `Ambient`, not `Backdrop` and not `Visualizer`. A `Visualizer` slot would imply Mix routes through
|
is named `Ambient`, not `Backdrop` and not `Visualizer`. A `Visualizer` slot would imply Mix routes through
|
||||||
it too; an `Ambient` slot says exactly what it carries: the behind-the-content environment layer for
|
it too; an `Ambient` slot says exactly what it carries: the behind-the-content environment layer for
|
||||||
scaffold-composing detail pages. Mode A and mode C mount the engine without the slot.
|
scaffold-composing detail pages. Mode A and mode C mount the engine without the slot. The **controls popover
|
||||||
|
is orthogonal to the slot** — it's an icon any host places, independent of how the *visualizer layer* is
|
||||||
|
mounted (slot vs. full-bleed vs. contained).
|
||||||
|
|
||||||
**§8b status: RESOLVED — modes A + B + C all live, with FULL PARITY on the controls across all three detail
|
**§8b status: RESOLVED — modes A + B + C all live, with FULL PARITY on the controls across all four hosts
|
||||||
hosts (Daniel, 2026-06-17).** All three hosting modes ship (Mix is the page, Cut/Session are ambient, the
|
via the popover (Daniel, 2026-06-17).** All three hosting *modes* ship (Mix is the page, Cut/Session are
|
||||||
home card is contained) **and** the seven-knob lava bar + lava-lamp toggle ride every Release Detail host —
|
ambient, the home card is contained) **and** the lava-lamp controls ride every host — Mix, Cut, Session, and
|
||||||
Mix, Cut, and Session — not Mix-only. The earlier "mode 1 (Mix + NowPlaying only, Cut/Session plain)"
|
the NowPlaying card — via the single popover-hosted panel (§3d-revised). The earlier "mode 1 (Mix +
|
||||||
fallback is closed; the earlier "controls Mix-only" default is overridden by Daniel's full-parity call. The
|
NowPlaying only, Cut/Session plain)" fallback is closed; the earlier "controls Mix-only" default is
|
||||||
Cut/Session ambient treatment **plus its in-place tuning controls** is the distinctive-feel win the
|
overridden; and the "is the NowPlaying card controls-suppressed?" sub-question is **dissolved by the
|
||||||
generalization buys; the `Ambient` slot carries the field and the controls bar rides the host composition
|
popover** (§8b-followup, now resolved). Each host is a one-visualizer-mount-plus-one-lava-lamp-icon
|
||||||
alongside it, so each detail page is a one-mount-plus-controls-bar composition. **One sub-question remains
|
composition. The remaining open item is the popover's anchor/positioning per host (§8e) — a layout detail,
|
||||||
(§8b-followup):** whether full parity also extends the seven-knob bar onto the small NowPlaying home card
|
not a presence decision.
|
||||||
(mode C) — default **no** (the card stays controls-suppressed), flagged for Daniel.
|
|
||||||
|
### 3g. Panel styling — the NowPlaying Hero look (theme tokens, no hardcoded hex)
|
||||||
|
|
||||||
|
Daniel's styling direction for the popover panel: **"same look and feel as the NowPlaying Hero."** Concretely:
|
||||||
|
|
||||||
|
- **Dark-navy primary background** — the same navy ground the NowPlaying Hero card sits on.
|
||||||
|
- **Green-accent knobs** with **light-colored icons**.
|
||||||
|
- **Muted-navy filler / circular border fill** around the knobs.
|
||||||
|
|
||||||
|
These map directly onto the existing token layer
|
||||||
|
(`DeepDrftShared.Client/wwwroot/styles/deepdrft-tokens.css` — the single source of truth; the Phase 10
|
||||||
|
reframe's "one source, no hardcoded hexes" discipline holds). The mapping staff-engineer should pull from:
|
||||||
|
|
||||||
|
| Panel element | Daniel's intent | Token (source of truth) | Value (for reference only — *use the token, not the hex*) |
|
||||||
|
|---------------|-----------------|--------------------------|-----------------------------------------------------------|
|
||||||
|
| Panel background | dark-navy primary | `--deepdrft-navy` (ground) / `--deepdrft-navy-mid` (elevated panel) | `#112338` / `#17283f` |
|
||||||
|
| Knob accent / active arc | green accent | `--deepdrft-green-accent` | `#3D7A68` |
|
||||||
|
| Knob icons / labels | light-colored | `--deepdrft-white` | `#FAFAF8` |
|
||||||
|
| Knob filler / circular border fill | muted-navy | `--deepdrft-navy-mid` (fill) / `--deepdrft-muted` (border-muted) | `#17283f` / `#8A9BB0` |
|
||||||
|
| Panel border / divider | subtle | `--deepdrft-border` (or the dark-theme `rgba(250,250,248,0.10)` divider) | — |
|
||||||
|
|
||||||
|
The NowPlaying Hero card itself (`NowPlayingCard.razor.css`) renders on a translucent off-white wash
|
||||||
|
(`rgba(250,250,248,0.06)` over the page's navy ground, `backdrop-filter: blur(8px)`) with the green accent
|
||||||
|
on its label/dot (`--deepdrft-green-accent`) and off-white title (`--deepdrft-white`). The panel should read
|
||||||
|
as a sibling of that card — the same navy-ground/green-accent/off-white vocabulary, the same restrained
|
||||||
|
translucency-over-navy feel — so a popover opened anywhere looks like it belongs to the same family as the
|
||||||
|
home card. **Staff-engineer pulls the tokens above; no inline hexes.** If a token is missing for a needed
|
||||||
|
shade (e.g. a knob's inactive track fill), prefer a `color-mix()` over the nearest token to a new hardcoded
|
||||||
|
value, and flag the gap rather than minting an untokened hex.
|
||||||
|
|
||||||
|
> **Note on the MudBlazor palette.** The MudBlazor theme (`DeepDrftShared.Client/Common/DeepDrftPalettes.cs`,
|
||||||
|
> applied via `<MudThemeProvider Theme="DeepDrftPalettes.Default">`) carries the *same* navy/green/off-white
|
||||||
|
> vocabulary in its `PaletteDark` (`Primary = #3D7A68` green-accent, `Background = #0D1B2A` navy,
|
||||||
|
> `Surface = #162437` navy-mid, `TextPrimary = #FAFAF8` off-white). For MudBlazor-component-level theming
|
||||||
|
> inside the popover, prefer `Color="Color.Primary"`/`Color.Surface` against that palette over CSS overrides
|
||||||
|
> where the component supports it; reach for the CSS `--deepdrft-*` tokens for the bespoke knob/panel chrome
|
||||||
|
> the RadialKnob owns. Either way the colors trace back to one source — the tokens file and the palette are
|
||||||
|
> the same vocabulary — so there is no third place to hardcode.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -479,15 +565,16 @@ datum (Direction B), the home card renders at full fidelity for *any* track, not
|
|||||||
the per-track model already requires for a multi-track Cut (where the release is fixed but the track
|
the per-track model already requires for a multi-track Cut (where the release is fixed but the track
|
||||||
scrolls). The two converge on one behavior: **re-fetch when the current track's identity changes.**
|
scrolls). The two converge on one behavior: **re-fetch when the current track's identity changes.**
|
||||||
Verify the guard keys on the current track, not the release.
|
Verify the guard keys on the current track, not the release.
|
||||||
- **Small surface, controls-suppressed by default.** The card is a small hero panel, not a full-bleed page.
|
- **Small surface, popover controls (full parity via the icon).** The card is a small hero panel, not a
|
||||||
Mount the visualizer **controls-suppressed** by default (no lava-lamp, no knob bar) and sized to the card,
|
full-bleed page — but under the popover-hosted model (§3d-revised) that is no longer a reason to suppress
|
||||||
not `position: fixed`. **Note this is now the *exception*, not the rule:** under Daniel's §8b full-parity
|
controls. The card places the **same lava-lamp icon → popover panel** as every other host; the panel
|
||||||
call the controls ride every Release *Detail* page (Mix, Cut, Session — §3d); the NowPlaying card is the
|
floats over the page on demand and doesn't compete with the card's compact title/sub layout the way an
|
||||||
one host that suppresses them by default, because a seven-knob tuning bar on a small home-card may be
|
inline eight-knob bar would have. **This is what dissolved §8b-followup** — the popover removed the
|
||||||
awkward. **Whether full parity extends here too is the open sub-question, §8b-followup** — the seam
|
awkwardness that justified suppression. The card's lava-lamp icon sits in/near the card chrome (its anchor
|
||||||
supports it either way (the controls are a separate component over shared state). **Flagged for
|
is one of the §8e positioning calls — a small card is the tightest anchor case). The visualizer canvas is
|
||||||
staff-engineer (§8d):** the
|
still sized to the card, not `position: fixed` (§6c container-sizing below). **Flagged for staff-engineer
|
||||||
renderer's footer-clip + full-viewport assumptions (`position: fixed; inset: 0`, clip-to-footer) are
|
(§8d):** the renderer's footer-clip + full-viewport assumptions (`position: fixed; inset: 0`,
|
||||||
|
clip-to-footer) are
|
||||||
written for a full-page backdrop; mounting it in a *contained card* needs the canvas to size to its
|
written for a full-page backdrop; mounting it in a *contained card* needs the canvas to size to its
|
||||||
container instead of the viewport. This is a real renderer-hosting wrinkle — the engine assumes
|
container instead of the viewport. This is a real renderer-hosting wrinkle — the engine assumes
|
||||||
full-window today. Either (i) parameterize the visualizer's sizing (full-viewport vs.
|
full-window today. Either (i) parameterize the visualizer's sizing (full-viewport vs.
|
||||||
@@ -498,9 +585,10 @@ datum (Direction B), the home card renders at full fidelity for *any* track, not
|
|||||||
- **Performance.** A WebGL2 lava render on the *home page* (the highest-traffic, first-paint surface) is a
|
- **Performance.** A WebGL2 lava render on the *home page* (the highest-traffic, first-paint surface) is a
|
||||||
heavier ask than on a detail page the user navigated to deliberately. Keep the existing `MAX_DPR = 2`
|
heavier ask than on a detail page the user navigated to deliberately. Keep the existing `MAX_DPR = 2`
|
||||||
cap and the `isPlaying`-gated rAF loop (it burns no frames when nothing plays — so an idle home page
|
cap and the `isPlaying`-gated rAF loop (it burns no frames when nothing plays — so an idle home page
|
||||||
pays nothing). If the lava is too heavy for the home card specifically, the controls-suppressed ambient
|
pays nothing). If the lava is too heavy for the home card specifically, the card's mount
|
||||||
backdrop can run a *cheaper* preset (fewer blobs) via a density default — but do not fork the renderer;
|
can run a *cheaper* preset (fewer blobs) via a density default — but do not fork the renderer;
|
||||||
use the existing density dial. **Flagged, not committed (§8d).**
|
use the existing density dial (now adjustable from the card's own popover too). **Flagged, not committed
|
||||||
|
(§8d).**
|
||||||
|
|
||||||
### 6d. What the card keeps
|
### 6d. What the card keeps
|
||||||
|
|
||||||
@@ -519,6 +607,15 @@ content + upload + CMS + backfill + fetch slice; the spec splits it into 12.B1 (
|
|||||||
backfill) and 12.B2 (the bridge's per-track fetch) so the heavy data work and the thin client rewire can be
|
backfill) and 12.B2 (the bridge's per-track fetch) so the heavy data work and the thin client rewire can be
|
||||||
sequenced and tested apart.
|
sequenced and tested apart.
|
||||||
|
|
||||||
|
**The controls work now consolidates into its own wave, 12.E** (the popover-panel revision). Under the
|
||||||
|
inline-bar model the controls were threaded into each detail-page mount, so 12.C carried "ambient layer +
|
||||||
|
controls bar" together. The popover model **pulls the controls out of the per-host mounts into one shared
|
||||||
|
component** built once (12.E) and *placed* by each host (an icon, §3d-revised). This is cleaner: 12.C and
|
||||||
|
12.D each reduce to "mount the visualizer layer + place the lava-lamp icon," and the panel/popover/styling
|
||||||
|
all live in one wave. 12.E depends only on the rename (12.A) — it touches the controls component + a new
|
||||||
|
popover host + the panel styling, none of which need the per-track fetch — so it can run in parallel with
|
||||||
|
the data work (12.B1/12.B2).
|
||||||
|
|
||||||
- **12.A — Rename to the abstraction (mechanical, no behavior change).** `Mix*` → `Waveform*` across the
|
- **12.A — Rename to the abstraction (mechanical, no behavior change).** `Mix*` → `Waveform*` across the
|
||||||
five files + the TS module + the import path + the DI registration (§3a). **Load-bearing prerequisite**
|
five files + the TS module + the import path + the DI registration (§3a). **Load-bearing prerequisite**
|
||||||
— every later wave references the generalized names. Acceptance: Mix detail behaves identically;
|
— every later wave references the generalized names. Acceptance: Mix detail behaves identically;
|
||||||
@@ -535,30 +632,45 @@ sequenced and tested apart.
|
|||||||
`EntryKey` and re-fetches on track-change (§4). **Depends on 12.A (renamed bridge) + 12.B1 (a datum to
|
`EntryKey` and re-fetches on track-change (§4). **Depends on 12.A (renamed bridge) + 12.B1 (a datum to
|
||||||
fetch).** Acceptance: the Mix detail page renders the same high-res lava via the new track-cardinal fetch;
|
fetch).** Acceptance: the Mix detail page renders the same high-res lava via the new track-cardinal fetch;
|
||||||
a non-Mix track now returns a high-res datum.
|
a non-Mix track now returns a high-res datum.
|
||||||
|
- **12.E — Popover-hosted control panel (the controls revision).** Turn the renamed
|
||||||
|
`WaveformVisualizerControls` into the **panel content** and build the `WaveformVisualizerControlPopover`
|
||||||
|
host pairing the lava-lamp trigger icon with that panel as overlay content (`MudPopover`, §3d-revised).
|
||||||
|
Style the panel to the **NowPlaying Hero look** from theme tokens (§3g — no hardcoded hex). Make the
|
||||||
|
state-scoping call (one shared `WaveformVisualizerControlState`, §3d-revised note). **Depends on 12.A**
|
||||||
|
(renamed controls component) only — no per-track datum needed, so it runs **parallel to 12.B**. Acceptance:
|
||||||
|
the lava-lamp icon opens a popover panel with all eight knobs, styled to the Hero look; turning a knob
|
||||||
|
drives the visualizer (where one is mounted) via the unchanged `Changed` seam; the panel is one component
|
||||||
|
reused everywhere. **This wave is the unit every host then places (§3d-revised).**
|
||||||
- **12.C — `Ambient` slot on the scaffold + mount on detail pages (mode B, §3f).** Promote the
|
- **12.C — `Ambient` slot on the scaffold + mount on detail pages (mode B, §3f).** Promote the
|
||||||
full-bleed/foreground/footer-clip pattern into `ReleaseDetailScaffold` as an optional `Ambient` slot
|
full-bleed/foreground/footer-clip pattern into `ReleaseDetailScaffold` as an optional `Ambient` slot
|
||||||
(§3c); Cut mounts the ambient layer **with the full seven-knob bar + lava-lamp toggle** (full parity,
|
(§3c); Cut mounts the ambient layer **and places the lava-lamp icon → popover** (full parity, §3d-revised);
|
||||||
§3d); Session mounts directly **also full-parity** (§3e). Mix is **unchanged** (mode A keeps its own
|
Session mounts directly **also full-parity** (§3e). Mix is **unchanged** as a visualizer layer (mode A
|
||||||
mount + controls). **Depends on 12.B2** (a datum to render). **§8b is resolved (full parity, no longer
|
keeps its own full-bleed mount); its only controls change is swapping the inline `TopRowCenter` bar for the
|
||||||
gated)** — Cut and Session ship with both the ambient layer and the controls. This wave also makes the
|
lava-lamp icon → popover (folded into 12.E's affordance). **Depends on 12.B2** (a datum to render) **and
|
||||||
state-scoping call (§3d note — recommend one shared `WaveformVisualizerControlState`). Acceptance: Mix
|
12.E** (the popover to place). **§8b is resolved (full parity, no longer gated)** — Cut and Session ship
|
||||||
unchanged; Cut and Session each show an ambient living layer **with a working seven-knob bar + lava-lamp
|
with both the ambient layer and the popover controls. Acceptance: Mix unchanged as a layer; Cut and Session
|
||||||
toggle**, rendering the current track's datum, no regression to the hero/content.
|
each show an ambient living layer **with a working lava-lamp icon → popover panel**, rendering the current
|
||||||
- **12.D — NowPlayingHero rewire (mode C, §3f).** Replace the synthetic bars with a contained,
|
track's datum, no regression to the hero/content.
|
||||||
controls-suppressed `<WaveformVisualizer>` driven by the live player, pointed at the current track (§6);
|
- **12.D — NowPlayingHero rewire (mode C, §3f).** Replace the synthetic bars with a contained
|
||||||
add the `Fill`/container-sizing mode (§6c). **Depends on 12.A + 12.B2** (renamed engine + a per-track
|
`<WaveformVisualizer>` driven by the live player, pointed at the current track (§6); add the
|
||||||
datum for whatever's playing). **Independent of 12.C** (different host; doesn't need the scaffold slot).
|
`Fill`/container-sizing mode (§6c); **place the lava-lamp icon → popover on the card** (full parity, §6c —
|
||||||
Acceptance: the home card shows the *real* high-res waveform of the playing track and sits at-rest when
|
the popover dissolves the old suppression). **Depends on 12.A + 12.B2 + 12.E** (renamed engine + a
|
||||||
nothing plays; no synthetic bars remain.
|
per-track datum + the popover to place). **Independent of 12.C** (different host; doesn't need the scaffold
|
||||||
|
slot). Acceptance: the home card shows the *real* high-res waveform of the playing track, sits at-rest when
|
||||||
|
nothing plays, and carries the lava-lamp icon → popover like every other host; no synthetic bars remain.
|
||||||
|
|
||||||
**Dependency shape:** `12.A → 12.B2 → (12.C ‖ 12.D)`, with **12.B1 a parallel server-side track** that
|
**Dependency shape:** `12.A → 12.B2 → (12.C ‖ 12.D)`, with **12.B1 a parallel server-side track** that
|
||||||
12.B2 depends on (`12.B1 → 12.B2`) but that can start cold day one (it needs no renamed client identifiers).
|
12.B2 depends on (`12.B1 → 12.B2`) but that can start cold day one (it needs no renamed client identifiers),
|
||||||
12.A is the cheap mechanical unblock; **12.B1 is the new load-bearing heavy** (compute + backfill); 12.B2 is
|
and **12.E (the popover controls) a third parallel track** depending only on 12.A — both 12.C and 12.D now
|
||||||
the thin client rewire that consumes it; 12.C (detail-page ambient hosts, **full-parity controls**) and 12.D
|
also depend on 12.E to place the icon. So: `12.A → {12.B1 → 12.B2, 12.E}`, then `(12.B2 ∧ 12.E) → (12.C ‖
|
||||||
(NowPlaying host, controls-suppressed by default) are independent siblings off 12.B2. The cold-start items
|
12.D)`. 12.A is the cheap mechanical unblock; **12.B1 is the load-bearing heavy** (compute + backfill); 12.B2
|
||||||
are **12.A** (touches everything, risks nothing) and **12.B1** (the data work — start it early; the backfill
|
is the thin client rewire that consumes it; **12.E is the controls consolidation** (one panel + popover +
|
||||||
gate is the only thing blocking it). With §8b resolved to full parity, 12.C no longer carries a mode-1
|
styling, built once); 12.C (detail-page ambient hosts) and 12.D (NowPlaying host) are independent siblings
|
||||||
fallback branch — Cut and Session are in, with controls.
|
that each mount a visualizer layer and place the popover. The cold-start items are **12.A** (touches
|
||||||
|
everything, risks nothing), **12.B1** (the data work — start it early; the backfill gate is the only thing
|
||||||
|
blocking it), and **12.E** (the controls panel — independent of all the data work). With §8b resolved to
|
||||||
|
full parity *and* the popover dissolving §8b-followup, 12.C and 12.D carry no controls-suppression branch —
|
||||||
|
every host places the same icon.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -572,31 +684,46 @@ fallback branch — Cut and Session are in, with controls.
|
|||||||
- **§8c (was: multi-track Cut's waveform) → dissolved by the per-track model.** The datum is per-track; the
|
- **§8c (was: multi-track Cut's waveform) → dissolved by the per-track model.** The datum is per-track; the
|
||||||
visualizer renders the *current* track's datum, so there is no release-level "which track represents the
|
visualizer renders the *current* track's datum, so there is no release-level "which track represents the
|
||||||
album" choice to make. The old "first track by `TrackNumber`" recommendation is moot.
|
album" choice to make. The old "first track by `TrackNumber`" recommendation is moot.
|
||||||
- **§8b (Cut/Session hosting + controls) → RESOLVED: all three modes live, FULL PARITY on controls.**
|
- **§8b (Cut/Session hosting + controls) → RESOLVED: all three modes live, FULL PARITY on controls, now
|
||||||
Daniel chose **option (3) — full parity on the lava controls** (over option (2) Mix-only controls): the
|
POPOVER-HOSTED.** Daniel chose **option (3) — full parity on the lava controls** (over option (2) Mix-only
|
||||||
visualizer rides every Release Detail host (Mix, Cut, Session) **and** the seven-knob bar + lava-lamp
|
controls): the visualizer rides every Release Detail host (Mix, Cut, Session) **and** the controls ride all
|
||||||
toggle ride all three, not just Mix. The three-hosting-mode *layout* framing (§3f — visualizer-is-the-page
|
three. The three-hosting-mode *layout* framing (§3f — visualizer-is-the-page on Mix, ambient on
|
||||||
on Mix, ambient on Cut/Session, contained on NowPlaying) is retained; "backdrop" stays retired as the
|
Cut/Session, contained on NowPlaying) is retained; "backdrop" stays retired as the wrong word for Mix.
|
||||||
wrong word for Mix. The only thing the flip changed: the `WaveformVisualizerControls` mount is **no longer
|
**Controls-hosting revision (2026-06-17):** the controls are no longer an inline knob *bar* per page — they
|
||||||
Mix-suppressed** on Cut/Session. The earlier mode-1 fallback (Mix + NowPlaying only) and the "controls
|
are a **single popover-hosted panel** triggered by the lava-lamp icon, placed identically on every host
|
||||||
Mix-only" default are both closed. One sub-question spun out — §8b-followup below.
|
(§3d-revised). The earlier mode-1 fallback (Mix + NowPlaying only) and the "controls Mix-only" default are
|
||||||
|
both closed. The sub-question that spun out (§8b-followup) is now **resolved by the popover** (below).
|
||||||
|
|
||||||
**Newly open (created by the full-parity flip and by Direction B + the per-track model):**
|
- **§8b-followup (do full-parity controls extend onto the NowPlaying card?) → RESOLVED by the popover.**
|
||||||
|
This sub-question existed *because* an inline eight-knob bar was awkward on a small, compact, high-traffic
|
||||||
|
card — so the earlier draft kept the card controls-suppressed by default and flagged the question for
|
||||||
|
Daniel. **The popover-hosting revision dissolves it:** the card places the same lava-lamp icon as every
|
||||||
|
other host, and the panel floats on demand rather than occupying the card's layout. There is no
|
||||||
|
compact-layout conflict left to weigh. **Resolution: the NowPlaying card gets the icon → popover like
|
||||||
|
everywhere else — full parity on all four surfaces** (Mix, Cut, Session, NowPlaying card). The only
|
||||||
|
remaining controls nuance is *where the icon anchors per host* — a positioning detail, now §8e.
|
||||||
|
|
||||||
**§8b-followup — Do the full-parity controls extend onto the NowPlaying home card (mode C)?**
|
**Newly open (created by the popover revision and by Direction B + the per-track model):**
|
||||||
Daniel's §8b full-parity call (option 3) was answered against the Release *Detail*-page framing — Mix, Cut,
|
|
||||||
and Session all get the seven-knob bar + lava-lamp toggle. The **NowPlaying home card** is a different host:
|
**§8e — Popover anchor/positioning per host (created by the popover revision).**
|
||||||
the contained mode-C panel, a small hero card on the highest-traffic surface, **not** a full detail page.
|
The popover dissolves the *presence* question but creates a *placement* one: where does the lava-lamp
|
||||||
The spec applies full-parity controls to the three detail pages and keeps the NowPlaying card
|
trigger icon sit, and where does the panel anchor, on each host? The hosts differ enough that one anchor
|
||||||
**controls-suppressed by default** (a living waveform readout with no knobs). Open: does Daniel want the
|
rule may not fit all:
|
||||||
seven-knob bar on the home card too, or does it stop at the detail pages? **A seven-knob tuning bar on a
|
- **Mix (mode A, full-bleed)** — the icon can take Mix's existing `TopRightAction` slot (it already mounts
|
||||||
small now-playing card may be awkward** — it competes with the card's compact title/sub layout and puts an
|
the lava-lamp glyph there); the panel anchors off that corner over the full-bleed field. Cleanest case.
|
||||||
expert affordance on the landing surface — which is why the default is *suppressed*. But the seam supports
|
- **Cut/Session (mode B, ambient)** — the icon rides the page chrome (Cut via the scaffold, Session via its
|
||||||
it either way (the controls are a separate component over shared `WaveformVisualizerControlState`), so
|
own composition); the panel floats over hero+content. Needs to clear the hero overlay and not collide with
|
||||||
flipping the card to controls-on is a one-line composition change in 12.D, not an engine change.
|
the share/play affordances already in the top row.
|
||||||
**Recommend keeping the card controls-suppressed** (full parity = detail pages; the home card stays a clean
|
- **NowPlaying card (mode C, small contained)** — the tightest case. A small card has little room for an
|
||||||
ambient readout) — but this is a Daniel call, surfaced not buried. Affects only 12.D's mount; everything
|
icon, and a popover panel sized to the Hero look may be larger than the card itself — so the panel likely
|
||||||
else in the phase is settled by the full-parity resolution.
|
anchors to the icon but **overflows the card bounds** (which is fine — popovers float above), yet its open
|
||||||
|
position must not cover the title/sub it's meant to accompany. Worth a deliberate anchor choice (e.g.
|
||||||
|
open upward/leftward away from the card body).
|
||||||
|
**Recommend:** a single `WaveformVisualizerControlPopover` with a per-host *anchor origin* parameter
|
||||||
|
(MudPopover already exposes `AnchorOrigin`/`TransformOrigin`), defaulted sensibly and overridden per host —
|
||||||
|
not a forked popover. This keeps one component while letting each host place it well. **Staff-engineer-owned
|
||||||
|
layout call; flagged for Daniel only because the card case (mode C) may look cramped and is worth a glance
|
||||||
|
in review.** Does not block the phase; the default anchor is shippable and tunable.
|
||||||
|
|
||||||
**§8a-new — How does the high-res backfill run, and is it gated?**
|
**§8a-new — How does the high-res backfill run, and is it gated?**
|
||||||
Direction B means every *existing* track needs its per-track high-res datum computed once. Two shapes:
|
Direction B means every *existing* track needs its per-track high-res datum computed once. Two shapes:
|
||||||
@@ -648,24 +775,31 @@ by a lava lamp on the landing page.
|
|||||||
3. **Track-cardinal fetch.** `GET api/track/{trackEntryKey}/waveform` (or the agreed track-cardinal route)
|
3. **Track-cardinal fetch.** `GET api/track/{trackEntryKey}/waveform` (or the agreed track-cardinal route)
|
||||||
returns the current track's high-res datum, 404 → graceful blank. The bridge resolves the *current
|
returns the current track's high-res datum, 404 → graceful blank. The bridge resolves the *current
|
||||||
track* and re-fetches on track change.
|
track* and re-fetches on track change.
|
||||||
4. **Mix unchanged (mode A).** The Mix detail page still renders the high-res lava with the seven-knob bar +
|
4. **Mix unchanged as a layer (mode A); controls via popover.** The Mix detail page still renders the
|
||||||
lava-lamp toggle, visualizer-is-the-page, at parity with the Phase 10 reframe — now via the
|
high-res lava visualizer-is-the-page at parity with the Phase 10 reframe — now via the track-cardinal
|
||||||
track-cardinal fetch.
|
fetch. Its controls are reached through the **lava-lamp icon → popover panel** (replacing the inline
|
||||||
5. **Ambient + controls on Cut/Session (mode B, full parity).** A Cut and a Session detail page show an
|
`TopRowCenter` bar), the same affordance every other host uses.
|
||||||
ambient living waveform layer rendering the *currently selected/playing track's* datum, **with a working
|
5. **Ambient + popover controls on Cut/Session (mode B, full parity).** A Cut and a Session detail page show
|
||||||
seven-knob bar + lava-lamp toggle** (full parity — controls are present and tunable in place, not
|
an ambient living waveform layer rendering the *currently selected/playing track's* datum, **with a
|
||||||
suppressed), with no regression to the hero/content.
|
working lava-lamp icon → popover control panel** (full parity — controls present and tunable in place via
|
||||||
6. **NowPlaying is real (mode C), controls-suppressed by default.** The home NowPlaying card shows the
|
the popover), with no regression to the hero/content.
|
||||||
|
6. **NowPlaying is real (mode C), with popover controls (full parity).** The home NowPlaying card shows the
|
||||||
*actual* high-res waveform of the playing track (scrolls/animates to the real signal, changes with track
|
*actual* high-res waveform of the playing track (scrolls/animates to the real signal, changes with track
|
||||||
changes), sits at-rest when nothing plays, and mounts **controls-suppressed by default** (no knob bar, no
|
changes), sits at-rest when nothing plays, and **carries the lava-lamp icon → popover panel like every
|
||||||
lava-lamp — pending §8b-followup). No hardcoded synthetic bars remain.
|
other host** (§8b-followup resolved by the popover). No hardcoded synthetic bars remain.
|
||||||
7. **One engine, three modes.** Mix (mode A), the Cut/Session ambient layer (mode B), and the NowPlaying
|
7. **One popover-hosted control panel, placed everywhere.** There is exactly one
|
||||||
|
`WaveformVisualizerControls` panel and one `WaveformVisualizerControlPopover` host; Mix, Cut, Session, and
|
||||||
|
the NowPlaying card all place the *same* lava-lamp icon → popover and get the *identical* panel — verified
|
||||||
|
by no per-host control fork. The panel is styled to the NowPlaying Hero look (§3g) from theme tokens, no
|
||||||
|
hardcoded hex.
|
||||||
|
8. **One engine, three modes.** Mix (mode A), the Cut/Session ambient layer (mode B), and the NowPlaying
|
||||||
card (mode C) all consume the *same* `WaveformVisualizer` component + renderer + state — verified by
|
card (mode C) all consume the *same* `WaveformVisualizer` component + renderer + state — verified by
|
||||||
there being exactly one of each, no per-host fork; the differences are hosting composition only
|
there being exactly one of each, no per-host fork; the differences are hosting composition only
|
||||||
(full-bleed vs. `Ambient` slot vs. contained; viewport- vs. container-sized; controls present on all
|
(full-bleed vs. `Ambient` slot vs. contained; viewport- vs. container-sized). Controls are uniform across
|
||||||
three detail hosts, suppressed-by-default on the contained home card).
|
all four hosts (the single popover panel, criterion 7).
|
||||||
8. **Read-only everywhere.** No host (including the NowPlaying card) exposes a seek/scrub/transport via
|
9. **Read-only everywhere.** No host (including the NowPlaying card) exposes a seek/scrub/transport via
|
||||||
the visualizer; the read-only contract holds on every mount.
|
the visualizer; the read-only contract holds on every mount. The popover panel exposes only the eight
|
||||||
9. **Bridge intact, re-keyed to track.** Single-owner handle, idempotent datum guard, `IsActivePlayer`
|
tuning dials — no transport.
|
||||||
|
10. **Bridge intact, re-keyed to track.** Single-owner handle, idempotent datum guard, `IsActivePlayer`
|
||||||
gating, and the `isPlaying`-gated rAF loop are unchanged across all mounts; the datum guard now re-arms
|
gating, and the `isPlaying`-gated rAF loop are unchanged across all mounts; the datum guard now re-arms
|
||||||
on **track** change (the multi-track Cut and the NowPlaying card both rely on this — they converge).
|
on **track** change (the multi-track Cut and the NowPlaying card both rely on this — they converge).
|
||||||
|
|||||||
Reference in New Issue
Block a user