1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 22:04:43 +00:00
Files
Mole/cmd/status/main.go

144 lines
2.9 KiB
Go

package main
import (
"fmt"
"os"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
const refreshInterval = time.Second
var (
Version = "dev"
BuildTime = ""
)
type tickMsg struct{}
type animTickMsg struct{}
type metricsMsg struct {
data MetricsSnapshot
err error
}
type model struct {
collector *Collector
width int
height int
metrics MetricsSnapshot
errMessage string
ready bool
lastUpdated time.Time
collecting bool
animFrame int
}
func newModel() model {
return model{
collector: NewCollector(),
}
}
func (m model) Init() tea.Cmd {
return tea.Batch(tickAfter(0), animTick())
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "q", "esc", "ctrl+c":
return m, tea.Quit
}
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
return m, nil
case tickMsg:
if m.collecting {
return m, nil
}
m.collecting = true
return m, m.collectCmd()
case metricsMsg:
if msg.err != nil {
m.errMessage = msg.err.Error()
} else {
m.errMessage = ""
}
m.metrics = msg.data
m.lastUpdated = msg.data.CollectedAt
m.collecting = false
// Mark ready after first successful data collection
if !m.ready {
m.ready = true
}
return m, tickAfter(refreshInterval)
case animTickMsg:
m.animFrame++
return m, animTickWithSpeed(m.metrics.CPU.Usage)
}
return m, nil
}
func (m model) View() string {
if !m.ready {
return "Loading..."
}
header := renderHeader(m.metrics, m.errMessage, m.animFrame, m.width)
cardWidth := 0
if m.width > 80 {
cardWidth = maxInt(24, m.width/2-4)
}
cards := buildCards(m.metrics, cardWidth)
if m.width <= 80 {
var rendered []string
for i, c := range cards {
if i > 0 {
rendered = append(rendered, "")
}
rendered = append(rendered, renderCard(c, cardWidth, 0))
}
return header + "\n" + lipgloss.JoinVertical(lipgloss.Left, rendered...)
}
return header + "\n" + renderTwoColumns(cards, m.width)
}
func (m model) collectCmd() tea.Cmd {
return func() tea.Msg {
data, err := m.collector.Collect()
return metricsMsg{data: data, err: err}
}
}
func tickAfter(delay time.Duration) tea.Cmd {
return tea.Tick(delay, func(time.Time) tea.Msg { return tickMsg{} })
}
func animTick() tea.Cmd {
return tea.Tick(200*time.Millisecond, func(time.Time) tea.Msg { return animTickMsg{} })
}
func animTickWithSpeed(cpuUsage float64) tea.Cmd {
// Higher CPU = faster animation (50ms to 300ms)
interval := 300 - int(cpuUsage*2.5)
if interval < 50 {
interval = 50
}
return tea.Tick(time.Duration(interval)*time.Millisecond, func(time.Time) tea.Msg { return animTickMsg{} })
}
func main() {
p := tea.NewProgram(newModel(), tea.WithAltScreen())
if err := p.Start(); err != nil {
fmt.Fprintf(os.Stderr, "system status error: %v\n", err)
os.Exit(1)
}
}