crabidy/architecture/tui-search.md

2.3 KiB

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.