Files
cameradar/internal/ui/state.go
T
2026-02-01 22:30:55 +01:00

189 lines
4.6 KiB
Go

package ui
import (
"context"
"strings"
"github.com/Ullaakut/cameradar/v6"
"github.com/charmbracelet/bubbles/progress"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
)
type modelState struct {
steps []cameradar.Step
status map[cameradar.Step]state
logs []logMsg
summary []summaryTable
summaryStreams []cameradar.Stream
summaryFinal bool
buildInfo BuildInfo
cancel context.CancelFunc
debug bool
spinner spinner.Model
progress progress.Model
width int
quitting bool
progressTotals map[cameradar.Step]int
progressCounts map[cameradar.Step]int
progressTarget float64
progressVisible float64
}
func (m *modelState) Init() tea.Cmd {
return m.spinner.Tick
}
func (m *modelState) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
switch typed := msg.(type) {
case stepMsg:
m.handleStepMsg(typed)
case logMsg:
m.handleLogMsg(typed)
case summaryMsg:
m.handleSummaryMsg(typed)
case progressMsg:
m.handleProgressMsg(typed)
case closeMsg:
m.quitting = true
case tea.KeyMsg:
if typed.Type == tea.KeyCtrlC {
if m.cancel != nil {
m.cancel()
}
m.quitting = true
return m, tea.Quit
}
case spinner.TickMsg:
cmds = m.handleSpinnerMsg(typed)
case tea.WindowSizeMsg:
m.handleWindowSizeMsg(typed)
case progress.FrameMsg:
}
if len(cmds) == 0 {
return m, nil
}
return m, tea.Batch(cmds...)
}
func (m *modelState) handleStepMsg(msg stepMsg) {
m.status[msg.step] = msg.state
if msg.message != "" {
level := logInfo
if msg.state == stateError {
level = logError
}
m.logs = append(m.logs, logMsg{level: level, step: msg.step, message: msg.message})
}
if msg.state == stateDone || msg.state == stateError {
markStepComplete(m, msg.step)
queueProgressUpdate(m)
}
m.summary = buildSummaryTables(m.summaryStreams, m.width, m.status, m.summaryFinal)
}
func (m *modelState) handleLogMsg(msg logMsg) {
m.logs = append(m.logs, msg)
}
func (m *modelState) handleSummaryMsg(msg summaryMsg) {
m.summaryStreams = msg.streams
m.summaryFinal = msg.final
m.summary = buildSummaryTables(msg.streams, m.width, m.status, msg.final)
if msg.final {
m.status[cameradar.StepSummary] = stateDone
markStepComplete(m, cameradar.StepSummary)
queueProgressUpdate(m)
m.quitting = true
}
}
func (m *modelState) handleProgressMsg(msg progressMsg) {
if msg.total > 0 {
m.progressTotals[msg.step] = msg.total
if m.progressCounts[msg.step] > msg.total {
m.progressCounts[msg.step] = msg.total
}
}
if msg.increment > 0 {
m.progressCounts[msg.step] += msg.increment
total := m.progressTotals[msg.step]
if total > 0 && m.progressCounts[msg.step] > total {
m.progressCounts[msg.step] = total
}
}
queueProgressUpdate(m)
}
func (m *modelState) handleSpinnerMsg(msg spinner.TickMsg) []tea.Cmd {
var cmds []tea.Cmd
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
cmds = append(cmds, cmd)
advanceProgress(m)
if m.quitting && progressComplete(*m) {
cmds = append(cmds, tea.Quit)
}
return cmds
}
func (m *modelState) handleWindowSizeMsg(msg tea.WindowSizeMsg) {
m.width = msg.Width
m.progress.Width = progressWidth(msg.Width)
m.summary = buildSummaryTables(m.summaryStreams, m.width, m.status, m.summaryFinal)
}
func (m *modelState) View() string {
var builder strings.Builder
builder.WriteString(sectionStyle.Render(m.buildInfo.TUIHeader()))
builder.WriteString("\n")
builder.WriteString(renderProgress(m))
builder.WriteString("\n")
spinnerView := m.spinner.View()
for _, step := range m.steps {
builder.WriteString(renderStep(step, m.status[step], spinnerView))
builder.WriteString("\n")
}
builder.WriteString("\n")
builder.WriteString(sectionStyle.Render("Logs"))
builder.WriteString("\n")
if len(m.logs) == 0 {
builder.WriteString(dimStyle.Render("No events yet."))
builder.WriteString("\n")
} else {
for _, entry := range m.logs {
builder.WriteString(renderLog(entry))
builder.WriteString("\n")
}
}
builder.WriteString("\n")
builder.WriteString(sectionStyle.Render("Summary"))
builder.WriteString("\n")
for i, summary := range m.summary {
if summary.title != "" {
builder.WriteString(subsectionStyle.Render(summary.title))
builder.WriteString("\n")
}
if summary.emptyMessage != "" {
builder.WriteString(dimStyle.Render(summary.emptyMessage))
builder.WriteString("\n")
} else {
builder.WriteString(summaryTableStyle.Render(summary.table.View()))
builder.WriteString("\n")
}
if i < len(m.summary)-1 {
builder.WriteString("\n")
}
}
return builder.String()
}