Files
codex/codex-rs/tui/src/chatwidget/side.rs
T
Eric TrautandGitHub d1235a0a78 Prevent Esc from dismissing or rewinding /side (#22710)
Addresses #22599

## Why
`/side` currently lets `Esc` return to the parent thread. Multiple users
reported that this collides with queued-steer UI that also advertises
`Esc`, so a timing-sensitive keypress can dismiss an ephemeral side chat
instead of sending the queued prompt.

After removing that dismissal shortcut, the same `Esc` path could fall
through to main-thread backtrack/edit-previous handling, which is not
valid for ephemeral side conversations. This keeps `/side` out of both
global `Esc` behaviors.

## What changed
- Remove `Esc` from the `/side` return shortcut matcher while keeping
the existing `Ctrl+C` and `Ctrl+D` behavior.
- Update side-conversation hints and blocked-command copy to advertise
`Ctrl+C` as the return shortcut.
- Rename the reserved `Esc` keymap label to describe backtracking only.
- Block backtrack/edit-previous handling while a side conversation is
active and report `Editing previous prompts is unavailable in side
conversations.` when that path would have fired.
- Keep composer-owned `Esc` behavior, such as Vim insert-mode escape,
routed locally.
- Refresh focused shortcut assertions and TUI snapshots for the updated
footer and new side-conversation error message.

## Verification
Manually tested `/side` use cases and `Esc`, `Ctrl+C`, `Ctrl+D`.
2026-05-14 20:51:08 -07:00

36 lines
1.2 KiB
Rust

//! Chat widget hooks for side-conversation mode.
//!
//! App-level side-thread lifecycle lives in `app::side`; this module owns the
//! chat-surface pieces that side mode toggles, such as the composer placeholder,
//! footer label, and inline `/side` message submission behavior.
use super::*;
impl ChatWidget {
pub(crate) fn submit_user_message_as_plain_user_turn(
&mut self,
user_message: UserMessage,
) -> Option<AppCommand> {
self.submit_user_message_with_shell_escape_policy(user_message, ShellEscapePolicy::Disallow)
}
pub(crate) fn set_side_conversation_active(&mut self, active: bool) {
self.active_side_conversation = active;
let placeholder = if active {
self.side_placeholder_text.clone()
} else {
self.normal_placeholder_text.clone()
};
self.bottom_pane.set_placeholder_text(placeholder);
self.bottom_pane.set_side_conversation_active(active);
}
pub(crate) fn side_conversation_active(&self) -> bool {
self.active_side_conversation
}
pub(crate) fn set_side_conversation_context_label(&mut self, label: Option<String>) {
self.bottom_pane.set_side_conversation_context_label(label);
}
}