Agent UI

Conrad Irwin created

Change summary

assets/settings/default.json                                |  10 
crates/agent_ui/src/agent_configuration.rs                  | 140 +++---
crates/agent_ui/src/agent_model_selector.rs                 |  15 
crates/agent_ui/src/agent_panel.rs                          |  18 
crates/agent_ui/src/agent_ui.rs                             |   4 
crates/agent_ui/src/context_server_configuration.rs         |   1 
crates/agent_ui/src/profile_selector.rs                     |  20 
crates/agent_ui/src/slash_command_settings.rs               |  33 +
crates/collab/src/tests/editor_tests.rs                     |   4 
crates/edit_prediction_button/src/edit_prediction_button.rs |  30 +
crates/language_models/src/provider/vercel.rs               |   1 
crates/outline_panel/src/outline_panel.rs                   |  10 
crates/outline_panel/src/outline_panel_settings.rs          |  10 
crates/settings/src/settings_content/project.rs             |  16 
crates/settings/src/settings_content/terminal.rs            |   2 
crates/theme/src/settings.rs                                |  48 +-
crates/theme_selector/src/icon_theme_selector.rs            |   4 
crates/theme_selector/src/theme_selector.rs                 |   4 
crates/vim/src/vim.rs                                       |   2 
crates/zed/src/zed.rs                                       |  20 
crates/zeta/src/init.rs                                     |  17 
21 files changed, 227 insertions(+), 182 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -1790,6 +1790,7 @@
     "anthropic": {
       "api_url": "https://api.anthropic.com"
     },
+    "bedrock": {},
     "google": {
       "api_url": "https://generativelanguage.googleapis.com"
     },
@@ -1811,7 +1812,14 @@
     },
     "mistral": {
       "api_url": "https://api.mistral.ai/v1"
-    }
+    },
+    "vercel": {
+      "api_url": "https://api.v0.dev/v1"
+    },
+    "x_ai": {
+      "api_url": "https://api.x.ai/v1"
+    },
+    "zed.dev": {}
   },
   "session": {
     /// Whether or not to restore unsaved buffers on restart.

crates/agent_ui/src/agent_configuration.rs 🔗

@@ -26,11 +26,9 @@ use language_model::{
 use notifications::status_toast::{StatusToast, ToastIcon};
 use project::{
     agent_server_store::{
-        AgentServerCommand, AgentServerStore, AllAgentServersSettings, CLAUDE_CODE_NAME,
-        CustomAgentServerSettings, GEMINI_NAME,
+        AgentServerStore, AllAgentServersSettings, CLAUDE_CODE_NAME, GEMINI_NAME,
     },
     context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
-    project_settings::{ContextServerSettings, ProjectSettings},
 };
 use settings::{Settings, SettingsStore, update_settings_file};
 use ui::{
@@ -414,7 +412,7 @@ impl AgentConfiguration {
             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);
+                    settings.agent.get_or_insert_default().set_always_allow_tool_actions(allow);
                 });
             },
         )
@@ -454,8 +452,8 @@ impl AgentConfiguration {
             play_sound_when_agent_done,
             move |state, _window, cx| {
                 let allow = state == &ToggleState::Selected;
-                update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
-                    settings.set_play_sound_when_agent_done(allow);
+                update_settings_file(fs.clone(), cx, move |settings, _| {
+                    settings.agent.get_or_insert_default().set_play_sound_when_agent_done(allow);
                 });
             },
         )
@@ -474,8 +472,8 @@ impl AgentConfiguration {
             use_modifier_to_send,
             move |state, _window, cx| {
                 let allow = state == &ToggleState::Selected;
-                update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
-                    settings.set_use_modifier_to_send(allow);
+                update_settings_file(fs.clone(), cx, move |settings, _| {
+                    settings.agent.get_or_insert_default().set_use_modifier_to_send(allow);
                 });
             },
         )
@@ -777,14 +775,14 @@ impl AgentConfiguration {
                                     async move |cx| {
                                         uninstall_extension_task.await?;
                                         cx.update(|cx| {
-                                            update_settings_file::<ProjectSettings>(
+                                            update_settings_file(
                                                 fs.clone(),
                                                 cx,
                                                 {
                                                     let context_server_id =
                                                         context_server_id.clone();
                                                     move |settings, _| {
-                                                        settings
+                                                        settings.project
                                                             .context_servers
                                                             .remove(&context_server_id.0);
                                                     }
@@ -879,67 +877,53 @@ impl AgentConfiguration {
                             .gap_1()
                             .child(context_server_configuration_menu)
                             .child(
-                                Switch::new("context-server-switch", is_running.into())
-                                    .color(SwitchColor::Accent)
-                                    .on_click({
-                                        let context_server_manager =
-                                            self.context_server_store.clone();
-                                        let fs = self.fs.clone();
-
-                                        move |state, _window, cx| {
-                                            let is_enabled = match state {
-                                                ToggleState::Unselected
-                                                | ToggleState::Indeterminate => {
-                                                    context_server_manager.update(
-                                                        cx,
-                                                        |this, cx| {
-                                                            this.stop_server(
-                                                                &context_server_id,
-                                                                cx,
-                                                            )
-                                                            .log_err();
-                                                        },
-                                                    );
-                                                    false
-                                                }
-                                                ToggleState::Selected => {
-                                                    context_server_manager.update(
-                                                        cx,
-                                                        |this, cx| {
-                                                            if let Some(server) =
-                                                                this.get_server(&context_server_id)
-                                                            {
-                                                                this.start_server(server, cx);
-                                                            }
-                                                        },
-                                                    );
-                                                    true
-                                                }
-                                            };
-                                            update_settings_file::<ProjectSettings>(
-                                                fs.clone(),
-                                                cx,
-                                                {
-                                                    let context_server_id =
-                                                        context_server_id.clone();
-
-                                                    move |settings, _| {
-                                                        settings
-                                                            .context_servers
-                                                            .entry(context_server_id.0)
-                                                            .or_insert_with(|| {
-                                                                ContextServerSettings::Extension {
-                                                                    enabled: is_enabled,
-                                                                    settings: serde_json::json!({}),
-                                                                }
-                                                            })
-                                                            .set_enabled(is_enabled);
+                            Switch::new("context-server-switch", is_running.into())
+                                .color(SwitchColor::Accent)
+                                .on_click({
+                                    let context_server_manager = self.context_server_store.clone();
+                                    let fs = self.fs.clone();
+
+                                    move |state, _window, cx| {
+                                        let is_enabled = match state {
+                                            ToggleState::Unselected
+                                            | ToggleState::Indeterminate => {
+                                                context_server_manager.update(cx, |this, cx| {
+                                                    this.stop_server(&context_server_id, cx)
+                                                        .log_err();
+                                                });
+                                                false
+                                            }
+                                            ToggleState::Selected => {
+                                                context_server_manager.update(cx, |this, cx| {
+                                                    if let Some(server) =
+                                                        this.get_server(&context_server_id)
+                                                    {
+                                                        this.start_server(server, cx);
                                                     }
-                                                },
-                                            );
-                                        }
-                                    }),
-                            ),
+                                                });
+                                                true
+                                            }
+                                        };
+                                        update_settings_file(fs.clone(), cx, {
+                                            let context_server_id = context_server_id.clone();
+
+                                            move |settings, _| {
+                                                settings
+                                                    .project
+                                                    .context_servers
+                                                    .entry(context_server_id.0)
+                                                    .or_insert_with(|| {
+                                                        settings::ContextServerSettingsContent::Extension {
+                                                            enabled: is_enabled,
+                                                            settings: serde_json::json!({}),
+                                                        }
+                                                    })
+                                                    .set_enabled(is_enabled);
+                                            }
+                                        });
+                                    }
+                                }),
+                        ),
                     ),
             )
             .map(|parent| {
@@ -1236,15 +1220,12 @@ fn show_unable_to_uninstall_extension_with_context_server(
                                     let context_server_id = context_server_id.clone();
                                     async move |_workspace_handle, cx| {
                                         cx.update(|cx| {
-                                            update_settings_file::<ProjectSettings>(
-                                                fs,
-                                                cx,
-                                                move |settings, _| {
-                                                    settings
-                                                        .context_servers
-                                                        .remove(&context_server_id.0);
-                                                },
-                                            );
+                                            update_settings_file(fs, cx, move |settings, _| {
+                                                settings
+                                                    .project
+                                                    .context_servers
+                                                    .remove(&context_server_id.0);
+                                            });
                                         })?;
                                         anyhow::Ok(())
                                     }
@@ -1294,6 +1275,7 @@ async fn open_new_agent_servers_entry_in_settings_editor(
                     .find(|name| {
                         !settings
                             .agent_servers
+                            .as_ref()
                             .is_some_and(|agent_servers| agent_servers.custom.contains_key(name))
                     });
                 if let Some(server_name) = server_name {

crates/agent_ui/src/agent_model_selector.rs 🔗

@@ -2,7 +2,6 @@ use crate::{
     ModelUsageContext,
     language_model_selector::{LanguageModelSelector, language_model_selector},
 };
-use agent_settings::AgentSettings;
 use fs::Fs;
 use gpui::{Entity, FocusHandle, SharedString};
 use picker::popover_menu::PickerPopoverMenu;
@@ -39,14 +38,12 @@ impl AgentModelSelector {
                         let model_id = model.id().0.to_string();
                         match &model_usage_context {
                             ModelUsageContext::InlineAssistant => {
-                                update_settings_file::<AgentSettings>(
-                                    fs.clone(),
-                                    cx,
-                                    move |settings, _cx| {
-                                        settings
-                                            .set_inline_assistant_model(provider.clone(), model_id);
-                                    },
-                                );
+                                update_settings_file(fs.clone(), cx, move |settings, _cx| {
+                                    settings
+                                        .agent
+                                        .get_or_insert_default()
+                                        .set_inline_assistant_model(provider.clone(), model_id);
+                                });
                             }
                         }
                     },

crates/agent_ui/src/agent_panel.rs 🔗

@@ -10,7 +10,9 @@ use project::agent_server_store::{
     AgentServerCommand, AllAgentServersSettings, CLAUDE_CODE_NAME, GEMINI_NAME,
 };
 use serde::{Deserialize, Serialize};
-use settings::DefaultAgentView as DefaultView;
+use settings::{
+    DefaultAgentView as DefaultView, LanguageModelProviderSetting, LanguageModelSelection,
+};
 use zed_actions::OpenBrowser;
 use zed_actions::agent::{OpenClaudeCodeOnboardingModal, ReauthenticateAgent};
 
@@ -1087,8 +1089,8 @@ impl AgentPanel {
         cx: &mut Context<Self>,
     ) {
         if action.persist {
-            update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, _| {
-                settings.agent_font_size = None;
+            update_settings_file(self.fs.clone(), cx, move |settings, _| {
+                settings.theme.agent_font_size = None;
             });
         } else {
             theme::reset_agent_font_size(cx);
@@ -1174,7 +1176,15 @@ impl AgentPanel {
                     && let Some(model) = provider.default_model(cx)
                 {
                     update_settings_file(self.fs.clone(), cx, move |settings, _| {
-                        settings.agent.get_or_insert_default().set_model(model)
+                        let provider = model.provider_id().0.to_string();
+                        let model = model.id().0.to_string();
+                        settings
+                            .agent
+                            .get_or_insert_default()
+                            .set_model(LanguageModelSelection {
+                                provider: LanguageModelProviderSetting(provider),
+                                model,
+                            })
                     });
                 }
 

crates/agent_ui/src/agent_ui.rs 🔗

@@ -24,7 +24,7 @@ use std::rc::Rc;
 use std::sync::Arc;
 
 use agent::ThreadId;
-use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
+use agent_settings::{AgentProfileId, AgentSettings};
 use assistant_slash_command::SlashCommandRegistry;
 use client::Client;
 use command_palette_hooks::CommandPaletteFilter;
@@ -40,7 +40,7 @@ use project::agent_server_store::AgentServerCommand;
 use prompt_store::PromptBuilder;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use settings::{Settings as _, SettingsStore};
+use settings::{LanguageModelSelection, Settings as _, SettingsStore};
 use std::any::TypeId;
 
 use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};

crates/agent_ui/src/context_server_configuration.rs 🔗

@@ -5,7 +5,6 @@ use extension::ExtensionManifest;
 use fs::Fs;
 use gpui::WeakEntity;
 use language::LanguageRegistry;
-use project::project_settings::ProjectSettings;
 use settings::update_settings_file;
 use ui::prelude::*;
 use util::ResultExt;

crates/agent_ui/src/profile_selector.rs 🔗

@@ -1,11 +1,10 @@
 use crate::{ManageProfiles, ToggleProfileSelector};
 use agent_settings::{
-    AgentDockPosition, AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles,
-    builtin_profiles,
+    AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles, builtin_profiles,
 };
 use fs::Fs;
 use gpui::{Action, Entity, FocusHandle, Subscription, prelude::*};
-use settings::{Settings as _, SettingsStore, update_settings_file};
+use settings::{DockPosition, Settings as _, SettingsStore, update_settings_file};
 use std::sync::Arc;
 use ui::{
     ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, PopoverMenu,
@@ -142,10 +141,13 @@ impl ProfileSelector {
             let fs = self.fs.clone();
             let provider = self.provider.clone();
             move |_window, cx| {
-                update_settings_file::<AgentSettings>(fs.clone(), cx, {
+                update_settings_file(fs.clone(), cx, {
                     let profile_id = profile_id.clone();
                     move |settings, _cx| {
-                        settings.set_profile(profile_id);
+                        settings
+                            .agent
+                            .get_or_insert_default()
+                            .set_profile(profile_id.0);
                     }
                 });
 
@@ -216,10 +218,10 @@ impl Render for ProfileSelector {
     }
 }
 
-fn documentation_side(position: AgentDockPosition) -> DocumentationSide {
+fn documentation_side(position: DockPosition) -> DocumentationSide {
     match position {
-        AgentDockPosition::Left => DocumentationSide::Right,
-        AgentDockPosition::Bottom => DocumentationSide::Left,
-        AgentDockPosition::Right => DocumentationSide::Left,
+        DockPosition::Left => DocumentationSide::Right,
+        DockPosition::Bottom => DocumentationSide::Left,
+        DockPosition::Right => DocumentationSide::Left,
     }
 }

crates/agent_ui/src/slash_command_settings.rs 🔗

@@ -1,8 +1,6 @@
-use anyhow::Result;
 use gpui::App;
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
+use settings::Settings;
+use util::MergeFrom;
 
 /// Settings for slash commands.
 #[derive(Debug, Default, Clone)]
@@ -18,18 +16,33 @@ pub struct CargoWorkspaceCommandSettings {
     pub enabled: bool,
 }
 
+// todo!() I think this setting is bogus... default.json has "slash_commands": {"project"}
 impl Settings for SlashCommandSettings {
-    fn from_defaults(content: &settings::SettingsContent, cx: &mut App) -> Self {
+    fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         Self {
             cargo_workspace: CargoWorkspaceCommandSettings {
-                enabled: content.project.slash_commands.unwrap(),
+                enabled: content
+                    .project
+                    .slash_commands
+                    .clone()
+                    .unwrap()
+                    .cargo_workspace
+                    .unwrap()
+                    .enabled
+                    .unwrap(),
             },
         }
     }
 
-    fn refine(&mut self, content: &settings::SettingsContent, cx: &mut App) {
-        todo!()
+    fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
+        let Some(slash_command) = content.project.slash_commands.as_ref() else {
+            return;
+        };
+        let Some(cargo_workspace) = slash_command.cargo_workspace.as_ref() else {
+            return;
+        };
+        self.cargo_workspace
+            .enabled
+            .merge_from(&cargo_workspace.enabled);
     }
-
-    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
 }

crates/collab/src/tests/editor_tests.rs 🔗

@@ -2241,14 +2241,14 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
 
     cx_a.update(|cx| {
         SettingsStore::update_global(cx, |store, cx| {
-            store.update_user_settings::<EditorSettings>(cx, |settings| {
+            store.update_user_settings(cx, |settings| {
                 settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::None);
             });
         });
     });
     cx_b.update(|cx| {
         SettingsStore::update_global(cx, |store, cx| {
-            store.update_user_settings::<EditorSettings>(cx, |settings| {
+            store.update_user_settings(cx, |settings| {
                 settings.editor.lsp_document_colors = Some(DocumentColorsRenderMode::Inlay);
             });
         });

crates/edit_prediction_button/src/edit_prediction_button.rs 🔗

@@ -948,9 +948,12 @@ async fn open_disabled_globs_setting_in_editor(
 }
 
 fn set_completion_provider(fs: Arc<dyn Fs>, cx: &mut App, provider: EditPredictionProvider) {
-    update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
-        file.features
-            .get_or_insert(Default::default())
+    update_settings_file(fs, cx, move |settings, _| {
+        settings
+            .project
+            .all_languages
+            .features
+            .get_or_insert_default()
             .edit_prediction_provider = Some(provider);
     });
 }
@@ -962,8 +965,11 @@ fn toggle_show_edit_predictions_for_language(
 ) {
     let show_edit_predictions =
         all_language_settings(None, cx).show_edit_predictions(Some(&language), cx);
-    update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
-        file.languages
+    update_settings_file(fs, cx, move |settings, _| {
+        settings
+            .project
+            .all_languages
+            .languages
             .0
             .entry(language.name())
             .or_default()
@@ -972,8 +978,11 @@ fn toggle_show_edit_predictions_for_language(
 }
 
 fn hide_copilot(fs: Arc<dyn Fs>, cx: &mut App) {
-    update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
-        file.features
+    update_settings_file(fs, cx, move |settings, _| {
+        settings
+            .project
+            .all_languages
+            .features
             .get_or_insert(Default::default())
             .edit_prediction_provider = Some(EditPredictionProvider::None);
     });
@@ -984,11 +993,12 @@ fn toggle_edit_prediction_mode(fs: Arc<dyn Fs>, mode: EditPredictionsMode, cx: &
     let current_mode = settings.edit_predictions_mode();
 
     if current_mode != mode {
-        update_settings_file::<AllLanguageSettings>(fs, cx, move |settings, _cx| {
-            if let Some(edit_predictions) = settings.edit_predictions.as_mut() {
+        update_settings_file(fs, cx, move |settings, _cx| {
+            if let Some(edit_predictions) = settings.project.all_languages.edit_predictions.as_mut()
+            {
                 edit_predictions.mode = mode;
             } else {
-                settings.edit_predictions =
+                settings.project.all_languages.edit_predictions =
                     Some(language_settings::EditPredictionSettingsContent {
                         mode,
                         ..Default::default()

crates/language_models/src/provider/vercel.rs 🔗

@@ -27,6 +27,7 @@ use crate::{AllLanguageModelSettings, ui::InstructionListItem};
 const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("vercel");
 const PROVIDER_NAME: LanguageModelProviderName = LanguageModelProviderName::new("Vercel");
 
+// todo!() -> Remove default implementation
 #[derive(Default, Clone, Debug, PartialEq)]
 pub struct VercelSettings {
     pub api_url: String,

crates/outline_panel/src/outline_panel.rs 🔗

@@ -38,7 +38,7 @@ use std::{
     u32,
 };
 
-use outline_panel_settings::{OutlinePanelDockPosition, OutlinePanelSettings, ShowIndentGuides};
+use outline_panel_settings::{LeftRightDockPosition, OutlinePanelSettings, ShowIndentGuides};
 use project::{File, Fs, GitEntry, GitTraversal, Project, ProjectItem};
 use search::{BufferSearchBar, ProjectSearchView};
 use serde::{Deserialize, Serialize};
@@ -4836,8 +4836,8 @@ impl Panel for OutlinePanel {
 
     fn position(&self, _: &Window, cx: &App) -> DockPosition {
         match OutlinePanelSettings::get_global(cx).dock {
-            OutlinePanelDockPosition::Left => DockPosition::Left,
-            OutlinePanelDockPosition::Right => DockPosition::Right,
+            LeftRightDockPosition::Left => DockPosition::Left,
+            LeftRightDockPosition::Right => DockPosition::Right,
         }
     }
 
@@ -4848,8 +4848,8 @@ impl Panel for OutlinePanel {
     fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
         settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
             let dock = match position {
-                DockPosition::Left | DockPosition::Bottom => OutlinePanelDockPosition::Left,
-                DockPosition::Right => OutlinePanelDockPosition::Right,
+                DockPosition::Left | DockPosition::Bottom => LeftRightDockPosition::Left,
+                DockPosition::Right => LeftRightDockPosition::Right,
             };
             settings.outline_panel.get_or_insert_default().dock = Some(dock);
         });

crates/outline_panel/src/outline_panel_settings.rs 🔗

@@ -1,6 +1,6 @@
 use editor::EditorSettings;
 use gpui::{App, Pixels};
-pub use settings::{OutlinePanelDockPosition, Settings, ShowIndentGuides};
+pub use settings::{LeftRightDockPosition, Settings, ShowIndentGuides};
 use ui::scrollbars::{ScrollbarVisibility, ShowScrollbar};
 use util::MergeFrom;
 
@@ -8,7 +8,7 @@ use util::MergeFrom;
 pub struct OutlinePanelSettings {
     pub button: bool,
     pub default_width: Pixels,
-    pub dock: OutlinePanelDockPosition,
+    pub dock: LeftRightDockPosition,
     pub file_icons: bool,
     pub folder_icons: bool,
     pub git_status: bool,
@@ -48,7 +48,7 @@ impl ScrollbarVisibility for OutlinePanelSettings {
 }
 
 impl Settings for OutlinePanelSettings {
-    fn from_defaults(content: &settings::SettingsContent, cx: &mut App) -> Self {
+    fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let panel = content.outline_panel.as_ref().unwrap();
         Self {
             button: panel.button.unwrap(),
@@ -64,7 +64,7 @@ impl Settings for OutlinePanelSettings {
             auto_reveal_entries: panel.auto_reveal_entries.unwrap(),
             auto_fold_dirs: panel.auto_fold_dirs.unwrap(),
             scrollbar: ScrollbarSettings {
-                show: panel.scrollbar.unwrap().show.unwrap(),
+                show: panel.scrollbar.unwrap().show.flatten().map(Into::into),
             },
             expand_outlines_with_depth: panel.expand_outlines_with_depth.unwrap(),
         }
@@ -93,7 +93,7 @@ impl Settings for OutlinePanelSettings {
         self.auto_fold_dirs.merge_from(&panel.auto_fold_dirs);
 
         if let Some(scrollbar) = panel.scrollbar.as_ref() {
-            self.scrollbar.show.merge_from(&scrollbar.show);
+            self.scrollbar.show.merge_from(&scrollbar.show.flatten());
         }
     }
     fn import_from_vscode(

crates/settings/src/settings_content/project.rs 🔗

@@ -172,6 +172,22 @@ pub enum ContextServerSettingsContent {
         settings: serde_json::Value,
     },
 }
+impl ContextServerSettingsContent {
+    pub fn set_enabled(&mut self, enabled: bool) {
+        match self {
+            ContextServerSettingsContent::Custom {
+                enabled: custom_enabled,
+                command: _,
+            } => {
+                *custom_enabled = enabled;
+            }
+            ContextServerSettingsContent::Extension {
+                enabled: ext_enabled,
+                settings: _,
+            } => *ext_enabled = enabled,
+        }
+    }
+}
 
 #[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema)]
 pub struct ContextServerCommand {

crates/settings/src/settings_content/terminal.rs 🔗

@@ -160,7 +160,7 @@ pub enum WorkingDirectory {
     Always { directory: String },
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
 pub struct ScrollbarSettingsContent {
     /// When to show the scrollbar in the terminal.
     ///

crates/theme/src/settings.rs 🔗

@@ -443,28 +443,32 @@ pub fn set_theme(
     }
 }
 
-//     /// Sets the icon theme for the given appearance to the icon theme with the specified name.
-//     pub fn set_icon_theme(&mut self, icon_theme_name: String, appearance: Appearance) {
-//         if let Some(selection) = self.icon_theme.as_mut() {
-//             let icon_theme_to_update = match selection {
-//                 IconThemeSelection::Static(theme) => theme,
-//                 IconThemeSelection::Dynamic { mode, light, dark } => match mode {
-//                     ThemeMode::Light => light,
-//                     ThemeMode::Dark => dark,
-//                     ThemeMode::System => match appearance {
-//                         Appearance::Light => light,
-//                         Appearance::Dark => dark,
-//                     },
-//                 },
-//             };
-
-//             *icon_theme_to_update = IconThemeName(icon_theme_name.into());
-//         } else {
-//             self.icon_theme = Some(IconThemeSelection::Static(IconThemeName(
-//                 icon_theme_name.into(),
-//             )));
-//         }
-//     }
+/// Sets the icon theme for the given appearance to the icon theme with the specified name.
+pub fn set_icon_theme(
+    current: &mut SettingsContent,
+    icon_theme_name: String,
+    appearance: Appearance,
+) {
+    if let Some(selection) = current.theme.icon_theme.as_mut() {
+        let icon_theme_to_update = match selection {
+            settings::IconThemeSelection::Static(theme) => theme,
+            settings::IconThemeSelection::Dynamic { mode, light, dark } => match mode {
+                ThemeMode::Light => light,
+                ThemeMode::Dark => dark,
+                ThemeMode::System => match appearance {
+                    Appearance::Light => light,
+                    Appearance::Dark => dark,
+                },
+            },
+        };
+
+        *icon_theme_to_update = IconThemeName(icon_theme_name.into());
+    } else {
+        current.theme.icon_theme = Some(settings::IconThemeSelection::Static(IconThemeName(
+            icon_theme_name.into(),
+        )));
+    }
+}
 
 /// Sets the mode for the theme.
 pub fn set_mode(content: &mut SettingsContent, mode: ThemeMode) {

crates/theme_selector/src/icon_theme_selector.rs 🔗

@@ -180,8 +180,8 @@ impl PickerDelegate for IconThemeSelectorDelegate {
 
         let appearance = Appearance::from(window.appearance());
 
-        update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, _| {
-            settings.set_icon_theme(theme_name.to_string(), appearance);
+        update_settings_file(self.fs.clone(), cx, move |settings, _| {
+            theme::set_icon_theme(settings, theme_name.to_string(), appearance);
         });
 
         self.selector

crates/theme_selector/src/theme_selector.rs 🔗

@@ -238,8 +238,8 @@ impl PickerDelegate for ThemeSelectorDelegate {
 
         let appearance = Appearance::from(window.appearance());
 
-        update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, _| {
-            settings.set_theme(theme_name.to_string(), appearance);
+        update_settings_file(self.fs.clone(), cx, move |settings, _| {
+            theme::set_theme(settings, theme_name.to_string(), appearance);
         });
 
         self.selector

crates/vim/src/vim.rs 🔗

@@ -266,7 +266,7 @@ pub fn init(cx: &mut App) {
         workspace.register_action(|workspace, _: &ToggleVimMode, _, cx| {
             let fs = workspace.app_state().fs.clone();
             let currently_enabled = Vim::enabled(cx);
-            update_settings_file::<VimModeSetting>(fs, cx, move |setting, _| {
+            update_settings_file(fs, cx, move |setting, _| {
                 setting.vim_mode = Some(!currently_enabled)
             })
         });

crates/zed/src/zed.rs 🔗

@@ -727,9 +727,10 @@ fn register_actions(
             let fs = app_state.fs.clone();
             move |_, action: &zed_actions::IncreaseUiFontSize, _window, cx| {
                 if action.persist {
-                    update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
+                    update_settings_file(fs.clone(), cx, move |settings, cx| {
                         let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx) + px(1.0);
                         let _ = settings
+                            .theme
                             .ui_font_size
                             .insert(theme::clamp_font_size(ui_font_size).0);
                     });
@@ -742,9 +743,10 @@ fn register_actions(
             let fs = app_state.fs.clone();
             move |_, action: &zed_actions::DecreaseUiFontSize, _window, cx| {
                 if action.persist {
-                    update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
+                    update_settings_file(fs.clone(), cx, move |settings, cx| {
                         let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx) - px(1.0);
                         let _ = settings
+                            .theme
                             .ui_font_size
                             .insert(theme::clamp_font_size(ui_font_size).0);
                     });
@@ -757,8 +759,8 @@ fn register_actions(
             let fs = app_state.fs.clone();
             move |_, action: &zed_actions::ResetUiFontSize, _window, cx| {
                 if action.persist {
-                    update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, _| {
-                        settings.ui_font_size = None;
+                    update_settings_file(fs.clone(), cx, move |settings, _| {
+                        settings.theme.ui_font_size = None;
                     });
                 } else {
                     theme::reset_ui_font_size(cx);
@@ -769,10 +771,11 @@ fn register_actions(
             let fs = app_state.fs.clone();
             move |_, action: &zed_actions::IncreaseBufferFontSize, _window, cx| {
                 if action.persist {
-                    update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
+                    update_settings_file(fs.clone(), cx, move |settings, cx| {
                         let buffer_font_size =
                             ThemeSettings::get_global(cx).buffer_font_size(cx) + px(1.0);
                         let _ = settings
+                            .theme
                             .buffer_font_size
                             .insert(theme::clamp_font_size(buffer_font_size).0);
                     });
@@ -785,10 +788,11 @@ fn register_actions(
             let fs = app_state.fs.clone();
             move |_, action: &zed_actions::DecreaseBufferFontSize, _window, cx| {
                 if action.persist {
-                    update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
+                    update_settings_file(fs.clone(), cx, move |settings, cx| {
                         let buffer_font_size =
                             ThemeSettings::get_global(cx).buffer_font_size(cx) - px(1.0);
                         let _ = settings
+                            .theme
                             .buffer_font_size
                             .insert(theme::clamp_font_size(buffer_font_size).0);
                     });
@@ -801,8 +805,8 @@ fn register_actions(
             let fs = app_state.fs.clone();
             move |_, action: &zed_actions::ResetBufferFontSize, _window, cx| {
                 if action.persist {
-                    update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, _| {
-                        settings.buffer_font_size = None;
+                    update_settings_file(fs.clone(), cx, move |settings, _| {
+                        settings.theme.buffer_font_size = None;
                     });
                 } else {
                     theme::reset_buffer_font_size(cx);

crates/zeta/src/init.rs 🔗

@@ -44,15 +44,14 @@ pub fn init(cx: &mut App) {
         );
 
         workspace.register_action(|workspace, _: &ResetOnboarding, _window, cx| {
-            update_settings_file::<AllLanguageSettings>(
-                workspace.app_state().fs.clone(),
-                cx,
-                move |file, _| {
-                    file.features
-                        .get_or_insert(Default::default())
-                        .edit_prediction_provider = Some(EditPredictionProvider::None)
-                },
-            );
+            update_settings_file(workspace.app_state().fs.clone(), cx, move |settings, _| {
+                settings
+                    .project
+                    .all_languages
+                    .features
+                    .get_or_insert_default()
+                    .edit_prediction_provider = Some(EditPredictionProvider::None)
+            });
         });
     })
     .detach();