crabidy/plan/seek.md

73 lines
3.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Plan — seek
Executes `architecture/seek.md` against `quality/seek.md`. One commit:
the change is a single thin path from key to sink, and half-wiring it
would leave a dead RPC.
## A — The engine (where the only real logic is)
- [x] **A1 — `PlayerEngine::seek_target`** — a pure function of
(current position, delta millis, known duration) returning the absolute
target: saturating on both ends, 0 as the floor, `duration - 1 s` as
the ceiling when the duration is known, no ceiling when it is 0.
Extracted as a free function so the arithmetic is testable without an
audio device. *Verifies:* G1, G5, G6, G7.
- [x] **A2 — Rewrite `PlayerEngine::seek_to`** to route through the same
helper, dropping the `clamp(1s, duration)` that panics whenever the
duration is unknown or under a second. *Verifies:* G1.
- [x] **A3 — `PlayerEngine::seek_by`** — reads `sink.get_pos()`, applies
A1, calls `try_seek`, and on success notifies
`PlayerMessage::Elapsed` with the new position so a paused seek is
visible. Refuses when stopped. *Verifies:* G4, G8, G9.
- [x] **A4 — `PlayerEngineCommand::SeekBy` + `Player::seek_by`** on the
existing bounded reply pattern. *Verifies:* G3.
- [x] **A5 — Engine tests**: extremes (`i64::MIN`/`MAX`), zero duration,
short track, composition, near-end saturation. *Verifies:* G1, G4G7.
## B — Wire and server
- [x] **B1 — Proto**: `rpc Seek(SeekRequest) returns (SeekResponse)` with
`sint32 delta_millis`, documented with the clamping contract.
*Verifies:* G10.
- [x] **B2 — `PlaybackCommand::Seek { delta_millis }`** + its `name()`
arm, and the playback handler that forwards to `player.seek_by` and
warns on failure. No state. *Verifies:* G2, G12.
- [x] **B3 — `RpcService::seek`** following the `change_volume` shape
(record the field, send, return empty). *Verifies:* G10.
- [x] **B4 — Server test** that the command reaches the player with the
delta unchanged. *Verifies:* G11.
## C — Clients
- [x] **C1 — TUI**: `Action::SeekForward`/`SeekBackward`, `,`/`.` bindings
(plus `<`/`>` for prev/next) in `Scope::Global` with help text,
`MessageFromUi::Seek(i64)`,
the `lib.rs` arm, `rpc.rs` wrapper, `SEEK_STEP` constant, dispatch
tests. *Verifies:* G13, G14, G15.
- [x] **C2 — Web**: the same two actions in `keymap.rs` (global, with
`HELP` rows), `Rpc::seek`, the `app.rs` dispatch arms, and `⏪`/`⏩`
buttons in the transport bar. *Verifies:* G13, G14, G15.
- [x] **C4 — Web click-to-seek**: `state::seek_offset_for_fraction` (pure,
native-testable) plus an `on:click` on the gauge reading its geometry
from `current_target`, and `cursor: pointer` on the bar. *Verifies:* G18.
- [x] **C3 — CLI**: `GlobalCmd::Seek { seconds: i32 }` with
`allow_negative_numbers`, and its executor arm. *Verifies:* G14.
## D — Docs and gates
- [x] **D1 — Docs**: key tables (`docs/src/clients/tui.md`,
`docs/src/clients/web.md`), the CLI page's `global` list, and the
within-track/end-of-track/HLS sentences in `docs/src/queue.md`.
*Verifies:* G16.
- [x] **D2 — Gates**: workspace tests, `check-features`, `cargo fmt`, a
wasm build of `cbd-web`, and the book build. *Verifies:* G17.
- [x] **D3 — `plan/summary.md`** entry, then commit.
## Deferred (recorded, not dropped)
- Absolute seek + click-to-seek on the web progress gauge (the gauge is
already rendered; needs one proto field and a click handler).
- Configurable step size, and a larger step on `<`/`>`.
- Chapter-aware seek for podcasts/audiobooks — no provider exposes
chapter marks through the library model.