agent_ui: Add skills menu item in message editor's context menu (#57407) (cherry-pick to preview) (#57820)

Cherry-pick of #57407 to preview

----
Closes AI-295

This PR adds a skills submenu within the "add context" menu in the agent
panel's message editor. This will hopefully be yet another way to find
skills in the app.

<img width="600" alt="Screenshot 2026-05-21 at 11  24@2x"

src="https://github.com/user-attachments/assets/43652081-3929-4ca0-a32b-464077d84dd5"
/>

Release Notes:

- N/A

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
This commit is contained in:
zed-zippy[bot] 2026-05-27 13:06:42 +00:00 committed by GitHub
parent a8594df7a4
commit a71a734d15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 89 additions and 15 deletions

View file

@ -12,6 +12,7 @@ use cloud_api_types::{SubmitAgentThreadFeedbackBody, SubmitAgentThreadFeedbackCo
use editor::actions::OpenExcerpts;
use feature_flags::AcpBetaFeatureFlag;
use crate::completion_provider::AvailableSkill;
use crate::message_editor::SharedSessionCapabilities;
use gpui::List;
@ -4157,6 +4158,8 @@ impl ThreadView {
let session_capabilities = self.session_capabilities.read();
let supports_images = session_capabilities.supports_images();
let supports_embedded_context = session_capabilities.supports_embedded_context();
let available_skills = session_capabilities.completion_skills();
drop(session_capabilities);
let has_editor_selection = workspace
.upgrade()
@ -4180,7 +4183,6 @@ impl ThreadView {
ContextMenu::build(window, cx, move |menu, _window, _cx| {
menu.key_context("AddContextMenu")
.header("Context")
.item(
ContextMenuEntry::new("Files & Directories")
.icon(IconName::File)
@ -4226,21 +4228,19 @@ impl ThreadView {
}
}),
)
.item(
ContextMenuEntry::new("Skills")
.icon(IconName::Sparkle)
.icon_color(Color::Muted)
.icon_size(IconSize::XSmall)
.handler({
let message_editor = message_editor.clone();
move |window, cx| {
message_editor.focus_handle(cx).focus(window, cx);
message_editor.update(cx, |editor, cx| {
editor.insert_context_type("skill", window, cx);
});
.when(!available_skills.is_empty(), |this| {
this.submenu_with_colored_icon("Skills", IconName::Sparkle, Color::Muted, {
let message_editor = message_editor.clone();
let available_skills = available_skills.clone();
move |mut menu, _window, _cx| {
for skill in &available_skills {
menu = menu
.item(Self::skill_menu_entry(skill, message_editor.clone()));
}
}),
)
menu
}
})
})
.item(
ContextMenuEntry::new("Image")
.icon(IconName::Image)
@ -4289,6 +4289,25 @@ impl ThreadView {
})
}
fn skill_menu_entry(
skill: &AvailableSkill,
message_editor: Entity<crate::message_editor::MessageEditor>,
) -> ContextMenuEntry {
let label = format!("{} ({})", skill.name, skill.source);
let skill = skill.clone();
ContextMenuEntry::new(label)
.icon(IconName::Sparkle)
.icon_color(Color::Muted)
.icon_size(IconSize::XSmall)
.handler(move |window, cx| {
message_editor.focus_handle(cx).focus(window, cx);
message_editor.update(cx, |editor, cx| {
editor.insert_skill_crease(&skill, window, cx);
});
})
}
fn render_follow_toggle(&self, cx: &mut Context<Self>) -> impl IntoElement {
let following = self.is_following(cx);

View file

@ -1514,6 +1514,61 @@ impl MessageEditor {
.detach_and_log_err(cx);
}
pub fn insert_skill_crease(
&mut self,
skill: &AvailableSkill,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(workspace) = self.workspace.upgrade() else {
return;
};
let mention_uri = MentionUri::Skill {
name: skill.name.to_string(),
source: skill.source.to_string(),
skill_file_path: skill.skill_file_path.clone(),
};
let link_text = mention_uri.as_link().to_string();
let content_len = link_text.len();
let mention_text = format!("{} ", link_text);
let crease_text: SharedString = mention_uri.name().into();
let start_anchor = self.editor.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let buffer_snapshot = snapshot.as_singleton()?;
let cursor = editor.selections.newest_anchor().start;
let text_anchor = snapshot
.anchor_to_buffer_anchor(cursor)?
.0
.bias_left(buffer_snapshot);
editor.insert(&mention_text, window, cx);
Some(text_anchor)
});
let Some(start_anchor) = start_anchor else {
return;
};
self.mention_set
.update(cx, |mention_set, cx| {
mention_set.confirm_mention_completion(
crease_text,
start_anchor,
content_len,
mention_uri,
false,
self.editor.clone(),
&workspace,
window,
cx,
)
})
.detach();
}
pub(crate) fn insert_selections(
&mut self,
selection: AgentContextSelection,