Restrict queued-message editing to its dedicated binding (#39064)

## What changed

- Stop treating Vim normal-mode history-up as an alias for editing the latest queued message.
- Continue to edit queued messages through the configured `chat.edit_queued_message` binding.

GitOrigin-RevId: d830e0976a38725e948d420c1a5f374bb13fe945
This commit is contained in:
Benjamin Carlsson authored and copyberry committed 2026-08-17 20:02:50 +00:00
1 parent b6e153c985
commit 1aa3a68e7b
4 files changed
+2 -108

No files matched your search

@@ -44,11 +44,6 @@
//! - Persistent cross-session history (text-only; no element ranges or attachments). //! - Persistent cross-session history (text-only; no element ranges or attachments).
//! - Local in-session history (full text + text elements + local/remote image attachments). //! - Local in-session history (full text + text elements + local/remote image attachments).
//! //!
//! Before composer history handles Vim-normal history-up, `ChatWidget` restores the latest queued
//! follow-up when the composer is empty and no popup is active. It removes the message from the
//! queue before placing it in the composer, so editing and requeuing it replaces the prior
//! revision instead of creating duplicates.
//!
//! When recalling a local entry, the composer rehydrates text elements and both attachment kinds //! When recalling a local entry, the composer rehydrates text elements and both attachment kinds
//! (local image paths + remote image URLs). //! (local image paths + remote image URLs).
//! When recalling a persistent entry, only the text is restored. //! When recalling a persistent entry, only the text is restored.
+1 -5
View File
@@ -1222,12 +1222,8 @@ impl BottomPane {
popup_consts::standard_popup_hint_line_for_keymap(&self.keymap.list) popup_consts::standard_popup_hint_line_for_keymap(&self.keymap.list)
} }
pub(crate) fn runtime_keymap(&self) -> &RuntimeKeymap {
&self.keymap
}
pub(crate) fn list_keymap(&self) -> crate::keymap::ListKeymap { pub(crate) fn list_keymap(&self) -> crate::keymap::ListKeymap {
self.runtime_keymap().list.clone() self.keymap.list.clone()
} }
/// Replace one or more active views whose IDs are in `view_ids` with a /// Replace one or more active views whose IDs are in `view_ids` with a
+1 -12
View File
@@ -111,18 +111,7 @@ impl ChatWidget {
} }
if key_event.kind == KeyEventKind::Press if key_event.kind == KeyEventKind::Press
&& (self.chat_keymap.edit_queued_message.is_pressed(key_event) && self.chat_keymap.edit_queued_message.is_pressed(key_event)
|| (self.bottom_pane.composer_is_empty()
&& self
.bottom_pane
.keymap_contexts()
.contains(crate::keymap::KeymapContext::VimNormal)
&& self
.bottom_pane
.runtime_keymap()
.vim_normal
.move_up
.is_pressed(key_event)))
&& self.has_queued_follow_up_messages() && self.has_queued_follow_up_messages()
&& self.bottom_pane.no_modal_or_popup_active() && self.bottom_pane.no_modal_or_popup_active()
{ {
@@ -1676,92 +1676,6 @@ async fn alt_up_edits_most_recent_queued_message() {
); );
} }
#[tokio::test]
async fn vim_normal_history_up_edits_queued_message_without_duplicating_it() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.thread_id = Some(ThreadId::new());
chat.toggle_vim_mode_and_notify();
chat.bottom_pane.set_task_running(/*running*/ true);
chat.bottom_pane
.set_composer_text("first queued message".to_string(), Vec::new(), Vec::new());
chat.handle_key_event(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
for expected in ["first queued message", "first queued message edited"] {
chat.handle_key_event(KeyEvent::new(KeyCode::Char('k'), KeyModifiers::NONE));
assert_eq!(chat.bottom_pane.composer_text(), expected);
assert!(chat.input_queue.queued_user_messages.is_empty());
assert!(
chat.input_queue
.queued_user_message_history_records
.is_empty()
);
chat.bottom_pane
.set_composer_text(format!("{expected} edited"), Vec::new(), Vec::new());
chat.handle_key_event(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
assert_eq!(chat.input_queue.queued_user_messages.len(), 1);
assert_eq!(
chat.input_queue.queued_user_message_history_records.len(),
1
);
}
assert_eq!(
chat.queued_user_message_texts(),
vec!["first queued message edited edited".to_string()]
);
}
#[tokio::test]
async fn vim_normal_queued_message_edit_uses_remapped_history_up() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let mut keymap = crate::keymap::RuntimeKeymap::defaults();
keymap.vim_normal.move_up = vec![crate::key_hint::plain(KeyCode::F(2))];
chat.bottom_pane.set_keymap_bindings(&keymap);
chat.toggle_vim_mode_and_notify();
chat.bottom_pane.set_task_running(/*running*/ true);
chat.input_queue
.queued_user_messages
.push_back(UserMessage::from("queued message").into());
chat.input_queue
.queued_user_message_history_records
.push_back(UserMessageHistoryRecord::UserMessageText);
chat.refresh_pending_input_preview();
chat.handle_key_event(KeyEvent::new(KeyCode::Char('k'), KeyModifiers::NONE));
assert!(chat.bottom_pane.composer_is_empty());
assert_eq!(chat.input_queue.queued_user_messages.len(), 1);
chat.handle_key_event(KeyEvent::new(KeyCode::F(2), KeyModifiers::NONE));
assert_eq!(chat.bottom_pane.composer_text(), "queued message");
assert!(chat.input_queue.queued_user_messages.is_empty());
assert!(
chat.input_queue
.queued_user_message_history_records
.is_empty()
);
let width = 80;
let height = chat.desired_height(width);
let mut terminal = ratatui::Terminal::new(ratatui::backend::TestBackend::new(width, height))
.expect("create terminal");
terminal
.draw(|frame| chat.render(frame.area(), frame.buffer_mut()))
.expect("render restored queued message");
insta::assert_snapshot!(
normalized_backend_snapshot(terminal.backend())
.lines()
.filter(|line| line.contains("queued message"))
.map(|line| line.trim_matches('"').trim())
.collect::<Vec<_>>()
.join("\n"),
@"› queued message"
);
}
#[tokio::test] #[tokio::test]
async fn unbound_queued_message_edit_does_not_fall_back_to_alt_up() { async fn unbound_queued_message_edit_does_not_fall_back_to_alt_up() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;