/ veet / internal / ui / progress.go
swadhinbiswas feat: live scan matrix, safety vacuuming & theme engine · 8/17/2026
30800be
94 lines · 2.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package ui

import (
	"strings"

	"github.com/charmbracelet/bubbles/progress"
	"github.com/charmbracelet/bubbles/spinner"
	"github.com/charmbracelet/lipgloss"
)

// ProgressPanel shows the uninstall progress bar plus a scrolling activity log.
type ProgressPanel struct {
	bar     progress.Model
	spinner spinner.Model
	steps   []string
	percent float64
	label   string
	running bool
}

// NewProgressPanel builds the progress + activity panel.
func NewProgressPanel() *ProgressPanel {
	bar := progress.New(progress.WithSolidFill(Primary))
	bar.Width = 40
	bar.ShowPercentage = true
	s := spinner.New()
	s.Style = lipgloss.NewStyle().Foreground(primary)
	return &ProgressPanel{bar: bar, spinner: s}
}

// Start begins an active run.
func (p *ProgressPanel) Start(label string) {
	p.running = true
	p.label = label
	p.percent = 0
	p.steps = nil
}

// Finish ends the active run.
func (p *ProgressPanel) Finish() {
	p.running = false
}

// Running reports whether a run is active.
func (p *ProgressPanel) Running() bool { return p.running }

// SetPercent updates the progress bar.
func (p *ProgressPanel) SetPercent(v float64) { p.percent = v }

// Push appends a step line, keeping the last 6.
func (p *ProgressPanel) Push(line string) {
	p.steps = append(p.steps, line)
	if len(p.steps) > 6 {
		p.steps = p.steps[len(p.steps)-6:]
	}
}

// Steps exposes the recent activity lines.
func (p *ProgressPanel) Steps() []string { return p.steps }

// View renders the panel (or empty when idle).
func (p *ProgressPanel) View(width ...int) string {
	if !p.running {
		return ""
	}
	w := 56
	if len(width) > 0 && width[0] > 10 {
		w = width[0]
	}
	var b strings.Builder
	b.WriteString(p.spinner.View() + " " + HighlightText.Render("UNINSTALLING & DEEP CLEANING..."))
	b.WriteString("\n")
	b.WriteString(" " + Sub.Render(Truncate(p.label, w-4)))
	b.WriteString("\n\n")
	p.bar.Width = w - 6
	b.WriteString(" " + p.bar.ViewAs(p.percent))
	b.WriteString("\n\n")
	b.WriteString(" " + Muted.Render("Recent Actions:"))
	b.WriteString("\n")
	if len(p.steps) == 0 {
		b.WriteString("   " + Muted.Render("• starting process..."))
		b.WriteString("\n")
	} else {
		for _, s := range p.steps {
			b.WriteString("   " + Truncate(s, w-6))
			b.WriteString("\n")
		}
	}
	if len(width) > 0 && width[0] > 0 {
		return Panel.Width(width[0]).Render(strings.TrimRight(b.String(), "\n"))
	}
	return Panel.Render(strings.TrimRight(b.String(), "\n"))
}