feat: ctrl-d only exits when there is no user input (#1589)

While this does make it so that `ctrl-d` will not exit Codex when the
composer is not empty, `ctrl-d` will still exit Codex if it is in the
"working" state.

Fixes https://github.com/openai/codex/issues/1443.
This commit is contained in:
Michael Bolin
2025-07-16 08:59:26 -07:00
committed by GitHub
parent f14b5adabf
commit 5b820c5ce7
5 changed files with 30 additions and 3 deletions

View File

@@ -199,7 +199,21 @@ impl<'a> App<'a> {
modifiers: crossterm::event::KeyModifiers::CONTROL,
..
} => {
self.app_event_tx.send(AppEvent::ExitRequest);
match &mut self.app_state {
AppState::Chat { widget } => {
if widget.composer_is_empty() {
self.app_event_tx.send(AppEvent::ExitRequest);
} else {
// Treat Ctrl+D as a normal key event when the composer
// is not empty so that it doesn't quit the application
// prematurely.
self.dispatch_key_event(key_event);
}
}
AppState::Login { .. } | AppState::GitWarning { .. } => {
self.app_event_tx.send(AppEvent::ExitRequest);
}
}
}
_ => {
self.dispatch_key_event(key_event);

View File

@@ -76,6 +76,11 @@ impl ChatComposer<'_> {
this
}
/// Returns true if the composer currently contains no user input.
pub(crate) fn is_empty(&self) -> bool {
self.textarea.is_empty()
}
/// Update the cached *context-left* percentage and refresh the placeholder
/// text. The UI relies on the placeholder to convey the remaining
/// context when the composer is empty.

View File

@@ -72,8 +72,7 @@ impl ChatComposerHistory {
return false;
}
let lines = textarea.lines();
if lines.len() == 1 && lines[0].is_empty() {
if textarea.is_empty() {
return true;
}
@@ -85,6 +84,7 @@ impl ChatComposerHistory {
return false;
}
let lines = textarea.lines();
matches!(&self.last_history_text, Some(prev) if prev == &lines.join("\n"))
}

View File

@@ -162,6 +162,10 @@ impl BottomPane<'_> {
}
}
pub(crate) fn composer_is_empty(&self) -> bool {
self.composer.is_empty()
}
pub(crate) fn is_task_running(&self) -> bool {
self.is_task_running
}

View File

@@ -432,6 +432,10 @@ impl ChatWidget<'_> {
}
}
pub(crate) fn composer_is_empty(&self) -> bool {
self.bottom_pane.composer_is_empty()
}
/// Forward an `Op` directly to codex.
pub(crate) fn submit_op(&self, op: Op) {
if let Err(e) = self.codex_op_tx.send(op) {