mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
The spinner goroutine was writing carriage-return + status text to os.Stdout every 100ms. In the non-interactive TTY path, the child process also writes directly to os.Stdout, causing both writers to race on the same file descriptor. The \r emitted by the spinner resets the cursor to column 0 mid-line, corrupting and truncating the child process output. Fix by writing all spinner/status output to os.Stderr, which is the standard Unix convention for diagnostic and status messages. This is also consistent with how progress.go and ShowWarning already behave. https://claude.ai/code/session_012jMiRSS4Jx9Bs7a2F4S6KN Co-authored-by: Claude <noreply@anthropic.com>
76 lines
1.2 KiB
Go
76 lines
1.2 KiB
Go
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
spinnerChan chan bool
|
|
spinnerMu sync.Mutex
|
|
)
|
|
|
|
func StartSpinner(msg string) {
|
|
StartSpinnerWithColor(msg, Colors.Normal)
|
|
}
|
|
|
|
func StartSpinnerWithColor(msg string, c ColorFn) {
|
|
if c == nil {
|
|
c = Colors.Normal
|
|
}
|
|
|
|
style := `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏`
|
|
frames := []rune(style)
|
|
length := len(frames)
|
|
|
|
spinnerMu.Lock()
|
|
|
|
// If a previous spinner exists, stop it cleanly before starting a new one
|
|
if spinnerChan != nil {
|
|
close(spinnerChan)
|
|
spinnerChan = nil
|
|
}
|
|
spinnerChan = make(chan bool)
|
|
spinnerMu.Unlock()
|
|
|
|
ticker := time.NewTicker(100 * time.Millisecond)
|
|
go func() {
|
|
pos := 0
|
|
|
|
for {
|
|
select {
|
|
case <-spinnerChan:
|
|
ticker.Stop()
|
|
return
|
|
case <-ticker.C:
|
|
fmt.Fprintf(os.Stderr, "\r%s ... %s", c("PMG: "+msg), string(frames[pos%length]))
|
|
pos += 1
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func StopSpinner() {
|
|
spinnerMu.Lock()
|
|
defer spinnerMu.Unlock()
|
|
|
|
if spinnerChan == nil {
|
|
return
|
|
}
|
|
|
|
// Gracefully handle the case where the spinner is already stopped
|
|
// and the channel is closed, yet client code calls StopSpinner() again.
|
|
defer func() {
|
|
_ = recover()
|
|
}()
|
|
|
|
close(spinnerChan)
|
|
|
|
spinnerChan = nil
|
|
|
|
fmt.Fprint(os.Stderr, "\r")
|
|
fmt.Fprintln(os.Stderr)
|
|
}
|