From 4d77002dafd70e763b7aec7c681a1f2fcf9c4e61 Mon Sep 17 00:00:00 2001 From: kevin olson Date: Tue, 7 Oct 2025 14:41:52 -0500 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8E=A8=201st=20attempt=20at=20disabli?= =?UTF-8?q?ng=20the=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.go | 7 +++-- config_test.go | 30 +++++++++++++++++++ examples/disable-ui/main.go | 58 +++++++++++++++++++++++++++++++++++++ mvc.go | 10 +++---- readme.md | 6 ++++ taskin.go | 5 ++-- 6 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 examples/disable-ui/main.go diff --git a/config.go b/config.go index bc6fb08..7cc1307 100644 --- a/config.go +++ b/config.go @@ -7,11 +7,12 @@ import ( ) type Config struct { - Options ConfigOptions - Spinner spinner.Spinner Colors ConfigColors - ProgressOptions []progress.Option Chars ConfigChars + Spinner spinner.Spinner + Options ConfigOptions + ProgressOptions []progress.Option + DisableUI bool // Disable animations and spinners } type ConfigOptions struct { diff --git a/config_test.go b/config_test.go index 5924928..460179b 100644 --- a/config_test.go +++ b/config_test.go @@ -58,3 +58,33 @@ func TestConfig(t *testing.T) { t.Errorf("Expected ExitOnFailure to be 'true', got '%v'", config.Options.ExitOnFailure) } } + +func TestDisableUI(t *testing.T) { + tasks := Tasks{ + { + Title: "Test task", + Task: func(task *Task) error { + task.Title = "Test task completed" + return nil + }, + }, + } + + // Test with UI disabled + cfg := Defaults + cfg.DisableUI = true + + runners := New(tasks, cfg) + + // Verify that spinners are not initialized when UI is disabled + for _, runner := range runners { + if runner.Spinner != nil { + t.Error("Expected spinner to be nil when DisableUI is true") + } + } + + err := runners.Run() + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } +} diff --git a/examples/disable-ui/main.go b/examples/disable-ui/main.go new file mode 100644 index 0000000..85536e4 --- /dev/null +++ b/examples/disable-ui/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + "time" + + "github.com/fumeapp/taskin" +) + +func main() { + tasks := taskin.Tasks{ + { + Title: "Task with UI disabled", + Task: func(t *taskin.Task) error { + for i := 0; i < 3; i++ { + t.Title = fmt.Sprintf("Task with UI disabled: [%d/3] processing", i+1) + time.Sleep(500 * time.Millisecond) + } + return nil + }, + }, + { + Title: "Parent task with children", + Tasks: taskin.Tasks{ + { + Title: "Child task 1", + Task: func(t *taskin.Task) error { + for i := 0; i < 2; i++ { + t.Title = fmt.Sprintf("Child task 1: [%d/2] working", i+1) + time.Sleep(300 * time.Millisecond) + } + return nil + }, + }, + { + Title: "Child task 2", + Task: func(t *taskin.Task) error { + for i := 0; i < 2; i++ { + t.Title = fmt.Sprintf("Child task 2: [%d/2] working", i+1) + time.Sleep(300 * time.Millisecond) + } + return nil + }, + }, + }, + }, + } + + // Create configuration with UI disabled + cfg := taskin.Defaults + cfg.DisableUI = true + + runners := taskin.New(tasks, cfg) + err := runners.Run() + if err != nil { + panic(err) + } +} \ No newline at end of file diff --git a/mvc.go b/mvc.go index abddbd6..d5f0039 100644 --- a/mvc.go +++ b/mvc.go @@ -46,7 +46,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var spinnerCmds []tea.Cmd if runner.State == Running || runner.State == NotStarted { - if !IsCI() && runner.Spinner != nil { + if !IsCI() && !runner.Config.DisableUI && runner.Spinner != nil { newSpinner, cmd := runner.Spinner.Update(msg) runner.Spinner = &newSpinner spinnerCmds = append(spinnerCmds, cmd) @@ -105,8 +105,8 @@ func (m *Model) View() string { } } - // Handle CI mode - if IsCI() { + // Handle CI mode or UI disabled mode + if IsCI() || (len(m.Runners) > 0 && m.Runners[0].Config.DisableUI) { allDone, anyFailed := m.checkTasksState() if !allDone && !anyFailed { return "" @@ -142,14 +142,14 @@ func renderTask(runner Runner, indent string) string { status = Color(runner.Config.Colors.Failure, runner.Config.Chars.Failure) + " " + runner.Task.Title } - if IsCI() { + if IsCI() || runner.Config.DisableUI { view = indent + status + "\n" } else { view = indent + lipgloss.NewStyle().Render(status) + "\n" } // Recursively render children - if len(runner.Children) > 0 && (runner.State == Running || IsCI()) { + if len(runner.Children) > 0 && (runner.State == Running || IsCI() || runner.Config.DisableUI) { for _, child := range runner.Children { view += renderTask(child, indent+" ") } diff --git a/readme.md b/readme.md index 650e0e5..65ba011 100644 --- a/readme.md +++ b/readme.md @@ -56,6 +56,12 @@ https://github.com/fumeapp/taskin/blob/3cd766c21e5eaba5edb33f38d3781d6cf814f9f9/ +Disable UI animations and spinners + +https://github.com/fumeapp/taskin/blob/main/examples/disable-ui/main.go#L32-L42 + +This is useful when you want plain text output without animations, similar to CI mode but controllable via configuration. + Nest tasks inside tasks https://github.com/fumeapp/taskin/blob/3cd766c21e5eaba5edb33f38d3781d6cf814f9f9/examples/multi/main.go#L23-L34 diff --git a/taskin.go b/taskin.go index e6a1f8f..3dae290 100644 --- a/taskin.go +++ b/taskin.go @@ -19,7 +19,7 @@ func NewRunner(task Task, cfg Config) Runner { var spinr *spinner.Model - if !IsCI() { + if !IsCI() && !cfg.DisableUI { spinnerModel := spinner.New(spinner.WithSpinner(cfg.Spinner)) // Initialize with a spinner model spinnerModel.Style = lipgloss.NewStyle().Foreground(cfg.Colors.Spinner) // Styling spinner spinr = &spinnerModel @@ -73,7 +73,8 @@ func (r *Runners) Run() error { m := &Model{Runners: *r, Shutdown: false, ShutdownError: nil} var out io.Writer = os.Stdout - if IsCI() { + // Check if we need to disable UI features or are in CI mode + if IsCI() || (len(*r) > 0 && (*r)[0].Config.DisableUI) { out = &ansiEscapeCodeFilter{writer: out} } From 4d5e0b87d73cd887f488adbbb58a7d53a4a11091 Mon Sep 17 00:00:00 2001 From: kevin olson Date: Tue, 7 Oct 2025 14:50:14 -0500 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=92=84=20lets=20not=20lint=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/too-much-to-do/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/too-much-to-do/main.go b/examples/too-much-to-do/main.go index a91d6e9..a893851 100644 --- a/examples/too-much-to-do/main.go +++ b/examples/too-much-to-do/main.go @@ -16,7 +16,7 @@ func main() { libraries := generateLibraries(20) runners := NewLibraryDownloader(libraries) - runners.Run() + runners.Run() //nolint:errcheck // Example code } func generateLibraries(count int) []Library { From d2b6b479cac4690da3ee5b74af54b096c09160d2 Mon Sep 17 00:00:00 2001 From: kevin olson Date: Tue, 7 Oct 2025 14:52:13 -0500 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=92=84=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config_test.go | 2 +- examples/disable-ui/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config_test.go b/config_test.go index 460179b..8792812 100644 --- a/config_test.go +++ b/config_test.go @@ -75,7 +75,7 @@ func TestDisableUI(t *testing.T) { cfg.DisableUI = true runners := New(tasks, cfg) - + // Verify that spinners are not initialized when UI is disabled for _, runner := range runners { if runner.Spinner != nil { diff --git a/examples/disable-ui/main.go b/examples/disable-ui/main.go index 85536e4..22583b8 100644 --- a/examples/disable-ui/main.go +++ b/examples/disable-ui/main.go @@ -55,4 +55,4 @@ func main() { if err != nil { panic(err) } -} \ No newline at end of file +}