swadhinbiswas feat: live scan matrix, safety vacuuming & theme engine · 8/17/2026
30800be
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"))
}