fix(web): hide unsupported plugin manager

Fixes #567
This commit is contained in:
Hakan Seven committed 2026-07-29 16:17:17 +03:00
1 parent 6686141d3b
commit 2424eeb74d
10 files changed
+56 -22

No files matched your search

+9
View File
@@ -18,6 +18,7 @@ browser can't provide. This page lists the differences.
| Parallelism | Multi-threaded (rayon) | Single-threaded |
| GPU backend | Vulkan / DX12 / Metal (wgpu) | WebGL2 (WebGPU where available) |
| Update check / external links | `ureq`, `open` | skipped / `window.open` |
| External plugins | Native packages + Plugin Manager | **No** — native libraries cannot run in a browser |
## Details
@@ -76,6 +77,14 @@ native and sequential `std` iterators on wasm, so the same call sites run in
parallel on the desktop and serially in the browser. Large drawings are slower
on the web.
### External plugins — web: unavailable
Marketplace plugins are native `.dll`, `.so`, or `.dylib` packages launched in
an isolated child process. Browsers cannot load those libraries or spawn the
plugin runner, so the web build does not show the Plugin Manager or register
its commands. Installed desktop plugins remain in the per-user plugins folder
and load again on the next desktop launch.
### Platform shims (`src/sys.rs`)
- `open_url`: `open::that` on native, `window.open(_blank)` on web.
- `download_bytes`: web only (Blob + anchor download).
+4 -3
View File
@@ -333,8 +333,9 @@ plugin dead, and reports a `CallTimeout` error via the normal plugin error path.
## Marketplace
The **Plugin Manager** (`PLUGINS` / `PLUGINMANAGER`, or the Start-page button)
installs plugins from GitHub Releases:
The desktop **Plugin Manager** (`PLUGINS` / `PLUGINMANAGER`, or the Start-page
button) installs plugins from GitHub Releases. It is hidden in the browser
build because marketplace packages are native dynamic libraries:
- **Curated registry** — [`plugins/registry.json`](../plugins/registry.json) in
this repo lists discoverable plugins. The host fetches it from `main` at
@@ -343,7 +344,7 @@ installs plugins from GitHub Releases:
[`plugins/README.md`](../plugins/README.md)); merged PRs reach every user with
no app update.
- **Manual link** — *Add a repository* (`owner/repo`) for unlisted or private
dev repos; linked repos persist in `settings.txt` (`plugin_repos=`).
dev repos; linked repos persist in `settings.json`.
- **Install / upgrade / reinstall** — pick a release from the dropdown and
*Install*; the host downloads the platform asset + `plugin.toml` into the
plugins folder, checking `api_version` first. Reinstalling overwrites and
+2 -2
View File
@@ -236,8 +236,6 @@ pub fn start_allowed(cmd: &str) -> bool {
| "REPORT"
| "CHANGELOG"
| "ABOUT"
| "PLUGINS"
| "PLUGINMANAGER"
| "DONATE"
| "WEBVERSION"
| "HELP"
@@ -247,6 +245,8 @@ pub fn start_allowed(cmd: &str) -> bool {
| "CUILOAD"
| "CUIIMPORT"
)
|| (!cfg!(target_arch = "wasm32")
&& matches!(cmd, "PLUGINS" | "PLUGINMANAGER"))
}
// ── Autocomplete registry — one-shot commands ──────────────────────────────
+7
View File
@@ -212,10 +212,17 @@ impl OpenCADStudio {
return Some(Task::done(Message::AboutOpen));
}
#[cfg(not(target_arch = "wasm32"))]
"PLUGINS" | "PLUGINMANAGER" => {
return Some(Task::done(Message::PluginManagerOpen));
}
#[cfg(target_arch = "wasm32")]
"PLUGINS" | "PLUGINMANAGER" => {
self.command_line
.push_info("External plugins are available in the desktop app.");
}
"CHANGELOG" => {
crate::sys::open_url("https://github.com/HakanSeven12/OpenCADStudio/releases");
self.command_line.push_info("Opening release notes...");
+1
View File
@@ -1313,6 +1313,7 @@ impl ClipboardDeps {
pub enum ModalKind {
About,
Shortcuts,
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
PluginManager,
UpdateNotice,
Layers,
+13 -5
View File
@@ -291,8 +291,11 @@ impl OpenCADStudio {
}
#[cfg(target_arch = "wasm32")]
pub(in crate::app) fn fetch_releases_task(&self, _repo: String) -> Task<Message> {
Task::none()
pub(in crate::app) fn fetch_releases_task(&self, repo: String) -> Task<Message> {
Task::done(Message::PluginReleasesFetched(
repo,
Err("External plugins are available in the desktop app.".to_string()),
))
}
/// Background task: fetch a repository README from its default branch.
@@ -306,8 +309,11 @@ impl OpenCADStudio {
}
#[cfg(target_arch = "wasm32")]
pub(in crate::app) fn fetch_plugin_readme_task(&self, _repo: String) -> Task<Message> {
Task::none()
pub(in crate::app) fn fetch_plugin_readme_task(&self, repo: String) -> Task<Message> {
Task::done(Message::PluginReadmeFetched(
repo,
Err("Plugin details are available in the desktop app.".to_string()),
))
}
/// Background task: download and install the `tag` release of `owner/repo`.
@@ -328,7 +334,9 @@ impl OpenCADStudio {
#[cfg(target_arch = "wasm32")]
pub(in crate::app) fn install_task(&self, _repo: String, _tag: String) -> Task<Message> {
Task::none()
Task::done(Message::PluginInstalled(Err(
"External plugins are available in the desktop app.".to_string(),
)))
}
/// Gather the full persisted config (all sections) from live app state.
+12 -8
View File
@@ -4056,14 +4056,20 @@ impl OpenCADStudio {
// ── Plugin Manager window ─────────────────────────────────────
Message::PluginManagerOpen => {
// 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(target_arch = "wasm32")]
{
self.command_line
.push_info("External plugins are available in the desktop app.");
return Task::none();
}
#[cfg(not(target_arch = "wasm32"))]
{
// 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.
self.plugin_registry_loading = true;
self.plugin_registry_error = None;
self.plugin_registry_error_details_open = false;
@@ -4100,8 +4106,6 @@ impl OpenCADStudio {
}
return Task::batch(tasks);
}
#[cfg(target_arch = "wasm32")]
Task::none()
}
Message::PluginManagerClose => {
self.close_active_modal();
+3 -3
View File
@@ -2299,12 +2299,12 @@ pub(super) fn start_page_view<'a>(
)
.into(),
outline_btn("Options", Message::OptionsOpen).into(),
outline_btn("Plugins", Message::PluginManagerOpen).into(),
];
// The web build is already in the browser, so only the desktop offers a
// link to the web version.
// External plugins are native dynamic libraries and the web build is
// already in the browser, so both actions are desktop-only.
#[cfg(not(target_arch = "wasm32"))]
{
secondary_items.push(outline_btn("Plugins", Message::PluginManagerOpen).into());
// Filled with the active theme's primary colour.
secondary_items.push(
button(text("OCS Web").size(14))
+2
View File
@@ -13,6 +13,8 @@
//! <lib<name>.so | .dll | .dylib>
//! ```
#![cfg_attr(target_arch = "wasm32", allow(dead_code))]
use std::path::PathBuf;
/// One entry in the curated plugin registry (`plugins/registry.json`).
+3 -1
View File
@@ -33,7 +33,9 @@ pub struct MarketView<'a> {
pub status: &'a str,
}
// Register the command names for autocomplete.
// External plugins are native dynamic libraries, so the browser build must not
// advertise a manager it cannot use.
#[cfg(not(target_arch = "wasm32"))]
inventory::submit!(crate::command::CommandRegistration {
names: &["PLUGINS", "PLUGINMANAGER"]
});