Krolyxon
Commit cf9c69a

rmptuicf9c69a

Document v0.2: README, changelog and contributor notes

Author
krolyxon
Date
2026-10-17 17:06 +0530
Parent
96dd350
Commit
cf9c69aaa741861afc051de83610e2d88f118357

+246 −178

4 files changed

  1. M .github/copilot-instructions.md +59 −23
  2. M CHANGELOG.md +21 −0
  3. M Cargo.toml +1 −1
  4. M README.md +165 −154
M .github/copilot-instructions.md View
@@ -1,32 +1,68 @@
1−# Copilot instructions for rmptui
1+# Contributor and agent instructions for rmptui
22
33 ## Build, test, and lint
44
55 - `cargo build` — compile the application.
6−- `cargo run` — run against MPD at `127.0.0.1:6600` by default.
7−- `cargo fmt --check` — check Rust formatting; use `cargo fmt` to format.
8−- `cargo test` — run all unit tests.
9−- `cargo test connection::tests::parses_quoted_mpd_values_and_escapes` — run one named test. Replace the filter with a test name or module path to target another test.
10−- `cargo clippy --all-targets -- -D warnings` — lint all targets and treat warnings as errors.
6+- `cargo run -- --config FILE` — run with a specific config (MPD at
7+ `127.0.0.1:6600` by default; `MPD_HOST`/`MPD_PORT` override it).
8+- `cargo fmt --check`, `cargo clippy --all-targets -- -D warnings`, `cargo test`.
9+- `cargo test mpd::protocol` — run the tests of one module; `tests/mpd_client.rs`
10+ exercises the client against a scripted fake MPD server.
1111
1212 ## Architecture
1313
14−- `src/main.rs` owns the Tokio runtime, reads `MPD_HOST`/`MPD_PORT`, initializes the terminal, and selects between terminal input, redraw ticks, and MPD change notifications. A separate `MpdClient` connection blocks on MPD `idle`, then sends a fresh `MpdSnapshot` to the UI; the app's command connection stays available for user actions.
15−- `src/config.rs` loads or creates the TOML configuration at `$CONFIG_HOME/rmptui/config.toml`, falling back to `$XDG_CONFIG_HOME` and then `~/.config`. Its generated TOML documents every option and keybinding. MPD host/port settings can be overridden with `MPD_HOST` and `MPD_PORT`.
16−- `src/connection.rs` implements MPD's line-oriented text protocol over Tokio TCP, including command quoting, response/error parsing, and conversion of MPD responses into app models. Keep protocol I/O asynchronous. The client processes a command's complete response before issuing another command on that connection.
17−- `src/app.rs` owns UI/application state and coordinates queue, browser, and playlist operations. `src/browser.rs` represents rows as `BrowserEntry::Directory` or `BrowserEntry::Song`, keeping song metadata with its row. Browser paths are MPD library-relative paths, not local filesystem paths. The Playlists view has separate list/song focus and supports stored-playlist track deletion and reordering through MPD's `playlistdelete` and `playlistmove` commands.
18−- `src/event_handler/handler.rs` maps Crossterm events to application operations. `src/ui.rs` renders the current app state with Ratatui; it should not perform MPD I/O. `src/tui.rs` owns terminal mode setup, drawing, and restoration.
19−- `src/list.rs` contains shared list cursor behavior. `src/utils.rs` provides the built-in audio-extension check used by the browser and directory operations.
14+- `mpd/` is a self-contained MPD client.
15+ - `protocol.rs`: pure command encoding (every argument quoted and escaped)
16+ and response parsing.
17+ - `connection.rs`: async TCP and Unix-socket connections.
18+ - `types.rs`: typed models.
19+ - `commands.rs`: command constructors.
20+ - `worker.rs`: two background tasks. The *idle* connection reports changed
21+ subsystems. The *command* connection runs `Request`s from an `MpdHandle`
22+ and answers `Query`s with `Reply`s. Both reconnect on their own.
23+- `app.rs` owns the state and runs one `tokio::select!` loop over terminal
24+ input, `AppEvent`s (MPD, lyrics, tag writes) and visualizer frames. UI code
25+ never awaits MPD: it sends requests and reacts to the resulting events.
26+- `state.rs` (`MpdState`) mirrors the server: status, current song, the queue
27+ (updated incrementally via `plchanges`), stored playlists, and a database
28+ version used to invalidate caches.
29+- `screens/` holds one component per screen behind the `Screen` trait (`draw`,
30+ `handle_action`, `on_mpd`, `on_enter`, `on_mouse`, `search`).
31+ - Screens receive a `Ctx` with read access to config and state, the
32+ `MpdHandle`, and an effect queue (`Effect`) for things only the app can do:
33+ notifications, popups, tag writes, lyric reloads.
34+ - Every screen sees every `MpdEvent`, so caches stay valid while hidden.
35+- `popups/` holds modal components (`Popup` trait); the topmost popup gets all
36+ keys. Destructive actions go through `ConfirmPopup`.
37+- `ui/` holds the frame chrome (header, tabs, progress, status line), the
38+ virtualized searchable `ListView`, and the `LineInput` editor.
39+- `config/`:
40+ - `default.toml` is embedded and is the single source of truth for
41+ defaults; user files are merged over it table by table.
42+ - `keys.rs` parses key specs and builds the per-context `Keymap`.
43+- `action.rs` defines the `Action`, `Context` and `ScreenId` enums. Each action
44+ declares the contexts it may be bound in. Lookup order is: popup or screen
45+ context → `navigation` → `global`.
46+- `lyrics/`: LRC parser and the async lookup service (local `.lrc` → cache →
47+ LRCLIB). `visualizer/`: fifo reader thread with FFT, plus bar smoothing.
48+ `tags.rs`: lofty-based tag writing.
2049
21−## Repository conventions
50+## Conventions
2251
23−- Keep the MPD protocol models and wire-format parsing in `connection.rs`; UI and application code should use those models rather than parse raw MPD responses.
24−- Use the crate-level `AppResult<T>` for errors crossing module boundaries. Avoid duplicating state that can be derived from the MPD status or other canonical model data.
25−- Keep configuration defaults and validation in `config.rs`, and read tunable behavior from `App.config` rather than adding new hard-coded runtime values. Route keyboard actions through `App.config.keybindings`; keep README controls and the generated defaults in sync. Text typed into search/name fields is content, not a keybinding, except for configured input-editing actions.
26−- When adding or changing config options, update all three: the runtime default/config validation, descriptive comments in the generated first-run TOML, and the README example/explanation. Existing partial configs should continue receiving defaults for newly added keybindings.
27−- Pass MPD library paths as protocol arguments. The MPD command writer quotes and escapes each argument, so do not construct raw command strings from user-provided names or paths.
28−- Preserve async boundaries through event handling and app operations; do not introduce blocking socket reads, polling threads, or synchronous terminal polling.
29−- Browser `/` search retains all positive-scoring fuzzy matches in result order. `Enter` returns to normal navigation while retaining browser results; `n` and `N` cycle them. Starting a new search or changing directories resets those results.
30−- In the Playlists view, `Left`/`h` and `Right`/`l` switch focus between playlist names and song contents. `Space`/`Enter` load a selected playlist from the playlist list; when song contents are focused, `Enter` plays that song, `d` deletes it from the stored playlist, and `J`/`K` reorder it. `D` deletes the selected stored playlist and `R` renames it. Keep these actions context-aware and all defaults documented in the README/config.
31−- When changing key behavior or features, keep the controls documented in `README.md` consistent with `event_handler/handler.rs`.
32−- `AppResult<T>` is `Send + Sync` because application errors cross Tokio task boundaries.
52+- Build MPD commands only through `mpd::commands` or `Command::arg`; never
53+ format raw command strings from user data. Filter expressions go through
54+ `protocol::filter_eq`/`filter_and`.
55+- Prefer position-based commands (`move`, `delete`) for actions users repeat
56+ quickly. They stay correct even when sent before the local queue refreshes.
57+- Every new action needs:
58+ - an `Action` variant with its contexts and description;
59+ - a default binding in `config/default.toml` (a test enforces this);
60+ - a README entry.
61+- Every new option needs:
62+ - a documented entry in `default.toml`;
63+ - a field in the matching config struct (`deny_unknown_fields` catches
64+ typos);
65+ - validation in `Config::validate` if it has limits;
66+ - README documentation.
67+- Screens must not block: do file and network I/O in tasks (see lyrics) or
68+ `spawn_blocking` (see tag writes) and report back through `AppEvent`.
M CHANGELOG.md View
@@ -1,3 +1,24 @@
1+## rmptui-v0.2.0
2+
3+A rewrite from scratch with a new architecture modelled on ncmpcpp and rmpc.
4+
5+- New screens: Media Library (artists → albums → songs), Tag Editor,
6+ spectrum Visualizer (MPD fifo output) and synced Lyrics (local `.lrc`
7+ files, cache and LRCLIB), plus a lyrics pane on the queue.
8+- Browser shows stored playlists at the bottom of the root folder;
9+ playlist editor, queue and browser keep every 0.1 feature.
10+- MPD I/O runs in background tasks; key presses never wait for the server,
11+ and the client reconnects automatically. Unix sockets are supported.
12+- Marks (`v`) for acting on several items, fuzzy search on every list,
13+ confirmation before clearing the queue or deleting playlists, a key help
14+ popup (`?`) and mouse support (select, double-click, wheel, tabs, seek).
15+- New configuration format with per-context key bindings, a theme, queue
16+ columns and lyrics/visualizer settings. The documented default file is the
17+ single source of truth; partial files keep all other defaults. 0.1 config
18+ files are detected and rejected with instructions.
19+- New `--config` and `--print-default-config` options; `RMPTUI_LOG` sets
20+ the log level.
21+
122 ## rmptui-v0.1.7
223
324 - Replace the outdated MPD crate with an asynchronous native MPD TCP protocol
M Cargo.toml View
@@ -5,7 +5,7 @@ description = "A fast, keyboard-driven MPD client for the terminal"
55 readme = "README.md"
66 version = "0.2.0"
77 edition = "2024"
8−rust-version = "1.85"
8+rust-version = "1.88"
99 license = "AGPL-3.0-only"
1010 repository = "https://github.com/krolyxon/rmptui"
1111 keywords = ["mpd", "music", "cli", "tui", "client"]
M README.md View
@@ -5,33 +5,42 @@
55
66 **A fast, keyboard-driven MPD client for your terminal.**
77
8−rmptui is a lightweight music player interface for [Music Player Daemon
9−(MPD)](https://www.musicpd.org/). Browse your library, manage the play queue,
10−work with playlists, and control playback without leaving the terminal.
8+rmptui is a terminal interface for [Music Player Daemon
9+(MPD)](https://www.musicpd.org/) in the spirit of ncmpcpp and rmpc. Browse your
10+library, manage the queue and playlists, edit tags, read synced lyrics and
11+watch a spectrum visualizer without leaving the terminal.
1112
1213 ![rmptui screenshot](https://raw.githubusercontent.com/krolyxon/rmptui/master/assets/ss.png)
1314
14−## Highlights
15−
16−- **Three focused views:** browse your music library, control the current queue,
17− and inspect saved playlists.
18−- **Metadata at a glance:** see artist, track, title, album, and duration in
19− the browser and queue.
20−- **Fuzzy, repeatable search:** search the current view with `/`; in the song
21− browser, keep the query and cycle matching folders and songs with `n` and
22− `N`.
23−- **Playlist tools:** load playlists, add selected tracks or folders to a
24− playlist, create playlists, and rename them.
25−- **Queue control:** play, remove, reorder, and append tracks.
26−- **Responsive MPD updates:** the client uses MPD's asynchronous `idle`
27− notifications instead of regularly polling the server.
28−- **Direct MPD connection:** speaks MPD's standard text protocol over
29− asynchronous TCP; no `mpd` crate or WebSocket proxy is required.
15+## Screens
16+
17+| Key | Screen | What it does |
18+| --- | --- | --- |
19+| `1` | **Queue** | The play queue with configurable columns. Play, remove, reorder, mark, shuffle, clear or save it as a playlist. `L` shows a synced lyrics pane next to it. |
20+| `2` | **Browse** | Your music folders, ncmpcpp style. Stored playlists are listed at the bottom of the root folder. |
21+| `3` | **Playlists** | Stored playlists and their songs: load, play from a song, remove or reorder songs, rename or delete playlists. |
22+| `4` | **Library** | Album artists → albums (with year) → songs. `t` switches between AlbumArtist and Artist. |
23+| `5` | **Tags** | Edit Title, Artist, Album Artist, Album, Track, Disc, Date, Genre and Comment of one or many songs. |
24+| `6` | **Visualizer** | A spectrum analyzer fed by an MPD fifo output. |
25+| `7` | **Lyrics** | Synced lyrics that follow playback, or plain lyrics you can scroll. |
26+
27+Other highlights:
28+
29+- **Instant controls.** All MPD traffic runs in background tasks over MPD's
30+ `idle` notifications, so seeking, volume changes and skipping never wait for
31+ the UI, and the UI never waits for the server.
32+- **Fuzzy search everywhere.** `/` searches the focused list; `n`/`N` cycle
33+ through the matches.
34+- **Marks.** `v` marks items; queue removal, add-to-playlist and tag edits
35+ then apply to every marked item.
36+- **Mouse support.** Click to select, double-click to play/open, scroll with
37+ the wheel, click a tab to switch screens and click the progress bar to seek.
38+- **TCP or Unix socket**, with password support and automatic reconnection.
3039
3140 ## Requirements
3241
3342 - A running MPD server with its music library configured.
34−- Rust and Cargo to build from source.
43+- Rust 1.88 or newer and Cargo to build from source.
3544
3645 ## Install
3746
@@ -42,9 +51,8 @@ copy it to `/usr/local/bin/rmptui`:
4251 ./install.sh
4352 ```
4453
45−The script uses `sudo` for the system-directory operations when needed. It
46−requires Rust and Cargo to build from source. To use a different binary
47−directory or uninstall rmptui:
54+The script uses `sudo` for the system-directory operations when needed. To use
55+a different binary directory or uninstall rmptui:
4856
4957 ```sh
5058 ./install.sh --bin-dir "$HOME/.local/bin"
@@ -63,170 +71,173 @@ cd rmptui
6371 cargo run --release
6472 ```
6573
66−To build and run without installing, use `cargo run --release`.
74+## Configuration
6775
68−On first launch, rmptui creates a default configuration file at
76+On first launch rmptui writes a fully commented configuration to
6977 `$CONFIG_HOME/rmptui/config.toml`. If `CONFIG_HOME` is not set, it uses
7078 `$XDG_CONFIG_HOME/rmptui/config.toml`, then `~/.config/rmptui/config.toml`.
71−Edit the file and restart rmptui to apply changes:
79+Use `rmptui --config FILE` to load a different file, and
80+`rmptui --print-default-config` to see every option with its default. Restart
81+rmptui to apply changes.
82+
83+You only need to keep the settings you change: anything left out keeps its
84+default, table by table. Unknown options and conflicting key bindings are
85+reported with the file and the offending value.
86+
87+> **Upgrading from 0.1:** the configuration format changed. rmptui refuses to
88+> start with a 0.1 file and tells you so; move the old file aside, start
89+> rmptui to generate the new one, and copy your MPD settings over.
90+
91+The main sections:
7292
7393 ```toml
7494 [mpd]
75−host = "127.0.0.1"
95+host = "127.0.0.1" # or a Unix socket path such as "~/.config/mpd/socket"
7696 port = 6600
77−password = "" # Set this only if your MPD server requires a password.
97+password = ""
98+music_directory = "" # needed for the tag editor and local .lrc files
7899
79100 [playback]
80−seek_seconds = 2
81−volume_step = 2
82−
83−[keybindings]
84−quit = ["q", "Ctrl+c"]
85−clear_queue = ["c", "C"]
86−toggle_pause = ["p"]
87−pause = ["s"]
88−toggle_repeat = ["r"]
89−toggle_random = ["z"]
90−open_playlist_menu = ["a"]
91−seek_forward = ["f"]
92−seek_backward = ["b"]
93−cycle_tabs = ["Tab"]
94−select_queue = ["1"]
95−select_browser = ["2"]
96−select_playlists = ["3"]
97−next_track = [">"]
98−previous_track = ["<"]
99−volume_up = ["=", "+"]
100−volume_down = ["-"]
101−toggle_mute = ["m"]
102−update_database = ["U"]
103−search = ["/"]
104−toggle_selected_item = ["Space"]
105−search_next = ["n"]
106−search_previous = ["N"]
107−move_down = ["j", "Down"]
108−move_up = ["k", "Up"]
109−activate = ["Enter", "l", "Right"]
110−go_back = ["h", "Left"]
111−go_to_top = ["g"]
112−go_to_bottom = ["G"]
113−queue_delete = ["d"]
114−queue_move_down = ["J"]
115−queue_move_up = ["K"]
116−playlist_rename = ["R"]
117−playlist_focus_contents = ["Right", "l"]
118−playlist_focus_list = ["Left", "h"]
119−playlist_delete_song = ["d"]
120−playlist_move_song_down = ["J"]
121−playlist_move_song_up = ["K"]
122−playlist_delete = ["D"]
123−playlist_load = ["Space", "Enter"]
124−popup_close = ["q", "Esc"]
125−popup_select = ["Enter"]
126−input_cancel = ["Esc"]
127−input_backspace = ["Backspace"]
128−input_cursor_left = ["Left"]
129−input_cursor_right = ["Right"]
130−input_submit = ["Enter"]
101+seek_seconds = 5
102+volume_step = 5
103+
104+[ui]
105+startup_screen = "queue"
106+mouse = true
107+lyrics_pane = false
108+queue_columns = [
109+ { field = "artist", width = 25 },
110+ { field = "track", width = 5 },
111+ { field = "title", width = 35 },
112+ { field = "album", width = 30 },
113+ { field = "duration", width = 7 },
114+]
115+
116+[theme]
117+accent = "magenta" # color names, 256-color indices or "#rrggbb"
118+visualizer = ["blue", "cyan", "green", "yellow", "red"]
119+
120+[lyrics]
121+online = true # look up lyrics on lrclib.net
122+cache = true
123+
124+[visualizer]
125+fifo = "/tmp/mpd.fifo"
131126 ```
132127
133−Runtime errors and panic details are appended to `/tmp/rmptui.log`. If logging
134−cannot be initialized, rmptui prints a warning and continues without a log
135−file.
128+The MPD connection can also be set with environment variables, which take
129+precedence over the file:
136130
137−Every listed action is configurable in `[keybindings]`; each value is an array
138−so actions can have multiple aliases. Remove an alias or set an action to `[]`
139−to disable it. A key can be a single character or a named key such as `Enter`,
140−`Esc`, `Tab`, `Space`, `Up`, `Down`, `Left`, `Right`, or `Backspace`. Add
141−modifiers with `Ctrl+`, `Alt+`, or `Shift+`, for example `Ctrl+x` or
142−`Alt+Enter`. Bindings are matched in the current UI context, so avoid assigning
143−the same key to different actions that can be active in the same context.
144−Characters typed into search and playlist-name fields remain text unless
145−configured as an input action.
131+| Variable | Description |
132+| --- | --- |
133+| `MPD_HOST` | Hostname, address or Unix socket path. Use `password@host` to set both host and password. |
134+| `MPD_PORT` | Port; an explicit port in `MPD_HOST` takes precedence. |
146135
147−`playback.seek_seconds` sets the `seek_forward`/`seek_backward` distance, and
148−`playback.volume_step` sets the `volume_up`/`volume_down` amount. Supported
149−audio formats remain built in. The config stores an MPD password as plain text;
150−newly created config files are restricted to the current user.
136+```sh
137+MPD_HOST=192.168.1.20 MPD_PORT=6600 rmptui
138+MPD_HOST='secret@localhost' rmptui
139+MPD_HOST=~/.config/mpd/socket rmptui
140+```
151141
152−The MPD connection settings can also be overridden with environment variables:
142+Logs (and panic details) are appended to `/tmp/rmptui.log`. Set
143+`RMPTUI_LOG=debug` for more detail.
153144
154−| Variable | Default | Description |
155−| --- | --- | --- |
156−| `MPD_HOST` | Config file | MPD hostname or address. Use `password@host` to set both host and password. |
157−| `MPD_PORT` | Config file | MPD port; an explicit port in `MPD_HOST` takes precedence. |
145+### Visualizer
158146
159−For example:
147+The visualizer reads raw audio from an MPD fifo output. Add this to
148+`mpd.conf` and restart MPD:
160149
161−```sh
162−MPD_HOST=192.168.1.20 MPD_PORT=6600 ./target/release/rmptui
150+```
151+audio_output {
152+ type "fifo"
153+ name "Visualizer feed"
154+ path "/tmp/mpd.fifo"
155+ format "44100:16:2"
156+}
163157 ```
164158
165−Or, for a password-protected local server:
159+`[visualizer]` must match the `path` and `format`. The fifo is only read while
160+the Visualizer screen is visible.
166161
167−```sh
168−MPD_HOST='secret@localhost' ./target/release/rmptui
169−```
162+### Lyrics
170163
171−## Controls
164+Lyrics are looked up in this order:
172165
173−The keys below are the defaults; edit the `[keybindings]` table above to change
174−them.
166+1. An `.lrc` file next to the song (`Song.flac` → `Song.lrc`), when the music
167+ directory is known.
168+2. The cache in `$XDG_CACHE_HOME/rmptui/lyrics` (`~/.cache` by default).
169+3. [LRCLIB](https://lrclib.net), a free lyrics database. Results are cached.
175170
176−### Navigation
171+Set `lyrics.online = false` to stay offline. `F5` on the Lyrics screen fetches
172+the lyrics of the current song again.
177173
178−| Key | Action |
179−| --- | --- |
180−| `Tab` | Cycle between Queue, Browser, and Playlists |
181−| `1` / `2` / `3` | Open Queue / Browser / Playlists |
182−| `j` / `Down` | Move selection down |
183−| `k` / `Up` | Move selection up |
184−| `g` / `G` | Jump to the top / bottom of the current list |
185−| `Enter` / `l` / `Right` | Activate the selected item |
186−| `h` / `Left` | Go to the parent directory in the browser |
187−| `q` / `Ctrl+C` | Quit |
174+### Tag editor
175+
176+MPD cannot change tags, so the tag editor writes the files directly. That
177+requires the music files to be on this machine: set `mpd.music_directory`, or
178+connect through MPD's Unix socket so rmptui can ask MPD for it. After saving,
179+rmptui asks MPD to rescan the edited folders.
188180
189−### Search
181+## Key bindings
190182
191−1. Press `/` and type a query.
192−2. In the browser, press `Enter` to keep the query and return to the list.
193−3. Press `n` for the next fuzzy match or `N` for the previous match. Results
194− wrap around when you reach either end.
195−4. Press `/` to start a new search, or `Esc` to cancel search input.
183+These are the defaults; every binding is configurable in the `[keys.*]` tables
184+of the config. `?` shows the active bindings inside rmptui.
196185
197−Search matches the names shown in the current view. Browser search includes
198−both folders and songs.
186+Bindings are grouped by context. A key is looked up in the focused popup or
187+screen first, then in `navigation`, then in `global`, so screen bindings
188+shadow global ones on that screen (for example `R` renames a playlist on the
189+Playlists screen and toggles consume elsewhere).
199190
200−### Playback and queue
191+### Global
201192
202193 | Key | Action |
203194 | --- | --- |
195+| `1`–`7` | Switch screen |
196+| `Tab` / `Shift+Tab` | Next / previous screen |
204197 | `p` | Toggle play/pause |
205−| `s` | Pause |
206−| `>` / `<` | Play next / previous track |
207−| `f` / `b` | Seek forward / backward by two seconds |
208−| `+` / `=` / `-` | Increase / decrease volume |
198+| `s` / `x` | Pause / stop |
199+| `>` / `<` | Next / previous track |
200+| `f` / `b` | Seek forward / backward |
201+| `+` `=` / `-` | Volume up / down |
209202 | `m` | Toggle mute |
210−| `r` / `z` | Toggle repeat / random |
211−| `d` | Remove the selected queue item |
212−| `Space` | Remove a queue item; toggle a browser song in the queue |
213−| `J` / `K` | Move the selected queue item down / up |
214−| `c` | Clear the queue |
215−| `a` | Open the add-to-playlist menu |
216−| `U` | Rescan the MPD music database |
217−
218−### Playlists
219−
220−The Playlists view has a playlist pane and a contents pane. Select a playlist
221−with `j`/`k` and press `Space` or `Enter` to load it into the queue and start
222−playback. Press `Right` or `l` to focus its songs and `Left` or `h` to return
223−to the playlist pane. In the contents pane, `j`/`k` selects a song, `Enter`
224−plays it, `d` removes it from the saved playlist, and `J`/`K` moves it
225−down/up. `D` deletes the selected saved playlist. `R` renames a playlist.
226−
227−Press `a` to open the add-to-playlist menu; choose **Current Playlist**, **New
228−Playlist**, or a saved playlist. When creating a playlist, type its name and
229−press `Enter` to save. Playlist contents update after each edit.
203+| `r` `z` `y` `R` | Toggle repeat, random, single, consume |
204+| `U` | Rescan the music database |
205+| `?` `F1` | Show key bindings |
206+| `q` `Ctrl+c` | Quit |
207+
208+### Lists
209+
210+| Key | Action |
211+| --- | --- |
212+| `j` `k` / arrows | Move down / up |
213+| `g` `G` / `Home` `End` | Top / bottom |
214+| `Ctrl+d` `Ctrl+u`, `PageDown` `PageUp` | Half page / page |
215+| `h` `l` / arrows | Back or previous column / open or next column |
216+| `Enter` | Play or open the selected item |
217+| `Space` | Add to the queue (toggles songs in the browser) |
218+| `a` | Add to the queue, a new playlist or an existing one |
219+| `v` / `V` | Mark item / clear marks |
220+| `/`, `n` / `N` | Search, next / previous match |
221+
222+### Screens
223+
224+| Screen | Key | Action |
225+| --- | --- | --- |
226+| Queue | `d` `Delete` | Remove the song (or all marked songs) |
227+| Queue | `J` / `K` | Move the song down / up |
228+| Queue | `c` | Clear the queue (asks first) |
229+| Queue | `Z` / `S` | Shuffle / save as playlist |
230+| Queue | `o` | Jump to the playing song |
231+| Queue | `L` | Toggle the lyrics pane |
232+| Playlists | `d`, `J` / `K` | Remove / move the selected song |
233+| Playlists | `R` / `D` | Rename / delete the playlist |
234+| Library | `t` | Switch between AlbumArtist and Artist |
235+| Tags | `Enter` | Edit the selected tag |
236+| Tags | `w` / `u` | Write / discard pending changes |
237+| Lyrics | `F5` | Fetch lyrics again |
238+
239+In text fields, `Enter` submits, `Esc` cancels, `Ctrl+w` deletes a word and
240+`Ctrl+a` / `Ctrl+e` jump to the start / end.
230241
231242 ## License
232243