on main help sign in
chore: change cancel logic
b1cd3f1Kujtim Hoxha
internal/tui/page/chat/chat.go+32−5
@@ -3,6 +3,7 @@ package chat
3import (
3import (
4 "context"
4 "context"
5 "strings"
5 "strings"
6+ "time"
6
7
7 "github.com/charmbracelet/bubbles/v2/key"
8 "github.com/charmbracelet/bubbles/v2/key"
8 tea "github.com/charmbracelet/bubbletea/v2"
9 tea "github.com/charmbracelet/bubbletea/v2"
@@ -33,6 +34,7 @@ type (
33 ChatFocusedMsg struct {
34 ChatFocusedMsg struct {
34 Focused bool // True if the chat input is focused, false otherwise
35 Focused bool // True if the chat input is focused, false otherwise
35 }
36 }
37+ CancelTimerExpiredMsg struct{}
36)
38)
37
39
38type ChatPage interface {
40type ChatPage interface {
@@ -57,6 +59,8 @@ type chatPage struct {
57 showDetails bool // Show details in the header
59 showDetails bool // Show details in the header
58 header header.Header
60 header header.Header
59 compactSidebar layout.Container
61 compactSidebar layout.Container
62+
63+ cancelPending bool // True if ESC was pressed once and waiting for second press
60}
64}
61
65
62func (p *chatPage) Init() tea.Cmd {
66func (p *chatPage) Init() tea.Cmd {
@@ -67,9 +71,19 @@ func (p *chatPage) Init() tea.Cmd {
67 )
71 )
68}
72}
69
73
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+
70func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
81func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
71 var cmds []tea.Cmd
82 var cmds []tea.Cmd
72 switch msg := msg.(type) {
83 switch msg := msg.(type) {
84+ case CancelTimerExpiredMsg:
85+ p.cancelPending = false
86+ return p, nil
73 case tea.WindowSizeMsg:
87 case tea.WindowSizeMsg:
74 h, cmd := p.header.Update(msg)
88 h, cmd := p.header.Update(msg)
75 cmds = append(cmds, cmd)
89 cmds = append(cmds, cmd)
@@ -181,10 +195,16 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
181 return p, tea.Batch(cmds...)
195 return p, tea.Batch(cmds...)
182 case key.Matches(msg, p.keyMap.Cancel):
196 case key.Matches(msg, p.keyMap.Cancel):
183 if p.session.ID != "" {
197 if p.session.ID != "" {
184− // Cancel the current session's generation process
198+ if p.cancelPending {
185− // This allows users to interrupt long-running operations
199+ // Second ESC press - actually cancel the session
200+ p.cancelPending = false
186− p.app.CoderAgent.Cancel(p.session.ID)
201+ p.app.CoderAgent.Cancel(p.session.ID)
187− return p, nil
202+ return p, nil
203+ } else {
204+ // First ESC press - start the timer
205+ p.cancelPending = true
206+ return p, p.cancelTimerCmd()
207+ }
188 }
208 }
189 case key.Matches(msg, p.keyMap.Details):
209 case key.Matches(msg, p.keyMap.Details):
190 if p.session.ID == "" || !p.compactMode {
210 if p.session.ID == "" || !p.compactMode {
@@ -336,7 +356,14 @@ func (p *chatPage) Bindings() []key.Binding {
336 p.keyMap.AddAttachment,
356 p.keyMap.AddAttachment,
337 }
357 }
338 if p.app.CoderAgent.IsBusy() {
358 if p.app.CoderAgent.IsBusy() {
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+ }
339− bindings = append([]key.Binding{p.keyMap.Cancel}, bindings...)
366+ bindings = append([]key.Binding{cancelBinding}, bindings...)
340 }
367 }
341
368
342 if p.chatFocused {
369 if p.chatFocused {