@@ -3,6 +3,7 @@ package chat
33import (
44 "context"
55 "strings"
6+ "time"
67
78 "github.com/charmbracelet/bubbles/v2/key"
89 tea "github.com/charmbracelet/bubbletea/v2"
@@ -33,6 +34,7 @@ type (
3334 ChatFocusedMsg struct {
3435 Focused bool // True if the chat input is focused, false otherwise
3536 }
37+ CancelTimerExpiredMsg struct{}
3638)
3739
3840type ChatPage interface {
@@ -57,6 +59,8 @@ type chatPage struct {
5759 showDetails bool // Show details in the header
5860 header header.Header
5961 compactSidebar layout.Container
62+
63+ cancelPending bool // True if ESC was pressed once and waiting for second press
6064}
6165
6266func (p *chatPage) Init() tea.Cmd {
@@ -67,9 +71,19 @@ func (p *chatPage) Init() tea.Cmd {
6771 )
6872}
6973
74+// cancelTimerCmd creates a command that expires the cancel timer after 2 seconds
75+func (p *chatPage) cancelTimerCmd() tea.Cmd {
76+ return tea.Tick(2*time.Second, func(time.Time) tea.Msg {
77+ return CancelTimerExpiredMsg{}
78+ })
79+}
80+
7081func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7182 var cmds []tea.Cmd
7283 switch msg := msg.(type) {
84+ case CancelTimerExpiredMsg:
85+ p.cancelPending = false
86+ return p, nil
7387 case tea.WindowSizeMsg:
7488 h, cmd := p.header.Update(msg)
7589 cmds = append(cmds, cmd)
@@ -181,10 +195,16 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
181195 return p, tea.Batch(cmds...)
182196 case key.Matches(msg, p.keyMap.Cancel):
183197 if p.session.ID != "" {
184− // Cancel the current session's generation process
185− // This allows users to interrupt long-running operations
186− p.app.CoderAgent.Cancel(p.session.ID)
187− return p, nil
198+ if p.cancelPending {
199+ // Second ESC press - actually cancel the session
200+ p.cancelPending = false
201+ p.app.CoderAgent.Cancel(p.session.ID)
202+ return p, nil
203+ } else {
204+ // First ESC press - start the timer
205+ p.cancelPending = true
206+ return p, p.cancelTimerCmd()
207+ }
188208 }
189209 case key.Matches(msg, p.keyMap.Details):
190210 if p.session.ID == "" || !p.compactMode {
@@ -336,7 +356,14 @@ func (p *chatPage) Bindings() []key.Binding {
336356 p.keyMap.AddAttachment,
337357 }
338358 if p.app.CoderAgent.IsBusy() {
339− bindings = append([]key.Binding{p.keyMap.Cancel}, bindings...)
359+ cancelBinding := p.keyMap.Cancel
360+ if p.cancelPending {
361+ cancelBinding = key.NewBinding(
362+ key.WithKeys("esc"),
363+ key.WithHelp("esc", "press again to cancel"),
364+ )
365+ }
366+ bindings = append([]key.Binding{cancelBinding}, bindings...)
340367 }
341368
342369 if p.chatFocused {