mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-31 19:05:00 +07:00
Update skill settings immediately after changes (#57447)
## Summary - Hide deleted skills immediately in Settings while deletion completes - Refresh the skill index after creating a skill so Settings updates without reopening Closes AI-299 Release Notes: - Fixed skill management so newly created and deleted skills update in Settings immediately.
This commit is contained in:
parent
ba350974af
commit
6753eb1736
5 changed files with 128 additions and 18 deletions
|
|
@ -1766,6 +1766,16 @@ impl NativeAgentConnection {
|
|||
.update(cx, |agent, cx| agent.ensure_skills_scan_started(cx));
|
||||
}
|
||||
|
||||
pub fn refresh_skills_for_project(&self, project: Entity<Project>, cx: &mut App) {
|
||||
self.0.update(cx, |agent, cx| {
|
||||
let project_id = agent.get_or_create_project_state(&project, cx);
|
||||
agent.ensure_skills_scan_started(cx);
|
||||
if let Some(state) = agent.projects.get_mut(&project_id) {
|
||||
state.project_context_needs_refresh.send(()).ok();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn available_skills(
|
||||
&self,
|
||||
session_id: &acp::SessionId,
|
||||
|
|
|
|||
|
|
@ -3070,10 +3070,42 @@ impl AgentPanel {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let this = cx.weak_entity();
|
||||
let on_saved = Rc::new(move |cx: &mut App| {
|
||||
this.update(cx, |this, cx| {
|
||||
if !this.has_open_project(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ensure_native_agent_connection(cx);
|
||||
let Some(connect_task) = this.connection_store.update(cx, |store, cx| {
|
||||
store
|
||||
.entry(&Agent::NativeAgent)
|
||||
.map(|entry| entry.read(cx).wait_for_connection())
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
let project = this.project.clone();
|
||||
cx.spawn(async move |_this, cx| -> Result<()> {
|
||||
let connected = connect_task.await?;
|
||||
if let Some(native_connection) = connected
|
||||
.connection
|
||||
.downcast::<agent::NativeAgentConnection>()
|
||||
{
|
||||
cx.update(|cx| native_connection.refresh_skills_for_project(project, cx));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
|
||||
open_skill_creator(
|
||||
Some(self.workspace.clone()),
|
||||
self.language_registry.clone(),
|
||||
self.fs.clone(),
|
||||
Some(on_saved),
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
|
|
|
|||
|
|
@ -23,18 +23,26 @@ pub(crate) fn render_skills_setup_page(
|
|||
.map(|idx| idx.global_skills.clone())
|
||||
.unwrap_or_default(),
|
||||
SettingsUiFile::Project((worktree_id, _)) => {
|
||||
let wt_id = usize::from(*worktree_id);
|
||||
let worktree_id = usize::from(*worktree_id);
|
||||
skill_index
|
||||
.and_then(|idx| {
|
||||
idx.project_skills
|
||||
.and_then(|index| {
|
||||
index
|
||||
.project_skills
|
||||
.iter()
|
||||
.find(|g| g.worktree_id.0 == wt_id)
|
||||
.map(|g| g.skills.clone())
|
||||
.find(|group| group.worktree_id.0 == worktree_id)
|
||||
.map(|group| group.skills.clone())
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
}
|
||||
.into_iter()
|
||||
.filter(|skill| {
|
||||
!settings_window
|
||||
.hidden_deleted_skill_directory_paths
|
||||
.contains(&skill.directory_path)
|
||||
})
|
||||
.collect();
|
||||
|
||||
v_flex()
|
||||
.id("skills-page")
|
||||
|
|
@ -129,20 +137,42 @@ fn render_skill_row(skill: &Skill, cx: &mut Context<SettingsWindow>) -> AnyEleme
|
|||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Delete Skill"))
|
||||
.on_click(cx.listener(
|
||||
move |_this, _event, _window, cx| {
|
||||
move |settings_window, _event, _window, cx| {
|
||||
let directory_path = directory_path.clone();
|
||||
if !settings_window
|
||||
.hidden_deleted_skill_directory_paths
|
||||
.insert(directory_path.clone())
|
||||
{
|
||||
return;
|
||||
}
|
||||
cx.notify();
|
||||
|
||||
let app_state = workspace::AppState::global(cx);
|
||||
let fs = app_state.fs.clone();
|
||||
cx.spawn(async move |_this, _cx| {
|
||||
fs.remove_dir(
|
||||
&directory_path,
|
||||
RemoveOptions {
|
||||
recursive: true,
|
||||
ignore_if_not_exists: true,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.log_err();
|
||||
cx.spawn(async move |settings_window, cx| {
|
||||
let remove_result = fs
|
||||
.remove_dir(
|
||||
&directory_path,
|
||||
RemoveOptions {
|
||||
recursive: true,
|
||||
ignore_if_not_exists: true,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = remove_result {
|
||||
log::error!(
|
||||
"failed to delete skill directory {}: {error:#}",
|
||||
directory_path.display()
|
||||
);
|
||||
settings_window
|
||||
.update(cx, |settings_window, cx| {
|
||||
settings_window
|
||||
.hidden_deleted_skill_directory_paths
|
||||
.remove(&directory_path);
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ mod components;
|
|||
mod page_data;
|
||||
pub mod pages;
|
||||
|
||||
use agent_skills::SkillIndex;
|
||||
use anyhow::{Context as _, Result};
|
||||
use editor::{Editor, EditorEvent};
|
||||
use futures::{StreamExt, channel::mpsc};
|
||||
|
|
@ -29,6 +30,7 @@ use std::{
|
|||
collections::{HashMap, HashSet},
|
||||
num::{NonZero, NonZeroU32},
|
||||
ops::Range,
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::{Arc, LazyLock, RwLock},
|
||||
time::Duration,
|
||||
|
|
@ -767,6 +769,7 @@ pub struct SettingsWindow {
|
|||
search_index: Option<Arc<SearchIndex>>,
|
||||
list_state: ListState,
|
||||
shown_errors: HashSet<String>,
|
||||
pub(crate) hidden_deleted_skill_directory_paths: HashSet<PathBuf>,
|
||||
pub(crate) regex_validation_error: Option<String>,
|
||||
last_copied_link_path: Option<&'static str>,
|
||||
}
|
||||
|
|
@ -1542,6 +1545,28 @@ impl SettingsWindow {
|
|||
})
|
||||
.detach();
|
||||
|
||||
cx.observe_global_in::<SkillIndex>(window, |this, _window, cx| {
|
||||
if let Some(skill_index) = cx.try_global::<SkillIndex>() {
|
||||
this.hidden_deleted_skill_directory_paths
|
||||
.retain(|directory_path| {
|
||||
skill_index
|
||||
.global_skills
|
||||
.iter()
|
||||
.chain(
|
||||
skill_index
|
||||
.project_skills
|
||||
.iter()
|
||||
.flat_map(|group| group.skills.iter()),
|
||||
)
|
||||
.any(|skill| skill.directory_path.as_path() == directory_path.as_path())
|
||||
});
|
||||
} else {
|
||||
this.hidden_deleted_skill_directory_paths.clear();
|
||||
}
|
||||
cx.notify();
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.on_window_closed(|cx, _window_id| {
|
||||
if let Some(existing_window) = cx
|
||||
.windows()
|
||||
|
|
@ -1689,6 +1714,7 @@ impl SettingsWindow {
|
|||
.tab_stop(false),
|
||||
search_index: None,
|
||||
shown_errors: HashSet::default(),
|
||||
hidden_deleted_skill_directory_paths: HashSet::default(),
|
||||
regex_validation_error: None,
|
||||
list_state,
|
||||
last_copied_link_path: None,
|
||||
|
|
@ -4548,6 +4574,7 @@ pub mod test {
|
|||
search_index: None,
|
||||
list_state: ListState::new(0, gpui::ListAlignment::Top, px(0.0)),
|
||||
shown_errors: HashSet::default(),
|
||||
hidden_deleted_skill_directory_paths: HashSet::default(),
|
||||
regex_validation_error: None,
|
||||
last_copied_link_path: None,
|
||||
}
|
||||
|
|
@ -4674,6 +4701,7 @@ pub mod test {
|
|||
search_index: None,
|
||||
list_state: ListState::new(0, gpui::ListAlignment::Top, px(0.0)),
|
||||
shown_errors: HashSet::default(),
|
||||
hidden_deleted_skill_directory_paths: HashSet::default(),
|
||||
regex_validation_error: None,
|
||||
last_copied_link_path: None,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use platform_title_bar::PlatformTitleBar;
|
|||
use release_channel::ReleaseChannel;
|
||||
use settings::{ActionSequence, Settings};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use theme_settings::ThemeSettings;
|
||||
use ui::{
|
||||
|
|
@ -113,6 +114,7 @@ pub fn open_skill_creator(
|
|||
workspace: Option<WeakEntity<Workspace>>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
fs: Arc<dyn Fs>,
|
||||
on_saved: Option<Rc<dyn Fn(&mut App)>>,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<WindowHandle<SkillCreator>>> {
|
||||
cx.spawn(async move |cx| {
|
||||
|
|
@ -161,7 +163,9 @@ pub fn open_skill_creator(
|
|||
..Default::default()
|
||||
},
|
||||
|window, cx| {
|
||||
cx.new(|cx| SkillCreator::new(workspace, language_registry, fs, window, cx))
|
||||
cx.new(|cx| {
|
||||
SkillCreator::new(workspace, language_registry, fs, on_saved, window, cx)
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
@ -173,6 +177,7 @@ pub struct SkillCreator {
|
|||
title_bar: Option<Entity<PlatformTitleBar>>,
|
||||
workspace: Option<WeakEntity<Workspace>>,
|
||||
fs: Arc<dyn Fs>,
|
||||
on_saved: Option<Rc<dyn Fn(&mut App)>>,
|
||||
name_editor: Entity<InputField>,
|
||||
description_editor: Entity<InputField>,
|
||||
body_editor: Entity<Editor>,
|
||||
|
|
@ -198,6 +203,7 @@ impl SkillCreator {
|
|||
workspace: Option<WeakEntity<Workspace>>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
fs: Arc<dyn Fs>,
|
||||
on_saved: Option<Rc<dyn Fn(&mut App)>>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
|
|
@ -321,6 +327,7 @@ impl SkillCreator {
|
|||
},
|
||||
workspace,
|
||||
fs,
|
||||
on_saved,
|
||||
name_editor,
|
||||
description_editor,
|
||||
body_editor,
|
||||
|
|
@ -468,6 +475,9 @@ impl SkillCreator {
|
|||
this.save_task = None;
|
||||
match result {
|
||||
Ok(path) => {
|
||||
if let Some(on_saved) = &this.on_saved {
|
||||
on_saved(cx);
|
||||
}
|
||||
if let Some(workspace) = workspace.as_ref().and_then(|w| w.upgrade()) {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.show_toast(
|
||||
|
|
|
|||
Loading…
Reference in a new issue