diff --git a/DeepDrftPublic.Client/CLAUDE.md b/DeepDrftPublic.Client/CLAUDE.md index f77e03e..e6d2772 100644 --- a/DeepDrftPublic.Client/CLAUDE.md +++ b/DeepDrftPublic.Client/CLAUDE.md @@ -10,7 +10,7 @@ All interactive UI for the site. Blazor WebAssembly. Pages, controls, the stream ## Actual structure -- `Pages/`: Routable components. `Home.razor` (hero/about), `SessionDetail.razor` (session detail — hero-dominant overlay composition rendered via ``: large background hero image with darkening gradient shim, cover thumbnail + title + play button overlaid near the hero's bottom, genre/date/share overlaid at the top; uses `MudContainer MaxWidth="Large"`; **does not compose `ReleaseDetailScaffold`** — `PlayTrack` is wired directly in its own `@code` block; mounts `` ambient engine + `` directly; renders `` below the hero for the release's description blurb), `MixDetail.razor` (mix detail — composes `ReleaseDetailScaffold` with `TopRightAction` lava-lamp ``; hero+meta rendered via `` in the scaffold's `Hero` slot with `ShowHeader="false"` suppressing the duplicate masthead; square ~600px cover-as-background with metadata overlaid; full-bleed `` is the mode-A centerpiece mounted by the page directly; renders `` below the hero for the release's description blurb), `CutDetail.razor` (album detail — composes `ReleaseDetailScaffold` with the `Ambient` slot carrying `` + `` for mode-B ambient layer; renders `` below the hero for the release's description blurb). **No demo pages** (`Counter.razor`, `Weather.razor` do not exist). +- `Pages/`: Routable components. `Home.razor` (hero/about), `SessionDetail.razor` (session detail — hero-dominant overlay composition rendered via ``: large background hero image with darkening gradient shim, cover thumbnail + title + play button overlaid near the hero's bottom, genre/date/share overlaid at the top; uses `MudContainer MaxWidth="Large"`; **does not compose `ReleaseDetailScaffold`** — `PlayTrack` is wired directly in its own `@code` block; mounts `` ambient engine + `` directly; renders `` below the hero for the release's description blurb), `MixDetail.razor` (mix detail — composes `ReleaseDetailScaffold` with `TopRightAction` lava-lamp ``; hero+meta rendered via `` in the scaffold's `Hero` slot with `ShowHeader="false"` suppressing the duplicate masthead; square ~600px cover-as-background with metadata overlaid; full-bleed `` is the mode-A centerpiece mounted by the page directly; renders `` below the hero for the release's description blurb), `CutDetail.razor` (album detail — composes `ReleaseDetailScaffold` with the `Ambient` slot carrying `` + `` for mode-B ambient layer; renders `` below the hero for the release's description blurb; each track row carries a per-track `` aligned far-right as the last flex child of `.cut-detail-track-row`), `FramePlayer.razor` (embeddable iframe player at `/FramePlayer`, uses `EmbedLayout`; two mutually-exclusive modes via query params: `TrackEntryKey` stages a single track as before; `ReleaseEntryKey` resolves the release's ordered tracks via `FramePlayerViewModel`, stages track 0 via `PlayerService.StageTrack`, and arms the queue via `Queue.Arm` — no JS interop in either path, so both run safely during prerender; the first play gesture in `AudioPlayerBar` routes through `Queue.Start()` which streams the current track and clears the armed state). **No demo pages** (`Counter.razor`, `Weather.razor` do not exist). - `Layout/`: `MainLayout.razor` (root layout, wraps in `AudioPlayerProvider`, hosts theme switcher), `DeepDrftMenu.razor` (branded menu bar), `NavMenu.razor` (nav list), `Pages.cs` (centralised nav index — `MenuPages` for header, `AllPages` for exhaustive list). - `Controls/`: Reusable components. - `TrackCard.razor`: Individual track display (image, name, artist, album, genre, release date). Play/pause icon controlled via `IsPaused` parameter. @@ -33,9 +33,11 @@ All interactive UI for the site. Blazor WebAssembly. Pages, controls, the stream - `NowPlayingStats.razor`: Home hero stat row. Three cards: Studio Cuts (total Cut-medium track count + zero-suppressed per-`ReleaseType` Cut release breakdown), Mixes (`MixReleaseCount` labelled "Sets" + `hh:mm` total mix runtime via `RuntimeFormat`), and Plays (static "XXX / Plays (Coming Soon)" odometer placeholder). Fetches `HomeStatsDto` via `IStatsDataService` on init; bridges the prerender fetch across the WASM seam with `PersistentComponentState` (persists only on a successful load, matching the medium-browse bridge pattern). Implements `IDisposable` to release the `PersistingComponentStateSubscription`. - `NowPlaying.razor`: Owns the home hero's right-side panel (`.now-playing-panel` — the outer wrapper formerly called `.hero-right` in `Home.razor`). Mounts `` as a full-bleed background inside `.np-visualizer-bg`, `` in `.np-visualizer-controls` (top-right corner), the three pulsing `.circle-deco` rings, and the content layer (hosts `` + ``). `Home.razor`'s `MudItem` renders `` directly with no wrapper. Subscribes to `IPlayerService.StateChanged` in `OnParametersSet` (reference-guarded, idempotent) and unsubscribes on dispose — needed because the player cascade is `IsFixed` (the provider's own re-render does not reach `NowPlaying`), so the subscription is the only way to re-render and re-propagate `ReleaseEntryKey`/`TrackId`/`TrackEntryKey` into `` when the playing track changes. - `ReleaseDetailScaffold.razor`: Shared scaffold for release detail pages. Gained an optional `Ambient` `RenderFragment` slot (Phase 12) — a full-bleed layer rendered behind the main content. Absent slot = no regression. Cut mounts `` + `` here; Mix uses its own full-bleed mount outside the scaffold. + - `SharePopover.razor`: Share affordance serving both track and release surfaces from one clipboard/popover-chrome source. **Track mode** (`EntryKey` set): copies the track's canonical URL and offers an iframe embed snippet pointing at `FramePlayer?TrackEntryKey=…`. **Release mode** (`ReleaseEntryKey` + `ReleaseMedium` set): copies the release's canonical detail URL (via `ReleaseRoutes.DetailHref`) and offers an iframe embed snippet pointing at `FramePlayer?ReleaseEntryKey=…`, which queues and auto-advances through the release's tracks on first play. Both modes offer the embed affordance — release mode no longer suppresses it. The iframe snippet is built by `EmbedSnippetBuilder`. A transient "Copied!" confirmation resets after a short delay. - `Helpers/`: Utilities and mapper functions. - `PlaybackIcons.cs`: Static `Resolve(isPlaying, isPaused, trackId, currentTrackId)` method — the sole glyph-mapping source for transport icons across all surfaces. Returns `(Icon, IsActive, IsPaused)` tuple. - `RuntimeFormat.cs`: Static `ToHoursMinutes(double totalSeconds)` helper. Formats a seconds value as `h:mm` (hours not zero-padded, minutes always two digits). Negative / non-finite inputs return `"0:00"`. Used by `NowPlayingStats` for the mix runtime figure. + - `EmbedSnippetBuilder.cs`: Static helper that builds the iframe embed snippet the share popover copies. `ForTrack(baseUri, trackEntryKey)` → `