Contributing Complex Plugins¶
Use this path when a plugin needs executable behavior (protocol handlers, upload logic, Tauri integration, native encoders). If the change is data-only, use a simple manifest plugin.
When to choose a complex plugin¶
Use a complex plugin if you need one or more of:
- custom network/protocol operations
- custom upload flow or progress semantics
- desktop/runtime behavior in Tauri
- custom container format encoder in native Rust
Use a simple plugin if you only need:
- printer preset packs
- material templates
- metadata + assets
Architecture rules¶
Complex plugin contributions must follow these rules:
- Plugin-owned behavior stays in
plugins/<vendor>/... - Core app surfaces stay generic (no vendor hardcoding in shared routes/registries)
- Registration is generated, not hand-wired
- Allowlist + integrity checks are required
Required integration flow¶
Source of truth¶
Each complex plugin must provide:
plugins/<vendor>/pluginDefinition.ts- default export of
ComplexPluginDefinition - includes
capabilitiesblock
Allowlist¶
Add plugin id to:
src/config/complex-plugin-allowlist.json
Generated registration¶
Generator:
scripts/generate-plugin-registry.mjs
Generated outputs:
src/features/plugins/generatedBuiltinComplexPlugins.tssrc/features/plugins/generatedBuiltinComplexPluginNetworkHandlers.tssrc/features/plugins/generatedBuiltinComplexPluginUploadHandlers.tssrc/features/plugins/generatedBuiltinComplexPluginFileTypeHandlers.tssrc-tauri/src/generated_builtin_plugins.rsrust/dragonfruit-slicer-v3/src/encoders/generated_plugin_encoders.rssrc-tauri/generated_crate_requirements.toml(audit of all plugin cargo dependencies)
Do not edit generated files manually.
Capability contract and entrypoints¶
capabilities in pluginDefinition.ts must match the files you provide.
| Capability flag | Required file(s) | Required export |
|---|---|---|
networkOperations: true |
plugins/<vendor>/network/networkHandlers.ts |
handlePluginNetworkOperation |
uploadWithProgress: true |
plugins/<vendor>/network/index.ts |
uploadPrintJobWithProgress |
tauriRuntimePlugin: true |
plugins/<vendor>/rust/plugin.rs + plugins/<vendor>/rust/network.rs |
runtime registration/dispatch symbols |
slicerEncoder: true |
plugins/<vendor>/slicing/rust/encoder_impl.rs |
create_plugin_encoder() |
fileType: true |
plugins/<vendor>/fileTypeHandlers.ts |
handleFileTypeImport |
If capabilities and files disagree, generation fails intentionally.
File type import capability¶
Set fileType: true when your plugin can import files of a given extension into DragonFruit scenes.
The plugin must:
- Declare one or more
fileTypesentries inpluginDefinition.ts(see typePluginFileTypeDefinitionincomplexPluginContracts.ts). - Export
handleFileTypeImportfromfileTypeHandlers.ts.
The host reads GENERATED_BUILTIN_COMPLEX_PLUGIN_FILE_TYPE_HANDLERS from the generated registry to dispatch file imports to the correct plugin at runtime. The host also reads the fileTypes metadata (from the definition) to build OS-level file picker filters and drive scene file routing.
Naming rule: File-type plugin code must use the format's technical acronym (e.g. LYS), never brand or product names.
Multiple container formats per plugin (optional)¶
If your plugin supports multiple container formats (e.g., Anycubic with both AFF and AZFF), provide:
plugins/<vendor>/slicing/formats.json
The function returns multiple encoder instances, one per format. Each encoder's output_format() method must match at least one extension in formats.json.
Extra Cargo crates for slicer encoder (optional)¶
If your encoder implementation requires extra Rust crates beyond the core dragonfruit-slicer-v3 deps, declare them in:
plugins/<vendor>/slicing/rust/requiredCrates.toml
Generator validates version conflicts and auto-merges dependencies into the slicer crate.
Validation commands (required)¶
Run these before opening a PR:
npm run generate:plugin-registrynpm run check:plugin-allowlistnpm run check:generated-plugin-registrycargo check --manifest-path rust/dragonfruit-slicer-v3/Cargo.tomlnpm run buildcargo check --manifest-path src-tauri/Cargo.toml
Optional but recommended:
npm test
Common generator failures¶
Common generation failures include:
- plugin discovered but missing from allowlist
- allowlisted plugin missing locally (often an uninitialized submodule)
- missing
capabilitiesblock - capability/file mismatch (declared capability but missing required file, or inverse)
- malformed
formats.json - malformed
requiredCrates.toml - crate version conflict across plugins
When these happen, fix contract mismatches first, then regenerate.
Safety requirements¶
Complex plugin PRs must preserve DragonFruit’s safety guarantees:
- no runtime code fetching/eval
- no untrusted binary execution paths
- strict input validation on network and file boundaries
- explicit timeout/error handling in protocol operations
PR checklist¶
Before requesting review:
- [ ] Plugin logic is isolated under
plugins/<vendor>/... - [ ]
pluginDefinition.tsexists, default-exports, and declares capabilities - [ ] Plugin ID is allowlisted
- [ ] Generated registries are up-to-date and committed
- [ ] No vendor hardcoding leaked into generic app routes/registries
- [ ] Validation commands pass
Useful references¶
- Plugin framework overview:
docs/dev/plugins-framework.md - Plugin source folder:
plugins/ - Generic plugin network route:
src/app/api/network/plugin/route.ts - Plugin settings UI:
src/components/settings/PluginsSettingsTab.tsx - Tauri plugin registry:
src-tauri/src/plugin_registry.rs