From 456ff72f9237df441f39eee46e042f7def7b511e Mon Sep 17 00:00:00 2001 From: Eden Reich Date: Thu, 13 Nov 2025 22:45:07 +0200 Subject: [PATCH] fix(cli): Maintain spinner visibility when exiting task management view - Check for active background tasks when exiting /tasks view with ESC - Restore spinner with task count if background tasks are running - Preserve spinner state when entering task management view - Improves UX consistency for background task indication - Fix staticcheck SA5011 false positives in test files Fixes issue where spinner would disappear even when background tasks were still running --- internal/app/chat.go | 14 +++++++ internal/handlers/chat_shortcut_handler.go | 8 +++- .../ui/components/conversation_view_test.go | 4 -- internal/ui/components/help_bar_test.go | 4 -- internal/ui/components/input_view_test.go | 4 -- internal/ui/components/status_view_test.go | 4 -- internal/ui/keybinding/registry_test.go | 40 ++++++++----------- 7 files changed, 38 insertions(+), 40 deletions(-) diff --git a/internal/app/chat.go b/internal/app/chat.go index 8a41f97d..a1c95f1b 100644 --- a/internal/app/chat.go +++ b/internal/app/chat.go @@ -526,6 +526,20 @@ func (app *ChatApplication) handleA2ATaskManagementCancelled(cmds []tea.Cmd) []t } app.focusedComponent = app.inputView + + if app.backgroundTaskService != nil { + backgroundTasks := app.backgroundTaskService.GetBackgroundTasks() + if len(backgroundTasks) > 0 { + cmds = append(cmds, func() tea.Msg { + return domain.SetStatusEvent{ + Message: fmt.Sprintf("Background tasks running (%d)", len(backgroundTasks)), + Spinner: true, + StatusType: domain.StatusDefault, + } + }) + } + } + return cmds } diff --git a/internal/handlers/chat_shortcut_handler.go b/internal/handlers/chat_shortcut_handler.go index 9ff6d77e..70c796ca 100644 --- a/internal/handlers/chat_shortcut_handler.go +++ b/internal/handlers/chat_shortcut_handler.go @@ -570,9 +570,15 @@ func (s *ChatShortcutHandler) handleShowA2ATaskManagementSideEffect() tea.Msg { } } + hasBackgroundTasks := false + if s.handler.backgroundTaskService != nil { + backgroundTasks := s.handler.backgroundTaskService.GetBackgroundTasks() + hasBackgroundTasks = len(backgroundTasks) > 0 + } + return domain.SetStatusEvent{ Message: "Task management interface", - Spinner: false, + Spinner: hasBackgroundTasks, TokenUsage: s.handler.getCurrentTokenUsage(), StatusType: domain.StatusDefault, } diff --git a/internal/ui/components/conversation_view_test.go b/internal/ui/components/conversation_view_test.go index 961fcb2e..c91d7c18 100644 --- a/internal/ui/components/conversation_view_test.go +++ b/internal/ui/components/conversation_view_test.go @@ -11,10 +11,6 @@ import ( func TestNewConversationView(t *testing.T) { cv := NewConversationView(nil) - if cv == nil { - t.Fatal("Expected ConversationView to be created, got nil") - } - if cv.width != 80 { t.Errorf("Expected default width 80, got %d", cv.width) } diff --git a/internal/ui/components/help_bar_test.go b/internal/ui/components/help_bar_test.go index ea087cef..60e65358 100644 --- a/internal/ui/components/help_bar_test.go +++ b/internal/ui/components/help_bar_test.go @@ -10,10 +10,6 @@ import ( func TestNewHelpBar(t *testing.T) { hb := NewHelpBar(nil) - if hb == nil { - t.Fatal("Expected HelpBar to be created, got nil") - } - if hb.width != 80 { t.Errorf("Expected default width 80, got %d", hb.width) } diff --git a/internal/ui/components/input_view_test.go b/internal/ui/components/input_view_test.go index 936b4134..1d34a79e 100644 --- a/internal/ui/components/input_view_test.go +++ b/internal/ui/components/input_view_test.go @@ -37,10 +37,6 @@ func TestNewInputView(t *testing.T) { mockModelService := &mockModelService{} iv := NewInputView(mockModelService) - if iv == nil { - t.Fatal("Expected InputView to be created, got nil") - } - if iv.text != "" { t.Errorf("Expected empty text, got '%s'", iv.text) } diff --git a/internal/ui/components/status_view_test.go b/internal/ui/components/status_view_test.go index 16372f9a..a091e078 100644 --- a/internal/ui/components/status_view_test.go +++ b/internal/ui/components/status_view_test.go @@ -8,10 +8,6 @@ import ( func TestNewStatusView(t *testing.T) { sv := NewStatusView(nil) - if sv == nil { - t.Fatal("Expected StatusView to be created, got nil") - } - if sv.width != 0 { t.Errorf("Expected default width 0, got %d", sv.width) } diff --git a/internal/ui/keybinding/registry_test.go b/internal/ui/keybinding/registry_test.go index d6a560a4..1e01ca4c 100644 --- a/internal/ui/keybinding/registry_test.go +++ b/internal/ui/keybinding/registry_test.go @@ -265,16 +265,14 @@ func TestKeyResolution(t *testing.T) { action := registry.Resolve("ctrl+c", mockContext) if action == nil { t.Fatal("Expected ctrl+c to resolve to an action") - } - if action.ID != "quit" { + } else if action.ID != "quit" { t.Errorf("Expected ctrl+c to resolve to 'quit', got %s", action.ID) } action = registry.Resolve("ctrl+r", mockContext) if action == nil { t.Fatal("Expected ctrl+r to resolve to an action") - } - if action.ID != "toggle_tool_expansion" { + } else if action.ID != "toggle_tool_expansion" { t.Errorf("Expected ctrl+r to resolve to 'toggle_tool_expansion', got %s", action.ID) } @@ -330,23 +328,23 @@ func TestActionHandlers(t *testing.T) { action := registry.Resolve("ctrl+c", mockContext) if action == nil { t.Fatal("Expected ctrl+c to resolve to quit action") - } - - cmd := action.Handler(mockContext, tea.KeyMsg{}) - if cmd == nil { - t.Error("Expected quit handler to return a command") + } else { + cmd := action.Handler(mockContext, tea.KeyMsg{}) + if cmd == nil { + t.Error("Expected quit handler to return a command") + } } action = registry.Resolve("ctrl+r", mockContext) if action == nil { t.Fatal("Expected ctrl+r to resolve to toggle action") - } + } else { + initialCallCount := mockContext.expandToggleCalls + _ = action.Handler(mockContext, tea.KeyMsg{}) - initialCallCount := mockContext.expandToggleCalls - _ = action.Handler(mockContext, tea.KeyMsg{}) - - if mockContext.expandToggleCalls != initialCallCount+1 { - t.Error("Expected toggle handler to call ToggleToolResultExpansion()") + if mockContext.expandToggleCalls != initialCallCount+1 { + t.Error("Expected toggle handler to call ToggleToolResultExpansion()") + } } } @@ -391,8 +389,7 @@ func TestConditionalKeyBindings(t *testing.T) { action := registry.Resolve("enter", emptyInputContext) if action == nil { t.Fatal("Expected enter key to resolve to enter_key_handler even when input is empty") - } - if action.ID != "enter_key_handler" { + } else if action.ID != "enter_key_handler" { t.Errorf("Expected enter to resolve to 'enter_key_handler', got %s", action.ID) } @@ -403,8 +400,7 @@ func TestConditionalKeyBindings(t *testing.T) { action = registry.Resolve("enter", nonEmptyInputContext) if action == nil { t.Fatal("Expected enter key to resolve to enter_key_handler when input has content") - } - if action.ID != "enter_key_handler" { + } else if action.ID != "enter_key_handler" { t.Errorf("Expected enter to resolve to 'enter_key_handler', got %s", action.ID) } } @@ -451,8 +447,7 @@ func TestActionRegistration(t *testing.T) { retrievedAction := registry.GetAction("test_action") if retrievedAction == nil { t.Fatal("Expected to retrieve registered action") - } - if retrievedAction.ID != "test_action" { + } else if retrievedAction.ID != "test_action" { t.Errorf("Expected retrieved action ID to be 'test_action', got %s", retrievedAction.ID) } @@ -462,8 +457,7 @@ func TestActionRegistration(t *testing.T) { resolvedAction := registry.Resolve("ctrl+t", mockContext) if resolvedAction == nil { t.Fatal("Expected custom action to be resolved") - } - if resolvedAction.ID != "test_action" { + } else if resolvedAction.ID != "test_action" { t.Errorf("Expected resolved action to be 'test_action', got %s", resolvedAction.ID) } }