Add help screen, connection indicators, README and release builds (M7)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+72
-3
@@ -34,8 +34,21 @@ type App struct {
|
||||
|
||||
message string
|
||||
overlays []overlay // dialogs and full-screen views stacked above the pages
|
||||
|
||||
services *tview.TextView
|
||||
state map[string]serviceState // keyed by service name
|
||||
}
|
||||
|
||||
// serviceState is the last known connection state of a backend service.
|
||||
type serviceState struct {
|
||||
known bool
|
||||
version string
|
||||
err error
|
||||
}
|
||||
|
||||
// serviceNames fixes the order of the connection indicators.
|
||||
var serviceNames = []string{"Radarr", "Sonarr", "SABnzbd"}
|
||||
|
||||
// overlay is a page stacked above the main views; focus is restored on close.
|
||||
type overlay struct {
|
||||
page string
|
||||
@@ -49,6 +62,9 @@ func New(cfg *config.Config) *App {
|
||||
tabs: tview.NewTextView().SetDynamicColors(true),
|
||||
pages: tview.NewPages(),
|
||||
status: tview.NewTextView().SetDynamicColors(true),
|
||||
|
||||
services: tview.NewTextView().SetDynamicColors(true).SetTextAlign(tview.AlignRight),
|
||||
state: map[string]serviceState{},
|
||||
}
|
||||
|
||||
radarrClient := radarr.NewClient(cfg.Radarr.URL, cfg.Radarr.APIKey)
|
||||
@@ -70,7 +86,9 @@ func New(cfg *config.Config) *App {
|
||||
}
|
||||
|
||||
layout := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(a.tabs, 1, 0, false).
|
||||
AddItem(tview.NewFlex().
|
||||
AddItem(a.tabs, 0, 1, false).
|
||||
AddItem(a.services, 34, 0, false), 1, 0, false).
|
||||
AddItem(a.pages, 0, 1, true).
|
||||
AddItem(a.status, 1, 0, false)
|
||||
|
||||
@@ -78,9 +96,56 @@ func New(cfg *config.Config) *App {
|
||||
a.app.SetInputCapture(a.handleKey)
|
||||
a.switchTo(0)
|
||||
a.renderStatus()
|
||||
a.renderServices()
|
||||
return a
|
||||
}
|
||||
|
||||
// SetService records the outcome of a request to a service and updates the
|
||||
// connection indicators. UI goroutine only. A nil err means reachable.
|
||||
func (a *App) SetService(name, version string, err error) {
|
||||
prev := a.state[name]
|
||||
if err == nil && version == "" {
|
||||
version = prev.version
|
||||
}
|
||||
a.state[name] = serviceState{known: true, version: version, err: err}
|
||||
a.renderServices()
|
||||
}
|
||||
|
||||
func (a *App) renderServices() {
|
||||
var s string
|
||||
for _, n := range serviceNames {
|
||||
st := a.state[n]
|
||||
switch {
|
||||
case !st.known:
|
||||
s += "[gray]○ " + n + "[-] "
|
||||
case st.err != nil:
|
||||
s += "[red]● " + n + "[-] "
|
||||
default:
|
||||
s += "[green]●[-] " + n + " "
|
||||
}
|
||||
}
|
||||
a.services.SetText(s)
|
||||
}
|
||||
|
||||
// serviceSummary describes every service for the help screen.
|
||||
func (a *App) serviceSummary() string {
|
||||
var b strings.Builder
|
||||
for _, n := range serviceNames {
|
||||
st := a.state[n]
|
||||
switch {
|
||||
case !st.known:
|
||||
fmt.Fprintf(&b, "[gray]○ %s: not contacted yet[-]\n", n)
|
||||
case st.err != nil:
|
||||
fmt.Fprintf(&b, "[red]● %s: %s[-]\n", n, tview.Escape(st.err.Error()))
|
||||
case st.version != "":
|
||||
fmt.Fprintf(&b, "[green]●[-] %s %s: connected\n", n, tview.Escape(st.version))
|
||||
default:
|
||||
fmt.Fprintf(&b, "[green]●[-] %s: connected\n", n)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// SetMessage shows a message in the status bar. UI goroutine only; use
|
||||
// post from other goroutines.
|
||||
func (a *App) SetMessage(text string) {
|
||||
@@ -99,7 +164,7 @@ func (a *App) Run() error {
|
||||
}
|
||||
|
||||
func (a *App) renderStatus() {
|
||||
hints := "[gray]q quit · Tab/1-4/F1-F4 views · / search"
|
||||
hints := "[gray]?/F9 help · q quit · Tab/F1-F4 views · / search"
|
||||
if len(a.overlays) > 0 {
|
||||
hints = "[gray]Esc back · Ctrl+C quit"
|
||||
}
|
||||
@@ -180,8 +245,12 @@ func (a *App) handleKey(ev *tcell.EventKey) *tcell.EventKey {
|
||||
a.app.Stop()
|
||||
return nil
|
||||
}
|
||||
// View switching that also works while typing in a search box. Not while a dialog is open.
|
||||
// Shortcuts that also work while typing in a search box. Not while a dialog is open.
|
||||
if len(a.overlays) == 0 {
|
||||
if ev.Key() == tcell.KeyF9 {
|
||||
a.showHelp()
|
||||
return nil
|
||||
}
|
||||
if i := a.viewShortcut(ev); i >= 0 && i < len(a.views) {
|
||||
a.switchTo(i)
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user