diff --git a/cbd-web/src/app.rs b/cbd-web/src/app.rs index 075120c..d9636ee 100644 --- a/cbd-web/src/app.rs +++ b/cbd-web/src/app.rs @@ -21,6 +21,13 @@ use crate::state::{ const VOLUME_STEP: f32 = 0.1; +/// The highest level the server will accept — it clamps to this in +/// `PlayerEngine::set_volume`, and now reports back the level it took, so a +/// slider that ran past this point had a dead stretch at its right end that +/// filled and then sprang back. Must track the engine's clamp; the audio +/// crate is native-only, so the value cannot be imported from it. +const MAX_VOLUME: f32 = 1.1; + /// How far one seek press moves the playing position, in milliseconds — the /// unit the wire speaks (architecture/seek.md D3). The step lives in the /// client; the server only ever receives an offset and applies it to the live @@ -1266,10 +1273,10 @@ fn Transport(store: Store) -> impl IntoView { diff --git a/docs/src/clients/web.md b/docs/src/clients/web.md index 12507d6..716efbc 100644 --- a/docs/src/clients/web.md +++ b/docs/src/clients/web.md @@ -18,7 +18,13 @@ the queue toolbar shows how many entries are waiting). Seek is there too — `,` and `.` move 15 seconds back and forward (`<` and `>` skip a whole track), as do the `⏪`/`⏩` buttons in -the transport bar. The `/` live filter is TUI-only for now. +the transport bar, and the progress bar is clickable — a click seeks to +that point in the track. The `/` live filter is TUI-only for now. + +The volume slider spans the server's whole accepted range, ending at 110% +— the server clamps there, so a slider that went further would have a +dead stretch at its right end. Its tooltip reports the current level as a +percentage; the TUI shows the same figure in its now-playing pane. ## How it is served diff --git a/plan/summary.md b/plan/summary.md index 778d6d9..24fc813 100644 --- a/plan/summary.md +++ b/plan/summary.md @@ -1587,3 +1587,23 @@ Not covered by a test: `Player::new` is only reachable through `Playback::new`, and the engine thread opens an audio device, so there is no device-free way to construct one here. `is_muted()` (a new engine getter — only `toggle_mute` existed, which cannot be used to *ask*) is likewise verified by reading. + +## Web volume slider bound (2026-07-27) + +The slider was `max="1.5"` while the engine clamps to `1.1`, so its right third +was unreachable: the fill stopped at 73% of the track and the rest stayed empty +no matter how far the thumb was dragged. Fixing the server to broadcast the +level it actually took (previous entry) is what made this visible — the thumb +now springs back from that dead stretch instead of sitting where it was +dropped. + +`max` is now a named `MAX_VOLUME` constant that has to track +`PlayerEngine::set_volume`'s clamp. It cannot be imported: `audio-player` is +native-only and the web client is wasm, so the two ends of this pair are kept +in agreement by the comment on each. + +Raising the engine's clamp instead was the alternative — 1.1 is deliberate +headroom, and more gain risks clipping — so the slider was the side to move. + +The tooltip also reports the level as a percentage now, which is the web +counterpart of the TUI's `Volume: 85%`.