forked from 1b-agent/opencadstudio-upstream
feat(plugins): redesign plugin manager
This commit is contained in:
1 parent
d80637cda7
commit
4e25e88c31
11 files changed
+741
-74
No files matched your search
+16
-1
@@ -295,6 +295,21 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// Background task: fetch a repository README from its default branch.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(in crate::app) fn fetch_plugin_readme_task(&self, repo: String) -> Task<Message> {
|
||||
let label = repo.clone();
|
||||
Task::perform(
|
||||
async move { crate::plugin::marketplace::fetch_readme(&repo) },
|
||||
move |result| Message::PluginReadmeFetched(label, result),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(in crate::app) fn fetch_plugin_readme_task(&self, _repo: String) -> Task<Message> {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// Background task: download and install the `tag` release of `owner/repo`.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(in crate::app) fn install_task(&self, repo: String, tag: String) -> Task<Message> {
|
||||
@@ -305,7 +320,7 @@ impl OpenCADStudio {
|
||||
.into_iter()
|
||||
.find(|r| r.tag == tag)
|
||||
.ok_or_else(|| format!("release {tag} not found"))?;
|
||||
crate::plugin::marketplace::install(&rel)
|
||||
crate::plugin::marketplace::install(&rel, &repo)
|
||||
},
|
||||
Message::PluginInstalled,
|
||||
)
|
||||
|
||||
+126
-12
@@ -3951,17 +3951,42 @@ impl OpenCADStudio {
|
||||
// Refresh the on-disk external-plugin list each time the manager
|
||||
// opens so newly dropped-in packages show up.
|
||||
self.external_plugins = crate::plugin::external::discover();
|
||||
self.marketplace_status.clear();
|
||||
self.active_modal = Some(super::ModalKind::PluginManager);
|
||||
// Fetch the curated registry and release lists for linked repos.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let mut tasks = vec![self.fetch_registry_task()];
|
||||
let release_repos: rustc_hash::FxHashSet<String> = self
|
||||
.plugin_repos
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(
|
||||
self.external_plugins
|
||||
.iter()
|
||||
.filter_map(|plugin| plugin.repository.clone()),
|
||||
)
|
||||
.collect();
|
||||
tasks.extend(
|
||||
self.plugin_repos
|
||||
.clone()
|
||||
release_repos
|
||||
.into_iter()
|
||||
.map(|r| self.fetch_releases_task(r)),
|
||||
);
|
||||
if self.selected_plugin_repo.is_none() {
|
||||
self.selected_plugin_repo = self
|
||||
.external_plugins
|
||||
.iter()
|
||||
.find_map(|plugin| plugin.repository.clone())
|
||||
.or_else(|| self.plugin_registry.first().map(|entry| entry.repo.clone()))
|
||||
.or_else(|| self.plugin_repos.first().cloned());
|
||||
}
|
||||
if let Some(repo) = self.selected_plugin_repo.clone() {
|
||||
if !self.plugin_readmes.contains_key(&repo)
|
||||
&& self.plugin_readme_loading.insert(repo.clone())
|
||||
{
|
||||
tasks.push(self.fetch_plugin_readme_task(repo));
|
||||
}
|
||||
}
|
||||
return Task::batch(tasks);
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -3985,26 +4010,66 @@ impl OpenCADStudio {
|
||||
self.plugin_repo_input = s;
|
||||
Task::none()
|
||||
}
|
||||
Message::PluginSearchInput(s) => {
|
||||
self.plugin_search_input = s;
|
||||
Task::none()
|
||||
}
|
||||
Message::PluginRepoAdd => {
|
||||
let repo = self
|
||||
.plugin_repo_input
|
||||
.trim()
|
||||
.trim_start_matches("https://github.com/")
|
||||
.trim_end_matches('/')
|
||||
.to_string();
|
||||
if repo.is_empty() || self.plugin_repos.contains(&repo) {
|
||||
let Some(repo) =
|
||||
crate::plugin::external::normalize_repository(&self.plugin_repo_input)
|
||||
else {
|
||||
self.marketplace_status =
|
||||
"Enter a GitHub URL or repository in owner/repo format.".to_string();
|
||||
return Task::none();
|
||||
};
|
||||
if self.plugin_repos.contains(&repo)
|
||||
|| self.plugin_registry.iter().any(|entry| entry.repo == repo)
|
||||
{
|
||||
self.marketplace_status = format!("{repo} is already in the catalog.");
|
||||
self.selected_plugin_repo = Some(repo.clone());
|
||||
if !self.plugin_readmes.contains_key(&repo)
|
||||
&& self.plugin_readme_loading.insert(repo.clone())
|
||||
{
|
||||
return self.fetch_plugin_readme_task(repo);
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
if self
|
||||
.external_plugins
|
||||
.iter()
|
||||
.any(|plugin| plugin.repository.as_deref() == Some(repo.as_str()))
|
||||
{
|
||||
self.marketplace_status = format!("{repo} is already installed.");
|
||||
self.selected_plugin_repo = Some(repo.clone());
|
||||
if !self.plugin_readmes.contains_key(&repo)
|
||||
&& self.plugin_readme_loading.insert(repo.clone())
|
||||
{
|
||||
return self.fetch_plugin_readme_task(repo);
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
self.plugin_repos.push(repo.clone());
|
||||
self.plugin_repo_input.clear();
|
||||
self.persist_settings_if_changed();
|
||||
self.marketplace_status = format!("Fetching releases for {repo}…");
|
||||
self.fetch_releases_task(repo)
|
||||
self.selected_plugin_repo = Some(repo.clone());
|
||||
self.plugin_readmes.remove(&repo);
|
||||
self.plugin_readme_loading.insert(repo.clone());
|
||||
Task::batch(vec![
|
||||
self.fetch_releases_task(repo.clone()),
|
||||
self.fetch_plugin_readme_task(repo),
|
||||
])
|
||||
}
|
||||
Message::PluginRepoRemove(repo) => {
|
||||
self.plugin_repos.retain(|r| r != &repo);
|
||||
self.repo_release_tags.remove(&repo);
|
||||
self.repo_selected_tag.remove(&repo);
|
||||
if self.selected_plugin_repo.as_deref() == Some(repo.as_str())
|
||||
&& !self.plugin_registry.iter().any(|entry| entry.repo == repo)
|
||||
{
|
||||
self.selected_plugin_repo =
|
||||
self.plugin_registry.first().map(|entry| entry.repo.clone());
|
||||
}
|
||||
self.persist_settings_if_changed();
|
||||
Task::none()
|
||||
}
|
||||
@@ -4012,11 +4077,32 @@ impl OpenCADStudio {
|
||||
// Fetch releases for every curated repo so the dropdowns fill in.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let tasks: Vec<_> = entries
|
||||
if self.selected_plugin_repo.is_none() {
|
||||
self.selected_plugin_repo = self
|
||||
.external_plugins
|
||||
.iter()
|
||||
.find_map(|plugin| {
|
||||
plugin.repository.clone().or_else(|| {
|
||||
entries
|
||||
.iter()
|
||||
.find(|entry| entry.name.eq_ignore_ascii_case(&plugin.name))
|
||||
.map(|entry| entry.repo.clone())
|
||||
})
|
||||
})
|
||||
.or_else(|| entries.first().map(|entry| entry.repo.clone()));
|
||||
}
|
||||
let mut tasks: Vec<_> = entries
|
||||
.iter()
|
||||
.map(|e| self.fetch_releases_task(e.repo.clone()))
|
||||
.collect();
|
||||
self.plugin_registry = entries;
|
||||
if let Some(repo) = self.selected_plugin_repo.clone() {
|
||||
if !self.plugin_readmes.contains_key(&repo)
|
||||
&& self.plugin_readme_loading.insert(repo.clone())
|
||||
{
|
||||
tasks.push(self.fetch_plugin_readme_task(repo));
|
||||
}
|
||||
}
|
||||
return Task::batch(tasks);
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -4073,7 +4159,10 @@ impl OpenCADStudio {
|
||||
.entry(repo.clone())
|
||||
.or_insert_with(|| first.clone());
|
||||
}
|
||||
self.marketplace_status = format!("{repo}: {} installable release(s)", tags.len());
|
||||
if self.marketplace_status == format!("Fetching releases for {repo}…") {
|
||||
self.marketplace_status =
|
||||
format!("Repository added. {} installable release(s) found.", tags.len());
|
||||
}
|
||||
self.repo_release_tags.insert(repo, tags);
|
||||
Task::none()
|
||||
}
|
||||
@@ -4085,6 +4174,27 @@ impl OpenCADStudio {
|
||||
self.repo_selected_tag.insert(repo, tag);
|
||||
Task::none()
|
||||
}
|
||||
Message::PluginReadmeSelect(repo) => {
|
||||
self.selected_plugin_repo = Some(repo.clone());
|
||||
if self.plugin_readme_loading.contains(&repo) {
|
||||
return Task::none();
|
||||
}
|
||||
if matches!(self.plugin_readmes.get(&repo), Some(Ok(_))) {
|
||||
return Task::none();
|
||||
}
|
||||
// A second click on an error state acts as retry.
|
||||
self.plugin_readmes.remove(&repo);
|
||||
self.plugin_readme_loading.insert(repo.clone());
|
||||
self.fetch_plugin_readme_task(repo)
|
||||
}
|
||||
Message::PluginReadmeFetched(repo, result) => {
|
||||
self.plugin_readme_loading.remove(&repo);
|
||||
self.plugin_readmes.insert(
|
||||
repo,
|
||||
result.map(|source| iced::widget::markdown::Content::parse(&source)),
|
||||
);
|
||||
Task::none()
|
||||
}
|
||||
Message::PluginInstall(repo) => {
|
||||
let Some(tag) = self.repo_selected_tag.get(&repo).cloned() else {
|
||||
return Task::none();
|
||||
@@ -4092,6 +4202,10 @@ impl OpenCADStudio {
|
||||
self.marketplace_status = format!("Installing {repo} {tag}…");
|
||||
self.install_task(repo, tag)
|
||||
}
|
||||
Message::PluginUpdate(repo, tag) => {
|
||||
self.marketplace_status = format!("Updating {repo} to {tag}…");
|
||||
self.install_task(repo, tag)
|
||||
}
|
||||
Message::PluginInstalled(Ok(id)) => {
|
||||
self.marketplace_status = format!("Installed '{id}'. Restart to load it.");
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
||||
Reference in new issue
Block a user