@page "/cuts/{EntryKey}" @using DeepDrftModels.DTOs @using DeepDrftPublic.Client.Controls @using DeepDrftPublic.Client.Services @inherits CutDetailBase @(ViewModel.Release?.Title ?? "Cut") - DeepDrft @if (ViewModel.IsLoading) {
} else if (ViewModel.NotFound || ViewModel.Release is null) {
Cut not found.
All cuts
} else { var release = ViewModel.Release; var hasGenre = release.Genre is not null; var hasYear = release.ReleaseDate is not null; var firstTrack = ViewModel.Tracks.Count > 0 ? ViewModel.Tracks[0] : null; @* Full-screen content body (Phase 20 Wave 2 §1): the scaffold has no Class param, so a thin wrapper carries the min-height. dd-detail-fill keeps the body >= viewport height (below the nav) so the ambient visualizer reads full-screen and the site footer is pushed below the fold. *@
@* Ambient living waveform behind the album hero + track list (Phase 12 §3c/§3f mode B). Cut is multi-track: anchor to the release's EntryKey and default to the first track by TrackNumber. The bridge follows the live playing track within the release automatically (keys on TrackId match OR shared ReleaseEntryKey), so the field re-renders to whichever track the listener starts; TrackEntryKey is the at-rest datum before playback. *@ @* Theater toggle sits immediately LEFT of the lava-lamp popover (Phase 20 §3). Both are controls over the experience, not release content, so both stay in Theater Mode (§4/OQ4). Wrapped so they cluster on the right rather than spreading across the SpaceBetween row. *@
@* Theater toggle only appears when this Cut is the currently-playing release (Phase 20 Wave 2 §3). ShowTheaterToggle folds in the subsystem gate + the release-playing check. *@ @* Lava-lamp icon → popover panel (full parity, §3d-revised). Sits top-right across from the back link, clear of the header's own Play/Share affordances below. *@
@* Theater Mode (Phase 20 §4, Wave 2 §2): the release content stays mounted and eases out via a collapsing wrapper so it does not pop — IsContentHidden collapses it to zero height when Theater is on AND this Cut is the playing release. OFF eases it back to its normal layout. *@
@* Header split: meta + Play/Share on the LEFT, bordered cover on the RIGHT (spec §3.1). *@
@release.Title @release.Artist @if (hasGenre || hasYear) {
@if (hasGenre) { @release.Genre } @if (hasGenre && hasYear) { · } @if (hasYear) { @release.ReleaseDate!.Value.Year }
}
@* Header Play loads the full album into the queue at index 0 (§3.4 seam, closed P11 W1). Disabled until at least one streamable track is resolved. *@ Play @* Append the whole album (TrackNumber order) to the queue — same ordered list header Play uses. Append-only: does not start playback (AC7/AC8). *@ @* Release-mode share: copies the canonical /cuts/{entryKey} URL, not a single track (§3b). *@
@if (!string.IsNullOrEmpty(release.ImagePath)) { } else { }
@* Theater Mode (Wave 2 §2): eased collapse, mirroring the Header region. *@
@* Blurb sits between the header and the track-list divider. *@ @if (ViewModel.Tracks.Count == 0) { No tracks in this cut yet. } else {
@for (var i = 0; i < ViewModel.Tracks.Count; i++) { var track = ViewModel.Tracks[i]; var index = i;
@track.TrackNumber
@track.TrackName @* Append this single track to the queue (append-only, does not play). *@
}
}
} @code { // PlayerService is cascaded by CutDetailBase (used there for the Theater release-playing predicate). [CascadingParameter] public IQueueService? Queue { get; set; } // Header Play: load the full album into the queue starting at track 0. private Task PlayAlbum() { if (ViewModel.Tracks.Count == 0) return Task.CompletedTask; if (Queue is not null) return Queue.PlayRelease(ViewModel.Tracks, 0); // Queue cascade absent (prerender or non-interactive): fall back to direct single-track play. return PlayerService is not null ? PlayerService.SelectTrackStreaming(ViewModel.Tracks[0]) : Task.CompletedTask; } // Row play: toggle if this track is already playing/paused, otherwise load the album at this // row's index so playback continues to the end from the chosen track. private async Task PlayTrack(TrackDto track, int index) { if (PlayerService is null) return; var isThisTrack = PlayerService.CurrentTrack?.Id == track.Id; if (isThisTrack && (PlayerService.IsPlaying || PlayerService.IsPaused)) { await PlayerService.TogglePlayPause(); return; } if (Queue is not null) { await Queue.PlayRelease(ViewModel.Tracks, index); } else { // Queue cascade absent: fall back to direct single-track play. await PlayerService.SelectTrackStreaming(track); } } }