agent: Add sound notification when done generating (#31472)
This PR adds the ability to hear a sound notification when the agent is done generating and/or needs user input. This setting is turned off by default and can be used together with the visual notification. The specific sound I'm using here comes from the [Material Design 2 Sound Library](https://m2.material.io/design/sound/sound-resources.html#). Release Notes: - agent: Added the ability to have a sound notification when the agent is done generating and/or needs user input.
This commit is contained in:
parent
fe0bcd14d2
commit
d211f88d23
9 changed files with 93 additions and 5 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -60,6 +60,7 @@ dependencies = [
|
|||
"assistant_slash_commands",
|
||||
"assistant_tool",
|
||||
"async-watch",
|
||||
"audio",
|
||||
"buffer_diff",
|
||||
"chrono",
|
||||
"client",
|
||||
|
|
|
@ -822,7 +822,12 @@
|
|||
// "primary_screen" - Show the notification only on your primary screen (default)
|
||||
// "all_screens" - Show these notifications on all screens
|
||||
// "never" - Never show these notifications
|
||||
"notify_when_agent_waiting": "primary_screen"
|
||||
"notify_when_agent_waiting": "primary_screen",
|
||||
// Whether to play a sound when the agent has either completed
|
||||
// its response, or needs user input.
|
||||
|
||||
// Default: false
|
||||
"play_sound_when_agent_done": false
|
||||
},
|
||||
// The settings for slash commands.
|
||||
"slash_commands": {
|
||||
|
|
BIN
assets/sounds/agent_done.wav
Executable file
BIN
assets/sounds/agent_done.wav
Executable file
Binary file not shown.
|
@ -26,6 +26,7 @@ assistant_slash_command.workspace = true
|
|||
assistant_slash_commands.workspace = true
|
||||
assistant_tool.workspace = true
|
||||
async-watch.workspace = true
|
||||
audio.workspace = true
|
||||
buffer_diff.workspace = true
|
||||
chrono.workspace = true
|
||||
client.workspace = true
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::ui::{
|
|||
use anyhow::Context as _;
|
||||
use assistant_settings::{AssistantSettings, NotifyWhenAgentWaiting};
|
||||
use assistant_tool::ToolUseStatus;
|
||||
use audio::{Audio, Sound};
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::actions::{MoveUp, Paste};
|
||||
use editor::scroll::Autoscroll;
|
||||
|
@ -996,9 +997,10 @@ impl ActiveThread {
|
|||
}
|
||||
ThreadEvent::Stopped(reason) => match reason {
|
||||
Ok(StopReason::EndTurn | StopReason::MaxTokens) => {
|
||||
let thread = self.thread.read(cx);
|
||||
let used_tools = self.thread.read(cx).used_tools_since_last_user_message();
|
||||
self.play_notification_sound(cx);
|
||||
self.show_notification(
|
||||
if thread.used_tools_since_last_user_message() {
|
||||
if used_tools {
|
||||
"Finished running tools"
|
||||
} else {
|
||||
"New message"
|
||||
|
@ -1011,6 +1013,7 @@ impl ActiveThread {
|
|||
_ => {}
|
||||
},
|
||||
ThreadEvent::ToolConfirmationNeeded => {
|
||||
self.play_notification_sound(cx);
|
||||
self.show_notification("Waiting for tool confirmation", IconName::Info, window, cx);
|
||||
}
|
||||
ThreadEvent::StreamedAssistantText(message_id, text) => {
|
||||
|
@ -1147,6 +1150,13 @@ impl ActiveThread {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
fn play_notification_sound(&self, cx: &mut App) {
|
||||
let settings = AssistantSettings::get_global(cx);
|
||||
if settings.play_sound_when_agent_done {
|
||||
Audio::play_sound(Sound::AgentDone, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn show_notification(
|
||||
&mut self,
|
||||
caption: impl Into<SharedString>,
|
||||
|
|
|
@ -327,6 +327,45 @@ impl AgentConfiguration {
|
|||
)
|
||||
}
|
||||
|
||||
fn render_sound_notification(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let play_sound_when_agent_done =
|
||||
AssistantSettings::get_global(cx).play_sound_when_agent_done;
|
||||
|
||||
h_flex()
|
||||
.gap_4()
|
||||
.justify_between()
|
||||
.flex_wrap()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_0p5()
|
||||
.max_w_5_6()
|
||||
.child(Label::new("Play sound when finished generating"))
|
||||
.child(
|
||||
Label::new(
|
||||
"Hear a notification sound when the agent is done generating changes or needs your input.",
|
||||
)
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Switch::new("play-sound-notification-switch", play_sound_when_agent_done.into())
|
||||
.color(SwitchColor::Accent)
|
||||
.on_click({
|
||||
let fs = self.fs.clone();
|
||||
move |state, _window, cx| {
|
||||
let allow = state == &ToggleState::Selected;
|
||||
update_settings_file::<AssistantSettings>(
|
||||
fs.clone(),
|
||||
cx,
|
||||
move |settings, _| {
|
||||
settings.set_play_sound_when_agent_done(allow);
|
||||
},
|
||||
);
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_general_settings_section(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.p(DynamicSpacing::Base16.rems(cx))
|
||||
|
@ -337,6 +376,7 @@ impl AgentConfiguration {
|
|||
.child(Headline::new("General Settings"))
|
||||
.child(self.render_command_permission(cx))
|
||||
.child(self.render_single_file_review(cx))
|
||||
.child(self.render_sound_notification(cx))
|
||||
}
|
||||
|
||||
fn render_context_servers_section(
|
||||
|
|
|
@ -105,6 +105,7 @@ pub struct AssistantSettings {
|
|||
pub profiles: IndexMap<AgentProfileId, AgentProfile>,
|
||||
pub always_allow_tool_actions: bool,
|
||||
pub notify_when_agent_waiting: NotifyWhenAgentWaiting,
|
||||
pub play_sound_when_agent_done: bool,
|
||||
pub stream_edits: bool,
|
||||
pub single_file_review: bool,
|
||||
pub model_parameters: Vec<LanguageModelParameters>,
|
||||
|
@ -285,6 +286,7 @@ impl AssistantSettingsContent {
|
|||
model_parameters: Vec::new(),
|
||||
preferred_completion_mode: None,
|
||||
enable_feedback: None,
|
||||
play_sound_when_agent_done: None,
|
||||
},
|
||||
VersionedAssistantSettingsContent::V2(ref settings) => settings.clone(),
|
||||
},
|
||||
|
@ -317,6 +319,7 @@ impl AssistantSettingsContent {
|
|||
model_parameters: Vec::new(),
|
||||
preferred_completion_mode: None,
|
||||
enable_feedback: None,
|
||||
play_sound_when_agent_done: None,
|
||||
},
|
||||
None => AssistantSettingsContentV2::default(),
|
||||
}
|
||||
|
@ -517,6 +520,14 @@ impl AssistantSettingsContent {
|
|||
.ok();
|
||||
}
|
||||
|
||||
pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
|
||||
self.v2_setting(|setting| {
|
||||
setting.play_sound_when_agent_done = Some(allow);
|
||||
Ok(())
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub fn set_single_file_review(&mut self, allow: bool) {
|
||||
self.v2_setting(|setting| {
|
||||
setting.single_file_review = Some(allow);
|
||||
|
@ -603,6 +614,7 @@ impl Default for VersionedAssistantSettingsContent {
|
|||
model_parameters: Vec::new(),
|
||||
preferred_completion_mode: None,
|
||||
enable_feedback: None,
|
||||
play_sound_when_agent_done: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -659,6 +671,10 @@ pub struct AssistantSettingsContentV2 {
|
|||
///
|
||||
/// Default: "primary_screen"
|
||||
notify_when_agent_waiting: Option<NotifyWhenAgentWaiting>,
|
||||
/// Whether to play a sound when the agent has either completed its response, or needs user input.
|
||||
///
|
||||
/// Default: false
|
||||
play_sound_when_agent_done: Option<bool>,
|
||||
/// Whether to stream edits from the agent as they are received.
|
||||
///
|
||||
/// Default: false
|
||||
|
@ -884,6 +900,10 @@ impl Settings for AssistantSettings {
|
|||
&mut settings.notify_when_agent_waiting,
|
||||
value.notify_when_agent_waiting,
|
||||
);
|
||||
merge(
|
||||
&mut settings.play_sound_when_agent_done,
|
||||
value.play_sound_when_agent_done,
|
||||
);
|
||||
merge(&mut settings.stream_edits, value.stream_edits);
|
||||
merge(&mut settings.single_file_review, value.single_file_review);
|
||||
merge(&mut settings.default_profile, value.default_profile);
|
||||
|
@ -1027,6 +1047,7 @@ mod tests {
|
|||
default_view: None,
|
||||
profiles: None,
|
||||
always_allow_tool_actions: None,
|
||||
play_sound_when_agent_done: None,
|
||||
notify_when_agent_waiting: None,
|
||||
stream_edits: None,
|
||||
single_file_review: None,
|
||||
|
|
|
@ -18,6 +18,7 @@ pub enum Sound {
|
|||
Unmute,
|
||||
StartScreenshare,
|
||||
StopScreenshare,
|
||||
AgentDone,
|
||||
}
|
||||
|
||||
impl Sound {
|
||||
|
@ -29,6 +30,7 @@ impl Sound {
|
|||
Self::Unmute => "unmute",
|
||||
Self::StartScreenshare => "start_screenshare",
|
||||
Self::StopScreenshare => "stop_screenshare",
|
||||
Self::AgentDone => "agent_done",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,9 +39,17 @@ To follow the agent reading through your codebase and performing edits, click on
|
|||
|
||||
### Get Notified {#get-notified}
|
||||
|
||||
If you send a prompt to the Agent and then move elsewhere, thus putting Zed in the background, a notification will pop up at the top right of your screen indicating that the Agent has completed its work.
|
||||
If you send a prompt to the Agent and then move elsewhere, thus putting Zed in the background, you can be notified of whether its response is finished either via:
|
||||
|
||||
You can customize the notification behavior, including the option to turn it off entirely, by using the `agent.notify_when_agent_waiting` settings key.
|
||||
- a visual notification that appears in the top right of your screen
|
||||
- or a sound notification
|
||||
|
||||
You can use both notification methods together or just pick one of them.
|
||||
|
||||
For the visual notification, you can customize its behavior, including the option to turn it off entirely, by using the `agent.notify_when_agent_waiting` settings key.
|
||||
For the sound notification, turn it on or off using the `agent.play_sound_when_agent_done` settings key.
|
||||
|
||||
#### Sound Notification
|
||||
|
||||
### Reviewing Changes {#reviewing-changes}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue