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 // scanErr is kept separately so a failed refresh does not erase the last // successful data from the display. scanErr error 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: snap := snapshot(msg) if snap.Err != nil { m.scanErr = snap.Err } else { m.snap = snap m.scanErr = nil } 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(), ScanError: m.scanErr, }) }