Skip to content

Commit b6242d1

Browse files
authored
Merge pull request #1413 from krissetto/stop-spinners-on-cancel
Make sure cancelling a stream with ESC stops all spinners
2 parents d30c481 + 3d6b100 commit b6242d1

File tree

3 files changed

+38
-3
lines changed

3 files changed

+38
-3
lines changed

pkg/app/app.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,10 @@ func (a *App) Run(ctx context.Context, cancel context.CancelFunc, message string
192192
a.session.AddMessage(session.UserMessage(message))
193193
}
194194
for event := range a.runtime.RunStream(ctx, a.session) {
195+
// If context is cancelled, continue draining but don't forward events.
196+
// This prevents the runtime from blocking on event sends.
195197
if ctx.Err() != nil {
196-
return
198+
continue
197199
}
198200
a.events <- event
199201
}
@@ -207,8 +209,10 @@ func (a *App) RunWithMessage(ctx context.Context, cancel context.CancelFunc, msg
207209
go func() {
208210
a.session.AddMessage(msg)
209211
for event := range a.runtime.RunStream(ctx, a.session) {
212+
// If context is cancelled, continue draining but don't forward events.
213+
// This prevents the runtime from blocking on event sends.
210214
if ctx.Err() != nil {
211-
return
215+
continue
212216
}
213217
a.events <- event
214218
}

pkg/tui/components/sidebar/sidebar.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/docker/cagent/pkg/runtime"
1616
"github.com/docker/cagent/pkg/session"
1717
"github.com/docker/cagent/pkg/tools"
18+
chatmsgs "github.com/docker/cagent/pkg/tui/components/messages"
1819
"github.com/docker/cagent/pkg/tui/components/scrollbar"
1920
"github.com/docker/cagent/pkg/tui/components/spinner"
2021
"github.com/docker/cagent/pkg/tui/components/tab"
@@ -89,6 +90,7 @@ type model struct {
8990
scrollbar *scrollbar.Model
9091
workingDirectory string
9192
queuedMessages []string // Truncated preview of queued messages
93+
streamCancelled bool // true after ESC cancel until next StreamStartedEvent
9294
}
9395

9496
// Option is a functional option for configuring the sidebar.
@@ -305,12 +307,20 @@ func (m *model) Update(msg tea.Msg) (layout.Model, tea.Cmd) {
305307
m.SetTokenUsage(msg)
306308
return m, nil
307309
case *runtime.MCPInitStartedEvent:
310+
// Ignore if stream was cancelled (stale event from before cancellation)
311+
if m.streamCancelled {
312+
return m, nil
313+
}
308314
m.mcpInit = true
309315
return m, m.spinner.Init()
310316
case *runtime.MCPInitFinishedEvent:
311317
m.mcpInit = false
312318
return m, nil
313319
case *runtime.RAGIndexingStartedEvent:
320+
// Ignore if stream was cancelled (stale event from before cancellation)
321+
if m.streamCancelled {
322+
return m, nil
323+
}
314324
// Use composite key: "ragName/strategyName" to differentiate strategies within same RAG manager
315325
key := msg.RAGName + "/" + msg.StrategyName
316326
slog.Debug("Sidebar received RAG indexing started event", "rag", msg.RAGName, "strategy", msg.StrategyName, "key", key)
@@ -336,6 +346,8 @@ func (m *model) Update(msg tea.Msg) (layout.Model, tea.Cmd) {
336346
m.sessionTitle = msg.Title
337347
return m, nil
338348
case *runtime.StreamStartedEvent:
349+
// New stream starting - reset cancelled flag and enable spinner
350+
m.streamCancelled = false
339351
m.workingAgent = msg.AgentName
340352
return m, m.spinner.Init()
341353
case *runtime.StreamStoppedEvent:
@@ -351,11 +363,26 @@ func (m *model) Update(msg tea.Msg) (layout.Model, tea.Cmd) {
351363
m.SetAgentSwitching(msg.Switching)
352364
return m, nil
353365
case *runtime.ToolsetInfoEvent:
366+
// Ignore loading state if stream was cancelled (stale event from before cancellation)
367+
if m.streamCancelled && msg.Loading {
368+
return m, nil
369+
}
354370
m.SetToolsetInfo(msg.AvailableTools, msg.Loading)
355371
if msg.Loading {
356372
return m, m.spinner.Init()
357373
}
358374
return m, nil
375+
case chatmsgs.StreamCancelledMsg:
376+
// Clear all spinner-driving state when stream is cancelled via ESC
377+
m.streamCancelled = true
378+
m.workingAgent = ""
379+
m.toolsLoading = false
380+
m.mcpInit = false
381+
// Clear any in-flight RAG indexing state
382+
for k := range m.ragIndexing {
383+
delete(m.ragIndexing, k)
384+
}
385+
return m, nil
359386
default:
360387
var cmds []tea.Cmd
361388

pkg/tui/page/chat/chat.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,12 @@ func (p *chatPage) Update(msg tea.Msg) (layout.Model, tea.Cmd) {
328328
model, cmd := p.messages.Update(msg)
329329
p.messages = model.(messages.Model)
330330

331+
// Forward to sidebar to stop its spinners
332+
sidebarModel, sidebarCmd := p.sidebar.Update(msg)
333+
p.sidebar = sidebarModel.(sidebar.Model)
334+
331335
var cmds []tea.Cmd
332-
cmds = append(cmds, cmd)
336+
cmds = append(cmds, cmd, sidebarCmd)
333337

334338
if msg.ShowMessage {
335339
cmds = append(cmds, p.messages.AddCancelledMessage())

0 commit comments

Comments
 (0)