on main help sign in
fix(noninteractive): spinner text on light backgrounds
commit339857700a871ce9d908b8b3eead6b4a06a832ed authorChristian Rocha <christian@rocha.is> dateMon 3 Nov 2025, 11:34 (1 year ago) parentc84de05
3 files changed+30−20
internal/agent/agent.go+1−1 internal/app/app.go+24−1 internal/format/spinner.go+5−18
internal/agent/agent.go+1−1
@@ -286,7 +286,7 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy
286286 // Strip leading newline from initial text content. This is is
287287 // particularly important in non-interactive mode where leading
288288 // newlines are very visible.
289− if len(currentAssistant.Parts) == 0 && strings.HasPrefix(text, "\n") {
289+ if len(currentAssistant.Parts) == 0 {
290290 text = strings.TrimPrefix(text, "\n")
291291 }
292292
internal/app/app.go+24−1
@@ -9,6 +9,7 @@ import (
99 "fmt"
1010 "io"
1111 "log/slog"
12+ "os"
1213 "sync"
1314 "time"
1415
@@ -27,7 +28,11 @@ import (
2728 "github.com/charmbracelet/crush/internal/permission"
2829 "github.com/charmbracelet/crush/internal/pubsub"
2930 "github.com/charmbracelet/crush/internal/session"
31+ "github.com/charmbracelet/crush/internal/tui/components/anim"
32+ "github.com/charmbracelet/crush/internal/tui/styles"
33+ "github.com/charmbracelet/lipgloss/v2"
3034 "github.com/charmbracelet/x/ansi"
35+ "github.com/charmbracelet/x/exp/charmtone"
3136)
3237
3338type App struct {
@@ -114,7 +119,25 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt
114119
115120 var spinner *format.Spinner
116121 if !quiet {
117− spinner = format.NewSpinner(ctx, cancel, "Generating")
122+ t := styles.CurrentTheme()
123+
124+ // Detect background color to set the appropriate color for the
125+ // spinner's 'Generating...' text. Without this, that text would be
126+ // unreadable in light terminals.
127+ hasDarkBG := true
128+ if f, ok := output.(*os.File); ok {
129+ hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, f)
130+ }
131+ defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
132+
133+ spinner = format.NewSpinner(ctx, cancel, anim.Settings{
134+ Size: 10,
135+ Label: "Generating",
136+ LabelColor: defaultFG,
137+ GradColorA: t.Primary,
138+ GradColorB: t.Secondary,
139+ CycleColors: true,
140+ })
118141 spinner.Start()
119142 }
120143
internal/format/spinner.go+5−18
@@ -8,7 +8,6 @@ import (
88
99 tea "github.com/charmbracelet/bubbletea/v2"
1010 "github.com/charmbracelet/crush/internal/tui/components/anim"
11− "github.com/charmbracelet/crush/internal/tui/styles"
1211 "github.com/charmbracelet/x/ansi"
1312)
1413
@@ -42,28 +41,16 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
4241}
4342
4443// NewSpinner creates a new spinner with the given message
45−func NewSpinner(ctx context.Context, cancel context.CancelFunc, message string) *Spinner {
46− t := styles.CurrentTheme()
47− model := model{
48− anim: anim.New(anim.Settings{
49− Size: 10,
50− Label: message,
51− LabelColor: t.FgBase,
52− GradColorA: t.Primary,
53− GradColorB: t.Secondary,
54− CycleColors: true,
55− }),
44+func NewSpinner(ctx context.Context, cancel context.CancelFunc, animSettings anim.Settings) *Spinner {
45+ m := model{
46+ anim: anim.New(animSettings),
5647 cancel: cancel,
5748 }
5849
59− prog := tea.NewProgram(
60− model,
61− tea.WithOutput(os.Stderr),
62− tea.WithContext(ctx),
63− )
50+ p := tea.NewProgram(m, tea.WithOutput(os.Stderr), tea.WithContext(ctx))
6451
6552 return &Spinner{
66− prog: prog,
53+ prog: p,
6754 done: make(chan struct{}, 1),
6855 }
6956}