ai-agent/internal/tui/progress.go
admin 8dc496b626
Some checks failed
CI / test (push) Has been cancelled
Release / release (push) Failing after 4m36s
first commit
2026-03-08 15:40:34 +07:00

162 lines
4.6 KiB
Go

package tui
import (
"fmt"
"strings"
"time"
"charm.land/lipgloss/v2"
)
// ProgressItem tracks progress for a long-running operation.
type ProgressItem struct {
ID string
Name string
Total float64
Completed float64
Started int64 // Unix timestamp
}
// ProgressTracker manages multiple progress items.
type ProgressTracker struct {
items map[string]*ProgressItem
isDark bool
styles ProgressStyles
}
// ProgressStyles holds styling for progress display.
type ProgressStyles struct {
Bar lipgloss.Style
Label lipgloss.Style
Percent lipgloss.Style
Completed lipgloss.Style
Empty lipgloss.Style
}
// DefaultProgressStyles returns default styles.
func DefaultProgressStyles(isDark bool) ProgressStyles {
if isDark {
return ProgressStyles{
Bar: lipgloss.NewStyle().Foreground(lipgloss.Color("#88c0d0")),
Label: lipgloss.NewStyle().Foreground(lipgloss.Color("#d8dee9")),
Percent: lipgloss.NewStyle().Foreground(lipgloss.Color("#81a1c1")),
Completed: lipgloss.NewStyle().Foreground(lipgloss.Color("#a3be8c")),
Empty: lipgloss.NewStyle().Foreground(lipgloss.Color("#4c566a")),
}
}
return ProgressStyles{
Bar: lipgloss.NewStyle().Foreground(lipgloss.Color("#4f8f8f")),
Label: lipgloss.NewStyle().Foreground(lipgloss.Color("#4c566a")),
Percent: lipgloss.NewStyle().Foreground(lipgloss.Color("#5e81ac")),
Completed: lipgloss.NewStyle().Foreground(lipgloss.Color("#4f8f38")),
Empty: lipgloss.NewStyle().Foreground(lipgloss.Color("#9ca0a8")),
}
}
// NewProgressTracker creates a new progress tracker.
func NewProgressTracker(isDark bool) *ProgressTracker {
return &ProgressTracker{
items: make(map[string]*ProgressItem),
isDark: isDark,
styles: DefaultProgressStyles(isDark),
}
}
// SetDark updates theme.
func (pt *ProgressTracker) SetDark(isDark bool) {
pt.isDark = isDark
pt.styles = DefaultProgressStyles(isDark)
}
// Start begins tracking a new progress item.
func (pt *ProgressTracker) Start(id, name string, total float64) {
pt.items[id] = &ProgressItem{
ID: id,
Name: name,
Total: total,
Started: time.Now().Unix(),
}
}
// Update sets the current progress.
func (pt *ProgressTracker) Update(id string, completed float64) {
if item, ok := pt.items[id]; ok {
item.Completed = completed
}
}
// Complete marks an item as done.
func (pt *ProgressTracker) Complete(id string) {
if item, ok := pt.items[id]; ok {
item.Completed = item.Total
}
}
// Remove stops tracking an item.
func (pt *ProgressTracker) Remove(id string) {
delete(pt.items, id)
}
// Get returns a progress item by ID.
func (pt *ProgressTracker) Get(id string) (*ProgressItem, bool) {
item, ok := pt.items[id]
return item, ok
}
// All returns all progress items.
func (pt *ProgressTracker) All() []*ProgressItem {
result := make([]*ProgressItem, 0, len(pt.items))
for _, item := range pt.items {
result = append(result, item)
}
return result
}
// Render returns the progress bar view for an item.
func (pt *ProgressTracker) Render(id string, width int) string {
item, ok := pt.items[id]
if !ok || item.Total == 0 {
return ""
}
percent := int((item.Completed / item.Total) * 100)
barWidth := width - 20 // Leave room for label and percentage
if barWidth < 5 {
barWidth = 5
}
filled := int((item.Completed / item.Total) * float64(barWidth))
bar := strings.Repeat("█", filled) + strings.Repeat("░", barWidth-filled)
label := pt.styles.Label.Render(item.Name)
percentStr := pt.styles.Percent.Render(fmt.Sprintf("%d%%", percent))
return fmt.Sprintf("%s [%s] %s", label, bar, percentStr)
}
// RenderSimple returns a simple progress bar without the label.
func (pt *ProgressTracker) RenderSimple(id string, width int) string {
item, ok := pt.items[id]
if !ok || item.Total == 0 {
return ""
}
percent := int((item.Completed / item.Total) * 100)
barWidth := width - 8 // Leave room for percentage
if barWidth < 5 {
barWidth = 5
}
filled := int((item.Completed / item.Total) * float64(barWidth))
bar := strings.Repeat("█", filled) + strings.Repeat("░", barWidth-filled)
percentStr := pt.styles.Percent.Render(fmt.Sprintf("%d%%", percent))
return fmt.Sprintf("[%s] %s", bar, percentStr)
}
// HasItems returns true if there are any progress items.
func (pt *ProgressTracker) HasItems() bool {
return len(pt.items) > 0
}