pinakes-core: expose required_endpoints alongside UI pages in plugin manager
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I32c95a03f106db8fef7eedd0362756a46a6a6964
This commit is contained in:
parent
1c351c0f53
commit
d059263209
2 changed files with 119 additions and 13 deletions
|
|
@ -602,7 +602,8 @@ impl PluginManager {
|
|||
/// List all UI pages provided by loaded plugins.
|
||||
///
|
||||
/// Returns a vector of `(plugin_id, page)` tuples for all enabled plugins
|
||||
/// that provide pages in their manifests.
|
||||
/// that provide pages in their manifests. Both inline and file-referenced
|
||||
/// page entries are resolved.
|
||||
pub async fn list_ui_pages(
|
||||
&self,
|
||||
) -> Vec<(String, pinakes_plugin_api::UiPage)> {
|
||||
|
|
@ -612,23 +613,126 @@ impl PluginManager {
|
|||
if !plugin.enabled {
|
||||
continue;
|
||||
}
|
||||
for entry in &plugin.manifest.ui.pages {
|
||||
let page = match entry {
|
||||
pinakes_plugin_api::manifest::UiPageEntry::Inline(page) => {
|
||||
(**page).clone()
|
||||
},
|
||||
pinakes_plugin_api::manifest::UiPageEntry::File { .. } => {
|
||||
// File-referenced pages require a base path to resolve;
|
||||
// skip them here as they should have been loaded at startup.
|
||||
continue;
|
||||
},
|
||||
};
|
||||
pages.push((plugin.id.clone(), page));
|
||||
let plugin_dir = plugin
|
||||
.manifest_path
|
||||
.as_ref()
|
||||
.and_then(|p| p.parent())
|
||||
.map(std::path::Path::to_path_buf);
|
||||
let Some(plugin_dir) = plugin_dir else {
|
||||
// No manifest path; serve only inline pages.
|
||||
for entry in &plugin.manifest.ui.pages {
|
||||
if let pinakes_plugin_api::manifest::UiPageEntry::Inline(page) = entry
|
||||
{
|
||||
pages.push((plugin.id.clone(), (**page).clone()));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
};
|
||||
match plugin.manifest.load_ui_pages(&plugin_dir) {
|
||||
Ok(loaded) => {
|
||||
for page in loaded {
|
||||
pages.push((plugin.id.clone(), page));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
"Failed to load UI pages for plugin '{}': {e}",
|
||||
plugin.id
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
pages
|
||||
}
|
||||
|
||||
/// List all UI pages provided by loaded plugins, including each plugin's
|
||||
/// declared endpoint allowlist.
|
||||
///
|
||||
/// Returns a vector of `(plugin_id, page, allowed_endpoints)` tuples. The
|
||||
/// `allowed_endpoints` list mirrors the `required_endpoints` field from the
|
||||
/// plugin manifest's `[ui]` section.
|
||||
pub async fn list_ui_pages_with_endpoints(
|
||||
&self,
|
||||
) -> Vec<(String, pinakes_plugin_api::UiPage, Vec<String>)> {
|
||||
let registry = self.registry.read().await;
|
||||
let mut pages = Vec::new();
|
||||
for plugin in registry.list_all() {
|
||||
if !plugin.enabled {
|
||||
continue;
|
||||
}
|
||||
let allowed = plugin.manifest.ui.required_endpoints.clone();
|
||||
let plugin_dir = plugin
|
||||
.manifest_path
|
||||
.as_ref()
|
||||
.and_then(|p| p.parent())
|
||||
.map(std::path::Path::to_path_buf);
|
||||
let Some(plugin_dir) = plugin_dir else {
|
||||
for entry in &plugin.manifest.ui.pages {
|
||||
if let pinakes_plugin_api::manifest::UiPageEntry::Inline(page) = entry
|
||||
{
|
||||
pages.push((plugin.id.clone(), (**page).clone(), allowed.clone()));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
};
|
||||
match plugin.manifest.load_ui_pages(&plugin_dir) {
|
||||
Ok(loaded) => {
|
||||
for page in loaded {
|
||||
pages.push((plugin.id.clone(), page, allowed.clone()));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
"Failed to load UI pages for plugin '{}': {e}",
|
||||
plugin.id
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
pages
|
||||
}
|
||||
|
||||
/// Collect CSS custom property overrides declared by all enabled plugins.
|
||||
///
|
||||
/// When multiple plugins declare the same property name, later-loaded plugins
|
||||
/// overwrite earlier ones. Returns an empty map if no plugins are loaded or
|
||||
/// none declare theme extensions.
|
||||
pub async fn list_ui_theme_extensions(
|
||||
&self,
|
||||
) -> std::collections::HashMap<String, String> {
|
||||
let registry = self.registry.read().await;
|
||||
let mut merged = std::collections::HashMap::new();
|
||||
for plugin in registry.list_all() {
|
||||
if !plugin.enabled {
|
||||
continue;
|
||||
}
|
||||
for (k, v) in &plugin.manifest.ui.theme_extensions {
|
||||
merged.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
merged
|
||||
}
|
||||
|
||||
/// List all UI widgets provided by loaded plugins.
|
||||
///
|
||||
/// Returns a vector of `(plugin_id, widget)` tuples for all enabled plugins
|
||||
/// that provide widgets in their manifests.
|
||||
pub async fn list_ui_widgets(
|
||||
&self,
|
||||
) -> Vec<(String, pinakes_plugin_api::UiWidget)> {
|
||||
let registry = self.registry.read().await;
|
||||
let mut widgets = Vec::new();
|
||||
for plugin in registry.list_all() {
|
||||
if !plugin.enabled {
|
||||
continue;
|
||||
}
|
||||
for widget in &plugin.manifest.ui.widgets {
|
||||
widgets.push((plugin.id.clone(), widget.clone()));
|
||||
}
|
||||
}
|
||||
widgets
|
||||
}
|
||||
|
||||
/// Check if a plugin is loaded and enabled
|
||||
pub async fn is_plugin_enabled(&self, plugin_id: &str) -> bool {
|
||||
let registry = self.registry.read().await;
|
||||
|
|
@ -746,6 +850,7 @@ mod tests {
|
|||
},
|
||||
capabilities: Default::default(),
|
||||
config: Default::default(),
|
||||
ui: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ mod tests {
|
|||
},
|
||||
capabilities: ManifestCapabilities::default(),
|
||||
config: HashMap::new(),
|
||||
ui: Default::default(),
|
||||
};
|
||||
|
||||
RegisteredPlugin {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue