From fbe8531c271b01483cb34a67a2f6ddff8c1d1aa1 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 9 Feb 2026 10:25:29 +0100 Subject: [PATCH] Prevent title generation spinner to spin forever Signed-off-by: David Gageot --- pkg/app/app.go | 20 +++++++++++++++++++- pkg/tui/components/sidebar/sidebar.go | 11 +++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index 218f844a6..80ef4a95a 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -931,16 +931,31 @@ func (a *App) generateTitle(ctx context.Context, userMessages []string) { if a.titleGen == nil { slog.Debug("No title generator available, skipping title generation") + // Emit empty title event so the UI clears any title-generation spinner + select { + case a.events <- runtime.SessionTitle(a.session.ID, ""): + case <-ctx.Done(): + } return } title, err := a.titleGen.Generate(ctx, a.session.ID, userMessages) if err != nil { slog.Error("Failed to generate session title", "session_id", a.session.ID, "error", err) + // Emit empty title event so the UI clears any title-generation spinner + select { + case a.events <- runtime.SessionTitle(a.session.ID, ""): + case <-ctx.Done(): + } return } if title == "" { + // Emit empty title event so the UI clears any title-generation spinner + select { + case a.events <- runtime.SessionTitle(a.session.ID, ""): + case <-ctx.Done(): + } return } @@ -950,7 +965,10 @@ func (a *App) generateTitle(ctx context.Context, userMessages []string) { } // Emit the title event to update the UI - a.events <- runtime.SessionTitle(a.session.ID, title) + select { + case a.events <- runtime.SessionTitle(a.session.ID, title): + case <-ctx.Done(): + } } // RegenerateSessionTitle triggers AI-based title regeneration for the current session. diff --git a/pkg/tui/components/sidebar/sidebar.go b/pkg/tui/components/sidebar/sidebar.go index 78a4babfa..4b035faaa 100644 --- a/pkg/tui/components/sidebar/sidebar.go +++ b/pkg/tui/components/sidebar/sidebar.go @@ -567,14 +567,16 @@ func (m *model) Update(msg tea.Msg) (layout.Model, tea.Cmd) { cmd := m.startSpinner() return m, cmd case *runtime.SessionTitleEvent: - m.sessionTitle = msg.Title - // Mark title as generated (enables pencil icon) - m.titleGenerated = true - // Clear regenerating state now that we have a title + // Clear regenerating state now that title generation is done if m.titleRegenerating { m.titleRegenerating = false m.stopSpinner() } + // Only update title and mark as generated if a non-empty title was provided + if msg.Title != "" { + m.sessionTitle = msg.Title + m.titleGenerated = true + } m.invalidateCache() return m, nil case *runtime.StreamStartedEvent: @@ -620,6 +622,7 @@ func (m *model) Update(msg tea.Msg) (layout.Model, tea.Cmd) { m.workingAgent = "" m.toolsLoading = false m.mcpInit = false + m.titleRegenerating = false // Force-stop main spinner if it was active (state is now cleared) if m.spinnerActive { m.spinnerActive = false