crabidy/architecture/tui-search.md

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.