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