diff --git a/crates/extensions_ui/src/components/extension_card.rs b/crates/extensions_ui/src/components/extension_card.rs index 75223df96fb8a16b8aedd59c9b3a931397ce7c50..326b0fc2668035be1bd30fa7b83066c94c468d30 100644 --- a/crates/extensions_ui/src/components/extension_card.rs +++ b/crates/extensions_ui/src/components/extension_card.rs @@ -40,7 +40,7 @@ impl RenderOnce for ExtensionCard { .bg(cx.theme().colors().elevated_surface_background) .border_1() .border_color(cx.theme().colors().border) - .rounded_sm() + .rounded_md() .children(self.children) .when(self.overridden_by_dev_extension, |card| { card.child( diff --git a/crates/extensions_ui/src/components/feature_upsell.rs b/crates/extensions_ui/src/components/feature_upsell.rs index d2d86937ff624f077fbc25f95cce893d9bc69531..e2e65f1598ad1a22a3fe854425a32013115616d6 100644 --- a/crates/extensions_ui/src/components/feature_upsell.rs +++ b/crates/extensions_ui/src/components/feature_upsell.rs @@ -1,6 +1,6 @@ use gpui::{AnyElement, Div, StyleRefinement}; use smallvec::SmallVec; -use ui::{ButtonLike, prelude::*}; +use ui::prelude::*; #[derive(IntoElement)] pub struct FeatureUpsell { @@ -46,21 +46,20 @@ impl FeatureUpsell { impl RenderOnce for FeatureUpsell { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { self.base - .p_4() + .py_2() + .px_4() .justify_between() - .border_color(cx.theme().colors().border) - .child(v_flex().overflow_hidden().child(Label::new(self.text))) + .flex_wrap() + .border_color(cx.theme().colors().border_variant) + .child(Label::new(self.text)) .child(h_flex().gap_2().children(self.children).when_some( self.docs_url, |el, docs_url| { el.child( - ButtonLike::new("open_docs") - .child( - h_flex() - .gap_2() - .child(Label::new("View docs")) - .child(Icon::new(IconName::ArrowUpRight)), - ) + Button::new("open_docs", "View Documentation") + .icon(IconName::ArrowUpRight) + .icon_size(IconSize::XSmall) + .icon_position(IconPosition::End) .on_click({ let docs_url = docs_url.clone(); move |_event, _window, cx| { diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 430b656f09679bf5c0e21512615c3c447e001f8b..7aeb7cb309b315aeed161c83e311a79dc0d41dc5 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -165,7 +165,7 @@ fn extension_provides_label(provides: ExtensionProvides) -> &'static str { ExtensionProvides::Languages => "Languages", ExtensionProvides::Grammars => "Grammars", ExtensionProvides::LanguageServers => "Language Servers", - ExtensionProvides::ContextServers => "Context Servers", + ExtensionProvides::ContextServers => "MCP Servers", ExtensionProvides::SlashCommands => "Slash Commands", ExtensionProvides::IndexedDocsProviders => "Indexed Docs Providers", ExtensionProvides::Snippets => "Snippets", @@ -573,6 +573,7 @@ impl ExtensionsPage { extension.authors.join(", ") )) .size(LabelSize::Small) + .color(Color::Muted) .truncate(), ) .child(Label::new("<>").size(LabelSize::Small)), @@ -594,7 +595,6 @@ impl ExtensionsPage { ) .icon_color(Color::Accent) .icon_size(IconSize::Small) - .style(ButtonStyle::Filled) .on_click(cx.listener({ let repository_url = repository_url.clone(); move |_, _, _, cx| { @@ -701,6 +701,7 @@ impl ExtensionsPage { extension.manifest.authors.join(", ") )) .size(LabelSize::Small) + .color(Color::Muted) .truncate(), ) .child( @@ -731,7 +732,6 @@ impl ExtensionsPage { ) .icon_color(Color::Accent) .icon_size(IconSize::Small) - .style(ButtonStyle::Filled) .on_click(cx.listener({ let repository_url = repository_url.clone(); move |_, _, _, cx| { @@ -751,8 +751,7 @@ impl ExtensionsPage { IconName::Ellipsis, ) .icon_color(Color::Accent) - .icon_size(IconSize::Small) - .style(ButtonStyle::Filled), + .icon_size(IconSize::Small), ) .menu(move |window, cx| { Some(Self::render_remote_extension_context_menu( @@ -950,19 +949,20 @@ impl ExtensionsPage { cx.theme().colors().border }; - h_flex().w_full().gap_2().key_context(key_context).child( - h_flex() - .flex_1() - .px_2() - .py_1() - .gap_2() - .border_1() - .border_color(editor_border) - .min_w(rems_from_px(384.)) - .rounded_lg() - .child(Icon::new(IconName::MagnifyingGlass)) - .child(self.render_text_input(&self.query_editor, cx)), - ) + h_flex() + .key_context(key_context) + .h_8() + .flex_1() + .min_w(rems_from_px(384.)) + .pl_1p5() + .pr_2() + .py_1() + .gap_2() + .border_1() + .border_color(editor_border) + .rounded_lg() + .child(Icon::new(IconName::MagnifyingGlass).color(Color::Muted)) + .child(self.render_text_input(&self.query_editor, cx)) } fn render_text_input( @@ -1193,52 +1193,6 @@ impl ExtensionsPage { upsell.when(ix < upsells_count, |upsell| upsell.border_b_1()) })) } - - fn build_extension_provides_filter_menu( - &self, - window: &mut Window, - cx: &mut Context, - ) -> Entity { - let this = cx.entity(); - ContextMenu::build(window, cx, |mut menu, _window, _cx| { - menu = menu.header("Extension Category").toggleable_entry( - "All", - self.provides_filter.is_none(), - IconPosition::End, - None, - { - let this = this.clone(); - move |_window, cx| { - this.update(cx, |this, cx| { - this.change_provides_filter(None, cx); - }); - } - }, - ); - - for provides in ExtensionProvides::iter() { - let label = extension_provides_label(provides); - - menu = menu.toggleable_entry( - label, - self.provides_filter == Some(provides), - IconPosition::End, - None, - { - let this = this.clone(); - move |_window, cx| { - this.update(cx, |this, cx| { - this.change_provides_filter(Some(provides), cx); - this.provides_filter = Some(provides); - }); - } - }, - ) - } - - menu - }) - } } impl Render for ExtensionsPage { @@ -1249,9 +1203,8 @@ impl Render for ExtensionsPage { .child( v_flex() .gap_4() - .p_4() - .border_b_1() - .border_color(cx.theme().colors().border) + .pt_4() + .px_4() .bg(cx.theme().colors().editor_background) .child( h_flex() @@ -1271,29 +1224,9 @@ impl Render for ExtensionsPage { .child( h_flex() .w_full() - .gap_2() - .justify_between() - .child(h_flex().gap_2().child(self.render_search(cx)).child({ - let this = cx.entity().clone(); - PopoverMenu::new("extension-provides-filter") - .menu(move |window, cx| { - Some(this.update(cx, |this, cx| { - this.build_extension_provides_filter_menu(window, cx) - })) - }) - .trigger_with_tooltip( - Button::new( - "extension-provides-filter-button", - self.provides_filter - .map(extension_provides_label) - .unwrap_or("All"), - ) - .icon(IconName::Filter) - .icon_position(IconPosition::Start), - Tooltip::text("Filter extensions by category"), - ) - .anchor(gpui::Corner::TopLeft) - })) + .gap_4() + .flex_wrap() + .child(self.render_search(cx)) .child( h_flex() .child( @@ -1343,6 +1276,47 @@ impl Render for ExtensionsPage { ), ), ) + .child( + h_flex() + .id("filter-row") + .gap_2() + .py_2p5() + .px_4() + .border_b_1() + .border_color(cx.theme().colors().border_variant) + .overflow_x_scroll() + .child( + Button::new("filter-all-categories", "All") + .when(self.provides_filter.is_none(), |button| { + button.style(ButtonStyle::Filled) + }) + .when(self.provides_filter.is_some(), |button| { + button.style(ButtonStyle::Subtle) + }) + .toggle_state(self.provides_filter.is_none()) + .on_click(cx.listener(|this, _event, _, cx| { + this.change_provides_filter(None, cx); + })), + ) + .children(ExtensionProvides::iter().map(|provides| { + let label = extension_provides_label(provides); + Button::new( + SharedString::from(format!("filter-category-{}", label)), + label, + ) + .style(if self.provides_filter == Some(provides) { + ButtonStyle::Filled + } else { + ButtonStyle::Subtle + }) + .toggle_state(self.provides_filter == Some(provides)) + .on_click({ + cx.listener(move |this, _event, _, cx| { + this.change_provides_filter(Some(provides), cx); + }) + }) + })), + ) .child(self.render_feature_upsells(cx)) .child( v_flex()