202 lines
10 KiB
Markdown
202 lines
10 KiB
Markdown
# Terminal UI — cbd-tui
|
|
|
|
<!-- toc -->
|
|
|
|
`cbd-tui` is the terminal client: a ratatui/crossterm application that
|
|
connects to a running server over gRPC and redraws from the pushed
|
|
update stream. It reads `cbd-tui.toml` for its connection defaults (see
|
|
[Configuration](../config.md)); flags before a subcommand override the
|
|
file, and running it with no subcommand starts the TUI (see
|
|
[Command line](./cli.md)).
|
|
|
|
## Layout
|
|
|
|
The screen has two focusable panes side by side and a now-playing pane:
|
|
|
|
- **Library** (left) — the current [library node](../library.md): the
|
|
child folders and tracks under the path you have browsed to. Entering
|
|
a folder replaces the listing.
|
|
- **Queue** (right) — the play [queue](../queue.md), with the playing
|
|
track highlighted.
|
|
- **Now playing** — the current track, a progress gauge, and the
|
|
frequency-spectrum bars below it (see [Spectrum](#frequency-spectrum),
|
|
fills the rest of the right column).
|
|
|
|
`Tab` cycles focus between the library and the queue; keys are routed to
|
|
whichever pane has focus (plus the global keys, which apply in either).
|
|
|
|
## Navigating
|
|
|
|
Navigation is vim-style. In the library, `j`/`k` move the selection,
|
|
`l` enters the selected folder, `h` goes back to the parent, and
|
|
`g`/`G` and `Ctrl-d`/`Ctrl-u` jump. `Enter` **replaces** the queue with
|
|
the current selection; `a` appends it and `L` queues it after the
|
|
current track. In the queue, `Enter` plays the selected track, `o`
|
|
jumps the selection to the playing track, `d` removes a track, `p`
|
|
inserts the library selection after the selected track, and `c`/`C`
|
|
clear the queue.
|
|
|
|
`%` creates a child of the open node where that is allowed (e.g. a
|
|
search term under `/tidal/search` — see [Search](../providers/search.md));
|
|
`e` renames such a node; `d` deletes the selection. Playback keys
|
|
(`Space`, `r`, `Ctrl-n`/`Ctrl-p`, volume, `m`, `z`, `x`) are global.
|
|
Press `?` at any time for the full binding table; `Esc` or `?` closes
|
|
it, and while it is open every other key is inert.
|
|
|
|
## Marks and saving
|
|
|
|
- `s` toggles a **mark** on the selected item. Marks live on the full
|
|
item list (they survive filtering) so a marked-but-hidden row still
|
|
counts when you queue or save.
|
|
- `v` (or `V`) enters **visual mode**: movement then marks or unmarks the
|
|
rows you sweep over, like vim's visual selection. The selection is
|
|
anchored where you entered it, so moving back over a row reverses it.
|
|
`Esc` leaves visual mode (keeping the marks and any `/` filter), and so
|
|
does any non-movement action. The pane title shows `— VISUAL` while it is
|
|
on. Both panes have it.
|
|
- `w` **saves** the selection — a library subtree, or in the queue pane
|
|
the whole queue — as a new folder of link files under
|
|
`/crabidy/<name>`; playback replays it from the source provider.
|
|
- `W` **captures** the selection into `/crabidy/<name>`: like `w`, but
|
|
each track's audio is fetched into the shared content store
|
|
(de-duplicated), so it plays back fully local afterwards. It works on
|
|
a library subtree and on the queue. Downloads can take a while;
|
|
progress shows in the library pane. See [The crabidy
|
|
store](../store.md).
|
|
- Captured rows are marked with a trailing `↓` (down-arrow) at the end of
|
|
the row, visible even while you browse another provider, so you can see
|
|
what you already have.
|
|
|
|
```admonish note
|
|
Saves and captures all live under the one `/crabidy` provider. Inside
|
|
`/crabidy`, `d` deletes a folder or track immediately with no
|
|
confirmation — it removes only the metadata toml, never the shared
|
|
store audio, which other saves may reference.
|
|
```
|
|
|
|
## The register: `y`, `d`, and `p`/`P`
|
|
|
|
The queue has marks and visual mode too, and they feed a **register** — one
|
|
in-memory slot, like vim's unnamed register:
|
|
|
|
- `y` **yanks** the selection into the register without changing anything.
|
|
It works in both panes: in the library it yanks the paths you have marked
|
|
(or the row under the cursor), in the queue the marked tracks.
|
|
- `d` in the queue **deletes** every marked row (or the cursor row) in one
|
|
go — and puts them in the register first.
|
|
- `c` and `C` also fill the register with what they clear, so emptying the
|
|
queue by accident is recoverable.
|
|
- `p` **pastes** the register after the cursor, `P` before it. The register
|
|
survives, so you can paste again.
|
|
|
|
That gives you the two vim moves: `d` then `P` puts the tracks back exactly
|
|
where they were, and `d` … `p` moves them somewhere else. The queue title
|
|
shows `register: n` while something is in it, so a paste is never blind.
|
|
|
|
```admonish note
|
|
`p` used to insert the **library** selection at the cursor. It now pastes
|
|
the register, so that flow is `y` in the library, then `p` in the queue.
|
|
`a`, `L`, and `Enter` still queue the library selection directly.
|
|
```
|
|
|
|
```admonish warning
|
|
The register lives in **your client**, in memory, one slot deep: another
|
|
client cannot undo your delete, a restart forgets it, and each write
|
|
overwrites the last. It holds *paths*, so a paste re-resolves them — a
|
|
yanked album node expands to its tracks at paste time, and a path that no
|
|
longer resolves (a search term you deleted meanwhile) does not come back.
|
|
```
|
|
|
|
## The `/` live filter
|
|
|
|
Pressing `/` opens a search input that filters the focused pane's items
|
|
live as you type (case-insensitive substring). It **narrows** the
|
|
visible list rather than jumping to a match, so you can filter and then
|
|
act on what is left. `Enter` keeps the filter applied and returns to
|
|
navigation; `Esc` clears it. Movement, marks, and queue actions all map
|
|
back to the real underlying rows, so acting on a filtered row affects
|
|
the right track.
|
|
|
|
```admonish tip
|
|
This `/` filter is a local view filter over items already listed. It is
|
|
distinct from the search-node feature (`%` on `/tidal/search` and
|
|
friends), which asks a provider for results. See
|
|
[Search](../providers/search.md).
|
|
```
|
|
|
|
Entering a library folder resets the filter (a fresh listing); the
|
|
queue's filter is preserved across the constant queue updates and only
|
|
its visible set is recomputed.
|
|
|
|
## Frequency spectrum
|
|
|
|
A row of frequency bars is drawn under the track progress while audio
|
|
plays, as block glyphs (`▁▂▃▄▅▆▇█`) in the accent color. The bars are
|
|
produced on the **server**: it taps its own audio output, runs an FFT
|
|
(~20 fps), folds the result into a few log-spaced bins, and streams
|
|
them on the update stream like every other bit of live state. So the
|
|
bars work whether the server is local or on another machine, and when
|
|
nothing is playing they fall to the floor.
|
|
|
|
Press `f` to show or hide them at runtime; `spectrum` in the client
|
|
config sets the startup default (`spectrum = true` is the default;
|
|
`false` starts hidden). Either way it is only a display choice — the
|
|
server always computes and streams the bars while audio flows.
|
|
|
|
## Key bindings
|
|
|
|
Global keys work in either pane. Pane keys apply only while that pane is
|
|
focused (several chords, like `j`/`Enter`/`d`, mean different things per
|
|
pane).
|
|
|
|
| Scope | Key | Action |
|
|
| ------- | ----------------- | ----------------------------------------- |
|
|
| Global | `?` | Show help |
|
|
| Global | `q` | Quit |
|
|
| Global | `Tab` | Switch between library and queue |
|
|
| Global | `Space` | Play/pause |
|
|
| Global | `r` | Restart current track |
|
|
| Global | `K` | Volume up |
|
|
| Global | `J` | Volume down |
|
|
| Global | `m` | Toggle mute |
|
|
| Global | `z` | Toggle shuffle |
|
|
| Global | `x` | Toggle repeat |
|
|
| Global | `Ctrl-n` | Next track |
|
|
| Global | `Ctrl-p` | Previous track |
|
|
| Global | `f` | Toggle the frequency spectrum |
|
|
| Library | `j` / `k` | Select next / previous item |
|
|
| Library | `g` / `G` | Select first / last item |
|
|
| Library | `Ctrl-d` | Jump 15 items down |
|
|
| Library | `Ctrl-u` | Jump 15 items up |
|
|
| Library | `h` | Go to parent folder |
|
|
| Library | `l` | Enter selected folder |
|
|
| Library | `s` | Mark / unmark selection |
|
|
| Library | `v` / `V` | Visual mode: movement toggles marks |
|
|
| Library | `y` | Yank selection into the register |
|
|
| Library | `Enter` | Replace queue with selection |
|
|
| Library | `a` | Append selection to queue |
|
|
| Library | `L` | Queue selection after current track |
|
|
| Library | `%` | Create node here (e.g. search term) |
|
|
| Library | `e` | Rename selected node |
|
|
| Library | `d` | Delete selection |
|
|
| Library | `w` | Save selection as bookmark |
|
|
| Library | `W` | Capture selection into /crabidy (audio) |
|
|
| Library | `/` | Filter this view |
|
|
| Queue | `j` / `k` | Select next / previous track |
|
|
| Queue | `g` / `G` | Select first / last track |
|
|
| Queue | `Ctrl-d` | Jump 15 tracks down |
|
|
| Queue | `Ctrl-u` | Jump 15 tracks up |
|
|
| Queue | `s` | Mark / unmark selection |
|
|
| Queue | `v` / `V` | Visual mode: movement toggles marks |
|
|
| Queue | `y` | Yank selection into the register |
|
|
| Queue | `o` | Select the playing track |
|
|
| Queue | `Enter` | Play selected track |
|
|
| Queue | `p` / `P` | Paste the register after / before |
|
|
| Queue | `d` | Remove selection (into the register) |
|
|
| Queue | `c` | Clear queue except current (to register) |
|
|
| Queue | `C` | Clear entire queue (to register) |
|
|
| Queue | `w` | Save queue under a name |
|
|
| Queue | `W` | Capture the queue into /crabidy (audio) |
|
|
| Queue | `/` | Filter this view |
|
|
| Help | `?` / `Esc` / `q` | Close help |
|