issues-tui: wide, short terminal dashboard for current issues
Standalone cmd that runs the same pipeline as the webhook notifications (alerts.LoadErrorEvents -> alerts.DetectAlerts -> cooldown suppressor) on a schedule and renders what is firing as a row of severity-colored cards - built for an ultra-wide, low display. A pattern shows PAGED when the scan would have notified and ongoing while suppressed; disappearing spikes are reported as recovered. -once prints one snapshot for cron/pipe use. Colors follow the otel-ui design-system tokens (dark and light).
This commit is contained in:
1 parent
36f00d49b9
commit
a96e95212a
6 files changed
+1048
-78
No files matched your search
@@ -0,0 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
// modelConfig is the static configuration the dashboard needs beyond the
|
||||
// scanner.
|
||||
type modelConfig struct {
|
||||
Interval time.Duration
|
||||
Title string
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
// tickMsg drives the clock and the scan countdown.
|
||||
type tickMsg time.Time
|
||||
|
||||
// snapshotMsg carries a finished scan back into the model.
|
||||
type snapshotMsg snapshot
|
||||
|
||||
type model struct {
|
||||
sc *scanner
|
||||
cfg modelConfig
|
||||
snap snapshot
|
||||
|
||||
scanning bool
|
||||
nextScanAt time.Time
|
||||
|
||||
width, height int
|
||||
ready bool
|
||||
}
|
||||
|
||||
func newModel(sc *scanner, cfg modelConfig) model {
|
||||
return model{sc: sc, cfg: cfg, nextScanAt: time.Now()}
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return tea.Batch(tickClock(), m.scanCmd())
|
||||
}
|
||||
|
||||
func tickClock() tea.Cmd {
|
||||
return tea.Tick(time.Second, func(t time.Time) tea.Msg { return tickMsg(t) })
|
||||
}
|
||||
|
||||
func (m *model) scanCmd() tea.Cmd {
|
||||
sc := m.sc
|
||||
return func() tea.Msg { return snapshotMsg(sc.Scan(context.Background(), time.Now())) }
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
m.width, m.height, m.ready = msg.Width, msg.Height, true
|
||||
return m, nil
|
||||
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "q", "esc", "ctrl+c":
|
||||
return m, tea.Quit
|
||||
case "r":
|
||||
if !m.scanning {
|
||||
m.scanning = true
|
||||
return m, m.scanCmd()
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case tickMsg:
|
||||
now := time.Time(msg)
|
||||
cmd := tickClock()
|
||||
if !m.scanning && !now.Before(m.nextScanAt) {
|
||||
m.scanning = true
|
||||
return m, tea.Batch(cmd, m.scanCmd())
|
||||
}
|
||||
return m, cmd
|
||||
|
||||
case snapshotMsg:
|
||||
m.snap = snapshot(msg)
|
||||
m.scanning = false
|
||||
m.nextScanAt = time.Now().Add(m.cfg.Interval)
|
||||
return m, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
return renderSnapshot(m.snap, snapshotOptions{
|
||||
Width: m.width, Height: m.height, Title: m.cfg.Title, BaseURL: m.cfg.BaseURL,
|
||||
Scanning: m.scanning, NextScanAt: m.nextScanAt, Now: time.Now(),
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user