# fai-studio-plugin-sdk Rust SDK for writing F∆I Studio plugins. ## Status **v0.1 builds.** Studio plugins targeting the `studio-plugin` world declared in `wit/studio-plugin.wit` can be compiled to `wasm32-wasip2` against this crate today. The proof-of- contract plugin is `studio-theme-solarized` in the fai-modules namespace. What's NOT yet implemented: - **Hub side**: a Component-Model loader path for `studio-plugin`-world components + the `InvokePlugin` gRPC RPC that Studio talks to. Tracked in `fai/platform/docs/advanced/studio-plugin-sdk.md`. - **Studio side**: `plugin_host.dart` that discovers installed `studio.*` capabilities and dispatches. - **Per-hook worlds**: today's v0.1 ships a single world (`studio-plugin`) that exports all three hooks (`theme`, `translate`, `output-view`). A plugin only caring about one hook still has to declare empty / declined impls for the other two. v0.2 splits into one world per hook. - **`#[plugin(...)]` proc-macro**: v0.1 ships a plain `export_plugin!` macro that wraps the wit-bindgen-emitted `__export_studio_plugin_impl!`. The proc-macro that generates the WIT bindings + the `Guest` impls in one pass arrives in v0.2. ## Usage ```toml # Cargo.toml of your plugin [package] name = "my_studio_plugin" version = "0.1.0" edition = "2021" # match SDK edition [lib] crate-type = ["cdylib"] [dependencies] fai-studio-plugin-sdk = { git = "https://git.flemming.ai/fai/studio-plugin-sdk", branch = "main" } ``` ```rust // src/lib.rs of your plugin use fai_studio_plugin_sdk::{bindings, export_plugin}; type ColorScheme = bindings::fai::studio_plugin::plugin_types::ColorScheme; type PluginError = bindings::fai::studio_plugin::plugin_types::PluginError; type RenderedOutput = bindings::fai::studio_plugin::plugin_types::RenderedOutput; struct MyPlugin; impl bindings::exports::fai::studio_plugin::theme::Guest for MyPlugin { fn theme_for(brightness: String) -> Result { // Return a ColorScheme or PluginError::Declined. } } // v0.1: every plugin must declare all three hooks. Plugins // that don't actually serve a hook return Declined. impl bindings::exports::fai::studio_plugin::translate::Guest for MyPlugin { fn translate(_: String, _: String, _: String) -> Result { Err(PluginError::Declined("not a translate plugin".to_string())) } } impl bindings::exports::fai::studio_plugin::output_view::Guest for MyPlugin { fn render(_: String, _: Vec) -> Result { Err(PluginError::Declined("not a view plugin".to_string())) } } export_plugin!(MyPlugin); ``` Then: ```bash cargo build --release --target wasm32-wasip2 fai pack . -o my-plugin-0.1.0.fai ``` ## WIT layout ``` wit/ ├── studio-plugin.wit # main world, fai:studio-plugin@0.1.0 └── deps/ └── platform/ └── world.wit # fai:platform@1.0.0 (host imports) ``` Both files are verbatim copies of the authoritative versions in `fai/platform/wit/`. When the contract changes upstream, re-copy here in lock-step. ## Why edition 2021 wit-bindgen 0.36 emits `#[link_section]` / unsafe-attribute syntax that the edition-2024 compiler rejects. Bump when moving to a wit-bindgen version with edition-2024-compatible codegen.