Colors no longer come from the terminal's palette, whose color 0 many color themes set to a grey that is not their background. A theme sets tview's default styles and the color names used in texts, all chosen from entries 16-255 of the 256-color table so terminals without 24-bit color show them exactly. "auto" picks light or dark from the background color the terminal reports. Settable in the Config view, applied at the next start (widgets copy their colors when created). Replaces using the terminal's own background and text colors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019reK4DUGYqDUjniWYJvwUW
149 lines
5.8 KiB
Go
149 lines
5.8 KiB
Go
package ui
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
// helpItem is one line of the key reference: the keys and what they do.
|
|
type helpItem struct{ keys, desc string }
|
|
|
|
// helpSection is a titled group of items, with an optional note above them.
|
|
type helpSection struct {
|
|
title string
|
|
note string
|
|
items []helpItem
|
|
}
|
|
|
|
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 (works everywhere, also while typing)"},
|
|
{"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, theme, notifications.", items: []helpItem{
|
|
{"↑↓", "move through all fields, one below the other, in the sections Radarr, Sonarr, SABnzbd and Interface; ↓ after the last field reaches Save and Revert"},
|
|
{"Tab", "jump between the fields and Save / Revert"},
|
|
{"Space", "toggle a checkbox; press a button"},
|
|
{"Test", "(button below the SABnzbd settings) checks the three services with the values typed in"},
|
|
{"Save, Revert", "(at the bottom) Save writes the file and applies everything at once (the theme at the next start); Revert discards edits"},
|
|
}},
|
|
{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"},
|
|
}},
|
|
}
|
|
|
|
const helpKeyWidth = 16 // columns for the keys, before the descriptions
|
|
|
|
// 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() {
|
|
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.SetBorderPadding(0, 0, 1, 0)
|
|
tv.SetText(text)
|
|
tv.SetInputCapture(func(ev *tcell.EventKey) *tcell.EventKey {
|
|
if ev.Key() == tcell.KeyEsc {
|
|
a.closeModal()
|
|
return nil
|
|
}
|
|
return ev
|
|
})
|
|
height := min(screenH-2, strings.Count(text, "\n")+2)
|
|
a.openModalHints(tv, width, height, "↑↓ PgUp PgDn scroll · Esc close")
|
|
}
|