diff --git a/docs/PLUGIN_DEVELOPMENT.md b/docs/PLUGIN_DEVELOPMENT.md index b2d0c3c..d2603c4 100644 --- a/docs/PLUGIN_DEVELOPMENT.md +++ b/docs/PLUGIN_DEVELOPMENT.md @@ -419,49 +419,92 @@ owlry.provider.register({ ## Rune Plugin API -Rune plugins use a Rust-like syntax with memory safety. +Rune plugins use a Rust-like syntax with memory safety. Requires `owlry-rune` runtime. + +```bash +# Install Rune runtime +yay -S owlry-rune + +# Create plugin directory +mkdir -p ~/.config/owlry/plugins/my-rune-plugin +``` ### Plugin Manifest +Rune plugins declare providers in `[[providers]]` sections of `plugin.toml`. The runtime reads these declarations and maps them to the plugin's `refresh()` and `query()` functions. + ```toml [plugin] id = "my-rune-plugin" name = "My Rune Plugin" version = "1.0.0" -entry = "main.rn" +description = "A custom Rune plugin" +entry = "main.rn" # Default: main.rn; entry_point also accepted [[providers]] -id = "runeprovider" -name = "Rune Provider" -type = "static" +id = "myrune" +name = "My Rune Provider" +prefix = ":myrune" # Activates with :myrune prefix in search +icon = "application-x-executable" +type = "static" # "static" (refresh at startup) or "dynamic" (query per keystroke) +type_id = "myrune" # Short ID shown as badge in UI ``` ### Rune API ```rune -use owlry::{Item, log, notify}; +use owlry::Item; +/// Called once at startup and on each hot-reload for static providers pub fn refresh() { let items = []; - items.push(Item::new("id", "Name", "command") - .description("Description") - .icon("icon-name")); + items.push(Item::new("item-1", "Hello from Rune", "echo 'Hello!'") + .description("A Rune greeting") + .icon("face-smile") + .keywords(["hello", "rune"])); + + items.push(Item::new("item-2", "Another Item", "notify-send 'Hi'") + .description("Send a notification") + .icon("dialog-information")); items } +/// Called per keystroke for dynamic providers pub fn query(q) { if q.is_empty() { return []; } - log::info(`Query: {q}`); - - [Item::new("result", `Result: {q}`, `echo {q}`)] + [Item::new("result", `Result: {q}`, `echo {q}`) + .description("Search result")] } ``` +### Item Builder + +The `Item` type is provided by the `owlry` module: + +```rune +// Create an item with required fields +let item = Item::new(id, name, command); + +// Optional builder methods (all return Item for chaining) +item = item.description("Description text"); +item = item.icon("icon-name"); // Freedesktop icon name +item = item.keywords(["tag1", "tag2"]); // Search keywords +``` + +### Logging + +```rune +owlry::log_info("Info message"); +owlry::log_warn("Warning message"); +owlry::log_error("Error message"); +owlry::log_debug("Debug message"); +``` + --- ## Best Practices @@ -489,19 +532,22 @@ extern "C" fn provider_refresh(handle: ProviderHandle) -> RVec { ``` ```lua --- Lua: Wrap in pcall for safety -function refresh() - local ok, result = pcall(function() - return load_items() - end) +-- Lua: Wrap refresh callback in pcall for safety +owlry.provider.register({ + name = "safe-provider", + refresh = function() + local ok, result = pcall(function() + return load_items() + end) - if not ok then - owlry.log.error("Failed: " .. result) - return {} - end + if not ok then + owlry.log.error("Failed: " .. result) + return {} + end - return result -end + return result + end, +}) ``` ### Icons