File tree Expand file tree Collapse file tree 4 files changed +36
-2
lines changed Expand file tree Collapse file tree 4 files changed +36
-2
lines changed Original file line number Diff line number Diff line change @@ -303,13 +303,21 @@ impl App {
303
303
self . transcript_overlay = Some ( TranscriptApp :: new ( self . transcript_lines . clone ( ) ) ) ;
304
304
tui. frame_requester ( ) . schedule_frame ( ) ;
305
305
}
306
- // Esc primes/advances backtracking when composer is empty.
306
+ // Esc primes/advances backtracking only in normal (not working) mode
307
+ // with an empty composer. In any other state, forward Esc so the
308
+ // active UI (e.g. status indicator, modals, popups) handles it.
307
309
KeyEvent {
308
310
code : KeyCode :: Esc ,
309
311
kind : KeyEventKind :: Press | KeyEventKind :: Repeat ,
310
312
..
311
313
} => {
312
- self . handle_backtrack_esc_key ( tui) ;
314
+ if self . chat_widget . is_normal_backtrack_mode ( )
315
+ && self . chat_widget . composer_is_empty ( )
316
+ {
317
+ self . handle_backtrack_esc_key ( tui) ;
318
+ } else {
319
+ self . chat_widget . handle_key_event ( key_event) ;
320
+ }
313
321
}
314
322
// Enter confirms backtrack when primed + count > 0. Otherwise pass to widget.
315
323
KeyEvent {
Original file line number Diff line number Diff line change @@ -307,6 +307,11 @@ impl ChatComposer {
307
307
result
308
308
}
309
309
310
+ /// Return true if either the slash-command popup or the file-search popup is active.
311
+ pub ( crate ) fn popup_active ( & self ) -> bool {
312
+ !matches ! ( self . active_popup, ActivePopup :: None )
313
+ }
314
+
310
315
/// Handle key event when the slash-command popup is visible.
311
316
fn handle_key_event_with_slash_popup ( & mut self , key_event : KeyEvent ) -> ( InputResult , bool ) {
312
317
let ActivePopup :: Command ( popup) = & mut self . active_popup else {
@@ -327,6 +332,13 @@ impl ChatComposer {
327
332
popup. move_down ( ) ;
328
333
( InputResult :: None , true )
329
334
}
335
+ KeyEvent {
336
+ code : KeyCode :: Esc , ..
337
+ } => {
338
+ // Dismiss the slash popup; keep the current input untouched.
339
+ self . active_popup = ActivePopup :: None ;
340
+ ( InputResult :: None , true )
341
+ }
330
342
KeyEvent {
331
343
code : KeyCode :: Tab , ..
332
344
} => {
Original file line number Diff line number Diff line change @@ -337,6 +337,13 @@ impl BottomPane {
337
337
self . is_task_running
338
338
}
339
339
340
+ /// Return true when the pane is in the regular composer state without any
341
+ /// overlays or popups and not running a task. This is the safe context to
342
+ /// use Esc-Esc for backtracking from the main view.
343
+ pub ( crate ) fn is_normal_backtrack_mode ( & self ) -> bool {
344
+ !self . is_task_running && self . active_view . is_none ( ) && !self . composer . popup_active ( )
345
+ }
346
+
340
347
/// Update the *context-window remaining* indicator in the composer. This
341
348
/// is forwarded directly to the underlying `ChatComposer`.
342
349
pub ( crate ) fn set_token_usage (
Original file line number Diff line number Diff line change @@ -1125,6 +1125,13 @@ impl ChatWidget {
1125
1125
self . bottom_pane . composer_is_empty ( )
1126
1126
}
1127
1127
1128
+ /// True when the UI is in the regular composer state with no running task,
1129
+ /// no modal overlay (e.g. approvals or status indicator), and no composer popups.
1130
+ /// In this state Esc-Esc backtracking is enabled.
1131
+ pub ( crate ) fn is_normal_backtrack_mode ( & self ) -> bool {
1132
+ self . bottom_pane . is_normal_backtrack_mode ( )
1133
+ }
1134
+
1128
1135
pub ( crate ) fn insert_str ( & mut self , text : & str ) {
1129
1136
self . bottom_pane . insert_str ( text) ;
1130
1137
}
You can’t perform that action at this time.
0 commit comments