mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-31 19:05:00 +07:00
agent_ui: Add skills menu item in message editor's context menu (#57407)
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
This commit is contained in:
parent
0307c7fb35
commit
7afcc87927
2 changed files with 89 additions and 15 deletions
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue