agent_ui: Queue prompts by default when sending them while generating (#46797)
This PR makes queueing a prompt the default behavior when sending a new one while there's an on-going generation. You can still send a prompt that will immediately interrupt the agent with the `cmd-shift-enter` keybinding, though, which preserves the current behavior. The main motivation for this change is to make the queueing not only more discoverable, but more useful as well, as we're parting from the assumption that most of the time, what you want is to queue it as opposed to interrupting it (even though it's still possible to do either through the keybinding I mentioned above or simply by stopping the generation and sending a new one). Here's a quick video: https://github.com/user-attachments/assets/37f92433-70ef-459f-98ff-41ed80e3e43f In the video, I show sending one prompt and then sending two others that fall straight into the queue. Then, in the middle of the generation of my first prompt, I use the `cmd-shift-enter` keybinding to send a prompt immediately, interrupting the agent, effectively being sent in front of the queue. Release Notes: - Agent: Made queueing prompts the default behavior when sending them while there's an on-going generation.
This commit is contained in:
1 parent
267cb754cf
commit
8e786bcd87
6 files changed
+46
-37
No files matched your search
@@ -308,7 +308,7 @@
|
||||
"shift-alt-y": "agent::KeepAll",
|
||||
"shift-alt-z": "agent::RejectAll",
|
||||
"ctrl-enter": "agent::ChatWithFollow",
|
||||
"ctrl-shift-enter": "agent::QueueMessage",
|
||||
"ctrl-shift-enter": "agent::SendImmediately",
|
||||
"ctrl-shift-alt-enter": "agent::SendNextQueuedMessage",
|
||||
"ctrl-shift-backspace": "agent::ClearMessageQueue",
|
||||
"ctrl-shift-v": "agent::PasteRaw",
|
||||
|
||||
@@ -356,7 +356,7 @@
|
||||
"shift-alt-y": "agent::KeepAll",
|
||||
"shift-alt-z": "agent::RejectAll",
|
||||
"cmd-enter": "agent::ChatWithFollow",
|
||||
"cmd-shift-enter": "agent::QueueMessage",
|
||||
"cmd-shift-enter": "agent::SendImmediately",
|
||||
"cmd-shift-alt-enter": "agent::SendNextQueuedMessage",
|
||||
"cmd-shift-backspace": "agent::ClearMessageQueue",
|
||||
"cmd-shift-v": "agent::PasteRaw",
|
||||
|
||||
@@ -310,7 +310,7 @@
|
||||
"shift-alt-y": "agent::KeepAll",
|
||||
"shift-alt-z": "agent::RejectAll",
|
||||
"ctrl-enter": "agent::ChatWithFollow",
|
||||
"ctrl-shift-enter": "agent::QueueMessage",
|
||||
"ctrl-shift-enter": "agent::SendImmediately",
|
||||
"ctrl-shift-alt-enter": "agent::SendNextQueuedMessage",
|
||||
"ctrl-shift-backspace": "agent::ClearMessageQueue",
|
||||
"ctrl-shift-v": "agent::PasteRaw",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::QueueMessage;
|
||||
use crate::SendImmediately;
|
||||
use crate::acp::AcpThreadHistory;
|
||||
use crate::{
|
||||
ChatWithFollow,
|
||||
@@ -53,7 +53,7 @@ pub struct MessageEditor {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum MessageEditorEvent {
|
||||
Send,
|
||||
Queue,
|
||||
SendImmediately,
|
||||
Cancel,
|
||||
Focus,
|
||||
LostFocus,
|
||||
@@ -508,18 +508,6 @@ impl MessageEditor {
|
||||
cx.emit(MessageEditorEvent::Send)
|
||||
}
|
||||
|
||||
pub fn queue(&mut self, cx: &mut Context<Self>) {
|
||||
if self.is_empty(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.clear_inlay_hints(cx);
|
||||
});
|
||||
|
||||
cx.emit(MessageEditorEvent::Queue)
|
||||
}
|
||||
|
||||
pub fn trigger_completion_menu(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let editor = self.editor.clone();
|
||||
|
||||
@@ -563,8 +551,16 @@ impl MessageEditor {
|
||||
self.send(cx);
|
||||
}
|
||||
|
||||
fn queue_message(&mut self, _: &QueueMessage, _: &mut Window, cx: &mut Context<Self>) {
|
||||
self.queue(cx);
|
||||
fn send_immediately(&mut self, _: &SendImmediately, _: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.is_empty(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.clear_inlay_hints(cx);
|
||||
});
|
||||
|
||||
cx.emit(MessageEditorEvent::SendImmediately)
|
||||
}
|
||||
|
||||
fn chat_with_follow(
|
||||
@@ -1009,7 +1005,7 @@ impl Render for MessageEditor {
|
||||
div()
|
||||
.key_context("MessageEditor")
|
||||
.on_action(cx.listener(Self::chat))
|
||||
.on_action(cx.listener(Self::queue_message))
|
||||
.on_action(cx.listener(Self::send_immediately))
|
||||
.on_action(cx.listener(Self::chat_with_follow))
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
.on_action(cx.listener(Self::paste_raw))
|
||||
|
||||
@@ -73,7 +73,7 @@ use crate::ui::{AgentNotification, AgentNotificationEvent, BurnModeTooltip, Usag
|
||||
use crate::{
|
||||
AgentDiffPane, AgentPanel, AllowAlways, AllowOnce, ClearMessageQueue, ContinueThread,
|
||||
ContinueWithBurnMode, CycleFavoriteModels, CycleModeSelector, ExpandMessageEditor, Follow,
|
||||
KeepAll, NewThread, OpenAgentDiff, OpenHistory, QueueMessage, RejectAll, RejectOnce,
|
||||
KeepAll, NewThread, OpenAgentDiff, OpenHistory, RejectAll, RejectOnce, SendImmediately,
|
||||
SendNextQueuedMessage, ToggleBurnMode, ToggleProfileSelector,
|
||||
};
|
||||
|
||||
@@ -1204,7 +1204,7 @@ impl AcpThreadView {
|
||||
) {
|
||||
match event {
|
||||
MessageEditorEvent::Send => self.send(window, cx),
|
||||
MessageEditorEvent::Queue => self.queue_message(window, cx),
|
||||
MessageEditorEvent::SendImmediately => self.interrupt_and_send(window, cx),
|
||||
MessageEditorEvent::Cancel => self.cancel_generation(cx),
|
||||
MessageEditorEvent::Focus => {
|
||||
self.cancel_editing(&Default::default(), window, cx);
|
||||
@@ -1256,7 +1256,7 @@ impl AcpThreadView {
|
||||
}
|
||||
}
|
||||
}
|
||||
ViewEvent::MessageEditorEvent(_editor, MessageEditorEvent::Queue) => {}
|
||||
ViewEvent::MessageEditorEvent(_editor, MessageEditorEvent::SendImmediately) => {}
|
||||
ViewEvent::MessageEditorEvent(editor, MessageEditorEvent::Send) => {
|
||||
self.regenerate(event.entry_index, editor.clone(), window, cx);
|
||||
}
|
||||
@@ -1300,7 +1300,7 @@ impl AcpThreadView {
|
||||
}
|
||||
|
||||
if thread.read(cx).status() != ThreadStatus::Idle {
|
||||
self.stop_current_and_send_new_message(window, cx);
|
||||
self.queue_message(window, cx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1344,6 +1344,23 @@ impl AcpThreadView {
|
||||
self.send_impl(self.message_editor.clone(), window, cx)
|
||||
}
|
||||
|
||||
fn interrupt_and_send(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(thread) = self.thread() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if self.is_loading_contents {
|
||||
return;
|
||||
}
|
||||
|
||||
if thread.read(cx).status() == ThreadStatus::Idle {
|
||||
self.send_impl(self.message_editor.clone(), window, cx);
|
||||
return;
|
||||
}
|
||||
|
||||
self.stop_current_and_send_new_message(window, cx);
|
||||
}
|
||||
|
||||
fn stop_current_and_send_new_message(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(thread) = self.thread().cloned() else {
|
||||
return;
|
||||
@@ -5617,13 +5634,7 @@ impl AcpThreadView {
|
||||
.tooltip(move |_window, cx| {
|
||||
if is_editor_empty && !is_generating {
|
||||
Tooltip::for_action("Type to Send", &Chat, cx)
|
||||
} else {
|
||||
let title = if is_generating {
|
||||
"Stop and Send Message"
|
||||
} else {
|
||||
"Send"
|
||||
};
|
||||
|
||||
} else if is_generating {
|
||||
let focus_handle = focus_handle.clone();
|
||||
|
||||
Tooltip::element(move |_window, cx| {
|
||||
@@ -5633,7 +5644,7 @@ impl AcpThreadView {
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.justify_between()
|
||||
.child(Label::new(title))
|
||||
.child(Label::new("Queue and Send"))
|
||||
.child(KeyBinding::for_action_in(&Chat, &focus_handle, cx)),
|
||||
)
|
||||
.child(
|
||||
@@ -5643,15 +5654,17 @@ impl AcpThreadView {
|
||||
.justify_between()
|
||||
.border_t_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.child(Label::new("Queue Message"))
|
||||
.child(Label::new("Send Immediately"))
|
||||
.child(KeyBinding::for_action_in(
|
||||
&QueueMessage,
|
||||
&SendImmediately,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
.into_any_element()
|
||||
})(_window, cx)
|
||||
} else {
|
||||
Tooltip::for_action("Send Message", &Chat, cx)
|
||||
}
|
||||
})
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
@@ -8846,7 +8859,7 @@ pub(crate) mod tests {
|
||||
editor.set_text("Message 2", window, cx);
|
||||
});
|
||||
thread_view.update_in(cx, |thread_view, window, cx| {
|
||||
thread_view.send(window, cx);
|
||||
thread_view.interrupt_and_send(window, cx);
|
||||
});
|
||||
|
||||
cx.update(|_, cx| {
|
||||
|
||||
@@ -123,8 +123,8 @@ actions!(
|
||||
ContinueWithBurnMode,
|
||||
/// Toggles burn mode for faster responses.
|
||||
ToggleBurnMode,
|
||||
/// Queues a message to be sent when generation completes.
|
||||
QueueMessage,
|
||||
/// Interrupts the current generation and sends the message immediately.
|
||||
SendImmediately,
|
||||
/// Sends the next queued message immediately.
|
||||
SendNextQueuedMessage,
|
||||
/// Clears all messages from the queue.
|
||||
|
||||
Reference in new issue
Block a user