Scarab Plugin Template
This is a template for creating your own Scarab terminal emulator plugins.
Quick Start
-
Copy this directory:
cp -r examples/plugin-template my-plugin cd my-plugin -
Update
Cargo.toml:- Change
nameto your plugin name - Update version and other metadata
- Change
-
Update
src/lib.rs:- Rename
ExamplePluginto your plugin name - Update the
PluginMetadatain the constructor - Implement the hooks you need
- Rename
-
Build your plugin:
cargo build --release -
Install your plugin:
mkdir -p ~/.config/scarab/plugins cp target/release/libscarab_plugin_example.so ~/.config/scarab/plugins/ -
Configure your plugin in
~/.config/scarab/plugins.toml:[[plugin]] name = "my-plugin" path = "~/.config/scarab/plugins/libmy_plugin.so" enabled = true [plugin.config] # Your custom configuration here
Plugin Structure
use scarab_plugin_api::*;
pub struct MyPlugin {
metadata: PluginMetadata,
// Your state here
}
impl Plugin for MyPlugin {
fn metadata(&self) -> &PluginMetadata {
&self.metadata
}
// Implement hooks you need
async fn on_output(&mut self, line: &str, ctx: &PluginContext) -> Result<Action> {
// Your logic here
Ok(Action::Continue)
}
}Available Hooks
on_load(&mut self, ctx: &mut PluginContext)- Called when plugin loadson_unload(&mut self)- Called when plugin unloadson_output(&mut self, line: &str, ctx: &PluginContext)- Before output displayedon_input(&mut self, input: &[u8], ctx: &PluginContext)- After input receivedon_pre_command(&mut self, command: &str, ctx: &PluginContext)- Before command executeson_post_command(&mut self, command: &str, exit_code: i32, ctx: &PluginContext)- After command completeson_resize(&mut self, cols: u16, rows: u16, ctx: &PluginContext)- Terminal resizedon_attach(&mut self, client_id: u64, ctx: &PluginContext)- Client attachedon_detach(&mut self, client_id: u64, ctx: &PluginContext)- Client detached
Hook Actions
Hooks can return different actions:
Action::Continue- Pass to next pluginAction::Stop- Stop processing, don’t call remaining pluginsAction::Modify(Vec<u8>)- Modify data and continue
Plugin Context
The PluginContext provides access to terminal state:
// Get terminal size
let (cols, rows) = ctx.get_size();
// Get cell at position
let cell = ctx.get_cell(x, y);
// Set cell at position
ctx.set_cell(x, y, cell);
// Get line of text
let line = ctx.get_line(y);
// Get environment variable
let value = ctx.get_env("HOME");
// Store/retrieve plugin data
ctx.set_data("key", "value");
let value = ctx.get_data("key");
// Logging
ctx.log(LogLevel::Info, "message");
// Send notification
ctx.notify("Important message");Configuration
Access plugin configuration through PluginContext:
// In on_load
async fn on_load(&mut self, ctx: &mut PluginContext) -> Result<()> {
// Get required config
let threshold: u32 = ctx.config.get("threshold")?;
// Get optional config
let enabled: bool = ctx.config.get_opt("enabled").unwrap_or(true);
Ok(())
}Best Practices
- Keep hooks fast - Aim for <1ms execution time
- Handle errors gracefully - Don’t panic, return
Result - Use logging - Help users debug issues
- Document configuration - Clearly document config options
- Test thoroughly - Write tests for your plugin logic
- Version carefully - Follow semver for compatibility
Example Plugins
See examples/ directory for more examples:
auto-notify- Notification on error keywordsvim-mode- Vim-style keybindingssession-logger- Log all terminal output
Troubleshooting
Plugin not loading
- Check file permissions:
chmod +x ~/.config/scarab/plugins/myplugin.so - Check Scarab logs:
scarab --log-level debug - Verify plugin path in
plugins.toml
Plugin disabled automatically
Plugins are auto-disabled after 3 consecutive failures. Check:
- Plugin logs for errors
- API version compatibility
- Configuration validity
Hook not being called
- Verify hook is implemented (not just default)
- Check plugin is enabled:
scarab plugins list - Check hook execution order in logs