Help screen: wrap descriptions under themselves, size to the terminal
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+124
-72
@@ -1,89 +1,140 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
const helpText = `[yellow]Global[-]
|
||||
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)
|
||||
q, Ctrl+C quit
|
||||
Esc close dialog / go back
|
||||
// helpItem is one line of the key reference: the keys and what they do.
|
||||
type helpItem struct{ keys, desc string }
|
||||
|
||||
[yellow]Movies and Series[-]
|
||||
/ focus the search box
|
||||
Enter, a add the selected title (dialog: ↑↓ move, ←→ change a
|
||||
dropdown, Space toggles, Enter/Space opens a list)
|
||||
i interactive release search (library titles)
|
||||
// helpSection is a titled group of items, with an optional note above them.
|
||||
type helpSection struct {
|
||||
title string
|
||||
note string
|
||||
items []helpItem
|
||||
}
|
||||
|
||||
[yellow]Library[-]
|
||||
Tab switch between the movies and series lists
|
||||
f cycle filter (all, missing, downloaded, monitored, unmonitored);
|
||||
every list has its own filters
|
||||
F filter by file properties: quality, codec (x265, x264, AV1),
|
||||
HDR / DV / SDR, audio (Atmos, TrueHD, DTS, DD+ ...), language.
|
||||
Changes apply live behind the dialog, also while you move through an
|
||||
open list; Esc undoes them. Options that would leave no
|
||||
results are not offered.
|
||||
Series are read on demand.
|
||||
/ text filter for the focused list only; each list keeps its
|
||||
own text (Enter or Esc returns to the list)
|
||||
t toggle monitored
|
||||
d remove from the library (files are kept unless you tick
|
||||
"Delete files")
|
||||
s automatic search (series: all missing, asks first)
|
||||
i, Enter movie: release search; series: seasons and episodes
|
||||
r reload
|
||||
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)"},
|
||||
{"q, Ctrl+C", "quit"},
|
||||
{"Esc", "close a dialog / go back"},
|
||||
}},
|
||||
{title: "Movies and Series", items: []helpItem{
|
||||
{"/", "focus the search box"},
|
||||
{"Enter, a", "add the selected title (dialog: ↑↓ move, ←→ change a dropdown, Space toggles, Enter/Space opens a list)"},
|
||||
{"i", "interactive release search (library titles)"},
|
||||
}},
|
||||
{title: "Library", items: []helpItem{
|
||||
{"Tab", "switch between the movies and series lists"},
|
||||
{"f", "cycle the status filter (all, missing, downloaded, monitored, unmonitored); every list has its own filters"},
|
||||
{"F", "filter by file properties: quality, codec (x265, x264, AV1), HDR / DV / SDR, audio (Atmos, TrueHD, DTS, DD+ ...), language. Changes apply live behind the dialog, also while you move through an open list; Esc undoes them. Options that would leave no results are not offered. Series files are read on demand."},
|
||||
{"/", "text filter for the focused list only; each list keeps its own text (Enter or Esc returns to the list)"},
|
||||
{"t", "toggle monitored"},
|
||||
{"d", "remove from the library (files are kept unless you tick \"Delete files\")"},
|
||||
{"s", "automatic search (series: all missing, asks first)"},
|
||||
{"i, Enter", "movie: release search; series: seasons and episodes"},
|
||||
{"r", "reload"},
|
||||
}},
|
||||
{title: "Calendar (F5)", items: []helpItem{
|
||||
{"[ ]", "move the two-week window back / forward by a week"},
|
||||
{"t", "back to today"},
|
||||
{"u", "include unmonitored items"},
|
||||
{"Enter, i", "release search for the selected movie / episode"},
|
||||
{"s", "automatic search"},
|
||||
{"r", "reload"},
|
||||
}},
|
||||
{title: "Seasons and episodes", items: []helpItem{
|
||||
{"Enter", "season: expand / collapse; episode: release search"},
|
||||
{"i, s, t", "release search, automatic search, toggle monitored (on a season they apply to the whole season)"},
|
||||
}},
|
||||
{title: "Release list", items: []helpItem{
|
||||
{"Enter, g", "grab the selected release"},
|
||||
{"s", "cycle the sort (best, size, age)"},
|
||||
{"f", "hide rejected releases"},
|
||||
{"F", "filter by quality, codec, HDR / DV, audio, language (read from the release title, quality and custom formats)"},
|
||||
}},
|
||||
{title: "Downloads", items: []helpItem{
|
||||
{"p", "pause / resume the SABnzbd queue"},
|
||||
{"d", "delete the selected job or entry"},
|
||||
{"b", "remove from the Radarr/Sonarr queue and blocklist"},
|
||||
{"t", "retry a failed download (history)"},
|
||||
{"r", "refresh now"},
|
||||
}},
|
||||
{title: "Config (F6)", note: "Edit the settings of the config file: service addresses and API keys, add-form defaults, refresh interval, posters, notifications.", items: []helpItem{
|
||||
{"↑↓, 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"},
|
||||
}},
|
||||
{title: "Log (F7)", note: "Popups fade away by themselves; everything they said is kept here.", items: []helpItem{
|
||||
{"↑↓, PgUp, PgDn", "scroll"},
|
||||
{"G", "follow new entries"},
|
||||
{"c", "clear the log"},
|
||||
}},
|
||||
}
|
||||
|
||||
[yellow]Calendar (F5)[-]
|
||||
[ ] move the two-week window back / forward by a week
|
||||
t back to today
|
||||
u include unmonitored items
|
||||
Enter, i release search for the selected movie / episode
|
||||
s automatic search r reload
|
||||
const helpKeyWidth = 16 // columns for the keys, before the descriptions
|
||||
|
||||
[yellow]Seasons and episodes[-]
|
||||
Enter season: expand/collapse; episode: release search
|
||||
i, s, t release search, automatic search, toggle monitored
|
||||
(on a season they apply to the whole season)
|
||||
|
||||
[yellow]Release list[-]
|
||||
Enter, g grab the selected release
|
||||
s cycle sort (best, size, age)
|
||||
f hide rejected releases
|
||||
F filter by quality, codec, HDR / DV, audio, language (read from
|
||||
the release title, quality and custom formats)
|
||||
|
||||
[yellow]Downloads[-]
|
||||
p pause / resume the SABnzbd queue
|
||||
d delete the selected job or entry
|
||||
b remove from arr queue + blocklist
|
||||
t retry a failed download (history)
|
||||
r refresh now
|
||||
|
||||
[yellow]Config (F6)[-]
|
||||
Edit the settings of the config file: service addresses and API keys, add-form
|
||||
defaults, refresh interval, posters, notifications.
|
||||
↑↓ move, Tab next section, Space toggles, Enter presses 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
|
||||
Letters such as q and ? are typed here; F9 opens this help
|
||||
|
||||
[yellow]Log (F7)[-]
|
||||
Popups fade away by themselves; everything they said is kept here.
|
||||
↑↓, PgUp/PgDn scroll G follow new entries c clear
|
||||
|
||||
[yellow]Connections[-]
|
||||
`
|
||||
// renderHelp lays the key reference out for a text area of the given width:
|
||||
// descriptions that do not fit wrap under themselves, not under the keys.
|
||||
func renderHelp(width int) string {
|
||||
var b strings.Builder
|
||||
descWidth := max(width-2-helpKeyWidth, 20)
|
||||
for i, sec := range helpSections {
|
||||
if i > 0 {
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
b.WriteString("[yellow]" + sec.title + "[-]\n")
|
||||
if sec.note != "" {
|
||||
for _, l := range flowWrap(tview.Escape(sec.note), width-2, width-2, 0) {
|
||||
b.WriteString(" " + l + "\n")
|
||||
}
|
||||
}
|
||||
for _, it := range sec.items {
|
||||
keys := tview.Escape(it.keys)
|
||||
pad := strings.Repeat(" ", max(helpKeyWidth-tview.TaggedStringWidth(keys), 1))
|
||||
lines := flowWrap(tview.Escape(it.desc), descWidth, descWidth, 0)
|
||||
for j, l := range lines {
|
||||
if j == 0 {
|
||||
b.WriteString(" " + keys + pad + l + "\n")
|
||||
} else {
|
||||
b.WriteString(strings.Repeat(" ", 2+helpKeyWidth) + l + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// showHelp opens the key reference together with the connection state.
|
||||
func (a *App) showHelp() {
|
||||
tv := tview.NewTextView().SetDynamicColors(true).SetScrollable(true)
|
||||
screenW, screenH := 100, 40
|
||||
if a.screen != nil {
|
||||
screenW, screenH = a.screen.Size()
|
||||
}
|
||||
width := min(100, screenW-4)
|
||||
textWidth := width - 3 // border and a column of air
|
||||
text := renderHelp(textWidth) + "\n[yellow]Connections[-]\n"
|
||||
for _, l := range strings.Split(strings.TrimRight(a.serviceSummary(), "\n"), "\n") {
|
||||
for j, w := range flowWrap(l, textWidth-2, textWidth-2, 0) {
|
||||
if j == 0 {
|
||||
text += " " + w + "\n"
|
||||
} else {
|
||||
text += " " + w + "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tv := tview.NewTextView().SetDynamicColors(true).SetScrollable(true).SetWrap(false)
|
||||
tv.SetBorder(true).SetTitle(" Help — Esc to close ")
|
||||
tv.SetText(helpText + a.serviceSummary())
|
||||
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() == '?')) {
|
||||
a.closeModal()
|
||||
@@ -91,5 +142,6 @@ func (a *App) showHelp() {
|
||||
}
|
||||
return ev
|
||||
})
|
||||
a.openModal(tv, 80, 40)
|
||||
height := min(screenH-2, strings.Count(text, "\n")+2)
|
||||
a.openModalHints(tv, width, height, "↑↓ PgUp PgDn scroll · Esc, q or ? close")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user