Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 32 additions & 11 deletions blink/internal/reporter/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,38 @@ import (
"os"
"strings"
"sync"
"time"
)

// pulseInterval is the minimum wall-clock gap between icon frames, so the bloom
// pulses at a calm pace regardless of how fast files are processed.
const pulseInterval = 450 * time.Millisecond

// Progress is an ECS-style progress bar rendered on a terminal. It is a no-op
// when the writer is not a terminal (piped output, CI), so scripts stay clean.
type Progress struct {
w *os.File
width int
active bool
total int
cur int
frame int
mu sync.Mutex
w *os.File
width int
active bool
label string
total int
cur int
frame int
lastTick time.Time
mu sync.Mutex
}

// iconFrames is a pulsing bloom that grows from a dot to a star and back, advanced one step per render.
var iconFrames = []string{"·", "∘", "○", "◯", "✦", "✧", "✦", "◯", "○", "∘"}

// NewProgress returns a progress bar; it only draws when w is a terminal.
func NewProgress(w *os.File) *Progress {
p := &Progress{w: w, width: 28}
// fix selects the verb shown next to the icon: "fixing" when writing, else "checking".
func NewProgress(w *os.File, fix bool) *Progress {
label := "checking"
if fix {
label = "fixing"
}
p := &Progress{w: w, width: 28, label: label}
if _, ok := winsize(w); ok {
p.active = true
}
Expand Down Expand Up @@ -71,11 +83,20 @@ func (p *Progress) Finish() {
_, _ = fmt.Fprintf(p.w, "\r%s\r", strings.Repeat(" ", p.width+24))
}

// brickOrange wraps s in Claude's terracotta foreground color (truecolor ANSI).
func brickOrange(s string) string {
return "\x1b[38;2;215;119;87m" + s + "\x1b[0m"
}

func (p *Progress) render() {
if p.total <= 0 {
return
}
now := time.Now()
if now.Sub(p.lastTick) >= pulseInterval {
p.frame++
p.lastTick = now
}
icon := iconFrames[p.frame%len(iconFrames)]
p.frame++
_, _ = fmt.Fprintf(p.w, "\r %s", icon)
_, _ = fmt.Fprintf(p.w, "\r %s %s..", brickOrange(icon), p.label)
}
2 changes: 1 addition & 1 deletion blink/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func run(args []string) int {
cfg.WithPaths(paths...)

start := time.Now()
progress := reporter.NewProgress(os.Stderr)
progress := reporter.NewProgress(os.Stderr, fix)

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
Expand Down
Loading