docs: log WaveformSeeker W2 completion in COMPLETED.md

This commit is contained in:
daniel-c-harvey
2026-06-05 17:36:03 -04:00
parent acd76e0601
commit 2ee0667aa2
+40
View File
@@ -6,6 +6,46 @@ Newest entries at the top. Group by phase/wave header (mirroring `PLAN.md` / `CM
---
## WaveformSeeker Wave 2 — DOM seekbar + Interop module
**Status:** W2 (WaveformSeeker component) landed on 2026-06-05 (branch `waveform-w2-seeker`, pending merge to dev).
### W2 — WaveformSeeker component (seekbar replacement)
**Landed 2026-06-05.**
Implemented the interactive WaveformSeeker component: a bar-chart-styled seekbar replacing `MudSlider` in `PlayerSeekZone`, with DOM-rendered progress split via CSS and lazy-loaded pointer-capture drag interop.
**Component changes (`DeepDrftPublic.Client/Controls/AudioPlayerBar`):**
- `WaveformSeeker.razor` (+ `.cs`, `.css`) — new component consuming `WaveformProfile double[]?` and `Duration`, rendering bars as DOM elements with clip-overlay progress. Single CSS variable (`--seek-position`) changes per seek gesture; no per-bar re-render.
- Pointer-capture drag wired via `waveformSeeker.js` (ES module, lazy-loaded). Calculates seek target from click/drag position and invokes `OnSeekRequested` callback (delegates to `IPlayerService.SeekAsync`).
- Flat floor-height fallback when profile is unavailable — seek gesture always works, with or without loudness data.
- `PlayerSeekZone.razor` — now hosts `WaveformSeeker` in place of the removed `MudSlider` placeholder.
**Interop changes (`DeepDrftPublic/Interop/audio/`):**
- New `waveformSeeker.ts` module (separate from the TS audio bundle) — `PointerCaptureHandler` class managing `pointerdown` / `pointermove` / `pointerup` lifecycle. Compiled to `waveformSeeker.js` in `wwwroot/js/audio/`.
- Module loaded on first use (not bundled with audio stack) to defer its parse cost until the player is expanded and the seekbar is visible.
**`.gitignore` scoping:**
- Added scoped negation to track hand-authored `waveformSeeker.js` alongside existing TS-output ignore rule — allows the compiled JS to be committed for fast startup without committing intermediate TS compiler outputs.
**Service changes (`IPlayerService` / `AudioPlayerService` / `StreamingAudioPlayerService`):**
- New `WaveformProfile double[]?` property added to service interface and implementations.
- Fetched fire-and-forget on track load via `GetWaveformProfileAsync(trackId, cancellationToken)` — existing HTTP call from W1-T2.
- Cancellable via the track-reset flow (same cancellation token that stops spectrum animation).
- Cleared on reset with all other track state.
**Testing:**
- Manual verification: seekbar renders flat when profile unavailable; dragable when profile present; CSS clip-overlay tracks seek position correctly.
**Architecture notes:**
- WaveformSeeker does not re-fetch the profile — it consumes the same `IPlayerService.WaveformProfile` fetched during track load. No additional HTTP round-trip per seek gesture.
- Interop module (`waveformSeeker.js`) is independent of the audio playback stack — can be updated or replaced without touching audio scheduling logic.
- Pointer-capture semantics ensure seek is responsive even when the browser's event queue is saturated by animation frames.
- Flat fallback ensures seek gestures always work, even on tracks with no profile data (uploaded before W1, or on profile-generation failure).
---
## WaveformSeeker Wave 1 — Loudness profile + layout refactor
**Status:** W1-T1 (backend loudness computation), W1-T2 (HTTP transport), and W1-T3 (player layout refactor) landed on 2026-06-05.