From 6d5784daa6ca7edb03c903af367525cd48e2bc6f Mon Sep 17 00:00:00 2001
From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Date: Wed, 30 Oct 2024 19:42:42 -0300
Subject: [PATCH] Adjust design of the slash command picker (#19973)
This PR removes the quote selection icon button from the footer and adds
it in the picker, and adds an icon field to each command entry. Final
result looks like:
https://github.com/user-attachments/assets/d177f1c1-b6f6-4652-9434-f6291b279e34
Release Notes:
- N/A
---
assets/icons/wand.svg | 1 +
crates/assistant/src/assistant_panel.rs | 39 +---
.../src/slash_command/auto_command.rs | 6 +-
.../src/slash_command/delta_command.rs | 5 +
.../src/slash_command/diagnostics_command.rs | 4 +
.../src/slash_command/file_command.rs | 6 +-
.../src/slash_command/project_command.rs | 7 +-
.../src/slash_command/prompt_command.rs | 4 +
.../src/slash_command/search_command.rs | 4 +
.../src/slash_command/symbols_command.rs | 4 +
.../src/slash_command/tab_command.rs | 6 +-
.../src/slash_command/terminal_command.rs | 4 +
crates/assistant/src/slash_command_picker.rs | 201 +++++++++++-------
.../src/assistant_slash_command.rs | 3 +
crates/ui/src/components/icon.rs | 1 +
15 files changed, 185 insertions(+), 110 deletions(-)
create mode 100644 assets/icons/wand.svg
diff --git a/assets/icons/wand.svg b/assets/icons/wand.svg
new file mode 100644
index 0000000000..a6704b1c42
--- /dev/null
+++ b/assets/icons/wand.svg
@@ -0,0 +1 @@
+
diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs
index 3d498d94eb..4f0462b66a 100644
--- a/crates/assistant/src/assistant_panel.rs
+++ b/crates/assistant/src/assistant_panel.rs
@@ -73,12 +73,11 @@ use std::{
};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
use text::SelectionGoal;
-use ui::TintColor;
use ui::{
prelude::*,
utils::{format_distance_from_now, DateTimeType},
Avatar, ButtonLike, ContextMenu, Disclosure, ElevationIndex, KeyBinding, ListItem,
- ListItemSpacing, PopoverMenu, PopoverMenuHandle, Tooltip,
+ ListItemSpacing, PopoverMenu, PopoverMenuHandle, TintColor, Tooltip,
};
use util::{maybe, ResultExt};
use workspace::{
@@ -4006,13 +4005,7 @@ impl Render for ContextEditor {
} else {
None
};
- let focus_handle = self
- .workspace
- .update(cx, |workspace, cx| {
- Some(workspace.active_item_as::(cx)?.focus_handle(cx))
- })
- .ok()
- .flatten();
+
v_flex()
.key_context("ContextEditor")
.capture_action(cx.listener(ContextEditor::cancel))
@@ -4060,28 +4053,7 @@ impl Render for ContextEditor {
.child(
h_flex()
.gap_1()
- .child(render_inject_context_menu(cx.view().downgrade(), cx))
- .child(
- IconButton::new("quote-button", IconName::Quote)
- .icon_size(IconSize::Small)
- .on_click(|_, cx| {
- cx.dispatch_action(QuoteSelection.boxed_clone());
- })
- .tooltip(move |cx| {
- cx.new_view(|cx| {
- Tooltip::new("Insert Selection").key_binding(
- focus_handle.as_ref().and_then(|handle| {
- KeyBinding::for_action_in(
- &QuoteSelection,
- &handle,
- cx,
- )
- }),
- )
- })
- .into()
- }),
- ),
+ .child(render_inject_context_menu(cx.view().downgrade(), cx)),
)
.child(
h_flex()
@@ -4376,6 +4348,7 @@ fn render_inject_context_menu(
Button::new("trigger", "Add Context")
.icon(IconName::Plus)
.icon_size(IconSize::Small)
+ .icon_color(Color::Muted)
.icon_position(IconPosition::Start)
.tooltip(|cx| Tooltip::text("Type / to insert via keyboard", cx)),
)
@@ -4550,7 +4523,7 @@ impl Render for ContextEditorToolbarItem {
.w_full()
.justify_between()
.gap_2()
- .child(Label::new("Insert Context"))
+ .child(Label::new("Add Context"))
.child(Label::new("/ command").color(Color::Muted))
.into_any()
},
@@ -4574,7 +4547,7 @@ impl Render for ContextEditorToolbarItem {
}
},
)
- .action("Insert Selection", QuoteSelection.boxed_clone())
+ .action("Add Selection", QuoteSelection.boxed_clone())
}))
}
}),
diff --git a/crates/assistant/src/slash_command/auto_command.rs b/crates/assistant/src/slash_command/auto_command.rs
index cc73f36ebf..61f720be6d 100644
--- a/crates/assistant/src/slash_command/auto_command.rs
+++ b/crates/assistant/src/slash_command/auto_command.rs
@@ -14,7 +14,7 @@ use language_model::{
use semantic_index::{FileSummary, SemanticDb};
use smol::channel;
use std::sync::{atomic::AtomicBool, Arc};
-use ui::{BorrowAppContext, WindowContext};
+use ui::{prelude::*, BorrowAppContext, WindowContext};
use util::ResultExt;
use workspace::Workspace;
@@ -37,6 +37,10 @@ impl SlashCommand for AutoCommand {
"Automatically infer what context to add".into()
}
+ fn icon(&self) -> IconName {
+ IconName::Wand
+ }
+
fn menu_text(&self) -> String {
self.description()
}
diff --git a/crates/assistant/src/slash_command/delta_command.rs b/crates/assistant/src/slash_command/delta_command.rs
index c9985d9f00..5c8bc2b023 100644
--- a/crates/assistant/src/slash_command/delta_command.rs
+++ b/crates/assistant/src/slash_command/delta_command.rs
@@ -10,6 +10,7 @@ use gpui::{Task, WeakView, WindowContext};
use language::{BufferSnapshot, LspAdapterDelegate};
use std::sync::{atomic::AtomicBool, Arc};
use text::OffsetRangeExt;
+use ui::prelude::*;
use workspace::Workspace;
pub(crate) struct DeltaSlashCommand;
@@ -27,6 +28,10 @@ impl SlashCommand for DeltaSlashCommand {
self.description()
}
+ fn icon(&self) -> IconName {
+ IconName::Diff
+ }
+
fn requires_argument(&self) -> bool {
false
}
diff --git a/crates/assistant/src/slash_command/diagnostics_command.rs b/crates/assistant/src/slash_command/diagnostics_command.rs
index c7475445ce..3f1e3e5e71 100644
--- a/crates/assistant/src/slash_command/diagnostics_command.rs
+++ b/crates/assistant/src/slash_command/diagnostics_command.rs
@@ -98,6 +98,10 @@ impl SlashCommand for DiagnosticsSlashCommand {
"Insert diagnostics".into()
}
+ fn icon(&self) -> IconName {
+ IconName::XCircle
+ }
+
fn menu_text(&self) -> String {
self.description()
}
diff --git a/crates/assistant/src/slash_command/file_command.rs b/crates/assistant/src/slash_command/file_command.rs
index 1d0fa2bf3e..3964754029 100644
--- a/crates/assistant/src/slash_command/file_command.rs
+++ b/crates/assistant/src/slash_command/file_command.rs
@@ -117,7 +117,7 @@ impl SlashCommand for FileSlashCommand {
}
fn description(&self) -> String {
- "Insert file".into()
+ "Insert file and/or directory".into()
}
fn menu_text(&self) -> String {
@@ -128,6 +128,10 @@ impl SlashCommand for FileSlashCommand {
true
}
+ fn icon(&self) -> IconName {
+ IconName::File
+ }
+
fn complete_argument(
self: Arc,
arguments: &[String],
diff --git a/crates/assistant/src/slash_command/project_command.rs b/crates/assistant/src/slash_command/project_command.rs
index d14cb310ad..ee6434ec03 100644
--- a/crates/assistant/src/slash_command/project_command.rs
+++ b/crates/assistant/src/slash_command/project_command.rs
@@ -24,7 +24,8 @@ use std::{
ops::DerefMut,
sync::{atomic::AtomicBool, Arc},
};
-use ui::{BorrowAppContext as _, IconName};
+
+use ui::prelude::*;
use workspace::Workspace;
pub struct ProjectSlashCommand {
@@ -50,6 +51,10 @@ impl SlashCommand for ProjectSlashCommand {
"Generate a semantic search based on context".into()
}
+ fn icon(&self) -> IconName {
+ IconName::Folder
+ }
+
fn menu_text(&self) -> String {
self.description()
}
diff --git a/crates/assistant/src/slash_command/prompt_command.rs b/crates/assistant/src/slash_command/prompt_command.rs
index 079d1425af..9eb44d3418 100644
--- a/crates/assistant/src/slash_command/prompt_command.rs
+++ b/crates/assistant/src/slash_command/prompt_command.rs
@@ -21,6 +21,10 @@ impl SlashCommand for PromptSlashCommand {
"Insert prompt from library".into()
}
+ fn icon(&self) -> IconName {
+ IconName::Library
+ }
+
fn menu_text(&self) -> String {
self.description()
}
diff --git a/crates/assistant/src/slash_command/search_command.rs b/crates/assistant/src/slash_command/search_command.rs
index 9c4938ce93..f4bc3e36b6 100644
--- a/crates/assistant/src/slash_command/search_command.rs
+++ b/crates/assistant/src/slash_command/search_command.rs
@@ -38,6 +38,10 @@ impl SlashCommand for SearchSlashCommand {
"Search your project semantically".into()
}
+ fn icon(&self) -> IconName {
+ IconName::SearchCode
+ }
+
fn menu_text(&self) -> String {
self.description()
}
diff --git a/crates/assistant/src/slash_command/symbols_command.rs b/crates/assistant/src/slash_command/symbols_command.rs
index 468c8d7126..2b261bc368 100644
--- a/crates/assistant/src/slash_command/symbols_command.rs
+++ b/crates/assistant/src/slash_command/symbols_command.rs
@@ -22,6 +22,10 @@ impl SlashCommand for OutlineSlashCommand {
"Insert symbols for active tab".into()
}
+ fn icon(&self) -> IconName {
+ IconName::ListTree
+ }
+
fn menu_text(&self) -> String {
self.description()
}
diff --git a/crates/assistant/src/slash_command/tab_command.rs b/crates/assistant/src/slash_command/tab_command.rs
index 771c0765ee..1579938c92 100644
--- a/crates/assistant/src/slash_command/tab_command.rs
+++ b/crates/assistant/src/slash_command/tab_command.rs
@@ -12,7 +12,7 @@ use std::{
path::PathBuf,
sync::{atomic::AtomicBool, Arc},
};
-use ui::{ActiveTheme, WindowContext};
+use ui::{prelude::*, ActiveTheme, WindowContext};
use util::ResultExt;
use workspace::Workspace;
@@ -31,6 +31,10 @@ impl SlashCommand for TabSlashCommand {
"Insert open tabs (active tab by default)".to_owned()
}
+ fn icon(&self) -> IconName {
+ IconName::FileTree
+ }
+
fn menu_text(&self) -> String {
self.description()
}
diff --git a/crates/assistant/src/slash_command/terminal_command.rs b/crates/assistant/src/slash_command/terminal_command.rs
index 2ca1d4041b..84dbb7146f 100644
--- a/crates/assistant/src/slash_command/terminal_command.rs
+++ b/crates/assistant/src/slash_command/terminal_command.rs
@@ -33,6 +33,10 @@ impl SlashCommand for TerminalSlashCommand {
"Insert terminal output".into()
}
+ fn icon(&self) -> IconName {
+ IconName::Terminal
+ }
+
fn menu_text(&self) -> String {
self.description()
}
diff --git a/crates/assistant/src/slash_command_picker.rs b/crates/assistant/src/slash_command_picker.rs
index 35ae90d412..3d667d7f82 100644
--- a/crates/assistant/src/slash_command_picker.rs
+++ b/crates/assistant/src/slash_command_picker.rs
@@ -1,19 +1,13 @@
use std::sync::Arc;
use assistant_slash_command::SlashCommandRegistry;
-use gpui::AnyElement;
-use gpui::DismissEvent;
-use gpui::WeakView;
-use picker::PickerEditorPosition;
-use ui::ListItemSpacing;
-
-use gpui::SharedString;
-use gpui::Task;
-use picker::{Picker, PickerDelegate};
-use ui::{prelude::*, ListItem, PopoverMenu, PopoverTrigger};
+use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakView};
+use picker::{Picker, PickerDelegate, PickerEditorPosition};
+use ui::{prelude::*, KeyBinding, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger};
use crate::assistant_panel::ContextEditor;
+use crate::QuoteSelection;
#[derive(IntoElement)]
pub(super) struct SlashCommandSelector {
@@ -27,6 +21,7 @@ struct SlashCommandInfo {
name: SharedString,
description: SharedString,
args: Option,
+ icon: IconName,
}
#[derive(Clone)]
@@ -37,6 +32,7 @@ enum SlashCommandEntry {
renderer: fn(&mut WindowContext<'_>) -> AnyElement,
on_confirm: fn(&mut WindowContext<'_>),
},
+ QuoteButton,
}
impl AsRef for SlashCommandEntry {
@@ -44,6 +40,7 @@ impl AsRef for SlashCommandEntry {
match self {
SlashCommandEntry::Info(SlashCommandInfo { name, .. })
| SlashCommandEntry::Advert { name, .. } => name,
+ SlashCommandEntry::QuoteButton => "Quote Selection",
}
}
}
@@ -145,16 +142,23 @@ impl PickerDelegate for SlashCommandDelegate {
}
ret
}
+
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext>) {
if let Some(command) = self.filtered_commands.get(self.selected_index) {
- if let SlashCommandEntry::Info(info) = command {
- self.active_context_editor
- .update(cx, |context_editor, cx| {
- context_editor.insert_command(&info.name, cx)
- })
- .ok();
- } else if let SlashCommandEntry::Advert { on_confirm, .. } = command {
- on_confirm(cx);
+ match command {
+ SlashCommandEntry::Info(info) => {
+ self.active_context_editor
+ .update(cx, |context_editor, cx| {
+ context_editor.insert_command(&info.name, cx)
+ })
+ .ok();
+ }
+ SlashCommandEntry::QuoteButton => {
+ cx.dispatch_action(Box::new(QuoteSelection));
+ }
+ SlashCommandEntry::Advert { on_confirm, .. } => {
+ on_confirm(cx);
+ }
}
cx.emit(DismissEvent);
}
@@ -181,46 +185,78 @@ impl PickerDelegate for SlashCommandDelegate {
.spacing(ListItemSpacing::Dense)
.selected(selected)
.child(
- h_flex()
+ v_flex()
.group(format!("command-entry-label-{ix}"))
.w_full()
.min_w(px(250.))
.child(
- v_flex()
- .child(
- h_flex()
- .child(div().font_buffer(cx).child({
- let mut label = format!("/{}", info.name);
- if let Some(args) =
- info.args.as_ref().filter(|_| selected)
- {
- label.push_str(&args);
- }
- Label::new(label).size(LabelSize::Small)
- }))
- .children(info.args.clone().filter(|_| !selected).map(
- |args| {
- div()
- .font_buffer(cx)
- .child(
- Label::new(args)
- .size(LabelSize::Small)
- .color(Color::Muted),
- )
- .visible_on_hover(format!(
- "command-entry-label-{ix}"
- ))
- },
- )),
- )
- .child(
- Label::new(info.description.clone())
- .size(LabelSize::Small)
- .color(Color::Muted),
- ),
+ h_flex()
+ .gap_1p5()
+ .child(Icon::new(info.icon).size(IconSize::XSmall))
+ .child(div().font_buffer(cx).child({
+ let mut label = format!("{}", info.name);
+ if let Some(args) = info.args.as_ref().filter(|_| selected)
+ {
+ label.push_str(&args);
+ }
+ Label::new(label).size(LabelSize::Small)
+ }))
+ .children(info.args.clone().filter(|_| !selected).map(
+ |args| {
+ div()
+ .font_buffer(cx)
+ .child(
+ Label::new(args)
+ .size(LabelSize::Small)
+ .color(Color::Muted),
+ )
+ .visible_on_hover(format!(
+ "command-entry-label-{ix}"
+ ))
+ },
+ )),
+ )
+ .child(
+ Label::new(info.description.clone())
+ .size(LabelSize::Small)
+ .color(Color::Muted),
),
),
),
+ SlashCommandEntry::QuoteButton => {
+ let focus = cx.focus_handle();
+ let key_binding = KeyBinding::for_action_in(&QuoteSelection, &focus, cx);
+
+ Some(
+ ListItem::new(ix)
+ .inset(true)
+ .spacing(ListItemSpacing::Dense)
+ .selected(selected)
+ .child(
+ v_flex()
+ .child(
+ h_flex()
+ .gap_1p5()
+ .child(Icon::new(IconName::Quote).size(IconSize::XSmall))
+ .child(
+ div().font_buffer(cx).child(
+ Label::new("selection").size(LabelSize::Small),
+ ),
+ ),
+ )
+ .child(
+ h_flex()
+ .gap_1p5()
+ .child(
+ Label::new("Insert editor selection")
+ .color(Color::Muted)
+ .size(LabelSize::Small),
+ )
+ .children(key_binding.map(|kb| kb.render(cx))),
+ ),
+ ),
+ )
+ }
SlashCommandEntry::Advert { renderer, .. } => Some(
ListItem::new(ix)
.inset(true)
@@ -251,31 +287,50 @@ impl RenderOnce for SlashCommandSelector {
name: command_name.into(),
description: menu_text,
args,
+ icon: command.icon(),
}))
})
- .chain([SlashCommandEntry::Advert {
- name: "create-your-command".into(),
- renderer: |cx| {
- v_flex()
- .child(
- h_flex()
- .font_buffer(cx)
- .items_center()
- .gap_1()
- .child(div().font_buffer(cx).child(
- Label::new("create-your-command").size(LabelSize::Small),
- ))
- .child(Icon::new(IconName::ArrowUpRight).size(IconSize::XSmall)),
- )
- .child(
- Label::new("Learn how to create a custom command")
- .size(LabelSize::Small)
- .color(Color::Muted),
- )
- .into_any_element()
+ .chain([
+ SlashCommandEntry::Advert {
+ name: "create-your-command".into(),
+ renderer: |cx| {
+ v_flex()
+ .w_full()
+ .child(
+ h_flex()
+ .w_full()
+ .font_buffer(cx)
+ .items_center()
+ .justify_between()
+ .child(
+ h_flex()
+ .items_center()
+ .gap_1p5()
+ .child(Icon::new(IconName::Plus).size(IconSize::XSmall))
+ .child(
+ div().font_buffer(cx).child(
+ Label::new("create-your-command")
+ .size(LabelSize::Small),
+ ),
+ ),
+ )
+ .child(
+ Icon::new(IconName::ArrowUpRight)
+ .size(IconSize::XSmall)
+ .color(Color::Muted),
+ ),
+ )
+ .child(
+ Label::new("Create your custom command")
+ .size(LabelSize::Small)
+ .color(Color::Muted),
+ )
+ .into_any_element()
+ },
+ on_confirm: |cx| cx.open_url("https://zed.dev/docs/extensions/slash-commands"),
},
- on_confirm: |cx| cx.open_url("https://zed.dev/docs/extensions/slash-commands"),
- }])
+ SlashCommandEntry::QuoteButton,
+ ])
.collect::>();
let delegate = SlashCommandDelegate {
diff --git a/crates/assistant_slash_command/src/assistant_slash_command.rs b/crates/assistant_slash_command/src/assistant_slash_command.rs
index de247602d8..58f4fcb9b4 100644
--- a/crates/assistant_slash_command/src/assistant_slash_command.rs
+++ b/crates/assistant_slash_command/src/assistant_slash_command.rs
@@ -62,6 +62,9 @@ pub type SlashCommandResult = Result String;
+ fn icon(&self) -> IconName {
+ IconName::Slash
+ }
fn label(&self, _cx: &AppContext) -> CodeLabel {
CodeLabel::plain(self.name(), None)
}
diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs
index 890476f5fe..e11b6edf32 100644
--- a/crates/ui/src/components/icon.rs
+++ b/crates/ui/src/components/icon.rs
@@ -284,6 +284,7 @@ pub enum IconName {
Update,
UserGroup,
Visible,
+ Wand,
Warning,
WholeWord,
XCircle,