diff --git a/crates/agent/src/assistant_configuration.rs b/crates/agent/src/assistant_configuration.rs index 33b97a771e..9a22805049 100644 --- a/crates/agent/src/assistant_configuration.rs +++ b/crates/agent/src/assistant_configuration.rs @@ -404,7 +404,7 @@ impl AssistantConfiguration { .gap_2() .child( h_flex().w_full().child( - Button::new("add-context-server", "Add MCPs Directly") + Button::new("add-context-server", "Add Custom Server") .style(ButtonStyle::Filled) .layer(ElevationIndex::ModalSurface) .full_width() diff --git a/crates/agent/src/assistant_configuration/add_context_server_modal.rs b/crates/agent/src/assistant_configuration/add_context_server_modal.rs index 7ffdb053ac..a1f686e029 100644 --- a/crates/agent/src/assistant_configuration/add_context_server_modal.rs +++ b/crates/agent/src/assistant_configuration/add_context_server_modal.rs @@ -2,7 +2,7 @@ use context_server::{ContextServerSettings, ServerCommand, ServerConfig}; use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, WeakEntity, prelude::*}; use serde_json::json; use settings::update_settings_file; -use ui::{Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*}; +use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*}; use ui_input::SingleLineInput; use workspace::{ModalView, Workspace}; @@ -34,9 +34,9 @@ impl AddContextServerModal { cx: &mut Context, ) -> Self { let name_editor = - cx.new(|cx| SingleLineInput::new(window, cx, "Your server name").label("Name")); + cx.new(|cx| SingleLineInput::new(window, cx, "my-custom-server").label("Name")); let command_editor = cx.new(|cx| { - SingleLineInput::new(window, cx, "Command").label("Command to run the context server") + SingleLineInput::new(window, cx, "Command").label("Command to run the MCP server") }); Self { @@ -46,7 +46,7 @@ impl AddContextServerModal { } } - fn confirm(&mut self, cx: &mut Context) { + fn confirm(&mut self, _: &menu::Confirm, cx: &mut Context) { let name = self .name_editor .read(cx) @@ -96,7 +96,7 @@ impl AddContextServerModal { cx.emit(DismissEvent); } - fn cancel(&mut self, cx: &mut Context) { + fn cancel(&mut self, _: &menu::Cancel, cx: &mut Context) { cx.emit(DismissEvent); } } @@ -112,38 +112,68 @@ impl Focusable for AddContextServerModal { impl EventEmitter for AddContextServerModal {} impl Render for AddContextServerModal { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let is_name_empty = self.name_editor.read(cx).is_empty(cx); let is_command_empty = self.command_editor.read(cx).is_empty(cx); + let focus_handle = self.focus_handle(cx); + div() .elevation_3(cx) .w(rems(34.)) .key_context("AddContextServerModal") - .on_action(cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(cx))) - .on_action(cx.listener(|this, _: &menu::Confirm, _window, cx| this.confirm(cx))) + .on_action( + cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(&menu::Cancel, cx)), + ) + .on_action( + cx.listener(|this, _: &menu::Confirm, _window, cx| { + this.confirm(&menu::Confirm, cx) + }), + ) .capture_any_mouse_down(cx.listener(|this, _, window, cx| { this.focus_handle(cx).focus(window); })) .on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent))) .child( Modal::new("add-context-server", None) - .header(ModalHeader::new().headline("Add Context Server")) + .header(ModalHeader::new().headline("Add MCP Server")) .section( - Section::new() - .child(self.name_editor.clone()) - .child(self.command_editor.clone()), + Section::new().child( + v_flex() + .gap_2() + .child(self.name_editor.clone()) + .child(self.command_editor.clone()), + ), ) .footer( ModalFooter::new() .start_slot( - Button::new("cancel", "Cancel").on_click( - cx.listener(|this, _event, _window, cx| this.cancel(cx)), - ), + Button::new("cancel", "Cancel") + .key_binding( + KeyBinding::for_action_in( + &menu::Cancel, + &focus_handle, + window, + cx, + ) + .map(|kb| kb.size(rems_from_px(12.))), + ) + .on_click(cx.listener(|this, _event, _window, cx| { + this.cancel(&menu::Cancel, cx) + })), ) .end_slot( Button::new("add-server", "Add Server") .disabled(is_name_empty || is_command_empty) + .key_binding( + KeyBinding::for_action_in( + &menu::Confirm, + &focus_handle, + window, + cx, + ) + .map(|kb| kb.size(rems_from_px(12.))), + ) .map(|button| { if is_name_empty { button.tooltip(Tooltip::text("Name is required")) @@ -153,9 +183,9 @@ impl Render for AddContextServerModal { button } }) - .on_click( - cx.listener(|this, _event, _window, cx| this.confirm(cx)), - ), + .on_click(cx.listener(|this, _event, _window, cx| { + this.confirm(&menu::Confirm, cx) + })), ), ), ) diff --git a/crates/agent/src/assistant_panel.rs b/crates/agent/src/assistant_panel.rs index f8f3c923ab..cea3a781a2 100644 --- a/crates/agent/src/assistant_panel.rs +++ b/crates/agent/src/assistant_panel.rs @@ -47,7 +47,7 @@ use crate::thread_history::{PastContext, PastThread, ThreadHistory}; use crate::thread_store::ThreadStore; use crate::ui::UsageBanner; use crate::{ - AgentDiff, ExpandMessageEditor, InlineAssistant, NewTextThread, NewThread, + AddContextServer, AgentDiff, ExpandMessageEditor, InlineAssistant, NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ThreadEvent, ToggleContextPicker, }; @@ -1123,14 +1123,16 @@ impl AssistantPanel { .action("Prompt Library", Box::new(OpenPromptLibrary::default())) .action("Settings", Box::new(OpenConfiguration)) .separator() + .header("MCPs") .action( - "Install MCPs", + "View Server Extensions", Box::new(zed_actions::Extensions { category_filter: Some( zed_actions::ExtensionCategoryFilter::ContextServers, ), }), ) + .action("Add Custom Server", Box::new(AddContextServer)) }, )) }), diff --git a/crates/ui/src/components/modal.rs b/crates/ui/src/components/modal.rs index a05c20f1a0..8266c7b3d2 100644 --- a/crates/ui/src/components/modal.rs +++ b/crates/ui/src/components/modal.rs @@ -249,10 +249,14 @@ impl ModalFooter { impl RenderOnce for ModalFooter { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { h_flex() - .flex_none() .w_full() + .mt_4() .p(DynamicSpacing::Base08.rems(cx)) - .justify_between() + .flex_none() + .justify_end() + .gap_1() + .border_t_1() + .border_color(cx.theme().colors().border_variant) .child(div().when_some(self.start_slot, |this, start_slot| this.child(start_slot))) .child(div().when_some(self.end_slot, |this, end_slot| this.child(end_slot))) }