Files
porthole/internal/ui/help.go
OpenCode Test 1421b4659e feat: implement ControlTower TUI for cluster and host monitoring
Add complete TUI application for monitoring Kubernetes clusters and host
systems. Features include:

Core features:
- Collector framework with concurrent scheduling
- Host collectors: disk, memory, load, network
- Kubernetes collectors: pods, nodes, workloads, events with informers
- Issue deduplication, state management, and resolve-after logic
- Bubble Tea TUI with table view, details pane, and filtering
- JSON export functionality

UX improvements:
- Help overlay with keybindings
- Priority/category filters with visual indicators
- Direct priority jump (0/1/2/3)
- Bulk acknowledge (Shift+A)
- Clipboard copy (y)
- Theme toggle (T)
- Age format toggle (d)
- Wide title toggle (t)
- Vi-style navigation (j/k)
- Home/End jump (g/G)
- Rollup drill-down in details

Robustness:
- Grace period for unreachable clusters
- Rollups for high-volume issues
- Flap suppression
- RBAC error handling

Files: All core application code with tests for host collectors,
engine, store, model, and export packages.
2025-12-24 13:29:51 -08:00

153 lines
3.1 KiB
Go

package ui
import (
"fmt"
"strings"
"github.com/charmbracelet/bubbles/key"
)
// HelpModel is the help overlay model.
type HelpModel struct {
visible bool
}
// NewHelp creates a new help model.
func NewHelp() HelpModel {
return HelpModel{
visible: false,
}
}
// Show displays the help overlay.
func (m *HelpModel) Show() {
m.visible = true
}
// Hide hides the help overlay.
func (m *HelpModel) Hide() {
m.visible = false
}
// Toggle toggles the help overlay visibility.
func (m *HelpModel) Toggle() {
m.visible = !m.visible
}
// IsVisible returns true if the help overlay is visible.
func (m HelpModel) IsVisible() bool {
return m.visible
}
// Render renders the help overlay.
func (m HelpModel) Render(keys KeyMap, styles Styles) string {
if !m.visible {
return ""
}
var b strings.Builder
// Title
title := styles.HeaderBar.Render("Keybindings - Press ? or esc to close")
b.WriteString(title)
b.WriteString("\n\n")
// Define keybinding groups
groups := []struct {
name string
binds []keyHelp
}{
{
name: "Global",
binds: []keyHelp{
{keys.Help, "Show/hide this help"},
{keys.Quit, "Quit the application"},
{keys.RefreshNow, "Refresh data now"},
},
},
{
name: "Filters",
binds: []keyHelp{
{keys.Search, "Search by title/details"},
{keys.Priority, "Cycle priority filter"},
{keys.Category, "Cycle category filter"},
},
},
{
name: "Navigation",
binds: []keyHelp{
{keys.FocusNext, "Toggle focus (table/details)"},
{keys.Sort, "Cycle sort order"},
{keys.JumpToTop, "Jump to top (g)"},
{keys.JumpToBottom, "Jump to bottom (G)"},
{keys.Down, "Move down (j)"},
{keys.Up, "Move up (k)"},
},
},
{
name: "Actions",
binds: []keyHelp{
{keys.AckToggle, "Acknowledge/unacknowledge issue"},
{keys.Export, "Export issues to JSON"},
},
},
}
// Render each group
for i, group := range groups {
if i > 0 {
b.WriteString("\n")
}
// Group header
groupTitle := styles.HeaderKey.Render(group.name + ":")
b.WriteString(groupTitle)
b.WriteString("\n")
// Keybindings in this group
for _, kb := range group.binds {
line := renderKeyHelp(kb, styles)
b.WriteString(line)
b.WriteString("\n")
}
}
// Render collector health icon legend
b.WriteString("\n")
legendTitle := styles.HeaderKey.Render("Legend:")
b.WriteString(legendTitle)
b.WriteString("\n")
legendText := styles.HeaderVal.Render(" Collector health: ✓ (OK), ⚠ (DEGRADED), ✗ (ERROR)")
b.WriteString(legendText)
b.WriteString("\n")
return b.String()
}
type keyHelp struct {
binding key.Binding
help string
}
func renderKeyHelp(kb keyHelp, styles Styles) string {
// Get key names from the binding
keys := kb.binding.Keys()
if len(keys) == 0 {
return ""
}
// Format key names
keyStr := strings.Join(keys, ", ")
keyStyled := styles.HeaderVal.Render(keyStr)
// Format help text
helpStyled := styles.HeaderVal.Render(kb.help)
// Combine with padding
padding := ""
if needed := 10 - len(keyStr); needed > 0 {
padding = strings.Repeat(" ", needed)
}
return fmt.Sprintf(" %s%s%s", keyStyled, padding, helpStyled)
}