diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index b447617c340dedb6795c0c950f08eee8f0c6d59e..386eeaca1924ace86e4138fdfc283bfe0c20fae0 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -6,7 +6,6 @@ mod tool_picker; use std::{ops::Range, sync::Arc}; -use agent_settings::AgentSettings; use anyhow::Result; use assistant_tool::{ToolSource, ToolWorkingSet}; use cloud_llm_client::{Plan, PlanV1, PlanV2}; @@ -29,10 +28,10 @@ use project::{ agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME}, context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore}, }; -use settings::{Settings, SettingsStore, update_settings_file}; +use settings::{SettingsStore, update_settings_file}; use ui::{ Chip, CommonAnimationExt, ContextMenu, Disclosure, Divider, DividerColor, ElevationIndex, - Indicator, PopoverMenu, Switch, SwitchColor, SwitchField, Tooltip, WithScrollbar, prelude::*, + Indicator, PopoverMenu, Switch, SwitchColor, Tooltip, WithScrollbar, prelude::*, }; use util::ResultExt as _; use workspace::{Workspace, create_and_open_local_file}; @@ -402,101 +401,6 @@ impl AgentConfiguration { ) } - fn render_command_permission(&mut self, cx: &mut Context) -> impl IntoElement { - let always_allow_tool_actions = AgentSettings::get_global(cx).always_allow_tool_actions; - let fs = self.fs.clone(); - - SwitchField::new( - "always-allow-tool-actions-switch", - Some("Allow running commands without asking for confirmation"), - Some( - "The agent can perform potentially destructive actions without asking for your confirmation.".into(), - ), - always_allow_tool_actions, - move |state, _window, cx| { - let allow = state == &ToggleState::Selected; - update_settings_file(fs.clone(), cx, move |settings, _| { - settings.agent.get_or_insert_default().set_always_allow_tool_actions(allow); - }); - }, - ) - } - - fn render_single_file_review(&mut self, cx: &mut Context) -> impl IntoElement { - let single_file_review = AgentSettings::get_global(cx).single_file_review; - let fs = self.fs.clone(); - - SwitchField::new( - "single-file-review", - Some("Enable single-file agent reviews"), - Some("Agent edits are also displayed in single-file editors for review.".into()), - single_file_review, - move |state, _window, cx| { - let allow = state == &ToggleState::Selected; - update_settings_file(fs.clone(), cx, move |settings, _| { - settings - .agent - .get_or_insert_default() - .set_single_file_review(allow); - }); - }, - ) - } - - fn render_sound_notification(&mut self, cx: &mut Context) -> impl IntoElement { - let play_sound_when_agent_done = AgentSettings::get_global(cx).play_sound_when_agent_done; - let fs = self.fs.clone(); - - SwitchField::new( - "sound-notification", - Some("Play sound when finished generating"), - Some( - "Hear a notification sound when the agent is done generating changes or needs your input.".into(), - ), - play_sound_when_agent_done, - move |state, _window, cx| { - let allow = state == &ToggleState::Selected; - update_settings_file(fs.clone(), cx, move |settings, _| { - settings.agent.get_or_insert_default().set_play_sound_when_agent_done(allow); - }); - }, - ) - } - - fn render_modifier_to_send(&mut self, cx: &mut Context) -> impl IntoElement { - let use_modifier_to_send = AgentSettings::get_global(cx).use_modifier_to_send; - let fs = self.fs.clone(); - - SwitchField::new( - "modifier-send", - Some("Use modifier to submit a message"), - Some( - "Make a modifier (cmd-enter on macOS, ctrl-enter on Linux or Windows) required to send messages.".into(), - ), - use_modifier_to_send, - move |state, _window, cx| { - let allow = state == &ToggleState::Selected; - update_settings_file(fs.clone(), cx, move |settings, _| { - settings.agent.get_or_insert_default().set_use_modifier_to_send(allow); - }); - }, - ) - } - - fn render_general_settings_section(&mut self, cx: &mut Context) -> impl IntoElement { - v_flex() - .p(DynamicSpacing::Base16.rems(cx)) - .pr(DynamicSpacing::Base20.rems(cx)) - .gap_2p5() - .border_b_1() - .border_color(cx.theme().colors().border) - .child(Headline::new("General Settings")) - .child(self.render_command_permission(cx)) - .child(self.render_single_file_review(cx)) - .child(self.render_sound_notification(cx)) - .child(self.render_modifier_to_send(cx)) - } - fn render_zed_plan_info(&self, plan: Option, cx: &mut Context) -> impl IntoElement { if let Some(plan) = plan { let free_chip_bg = cx @@ -1141,7 +1045,6 @@ impl Render for AgentConfiguration { .track_scroll(&self.scroll_handle) .size_full() .overflow_y_scroll() - .child(self.render_general_settings_section(cx)) .child(self.render_agent_servers_section(cx)) .child(self.render_context_servers_section(window, cx)) .child(self.render_provider_configuration_section(cx)), diff --git a/crates/settings/src/settings_content/agent.rs b/crates/settings/src/settings_content/agent.rs index 9644cbb3bd455f42052d0c4c45d958d9a492d712..80326c84688697ab4dcbc83d9013cf3acab26d2b 100644 --- a/crates/settings/src/settings_content/agent.rs +++ b/crates/settings/src/settings_content/agent.rs @@ -194,7 +194,19 @@ pub enum DefaultAgentView { TextThread, } -#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)] +#[derive( + Copy, + Clone, + Default, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + strum::VariantArray, + strum::VariantNames, +)] #[serde(rename_all = "snake_case")] pub enum NotifyWhenAgentWaiting { #[default] diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index 59eacdc64301ddfab39d13040934d696bbcf7e58..912f5e18b8e7c9b44e0c22bbc087639f4d7a4361 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -3122,6 +3122,82 @@ pub(crate) fn settings_data() -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Agent Panel"), + SettingsPageItem::SettingItem(SettingItem { + title: "Agent Panel Button", + description: "Whether to show the agent panel button in the status bar", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.button + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.agent.get_or_insert_default().button + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Agent Panel Dock", + description: "Where to dock the agent panel.", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.dock + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.agent.get_or_insert_default().dock + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Agent Panel Default Width", + description: "Default width when the agent panel is docked to the left or right", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.default_width + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content.agent.get_or_insert_default().default_width + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Agent Panel Default Height", + description: "Default height when the agent panel is docked to the bottom", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.default_height + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .default_height + }, + }), + metadata: None, + files: USER, + }), ], }, SettingsPage { @@ -4357,6 +4433,196 @@ pub(crate) fn settings_data() -> Vec { metadata: None, files: USER, }), + SettingsPageItem::SectionHeader("Agent Configuration"), + SettingsPageItem::SettingItem(SettingItem { + title: "Always Allow Tool Actions", + description: "When enabled, the agent can run potentially destructive actions without asking for your confirmation. This setting has no effect on external agents.", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.always_allow_tool_actions + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .always_allow_tool_actions + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Single File Review", + description: "When enabled, agent edits will also be displayed in single-file buffers for review", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.single_file_review + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .single_file_review + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Enable Feedback", + description: "Show voting thumbs up/down icon buttons for feedback on agent edits", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.enable_feedback + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .enable_feedback + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Notify When Agent Waiting", + description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.notify_when_agent_waiting + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .notify_when_agent_waiting + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Play Sound When Agent Done", + description: "Whether to play a sound when the agent has either completed its response, or needs user input", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.play_sound_when_agent_done + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .play_sound_when_agent_done + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Expand Edit Card", + description: "Whether to have edit cards in the agent panel expanded, showing a preview of the diff", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.expand_edit_card + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .expand_edit_card + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Expand Terminal Card", + description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.expand_terminal_card + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .expand_terminal_card + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Use Modifier To Send", + description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.use_modifier_to_send + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .use_modifier_to_send + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Message Editor Min Lines", + description: "Minimum number of lines to display in the agent message editor", + field: Box::new(SettingField { + pick: |settings_content| { + if let Some(agent) = &settings_content.agent { + &agent.message_editor_min_lines + } else { + &None + } + }, + pick_mut: |settings_content| { + &mut settings_content + .agent + .get_or_insert_default() + .message_editor_min_lines + }, + }), + metadata: None, + files: USER, + }), ], }, SettingsPage { diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 7282bdbf593e6d074d2f39b849fbfa525df37596..431b88f812f4114b0b20d1a2b333366ff792d7b8 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -376,7 +376,8 @@ fn init_renderers(cx: &mut App) { .add_basic_renderer::(render_dropdown) .add_basic_renderer::(render_dropdown) .add_basic_renderer::(render_dropdown) - .add_basic_renderer::(render_dropdown); + .add_basic_renderer::(render_dropdown) + .add_basic_renderer::(render_dropdown); // .add_renderer::(|settings_field, file, _, window, cx| { // render_dropdown(*settings_field, file, window, cx) // });