Remove the '?' help key from the docs and help: F9 is the help key

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 23:42:07 +02:00
co-authored by Claude Sonnet 5.5
parent 79b440f6f7
commit ff9e59a310
5 changed files with 13 additions and 13 deletions
+1 -1
View File
@@ -36,6 +36,6 @@ First release.
retry, remove and blocklist.
- **Posters:** a thumbnail at the top right of the detail panes with the text flowing around it (quadrant or, on capable terminals, sextant block rendering; `ui.cover_style`).
- **Log (F7) and popups:** feedback appears as fading popups; the log keeps the history.
- Connection indicators for all three services, context-sensitive key hints, `?` help.
- Connection indicators for all three services, context-sensitive key hints, a help screen (F9).
- JSON config with environment overrides (`ARR_TUI_*`), `--version`, cross-platform builds
(`make dist`).
+2 -2
View File
@@ -36,7 +36,7 @@ A terminal UI to search for movies and series with [Radarr](https://radarr.video
whichever view is open. SABnzbd's history is checked every 15 seconds; downloads that finished
before arr-tui started are not announced. `"ui": {"notify_downloads": false}` turns it off,
`"bell": true` also rings the terminal bell.
- Connection indicators for all three services in the top right; `?` shows every key.
- Connection indicators for all three services in the top right; `F9` shows every key.
Requires Radarr/Sonarr v3+ API (Radarr 5/6, Sonarr 4) and SABnzbd 4+. Only Usenet releases are
shown for grabbing, torrents are hidden.
@@ -74,7 +74,7 @@ Keep the file private (`chmod 600`); it holds your API keys.
|---|---|
| `F1`–`F7` | switch view (Movies, Series, Library, Downloads, Calendar, Config, Log) |
| `Tab`, `Shift+Tab` | switch panel within a view |
| `?` / `F9` | help |
| `F9` | help |
| `/` | focus search box |
| `Enter`, `a` | add selected title |
| `i` | interactive release search |
+4 -4
View File
@@ -62,14 +62,14 @@ Notes:
- SABnzbd queue (name, size, progress, ETA, speed) and history (completed / failed, with retry).
- Radarr and Sonarr queues (status, warnings, import errors), so stuck imports are visible.
- Global speed / pause state in the status bar. Pause/resume and delete a SABnzbd job.
- **Status bar / help:** the bottom bar lists the keys that work in the current view or panel; the top right shows the connection state of each service; `?` opens the full key reference.
- **Status bar / help:** the bottom bar lists the keys that work in the current view or panel; the top right shows the connection state of each service; `F9` opens the full key reference.
- **Posters:** the detail panes show the poster at the top right; the head (title, facts, path) and the plot are one text that flows along the poster's left side and continues at full width below it (text is wrapped by arr-tui itself, with a narrower width for the first lines); on panes too small for the poster, the text simply uses the full width. Two ways to draw the poster, chosen by `ui.cover_style`:
- **Real images** (`kitty`, and `auto` in kitty/Ghostty): the Kitty graphics protocol with Unicode placeholders. The poster (kept up to 640 px tall) is uploaded once as PNG (`a=t,f=100,q=2`, 4096-byte base64 chunks), placed virtually with the cell footprint (`a=p,U=1,c=,r=`; a new size deletes and recreates the placement), and shown by writing U+10EEEE placeholder characters whose combining marks give row and column (the 297-entry table from the protocol) and whose foreground color is the image id. Since it is ordinary text, popups, dialogs and view changes cover it correctly and tcell's diffing keeps it in place. The footprint uses the real cell size in pixels (from the terminal's window size) and a box whose shape is within 3% of the poster's; the remaining sliver is cropped from the poster's edges before upload (one upload per size, the previous one is deleted), because the terminal letterboxes an image that does not fit its box exactly. As a second safeguard the placeholder cells use the panes' background color, so anything the picture does not cover blends in (without it the terminal's own, slightly different, default background showed as grey bars beside the poster). Writes to the terminal go through the screen's tty on the UI goroutine, so they cannot interleave with drawing. Requires 24-bit color; otherwise, and inside tmux/screen, the block fallback is used. Terminals are recognised from `TERM`/`TERM_PROGRAM`/`KITTY_WINDOW_ID`/`GHOSTTY_RESOURCES_DIR`. The go-termimg library was looked at and not used: its placeholder support is marked experimental, its diacritic table does not match the protocol's, and it writes images straight to the terminal instead of through the screen.
- **Block characters** (`quadrant`, `sextant`, and the fallback): every cell shows several pixels in the best two-color split (`quadrant`: 2×2 pixels, glyphs from the Block Elements range every font has; `sextant`: 2×3 pixels, sharper, glyphs from Symbols for Legacy Computing). `auto` picks sextants on kitty, WezTerm, foot and Ghostty, and on other terminals when fontconfig lists a font with the sextant characters (`fc-list :charset=1fb00`, about 20 ms at startup; terminals draw glyphs their font lacks from fallback fonts; skipped over SSH because the installed fonts are the remote machine's), and quadrants otherwise.
Posters are JPEG/PNG, decoded and downscaled in the process. Library items fetch `/MediaCover/<id>/poster-250.jpg` from their Radarr/Sonarr instance with the API key; search results fall back to the provider's `remoteUrl` (fetched without credentials). Posters are cached in memory (the terminal's copies are deleted when the cache is dropped and on exit), failed downloads are not retried for two minutes, and the poster shrinks or disappears when the pane is too short. `ui.covers: false` disables it.
- **Calendar view (F5):** a two-week window (starting today, moved a week at a time with `[`/`]`, `t` returns to today) with the library's movie releases and airing episodes, grouped by day. Radarr `GET /calendar` supplies the cinema, digital and physical release dates (taken as written: midnight UTC means that calendar day) and Sonarr `GET /calendar?includeSeries=true` the episodes with their air time (shown in local time). Unmonitored items are included with `u`. A movie appears once per release date in the window. Each entry shows type, title, episode or release kind and status (downloaded / missing / upcoming); the detail pane shows poster and plot; `Enter`/`i` starts the release search and `s` an automatic search.
- **Download notifications:** a background poller (independent of the visible view) reads SABnzbd's history every 15 s and announces downloads that reach `Completed` or `Failed` as a popup and log entry; the first poll only records the current state. `ui.notify_downloads` (default true) and `ui.bell` (default false, rings the terminal bell) control it. Connection errors are not reported here, the indicators and the log already do that.
- **Config view (F6):** a form for every setting of the config file, in four sections (Radarr, Sonarr, SABnzbd, Interface) plus Test connections / Save / Revert. The fields start from the settings in use; API keys are masked (a checkbox shows them). `Test connections` builds temporary clients from the typed values and reports each service. `Save` validates like startup does (URLs, non-empty keys, refresh 1–3600 s), then writes the file from the file's own contents plus the edits (`config.LoadFile`), so values that only come from `ARR_TUI_*` environment variables are left as the file has them and are shown greyed out; the write is atomic (temp file + rename) and keeps the file's permissions (`0600` when new); defaults such as `covers: true` stay out of the file unless it already named them. Everything then applies without a restart: the Radarr/Sonarr/SABnzbd clients are re-pointed (`Reconfigure`, guarded by a mutex), the connection indicators, add-form defaults and options are refreshed, the library and calendar reload, the Downloads interval, notifications, bell and poster style are switched live. `q` and `?` are typed text in this view (`F9` opens the help).
- **Config view (F6):** a form for every setting of the config file, in four sections (Radarr, Sonarr, SABnzbd, Interface) plus Test connections / Save / Revert. The fields start from the settings in use; API keys are masked (a checkbox shows them). `Test connections` builds temporary clients from the typed values and reports each service. `Save` validates like startup does (URLs, non-empty keys, refresh 1–3600 s), then writes the file from the file's own contents plus the edits (`config.LoadFile`), so values that only come from `ARR_TUI_*` environment variables are left as the file has them and are shown greyed out; the write is atomic (temp file + rename) and keeps the file's permissions (`0600` when new); defaults such as `covers: true` stay out of the file unless it already named them. Everything then applies without a restart: the Radarr/Sonarr/SABnzbd clients are re-pointed (`Reconfigure`, guarded by a mutex), the connection indicators, add-form defaults and options are refreshed, the library and calendar reload, the Downloads interval, notifications, bell and poster style are switched live. `q` is typed text in this view (`F9` opens the help).
- **Notifications and log (F7):** feedback about actions (added, deleted, grabbed, failures) appears as popups in the bottom right that fade away (errors stay longer). Everything is also kept in the Log view; only connection changes of the background polling are logged, not every failed poll.
### 4.2 v0.2 (done, M8)
@@ -111,7 +111,7 @@ Keybindings (the status bar shows the ones that apply to the current view):
|---|---|
| `F1-F7` | Switch view (Movies, Series, Library, Downloads, Calendar, Config, Log); also while typing, and from full-screen views. Dialogs block them. |
| `Tab` / `Shift+Tab` | Switch panel within the current view (never the view): search box / results, Downloads panels, Library lists |
| `F9` / `?` | Help. `F9` works everywhere; `?` only when no text field has the focus (it is text in search boxes and in the Config view) |
| `F9` | Help (works everywhere, also while typing) |
| `q` / `Ctrl+C` | Quit |
| `Esc` | Close a dialog or a full-screen view |
| `/` | Movies, Series, Library: focus the search / text filter |
@@ -203,7 +203,7 @@ The file should be created with mode `0600`; warn if it is more permissive. API
- [x] **M4** — SABnzbd client and Downloads view (queue + history) with live updates and pause/resume/delete.
- [x] **M5** — Radarr/Sonarr queues merged into the Downloads view.
- [x] **M6** — Interactive release search (movies, then series seasons/episodes) and grab.
- [x] **M7** — Polish: help screen (`?`/F9), per-service connection indicators, README, Makefile release builds, `--version`.
- [x] **M7** — Polish: help screen (F9), per-service connection indicators, README, Makefile release builds, `--version`.
- [x] **M8** — Library view (movies/series, filters, text filter, monitor toggle, automatic search) and series drill-down (seasons/episodes, monitoring, search, release search).
- [x] **M9** — Popup notifications, Log view (F7), context-sensitive key hints in the status bar.
- [x] **M10** — Download completion notifications, remove from library, calendar view (F5; the Log moves to F7, after the Config view on F6). Multiple instances per service are out of scope.
+2 -2
View File
@@ -25,7 +25,7 @@ type view struct {
onShow, onHide func()
// hints lists the keys that work in this view (shown in the status bar).
hints func() string
// captureKeys makes the global single-key shortcuts (q, ?) stand back
// captureKeys makes the global single-key shortcut (q) stands back
// while this view is shown, because its fields take typed text.
captureKeys bool
}
@@ -370,7 +370,7 @@ func (a *App) renderStatus() {
if v.hints != nil {
parts = append(parts, v.hints())
}
if v.captureKeys { // q and ? are text there
if v.captureKeys { // q is text there
parts = append(parts, "F9 help · F1-F7 views")
} else {
parts = append(parts, "F9 help · F1-F7 · q quit")
+4 -4
View File
@@ -21,7 +21,7 @@ var helpSections = []helpSection{
{title: "Global", items: []helpItem{
{"F1-F7", "switch view (also from the release and episode lists)"},
{"Tab, Shift+Tab", "switch panel within a view"},
{"F9, ?", "this help. F9 works everywhere; ? only when no text field has the focus (press Esc first in a search box)"},
{"F9", "this help (works everywhere, also while typing)"},
{"q, Ctrl+C", "quit"},
{"Esc", "close a dialog / go back"},
}},
@@ -70,7 +70,7 @@ var helpSections = []helpSection{
{"↑↓, Tab", "move between fields / to the next section"},
{"Space", "toggle a checkbox"},
{"Enter", "press a button: Test connections checks the three services with the values typed in; Save writes the file and applies everything at once; Revert discards edits"},
{"q, ?", "are typed here as text; F9 opens this help"},
{"q", "is typed here as text (it does not quit); F9 opens this help"},
}},
{title: "Log (F7)", note: "Popups fade away by themselves; everything they said is kept here.", items: []helpItem{
{"↑↓, PgUp, PgDn", "scroll"},
@@ -136,12 +136,12 @@ func (a *App) showHelp() {
tv.SetBorderPadding(0, 0, 1, 0)
tv.SetText(text)
tv.SetInputCapture(func(ev *tcell.EventKey) *tcell.EventKey {
if ev.Key() == tcell.KeyEsc || (ev.Key() == tcell.KeyRune && (ev.Rune() == 'q' || ev.Rune() == '?')) {
if ev.Key() == tcell.KeyEsc || (ev.Key() == tcell.KeyRune && ev.Rune() == 'q') {
a.closeModal()
return nil
}
return ev
})
height := min(screenH-2, strings.Count(text, "\n")+2)
a.openModalHints(tv, width, height, "↑↓ PgUp PgDn scroll · Esc, q or ? close")
a.openModalHints(tv, width, height, "↑↓ PgUp PgDn scroll · Esc or q close")
}