fix(tui): honor remapped submit keys

Remove the legacy Enter/Tab submission fallback so composer submit and
queue actions are dispatched only through the resolved keymap bindings.

Add regression coverage for remapped submit and queue keys to ensure
unconfigured Enter/Tab presses no longer bypass `tui.keymap`.
This commit is contained in:
Felipe Coury
2026-04-19 18:32:50 -03:00
parent 76a03c489a
commit 7b5b3cd2e0

View File

@@ -2792,7 +2792,9 @@ impl ChatComposer {
} else {
self.footer_mode = reset_mode_after_activity(self.footer_mode);
}
if self.queue_keys.is_pressed(key_event) && !self.is_bang_shell_command() {
if self.queue_keys.is_pressed(key_event)
&& (self.is_task_running || !self.is_bang_shell_command())
{
return self.handle_submission(self.is_task_running);
}
@@ -2840,19 +2842,6 @@ impl ChatComposer {
}
self.handle_input_basic(key_event)
}
KeyEvent {
code: KeyCode::Tab,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
..
} if self.is_task_running || !self.is_bang_shell_command() => {
self.handle_submission(self.is_task_running)
}
KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
..
} => self.handle_submission(/*should_queue*/ false),
input => self.handle_input_basic(input),
}
}
@@ -6785,6 +6774,68 @@ mod tests {
assert_queued_slash("/does-not-exist");
}
#[test]
fn remapped_submit_does_not_fall_back_to_enter() {
use crate::key_hint;
use crate::keymap::RuntimeKeymap;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;
let (tx, _rx) = unbounded_channel::<AppEvent>();
let sender = AppEventSender::new(tx);
let mut composer = ChatComposer::new(
/*has_input_focus*/ true,
sender,
/*enhanced_keys_supported*/ false,
"Ask Codex to do anything".to_string(),
/*disable_paste_burst*/ false,
);
composer
.textarea
.set_text_clearing_elements("explain the change");
composer.textarea.set_cursor(composer.textarea.text().len());
let mut keymap = RuntimeKeymap::defaults();
keymap.composer.submit = vec![key_hint::ctrl(KeyCode::Char('j'))];
composer.set_keymap_bindings(&keymap);
let (result, _needs_redraw) =
composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
assert_eq!(InputResult::None, result);
assert_eq!("explain the change\n", composer.textarea.text());
}
#[test]
fn remapped_queue_does_not_fall_back_to_tab() {
use crate::key_hint;
use crate::keymap::RuntimeKeymap;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;
let (tx, _rx) = unbounded_channel::<AppEvent>();
let sender = AppEventSender::new(tx);
let mut composer = ChatComposer::new(
/*has_input_focus*/ true,
sender,
/*enhanced_keys_supported*/ false,
"Ask Codex to do anything".to_string(),
/*disable_paste_burst*/ false,
);
composer.set_task_running(/*running*/ true);
composer.textarea.set_text_clearing_elements("queue me");
let mut keymap = RuntimeKeymap::defaults();
keymap.composer.queue = vec![key_hint::ctrl(KeyCode::Char('q'))];
composer.set_keymap_bindings(&keymap);
let (result, _needs_redraw) =
composer.handle_key_event(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
assert_eq!(InputResult::None, result);
assert_eq!("queue me", composer.textarea.text());
}
#[test]
fn tab_queues_leading_space_slash_as_plain_text_while_task_running() {
use crossterm::event::KeyCode;