docs: archive play-state icon normalization; update DeepDrftPublic.Client CLAUDE.md
Deploy DeepDrftAPI / Build, Publish & Bundle (push) Successful in 1m57s
Deploy DeepDrftManager / Build & Publish (push) Successful in 59s
Deploy DeepDrftPublic / Build & Publish (push) Successful in 3m34s
Deploy DeepDrftAPI / Deploy (push) Successful in 1m32s
Deploy DeepDrftManager / Deploy (push) Successful in 1m30s
Deploy DeepDrftPublic / Deploy (push) Successful in 1m26s
Deploy DeepDrftAPI / Build, Publish & Bundle (push) Successful in 1m57s
Deploy DeepDrftManager / Build & Publish (push) Successful in 59s
Deploy DeepDrftPublic / Build & Publish (push) Successful in 3m34s
Deploy DeepDrftAPI / Deploy (push) Successful in 1m32s
Deploy DeepDrftManager / Deploy (push) Successful in 1m30s
Deploy DeepDrftPublic / Deploy (push) Successful in 1m26s
This commit is contained in:
@@ -6,6 +6,82 @@ Newest entries at the top. Group by phase/wave header (mirroring `PLAN.md` / `CM
|
||||
|
||||
---
|
||||
|
||||
## Play-State Icon Normalization
|
||||
|
||||
**Status:** Phases 1–4 landed on 2026-06-06 (branches `track-card-play-state-wave1`, `track-card-play-state-wave2`, merged to dev).
|
||||
|
||||
### Phase 1 — Fix the gallery bug (correctness, smallest viable change)
|
||||
|
||||
**Landed 2026-06-06.**
|
||||
|
||||
Bound `TrackCard.IsPlaying` to real playback state instead of selection identity. In `TracksView`/`TracksGallery`, active track is now computed as `PlayerService.IsPlaying && CurrentTrack?.Id == track.Id`. Switched the card glyph from `MusicNote` to the `PlayArrow`/`Pause` vocabulary via `IsPaused` and `OnPause` parameters. Expanded `TracksView.OnPlayerStateChanged` to re-render on any state change, not only on `!IsLoaded` — ensures the gallery correctly reflects pause, play, track-change, and end-of-playback transitions.
|
||||
|
||||
**Component changes:**
|
||||
- `TrackCard.razor` — added `[Parameter] bool IsPaused`, `[Parameter] EventCallback OnPause` parameters; removed `MusicNote` icon; now conditionally renders `PlayArrow` when not playing or `Pause` when playing.
|
||||
- `TracksView.razor` — removed `_selectedTrack` field (selection now fully derived from service); removed `_clickCount`, `_lifecycleStatus`, `TestInteractivity` dev scaffolding; `OnPlayerStateChanged` now calls `StateHasChanged()` unconditionally instead of only on `!IsLoaded`.
|
||||
- `TracksGallery.razor` — removed internal `SelectedTrack` mutation and `StateHasChanged` calls on play click; now fully controlled by parent; `SelectedTrack` parameter is read-only.
|
||||
|
||||
**Architecture notes:**
|
||||
- Resolves the reported bug: gallery card now shows correct play/pause icon reflecting actual playback state.
|
||||
- Enabling pause affordance on cards required extending `TrackCard` with `IsPaused` + `OnPause`, preserving the component's presentational contract (stays parameter-driven, lives in shared library).
|
||||
- `TracksView.OnPlayerStateChanged` subscription pattern unchanged; expansion from selective to unconditional re-render ensures high-frequency state changes (like spectrum animation or per-sample progress) do not cause visual lag in the gallery.
|
||||
|
||||
### Phase 2 — Collapse dual selection state (SRP, prevents regression)
|
||||
|
||||
**Landed 2026-06-06.**
|
||||
|
||||
Eliminated divergence between `TracksView._selectedTrack` and `PlayerService.CurrentTrack`. `TracksGallery` is now fully controlled — the parent supplies and owns the active-track identity via parameter binding. Selection state is single-sourced from the player service.
|
||||
|
||||
**Component changes:**
|
||||
- `TracksGallery.razor` — removed parameter-field write in `HandlePlayClick`; no longer calls `StateHasChanged()` on click. Raises `SelectedTrackChanged` callback for the parent to route.
|
||||
- `TracksView.razor` — removed `_selectedTrack` backing field and its local mutation.
|
||||
|
||||
**Architecture notes:**
|
||||
- Resolves the secondary defect: gallery's notion of "active track" can no longer lag the player.
|
||||
- `TracksGallery` now a pure presentational component (reads `SelectedTrack`, raises `SelectedTrackChanged`, renders); all state derivation lives in the parent or the service.
|
||||
|
||||
### Phase 3 — Introduce the single transport-state resolver (DRY)
|
||||
|
||||
**Landed 2026-06-06.**
|
||||
|
||||
Introduced a unified glyph-mapping source: `PlaybackIcons.Resolve()` static method in `DeepDrftPublic.Client/Helpers/PlaybackIcons.cs`. This is the sole function responsible for mapping `(IsPlaying, IsPaused, trackId?, CurrentTrackId?)` to the correct transport icon (`PlayArrow`, `Pause`, or null). Replaces all hand-rolled ternaries across `TrackCard`, `PlayerControls`, and other surfaces.
|
||||
|
||||
**New code (`DeepDrftPublic.Client/Helpers`):**
|
||||
- `PlaybackIcons.cs` — static `Resolve(bool isPlaying, bool isPaused, long? trackId, long? currentTrackId)` method returning `(string? Icon, bool IsActive, bool IsPaused)` tuple. Icon mapping is the single source of truth.
|
||||
|
||||
**Component changes:**
|
||||
- `PlayerControls.razor(.cs)` — `IsPlaying` parameter removed from the `AudioPlayerBar → PlayerTransportZone → PlayerControls` chain. Instead, `PlayerControls` now subscribes to `IPlayerService.StateChanged` directly and calls `PlaybackIcons.Resolve()` to determine which icon to render and whether buttons are enabled/disabled.
|
||||
- `TrackCard.razor` — consumes the tuple returned by `PlaybackIcons.Resolve()` to set `Icon`, `IsActive` (CSS class for highlighting), and `Disabled` state on the FAB.
|
||||
|
||||
**Architecture notes:**
|
||||
- Eliminates the three-way duplication of "which icon for this state" logic.
|
||||
- Icon vocabulary is now standardized across all surfaces (`PlayArrow`/`Pause` pair, no `MusicNote`).
|
||||
- Future surfaces (queue list, now-playing chip, etc.) call the same `Resolve()` function instead of re-implementing the mapping.
|
||||
|
||||
### Phase 4 (optional, deferred) — Promote to a PlayStateIcon component
|
||||
|
||||
**Landed 2026-06-06.**
|
||||
|
||||
Created a new `PlayStateIcon.razor` component in `DeepDrftPublic.Client/Controls/` that encapsulates subscription + icon mapping + rendering. Rather than each surface calling `PlaybackIcons.Resolve()` and threading icons through parameters, surfaces now drop in `<PlayStateIcon />` and the component handles cascading, state subscription, and icon selection in one place.
|
||||
|
||||
**New component (`DeepDrftPublic.Client/Controls/PlayStateIcon.razor`):**
|
||||
- Injects `IPlayerService` and subscribes to `StateChanged` on mount.
|
||||
- Cascades `[CascadingParameter] DarkModeSettings DarkMode` for theming.
|
||||
- Renders an icon button (or FAB) with the correct glyph via `PlaybackIcons.Resolve()`.
|
||||
- Forwards `Disabled` parameter to the rendered MudIconButton/MudFab.
|
||||
- Raises `OnClick` callback when user clicks.
|
||||
|
||||
**Component changes:**
|
||||
- `PlayerControls.razor` — refactored to render its play/pause button via `<PlayStateIcon />` instead of a parameter-driven button. `IsPlaying` parameter removed from the component signature.
|
||||
- The `AudioPlayerBar → PlayerTransportZone → PlayerControls` chain no longer threads `IsPlaying`/`IsPaused` down; subscription happens inside `PlayStateIcon`.
|
||||
|
||||
**Architecture notes:**
|
||||
- `PlayStateIcon` handles the seam between `IPlayerService` (source of truth) and transport-icon rendering (presentation). This was the third surface (after `TrackCard` and `PlayerControls`); Phase 4 was triggered by the appearance of the third call site.
|
||||
- Reduces parameter threading in the component tree (no more passing state flags through intermediate layers).
|
||||
- New surfaces that need play/pause icons (queue list, hover-row play button, etc.) now have a reusable, off-the-shelf component instead of re-implementing subscription and mapping.
|
||||
|
||||
---
|
||||
|
||||
## WaveformSeeker Wave 3 — CMS PreProcessing panel
|
||||
|
||||
**Status:** W3 (CMS track-preprocessing panel) refactored on 2026-06-05 (branch `waveform-w3-cms`, merged to dev).
|
||||
|
||||
Reference in New Issue
Block a user