forked from 1b-agent/opencadstudio-upstream
Gate target-specific code so neither build warns about the other's items: native-only helpers (view, window icon, file dialogs, file association, plugin host/guard/registry, self-update) are cfg'd to non-wasm; the wasm-only font fallback (script_of / cjk_lang / request) is cfg'd to wasm. Drop an unused mut, allow the few genuinely-conditional dead items (mesh_click_hit, GPU layout helpers, parity stubs), and gate the embedded PlotSettings-unrelated dead constants. Both targets now build clean (only the unavoidable nom/quick-xml future-incompat dependency notice remains). Co-Authored-By: Claude Opus 4.8 <[email protected]>
90 lines
3.4 KiB
Rust
90 lines
3.4 KiB
Rust
// Plugin registry — external (dynamically-loaded) plugins only. OpenCADStudio
|
|
// ships no built-in add-ons; every plugin is a cdylib loaded from the plugins
|
|
// folder at startup (see `external`) and the marketplace installs them there.
|
|
|
|
use crate::app::OpenCADStudio;
|
|
use crate::modules::{registry as core_registry, CadModule};
|
|
|
|
/// Core ribbon tabs plus every loaded external add-on tab.
|
|
pub fn all_ribbon_modules() -> Vec<Box<dyn CadModule>> {
|
|
ribbon_modules_enabled(&rustc_hash::FxHashSet::default())
|
|
}
|
|
|
|
/// Core ribbon tabs plus the tabs of loaded external plugins whose id is **not**
|
|
/// in `disabled` (sorted by `manifest.ribbon_order`).
|
|
pub fn ribbon_modules_enabled(
|
|
disabled: &rustc_hash::FxHashSet<String>,
|
|
) -> Vec<Box<dyn CadModule>> {
|
|
#[cfg_attr(target_arch = "wasm32", allow(unused_mut))]
|
|
let mut core = core_registry::all_modules();
|
|
// Dynamically-loaded external plugins contribute tabs (their libraries stay
|
|
// resident for the session, so these vtables remain valid).
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
{
|
|
let mut addons: Vec<(i32, Box<dyn CadModule>)> = Vec::new();
|
|
crate::plugin::external::with_loaded(|loaded| {
|
|
for lp in loaded {
|
|
if disabled.contains(lp.id.as_str()) {
|
|
continue;
|
|
}
|
|
// Guard the plugin's ribbon build so a panic there can't take
|
|
// down the host — the plugin just contributes no tab. (#145)
|
|
if let Some(entry) = crate::plugin::guard("ribbon", || {
|
|
(lp.plugin().manifest().ribbon_order, lp.plugin().ribbon())
|
|
}) {
|
|
addons.push(entry);
|
|
}
|
|
}
|
|
});
|
|
addons.sort_by_key(|(order, _)| *order);
|
|
core.extend(addons.into_iter().map(|(_, ribbon)| ribbon));
|
|
}
|
|
let _ = disabled;
|
|
core
|
|
}
|
|
|
|
/// Dispatch `cmd` to a loaded external plugin (skipping disabled ones).
|
|
/// Returns true if one handled it.
|
|
pub(crate) fn try_dispatch(app: &mut OpenCADStudio, tab: usize, cmd: &str) -> bool {
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
{
|
|
use super::host::HostSession;
|
|
let disabled = app.disabled_plugin_ids();
|
|
let handled = crate::plugin::external::with_loaded(|loaded| {
|
|
let mut host = HostSession::new(app, tab);
|
|
for lp in loaded {
|
|
if disabled.contains(lp.id.as_str()) {
|
|
continue;
|
|
}
|
|
// A panic inside the plugin's dispatch must not crash the host;
|
|
// treat a panicking plugin as "didn't handle it". (#145)
|
|
if crate::plugin::guard("dispatch", || lp.plugin().dispatch(&mut host, cmd))
|
|
.unwrap_or(false)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
});
|
|
if handled {
|
|
return true;
|
|
}
|
|
}
|
|
let _ = (app, tab, cmd);
|
|
false
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn ribbon_is_core_only_without_external_plugins() {
|
|
// No external plugins are loaded under test, so the ribbon is exactly
|
|
// the built-in core tabs.
|
|
let titles: Vec<&str> = all_ribbon_modules().iter().map(|m| m.title()).collect();
|
|
assert!(!titles.is_empty(), "expected core ribbon tabs");
|
|
assert_eq!(titles.len(), core_registry::all_modules().len());
|
|
}
|
|
}
|