feat(plugin): load external cdylib plugins at runtime (phase 2)
Move BuiltinPlugin into ocs_plugin_api (host feature) so out-of-tree crates can implement it, and add export_plugin! to emit the two C symbols a cdylib exposes: ocs_plugin_api_version (checked first, so an ABI-incompatible build never runs) and ocs_plugin_register -> boxed BuiltinPlugin. The host loads every compatible package from the plugins folder at startup via libloading (desktop only), keeps the library resident for the session, and merges its ribbon tab + command dispatch into the same paths as built-ins (honouring enable/disable). The Plugin Manager shows external packages with a Loaded / incompatible status. Approach B: the plugin hands back a boxed trait object, assuming a matching toolchain + ocs_plugin_api version (the version symbol enforces the latter). crates/ocs_example_plugin is the reference cdylib. Part of the #100 extensibility epic (phase 2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f6219a9609
commit
29c71be101
14 changed files with 402 additions and 36 deletions
|
|
@ -156,13 +156,15 @@ fn status_badge<'a>(label: &str, color: Color) -> Element<'a, Message> {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn external_card<'a>(p: &ExternalPlugin) -> Element<'a, Message> {
|
||||
let (status, color) = if !p.api_compatible() {
|
||||
fn external_card<'a>(p: &ExternalPlugin, loaded: bool) -> Element<'a, Message> {
|
||||
let (status, color) = if loaded {
|
||||
("Loaded", Color { r: 0.2, g: 0.5, b: 0.3, a: 1.0 })
|
||||
} else if !p.api_compatible() {
|
||||
("API incompatible", Color { r: 0.55, g: 0.28, b: 0.28, a: 1.0 })
|
||||
} else if !p.lib_present {
|
||||
("No library", Color { r: 0.5, g: 0.42, b: 0.2, a: 1.0 })
|
||||
} else {
|
||||
("Ready to load", Color { r: 0.2, g: 0.45, b: 0.28, a: 1.0 })
|
||||
("Restart to load", Color { r: 0.5, g: 0.42, b: 0.2, a: 1.0 })
|
||||
};
|
||||
let header = row![
|
||||
text(p.name.clone()).size(15).color(WHITE),
|
||||
|
|
@ -201,6 +203,7 @@ pub fn view_window<'a>(
|
|||
plugins: &[&'static PluginManifest],
|
||||
disabled: &FxHashSet<String>,
|
||||
externals: &[ExternalPlugin],
|
||||
loaded: &FxHashSet<String>,
|
||||
) -> Element<'a, Message> {
|
||||
let title = text("Installed Plugins").size(20).color(WHITE);
|
||||
let subtitle = text(format!(
|
||||
|
|
@ -229,7 +232,7 @@ pub fn view_window<'a>(
|
|||
.color(DIM),
|
||||
);
|
||||
for p in externals {
|
||||
list = list.push(external_card(p));
|
||||
list = list.push(external_card(p, loaded.contains(&p.id)));
|
||||
}
|
||||
}
|
||||
let body: Element<'_, Message> = scrollable(list.width(Fill)).height(Fill).into();
|
||||
|
|
|
|||
Loading…
Reference in a new issue