57 lines
2.3 KiB
Markdown
57 lines
2.3 KiB
Markdown
# TUI `/` search filter
|
|
|
|
Pressing `/` in the library or queue pane opens a search input that
|
|
filters that pane's items live as you type (case-insensitive
|
|
substring). A small feature; recorded here for the couple of decisions
|
|
that were not obvious.
|
|
|
|
## Decisions
|
|
|
|
### D1 — filter, not jump
|
|
|
|
Vim's `/` jumps to the next match; here `/` *narrows* the visible list
|
|
to matching rows. For a music library ("show me everything with
|
|
'radiohead'") filtering is the more useful reading of "search in the
|
|
items", and it composes with the existing queue/mark actions — you
|
|
filter, then act on what is left.
|
|
|
|
### D2 — the filter lives on the pane, the input on the app
|
|
|
|
The pane (`Library`/`Queue`) owns a `Filter` (the query plus the list
|
|
of visible real indices). The `/` input mode (`App::search`) only holds
|
|
the editing buffer and which pane is focused. So the filter *survives*
|
|
closing the input: `Enter` keeps it applied and returns to navigation,
|
|
`Esc` clears it. This matches the other modal overlays (input, confirm)
|
|
in how keys are routed while it is open.
|
|
|
|
### D3 — view indices vs real indices
|
|
|
|
The panes keep their full item list; the filter maps between the
|
|
*view* index (what the selection bar and `StatefulList` navigation key
|
|
off) and the *real* index into the list. This matters most for the
|
|
queue: removal and set-current send **real queue positions** to the
|
|
server, so a filtered selection must map back before it is sent —
|
|
otherwise `d` on the third visible row would remove the wrong track.
|
|
Marks likewise live on the full list, so a marked-but-hidden item still
|
|
counts when queueing.
|
|
|
|
Because `StatefulList` already routes through `get_size`/`select`/
|
|
`selected`, pointing those at the filtered view made all the movement
|
|
keys (`j`/`k`/`g`/`G`/`Ctrl-d`/`Ctrl-u`) work on the filtered list with
|
|
no per-key changes.
|
|
|
|
### D4 — lifecycle
|
|
|
|
- **Library**: entering a node is a fresh listing, so search mode is
|
|
reset there (a stale filter from the previous folder would be
|
|
confusing).
|
|
- **Queue**: the queue is re-sent constantly (position ticks,
|
|
resolving), so an active search is *preserved* across updates and
|
|
only its visible set is recomputed.
|
|
|
|
## Scope
|
|
|
|
Implemented in the TUI only, per the request. The web client
|
|
(`architecture/web-client.md`) could mirror it later for parity; noted
|
|
as a follow-up, not done here.
|