extensions_ui: Add design changes to expose the filters more (#29582)
Closes https://github.com/zed-industries/zed/issues/28086 The main motivator for this change is to have the "MCP Servers" filter more clearly visible. And because of this, all other filters end up more visible, as they're not in a dropdown menu anymore. Ended up pushing some other small changes here and there as well. This is our final product: <img src="https://github.com/user-attachments/assets/16ac78b6-72d9-4a8a-801b-b4b992221331" width="700"/> Release Notes: - N/A
This commit is contained in:
parent
756fcd0733
commit
9b37206147
3 changed files with 75 additions and 102 deletions
|
@ -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(
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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<Self>,
|
||||
) -> Entity<ContextMenu> {
|
||||
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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue