on main help sign in
feat: show progress bar on boot for feedback (#1371)
0182d94Andrey Nering
internal/app/app.go+10−4
@@ -28,6 +28,7 @@ import (
28 "github.com/charmbracelet/crush/internal/permission"
28 "github.com/charmbracelet/crush/internal/permission"
29 "github.com/charmbracelet/crush/internal/pubsub"
29 "github.com/charmbracelet/crush/internal/pubsub"
30 "github.com/charmbracelet/crush/internal/session"
30 "github.com/charmbracelet/crush/internal/session"
31+ "github.com/charmbracelet/crush/internal/term"
31 "github.com/charmbracelet/crush/internal/tui/components/anim"
32 "github.com/charmbracelet/crush/internal/tui/components/anim"
32 "github.com/charmbracelet/crush/internal/tui/styles"
33 "github.com/charmbracelet/crush/internal/tui/styles"
33 "github.com/charmbracelet/lipgloss/v2"
34 "github.com/charmbracelet/lipgloss/v2"
@@ -191,9 +192,12 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt
191
192
192 messageEvents := app.Messages.Subscribe(ctx)
193 messageEvents := app.Messages.Subscribe(ctx)
193 messageReadBytes := make(map[string]int)
194 messageReadBytes := make(map[string]int)
195+ supportsProgressBar := term.SupportsProgressBar()
194
196
195 defer func() {
197 defer func() {
198+ if supportsProgressBar {
196− _, _ = fmt.Printf(ansi.ResetProgressBar)
199+ _, _ = fmt.Fprintf(os.Stderr, ansi.ResetProgressBar)
200+ }
197
201
198 // Always print a newline at the end. If output is a TTY this will
202 // Always print a newline at the end. If output is a TTY this will
199 // prevent the prompt from overwriting the last line of output.
203 // prevent the prompt from overwriting the last line of output.
@@ -201,9 +205,11 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt
201 }()
205 }()
202
206
203 for {
207 for {
208+ if supportsProgressBar {
204− // HACK: Reinitialize the terminal progress bar on every iteration so
209+ // HACK: Reinitialize the terminal progress bar on every iteration so
205− // it doesn't get hidden by the terminal due to inactivity.
210+ // it doesn't get hidden by the terminal due to inactivity.
206− _, _ = fmt.Printf(ansi.SetIndeterminateProgressBar)
211+ _, _ = fmt.Fprintf(os.Stderr, ansi.SetIndeterminateProgressBar)
212+ }
207
213
208 select {
214 select {
209 case result := <-done:
215 case result := <-done:
internal/cmd/root.go+12−3
@@ -18,11 +18,13 @@ import (
18 "github.com/charmbracelet/crush/internal/config"
18 "github.com/charmbracelet/crush/internal/config"
19 "github.com/charmbracelet/crush/internal/db"
19 "github.com/charmbracelet/crush/internal/db"
20 "github.com/charmbracelet/crush/internal/event"
20 "github.com/charmbracelet/crush/internal/event"
21+ termutil "github.com/charmbracelet/crush/internal/term"
21 "github.com/charmbracelet/crush/internal/tui"
22 "github.com/charmbracelet/crush/internal/tui"
22 "github.com/charmbracelet/crush/internal/version"
23 "github.com/charmbracelet/crush/internal/version"
23 "github.com/charmbracelet/fang"
24 "github.com/charmbracelet/fang"
24 "github.com/charmbracelet/lipgloss/v2"
25 "github.com/charmbracelet/lipgloss/v2"
25 uv "github.com/charmbracelet/ultraviolet"
26 uv "github.com/charmbracelet/ultraviolet"
27+ "github.com/charmbracelet/x/ansi"
26 "github.com/charmbracelet/x/exp/charmtone"
28 "github.com/charmbracelet/x/exp/charmtone"
27 "github.com/charmbracelet/x/term"
29 "github.com/charmbracelet/x/term"
28 "github.com/spf13/cobra"
30 "github.com/spf13/cobra"
@@ -32,7 +34,6 @@ func init() {
32 rootCmd.PersistentFlags().StringP("cwd", "c", "", "Current working directory")
34 rootCmd.PersistentFlags().StringP("cwd", "c", "", "Current working directory")
33 rootCmd.PersistentFlags().StringP("data-dir", "D", "", "Custom crush data directory")
35 rootCmd.PersistentFlags().StringP("data-dir", "D", "", "Custom crush data directory")
34 rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug")
36 rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug")
35−
36 rootCmd.Flags().BoolP("help", "h", false, "Help")
37 rootCmd.Flags().BoolP("help", "h", false, "Help")
37 rootCmd.Flags().BoolP("yolo", "y", false, "Automatically accept all permissions (dangerous mode)")
38 rootCmd.Flags().BoolP("yolo", "y", false, "Automatically accept all permissions (dangerous mode)")
38
39
@@ -74,7 +75,7 @@ crush run "Explain the use of context in Go"
74crush -y
75crush -y
75 `,
76 `,
76 RunE: func(cmd *cobra.Command, args []string) error {
77 RunE: func(cmd *cobra.Command, args []string) error {
77− app, err := setupApp(cmd)
78+ app, err := setupAppWithProgressBar(cmd)
78 if err != nil {
79 if err != nil {
79 return err
80 return err
80 }
81 }
@@ -92,7 +93,6 @@ crush -y
92 tea.WithEnvironment(env),
93 tea.WithEnvironment(env),
93 tea.WithContext(cmd.Context()),
94 tea.WithContext(cmd.Context()),
94 tea.WithFilter(tui.MouseEventFilter)) // Filter mouse events based on focus state
95 tea.WithFilter(tui.MouseEventFilter)) // Filter mouse events based on focus state
95−
96 go app.Subscribe(program)
96 go app.Subscribe(program)
97
97
98 if _, err := program.Run(); err != nil {
98 if _, err := program.Run(); err != nil {
@@ -150,6 +150,15 @@ func Execute() {
150 }
150 }
151}
151}
152
152
153+func setupAppWithProgressBar(cmd *cobra.Command) (*app.App, error) {
154+ if termutil.SupportsProgressBar() {
155+ _, _ = fmt.Fprintf(os.Stderr, ansi.SetIndeterminateProgressBar)
156+ defer func() { _, _ = fmt.Fprintf(os.Stderr, ansi.ResetProgressBar) }()
157+ }
158+
159+ return setupApp(cmd)
160+}
161+
153// setupApp handles the common setup logic for both interactive and non-interactive modes.
162// setupApp handles the common setup logic for both interactive and non-interactive modes.
154// It returns the app instance, config, cleanup function, and any error.
163// It returns the app instance, config, cleanup function, and any error.
155func setupApp(cmd *cobra.Command) (*app.App, error) {
164func setupApp(cmd *cobra.Command) (*app.App, error) {
internal/term/term.gonew file+15
@@ -0,0 +1,15 @@
1+package term
2+
3+import (
4+ "os"
5+ "strings"
6+)
7+
8+// SupportsProgressBar tries to determine whether the current terminal supports
9+// progress bars by looking into environment variables.
10+func SupportsProgressBar() bool {
11+ termProg := os.Getenv("TERM_PROGRAM")
12+ _, isWindowsTerminal := os.LookupEnv("WT_SESSION")
13+
14+ return isWindowsTerminal || strings.Contains(strings.ToLower(termProg), "ghostty")
15+}