Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

### Added

- **Detach a screen into a pane**: open the lyrics (`B`), the visualizer (`v`), cover art (`G`), the queue (`Q`) or the mini player (`T`) and press `Alt+d`. The view leaves full screen and stays on screen in a pane beside the library, so you can see the lyrics and the visualizer at the same time in one terminal. `Alt+d` in a focused pane puts it back to full screen. `Alt+h/j/k/l` (or `Alt` with an arrow key) moves the focus between panes, `Alt+H/J/K/L` (or `Alt+Shift` with an arrow key) swaps the focused pane with its neighbor, `Alt+r` turns its split, `Alt+=` and `Alt+-` resize it, `Alt+x` closes it, `Alt+0` closes every pane and `Alt+x` on the main layout hides it, with the search box, help, settings and the playbar still on screen. With mouse input on, a click focuses a pane, `[x]` closes it and a drag on the line between two panes resizes them. The layout is saved in `state.yml` and comes back at the next launch. Every key can be rebound in `config.yml`.
- **Search the add-to-playlist picker**: press `/` (the `search` key) in the picker that `w` and `W` open and type part of a playlist name. The list narrows to the matching playlists you can add to, including the ones inside folders. `Enter` keeps the search and a second `Enter` adds the track, `Esc` clears the search, and a second `Esc` closes the picker ([#924](https://github.com/LargeModGames/spotatui/issues/924)).

### Fixed

- **The visualizer opens the one you picked last**: while a Spotify track played, the visualizer screen and its pane always opened on the Spotify pitch view, also after `V` had picked another style. They now open the visualizer you picked last, and the choice stays after a restart.
- **Smoother scrolling in terminal Settings**: Fixed an issue where navigating the Settings list caused the screen to jump erratically. The view now stays stable, scrolling only when your cursor reaches the top or bottom of the box.
- **The AI DJ prompt's caret follows the cursor**: after Left, Home or a Backspace in the middle of the text, the caret stayed painted at the end of the prompt. It now sits where the next character goes ([#945](https://github.com/LargeModGames/spotatui/issues/945)).
- **The log's startup line lists every compiled feature**: `features:` left out `soloist`, `gui`, `apple-music`, `spotify-session`, the playback backends and the shared decode features, so a release build with Soloist looked like it had none. A test now fails when a feature in `Cargo.toml` is missing from the list ([#949](https://github.com/LargeModGames/spotatui/issues/949)).
Expand Down
35 changes: 35 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,41 @@ behavior:
- Unknown position strings fall back to the default with a warning.
- Mouse hit-testing follows every arrangement automatically.

### Detached panes

The lyrics, the visualizer, cover art, the queue and the mini player can leave full screen and stay beside the main layout as panes. Open one of them and press `Alt+d`. The new pane goes beside the focused pane: side by side when that pane is wide, stacked when it is tall.

| Key | Does |
|-----|------|
| `Alt+d` | Detach the open full-screen view into a pane, or put the focused pane back to full screen |
| `Alt+x` | Close the focused pane, or hide the main layout when it has focus |
| `Alt+h` / `Alt+j` / `Alt+k` / `Alt+l`, or `Alt` with an arrow key | Focus the pane on the left, below, above, on the right |
| `Alt+H` / `Alt+J` / `Alt+K` / `Alt+L`, or `Alt+Shift` with an arrow key | Swap the focused pane with that neighbor |
| `Alt+r` | Turn the split of the focused pane (side by side or stacked) |
| `Alt+=` / `Alt+-` | Make the focused pane larger or smaller |
| `Alt+0` | Close every pane and show the main layout again |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

- A focused pane gets the keys of its view (for example `V` cycles the visualizer style). `Esc` or the back key gives the focus back to the main layout. The open key of a detached view (`B` for the lyrics) focuses its pane.
- With mouse input on, a click focuses a pane, `[x]` on its border closes it, and a drag on the line between two panes resizes them.
- The keys are `detach_pane`, `close_pane`, `focus_pane_left`, `focus_pane_down`, `focus_pane_up`, `focus_pane_right`, `move_pane_left`, `move_pane_down`, `move_pane_up`, `move_pane_right`, `rotate_pane`, `grow_pane` and `shrink_pane` under `keybindings` in `config.yml`.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
- The layout is saved as `pane_layout` in `state.yml`, a tree you can also edit by hand:

```yaml
pane_layout:
axis: horizontal # horizontal = side by side, vertical = stacked
percent: 60 # the share of `first`, 10..=90
first: main
second:
axis: vertical
percent: 50
first: lyrics
second: visualizer
```

The leaves are `main`, `lyrics`, `visualizer`, `cover_art`, `queue` and `mini_player`. A leaf can appear only once; otherwise the layout loads as `main` alone. A layout without `main` is a hidden main layout.
- `Alt+d` refuses a new pane when it, or the main layout, would be too small for the terminal. If the window becomes smaller than a saved layout needs, the panes hide and the main layout takes the whole window. They come back when the window is large enough. While they hide, `Alt+x` closes one hidden pane at a time.
- `Alt+x` with the focus on the main layout hides it, when at least one pane is open. The search box, the help button and the settings button stay in a row above the panes, and the playbar stays in its usual place. The main layout comes back when a key opens a new screen in it, for example a search or a jump to an album, or when you press `Alt+0`.

## Other behavior keys

These keys also go under `behavior:`. Defaults below are the built-in values when a key is omitted; most can also be changed in Settings.
Expand Down
31 changes: 27 additions & 4 deletions docs/keybindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,29 @@ key and its default from `UserConfig::new`. The five AI DJ keys live in the
| Like / save track | `F` | `like_track` |
| Generate recap | `R` | `generate_recap` |

### Pane keys

These keys arrange the views you detach beside the main layout. `Alt` with an
arrow key also moves the focus, and `Alt-Shift` with an arrow key swaps panes. See
[Detached panes](configuration.md#detached-panes).

| Action | Default | Config key |
|---|---|---|
| Detach the open full-screen view into a pane, or put the focused pane back to full screen | `Alt-d` | `detach_pane` |
| Close the focused pane, or hide the main layout when it has focus | `Alt-x` | `close_pane` |
| Focus the pane on the left | `Alt-h` | `focus_pane_left` |
| Focus the pane below | `Alt-j` | `focus_pane_down` |
| Focus the pane above | `Alt-k` | `focus_pane_up` |
| Focus the pane on the right | `Alt-l` | `focus_pane_right` |
| Swap the focused pane with the pane on the left | `Alt-H` | `move_pane_left` |
| Swap the focused pane with the pane below | `Alt-J` | `move_pane_down` |
| Swap the focused pane with the pane above | `Alt-K` | `move_pane_up` |
| Swap the focused pane with the pane on the right | `Alt-L` | `move_pane_right` |
| Turn the split of the focused pane | `Alt-r` | `rotate_pane` |
| Make the focused pane larger | `Alt-=` | `grow_pane` |
| Make the focused pane smaller | `Alt--` | `shrink_pane` |
| Close every pane and show the main layout again | `Alt-0` | `reset_panes` |

A few keys in the help menu are fixed and have no config name: the sort menu
(`,`), the visualizer style picker (`V`), list jumps `H` / `M` / `L`, the
layout keys (`{` / `}`, `(` / `)`, `|`), `s` (save the selected track), and
Expand All @@ -87,15 +110,15 @@ keybindings:
# ... etc
```

Every row in the table above is a `keybindings:` entry: the config key names
the action and the value is its new default. Use the table as the full list of
rebindable names — 40 actions plus the five AI DJ keys below.
Every row in the tables above is a `keybindings:` entry: the config key names
the action and the value is its new default. Use the tables as the full list of
rebindable names — 40 actions, the 14 pane keys and the five AI DJ keys below.

### Key Format

- Single keys: `"a"`, `"/"`, `" "` (or `"space"`)
- With Ctrl: `"ctrl-q"`, `"ctrl-s"`, `"ctrl--"` (Ctrl and the `-` key)
- With Alt: `"alt-,"`, `"alt-s"`, `"alt--"`
- With Alt: `"alt-,"`, `"alt-s"`, `"alt--"`, `"alt-H"` for Alt with Shift, and `"alt-left"` or `"alt-shift-left"` for an arrow (left, right, up, down)
- With Shift: use the capital letter, `"A"`, `"C"` (Shift is not a modifier of its own)
- Special keys: `"enter"`, `"tab"`, `"esc"` (or `"escape"`), `"backspace"`,
`"del"`, `"left"` / `"right"` / `"up"` / `"down"`, `"pageup"` / `"pagedown"`,
Expand Down
2 changes: 1 addition & 1 deletion gui/src/bindings/AppStatusStrings.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type AppStatusStrings = { music_error: string, audio_backend_failed: string, audio_backend_no_alsa_default: string, audio_device_paused: string, audio_device_moved: string, audio_device_waiting: string, radio_device_lost: string, radio_ended: string, current_source_cannot_like: string, loading_playlist: string, community_pin_hidden: string, native_disconnected: string, native_reconnecting: string, native_request_expired: string, resuming_request: string, track_cannot_add: string, youtube_no_playlists: string, playlist_destinations_loading: string, no_editable_playlists: string, playlist_tracks_only: string, playlist_needs_spotify: string, no_current_track: string, youtube_no_open_playlist: string, youtube_playlist: string, track_cannot_edit: string, remove_requires_playlist: string, remove_position_unknown: string, settings_shortcut_fallback: string, queued_no_album: string, jump_album_spotify: string, playing_no_artist: string, queued_no_artist: string, jump_artist_spotify: string, queue_no_context: string, jump_context_spotify: string, friend_code_loading: string, clipboard_unavailable: string, clipboard_copy_failed: string, no_playlist_link: string, highlight_playlist_mirror: string, no_mirror_source: string, radio_no_url: string, radio_position_missing: string, another_source_playing: string, track_no_id: string, album_no_id: string, music_wait_pause: string, music_volume_floor: string, music_did_not_start: string, music_quit: string, music_requires_macos: string, all_songs: string, music_track_unavailable: string, queue_no_uri: string, radio_cannot_queue: string, shuffle_on: string, shuffle_off: string, repeat_off: string, repeat_all: string, repeat_one: string, shuffle_unsupported: string, repeat_unsupported: string, recap_disabled: string, native_no_response: string, native_restore_failed: string, dj_previous_request: string, dj_auto_queue_wait: string, dj_auto_queue_on: string, dj_auto_queue_off: string, dj_fresh_only: string, dj_everything: string, dj_busy: string, dj_shifting: string, dj_keep_brain: string, nothing_playing: string, spotify_parked: string, spotify_not_connected: string, party_needs_spotify: string, music_cannot_queue: string, friend_code_copied: string, sync_hint: string, mirroring: string, radio_favorited: string, radio_already_favorited: string, radio_favorite_failed: string, radio_configured: string, radio_removed: string, radio_not_favorited: string, radio_remove_failed: string, state_save_failed: string, shortcut_save_failed: string, shortcut_saved: string, clipboard_set_failed: string, queued: string, recap_browser_failed: string, dj_shift_dropped: string, dj_brain: string, dj_brain_not_ready: string, dj_save_failed: string, playlist_searching: string, unfollow_profile_loading: string, no_playback_device_selected: string, no_playback_devices: string, playback_device_unavailable: string, playback_device_no_id: string, entry_not_playable: string, liked_loading: string, no_radio_selected: string, switching_device: string, source_selected: string, announcement_save_failed: string, link_open_failed: string, capture_unavailable: string, no_radio_playing: string, search_needs_spotify: string, copy_needs_spotify: string, shortcut_session_only: string, like_source_unavailable: string, search_source_unavailable: string, paused: string, paused_details: string, visualizer_config: string, };
export type AppStatusStrings = { music_error: string, audio_backend_failed: string, audio_backend_no_alsa_default: string, audio_device_paused: string, audio_device_moved: string, audio_device_waiting: string, radio_device_lost: string, radio_ended: string, current_source_cannot_like: string, loading_playlist: string, community_pin_hidden: string, native_disconnected: string, native_reconnecting: string, native_request_expired: string, resuming_request: string, track_cannot_add: string, youtube_no_playlists: string, playlist_destinations_loading: string, no_editable_playlists: string, playlist_tracks_only: string, playlist_needs_spotify: string, no_current_track: string, youtube_no_open_playlist: string, youtube_playlist: string, track_cannot_edit: string, remove_requires_playlist: string, remove_position_unknown: string, settings_shortcut_fallback: string, queued_no_album: string, jump_album_spotify: string, playing_no_artist: string, queued_no_artist: string, jump_artist_spotify: string, queue_no_context: string, jump_context_spotify: string, friend_code_loading: string, clipboard_unavailable: string, clipboard_copy_failed: string, no_playlist_link: string, highlight_playlist_mirror: string, no_mirror_source: string, radio_no_url: string, radio_position_missing: string, another_source_playing: string, track_no_id: string, album_no_id: string, music_wait_pause: string, music_volume_floor: string, music_did_not_start: string, music_quit: string, music_requires_macos: string, all_songs: string, music_track_unavailable: string, queue_no_uri: string, radio_cannot_queue: string, shuffle_on: string, shuffle_off: string, repeat_off: string, repeat_all: string, repeat_one: string, shuffle_unsupported: string, repeat_unsupported: string, recap_disabled: string, native_no_response: string, native_restore_failed: string, dj_previous_request: string, dj_auto_queue_wait: string, dj_auto_queue_on: string, dj_auto_queue_off: string, dj_fresh_only: string, dj_everything: string, dj_busy: string, dj_shifting: string, dj_keep_brain: string, nothing_playing: string, spotify_parked: string, spotify_not_connected: string, party_needs_spotify: string, music_cannot_queue: string, friend_code_copied: string, sync_hint: string, mirroring: string, radio_favorited: string, radio_already_favorited: string, radio_favorite_failed: string, radio_configured: string, radio_removed: string, radio_not_favorited: string, radio_remove_failed: string, state_save_failed: string, shortcut_save_failed: string, shortcut_saved: string, clipboard_set_failed: string, queued: string, recap_browser_failed: string, dj_shift_dropped: string, dj_brain: string, dj_brain_not_ready: string, dj_save_failed: string, playlist_searching: string, unfollow_profile_loading: string, no_playback_device_selected: string, no_playback_devices: string, playback_device_unavailable: string, playback_device_no_id: string, entry_not_playable: string, liked_loading: string, no_radio_selected: string, switching_device: string, source_selected: string, announcement_save_failed: string, link_open_failed: string, capture_unavailable: string, no_radio_playing: string, search_needs_spotify: string, copy_needs_spotify: string, shortcut_session_only: string, like_source_unavailable: string, search_source_unavailable: string, paused: string, paused_details: string, visualizer_config: string, pane_nothing_to_detach: string, pane_hidden: string, pane_no_room: string, };
Loading
Loading