Script Plugins - ArunPrakashG/native-launcher GitHub Wiki
Script Plugins
Script plugins allow you to extend Native Launcher functionality using any programming language without compiling Rust code. Create plugins with simple shell scripts, Python, Node.js, Ruby, or any executable.
Quick Start
1. Create Your First Plugin
# Create plugin directory
mkdir -p ~/.config/native-launcher/plugins/hello-world
cd ~/.config/native-launcher/plugins/hello-world
2. Write Manifest (plugin.toml)
[metadata]
name = "Hello World"
description = "My first plugin"
author = "Your Name"
version = "1.0.0"
priority = 600
triggers = ["hello ", "hi "]
[execution]
script = "hello.sh"
interpreter = "bash"
output_format = "json"
timeout_ms = 1000
3. Write Script (hello.sh)
#!/usr/bin/env bash
cat <<EOF
{
"results": [
{
"title": "Hello, $1!",
"subtitle": "Press Enter to copy greeting",
"command": "echo 'Hello, $1!' | wl-copy && notify-send 'Copied' 'Greeting copied'"
}
]
}
EOF
4. Make Executable and Test
chmod +x hello.sh
./hello.sh "World" # Test directly
5. Use in Launcher
Restart Native Launcher and type: hello World
How Script Plugins Work
Architecture
User types: "weather Tokyo"
↓
Launcher checks triggers → Finds "weather " plugin
↓
Execute: bash weather.sh "Tokyo"
↓
Parse JSON/text output
↓
Display results in launcher
↓
User selects → Execute result command
Plugin Discovery
Native Launcher scans these directories on startup:
~/.config/native-launcher/plugins/- Your plugins/usr/share/native-launcher/plugins/- System plugins./plugins/- Development plugins (current directory)
Each subdirectory with a plugin.toml is loaded as a plugin.
Manifest Format
Complete Example
[metadata]
name = "Plugin Name" # Display name
description = "What it does" # Short description
author = "Your Name" # Author
version = "1.0.0" # Semantic version
priority = 600 # 0-1000 (higher = searched first)
icon = "icon-name" # Optional icon
# Command triggers (include trailing space for word-based)
triggers = ["command ", "cmd ", "c "]
[execution]
script = "main.sh" # Script path (relative to plugin dir)
interpreter = "bash" # Optional: bash, python3, node, ruby
output_format = "json" # "json" or "text"
timeout_ms = 3000 # Max execution time (milliseconds)
show_on_empty = false # Show results when query is empty
# Optional environment variables
[environment]
API_KEY = "your-key"
DEBUG = "true"
Priority System
Determines search order (higher priority = searched first):
| Priority | Usage |
|---|---|
| 900-1000 | Critical system plugins |
| 850 | Advanced Calculator |
| 750 | SSH Plugin |
| 700 | File Browser |
| 600-699 | User plugins (high) ← Your plugins |
| 500-599 | User plugins (normal) ← Your plugins |
| 400-499 | User plugins (low) |
| 0-399 | Experimental |
Recommendation: Use 600 for important plugins, 500 for normal plugins.
Triggers
Word-based triggers (require trailing space):
triggers = ["weather ", "w "]
# Matches: "weather Tokyo", "w London"
# Doesn't match: "weatherman" (no space)
Symbol-based triggers (no space needed):
triggers = ["#", "@color"]
# Matches: "#FF5733", "@color red"
Multiple triggers:
triggers = ["calc ", "calculate ", "="]
# Gives users flexibility
Output Formats
JSON Format (Recommended)
Advantages: Structured, supports all fields, easier to parse
Structure:
{
"results": [
{
"title": "Result Title",
"subtitle": "Optional description",
"command": "shell command to run",
"icon": "optional-icon-name"
}
]
}
Python Example:
#!/usr/bin/env python3
import json
import sys
query = sys.argv[1] if len(sys.argv) > 1 else ""
results = {
"results": [
{
"title": f"You searched for: {query}",
"subtitle": "Click to copy",
"command": f"echo '{query}' | wl-copy"
}
]
}
print(json.dumps(results))
Bash Example:
#!/usr/bin/env bash
QUERY="$1"
cat <<EOF
{
"results": [
{
"title": "Result: $QUERY",
"subtitle": "Press Enter",
"command": "notify-send 'You selected' '$QUERY'"
}
]
}
EOF
Text Format (Simple)
Advantages: Simple, quick for basic plugins
Format: title|subtitle|command (pipe-separated)
Example:
#!/usr/bin/env bash
echo "First Option|Does something cool|echo 'option1'"
echo "Second Option|Does something else|echo 'option2'"
echo "Simple Result" # Just title (command = title)
Manifest:
[execution]
output_format = "text"
Built-in Example Plugins
🌤️ Weather
Get weather forecasts for any location.
Install:
cp -r /path/to/examples/plugins/weather ~/.config/native-launcher/plugins/
Usage:
weather Tokyo- Current weather in Tokyow London- Quick weather lookup
Features:
- Current conditions (temp, humidity, wind)
- Copy to clipboard
- Open forecast in browser
- ASCII art display
😀 Emoji Search
Search 200+ emojis by keyword.
Install:
cp -r /path/to/examples/plugins/emoji ~/.config/native-launcher/plugins/
Usage:
emoji smile- Find smiling emojisem heart- Find heart emojis:fire- Quick emoji search
Features:
- Keyword-based search
- Instant clipboard copy
- Desktop notification
🎨 Color Picker
Convert colors between formats.
Install:
cp -r /path/to/examples/plugins/color ~/.config/native-launcher/plugins/
Usage:
color #FF5733- Convert hexcol rgb(255,87,51)- Convert RGB#FF5733- Quick color lookup
Features:
- Hex ↔ RGB ↔ HSL conversion
- CSS variable format
- Tailwind CSS hints
Common Patterns
Clipboard Integration
Always use wl-copy for Wayland clipboard:
# Copy text
echo "Hello" | wl-copy
# Copy with notification
echo "Result" | wl-copy && notify-send 'Copied' 'Result'
# Copy without newline
echo -n "No newline" | wl-copy
In results:
{
"command": "echo -n 'Copy me' | wl-copy && notify-send 'Copied' 'Success'"
}
API Calls
Bash with curl:
#!/usr/bin/env bash
QUERY="$1"
DATA=$(curl -s "https://api.example.com/search?q=$QUERY")
# Parse with jq
TITLE=$(echo "$DATA" | jq -r '.title')
cat <<EOF
{
"results": [{"title": "$TITLE", "command": "echo 'Done'"}]
}
EOF
Python with requests:
#!/usr/bin/env python3
import requests
import json
import sys
query = sys.argv[1]
response = requests.get(f"https://api.example.com/search?q={query}")
data = response.json()
results = {
"results": [
{"title": data['title'], "command": "echo 'Done'"}
]
}
print(json.dumps(results))
File Operations
Open file/URL:
{
"command": "xdg-open '/path/to/file.pdf'"
}
{
"command": "xdg-open 'https://example.com'"
}
Terminal commands:
{
"command": "alacritty -e bash -c 'command; read -p \"Press Enter\"'"
}
Error Handling
Show errors as results:
#!/usr/bin/env bash
if [ -z "$1" ]; then
cat <<EOF
{
"results": [
{
"title": "Enter a query",
"subtitle": "Example: mycommand something",
"command": "echo 'No query'"
}
]
}
EOF
exit 0
fi
DATA=$(curl -s "https://api.example.com/$1" 2>/dev/null)
if [ $? -ne 0 ]; then
cat <<EOF
{
"results": [
{
"title": "API Error",
"subtitle": "Could not fetch data",
"command": "echo 'Error'"
}
]
}
EOF
exit 0
fi
# Process data...
Best Practices
Performance
✅ Do:
- Keep scripts fast (<100ms target)
- Cache API responses
- Limit results to 10-20
- Set reasonable timeouts
❌ Don't:
- Make slow API calls on every keystroke
- Return hundreds of results
- Use blocking operations
User Experience
✅ Do:
- Write clear, descriptive titles
- Explain what happens in subtitle
- Provide helpful empty-query hints
- Use desktop notifications for feedback
❌ Don't:
- Use cryptic abbreviations
- Return empty results silently
- Assume users know what will happen
Security
✅ Do:
- Quote all variables in shell commands
- Validate input before processing
- Check file paths exist
- Use proper escaping
❌ Don't:
- Use
evalon user input - Execute unvalidated commands
- Trust external data blindly
Bad (Command Injection):
command="$1" # User types: "; rm -rf /"
eval "$command" # ⚠️ DANGEROUS!
Good (Safe):
QUERY="$1"
echo "Safe: $QUERY" | wl-copy
Debugging & Testing
Test Plugin Directly
cd ~/.config/native-launcher/plugins/my-plugin
./script.sh "test query"
Validate JSON Output
./script.sh "query" | jq
# If jq fails, you have invalid JSON
Enable Debug Logs
RUST_LOG=debug native-launcher 2>&1 | grep -i plugin
Common Issues
Plugin not loading:
# Check TOML syntax
cat plugin.toml
# Check script exists
ls -lh script.sh
# Check executable permission
chmod +x script.sh
# Check logs
RUST_LOG=debug native-launcher
Script not executing:
# Test directly
./script.sh "test"
# Check shebang
head -1 script.sh # Should be #!/usr/bin/env bash
# Check interpreter
which python3
which bash
No results showing:
# Validate JSON
./script.sh "test" | jq
# Check trigger matches
# Query must start with trigger
# Verify priority
# Higher priority = searched first
Advanced Topics
Persistent State
Store plugin state in cache directory:
#!/usr/bin/env bash
CACHE_DIR="$HOME/.cache/native-launcher/my-plugin"
STATE_FILE="$CACHE_DIR/state.json"
mkdir -p "$CACHE_DIR"
# Read state
if [ -f "$STATE_FILE" ]; then
LAST_QUERY=$(jq -r '.last_query' "$STATE_FILE")
fi
# Save state
echo "{\"last_query\": \"$1\"}" > "$STATE_FILE"
Background Updates
Use systemd user timer for periodic tasks:
# ~/.config/systemd/user/my-plugin-update.timer
[Unit]
Description=My Plugin Update
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
[Install]
WantedBy=timers.target
# ~/.config/systemd/user/my-plugin-update.service
[Unit]
Description=Update My Plugin Cache
[Service]
Type=oneshot
ExecStart=/home/user/.config/native-launcher/plugins/my-plugin/update.sh
Enable:
systemctl --user enable --now my-plugin-update.timer
Multi-Language Scripts
Python with virtual environment:
[execution]
script = "venv/bin/python"
interpreter = "" # Script handles everything
[environment]
PYTHONPATH = "/path/to/venv/lib/python3.x/site-packages"
Node.js:
[execution]
script = "index.js"
interpreter = "node"
Ruby:
[execution]
script = "main.rb"
interpreter = "ruby"
Plugin Ideas
Suggested Plugins to Build
Search & Information:
- 📖 Dictionary - Word definitions (dict.org)
- 🌐 Translation - Translate text
- 📰 News - Headlines from API
- 🔍 Wikipedia - Quick lookups
- 🎬 IMDb Search - Movie info
Productivity:
- 📋 Clipboard History - Browse clipboard
- 📝 Snippet Manager - Code snippets
- 🔖 Bookmark Manager - Browser bookmarks
- 📅 Calendar - Quick event creation
- ⏰ Timer - Countdown timers
Development:
- 🐙 GitHub - Search repos/issues
- 📦 NPM Search - Package lookup
- 🦀 Crates.io - Rust crates
- 💻 StackOverflow - Question search
- 🐳 Docker - Container management
System:
- 💻 System Info - CPU/RAM/disk
- 🔧 Process Killer - Find and kill processes
- 📁 Recent Files - File history
- 🔌 Network Info - IP, speed test
- 🔋 Battery Status - Battery info
Utilities:
- 🔐 Password Generator - Secure passwords
- 📊 QR Code - Generate QR codes
- 🔢 Base64 - Encode/decode
- 🔑 Hash Calculator - MD5/SHA256
- 📏 Unit Converter - Advanced conversions
- 💱 Cryptocurrency - Live prices
- 📈 Stock Ticker - Stock quotes
Dependencies
Required Packages
For launcher:
wl-clipboard- Clipboard supportlibnotify- Desktop notifications
For Python plugins:
python3- Python interpreterpython3-pip- Package manager (for dependencies)
For API-based plugins:
curlorwget- HTTP requestsjq- JSON parsing (bash scripts)
Installation
Arch Linux:
sudo pacman -S wl-clipboard libnotify python python-pip curl jq
Ubuntu/Debian:
sudo apt install wl-clipboard libnotify-bin python3 python3-pip curl jq
Fedora:
sudo dnf install wl-clipboard libnotify python3 python3-pip curl jq
Distribution
Sharing Your Plugin
1. Create repository:
my-awesome-plugin/
├── README.md
├── plugin.toml
├── script.sh
├── LICENSE
└── examples/
└── usage.md
2. Installation instructions:
## Installation
```bash
git clone https://github.com/user/my-plugin \
~/.config/native-launcher/plugins/my-plugin
```
Or download:
wget https://github.com/user/my-plugin/releases/latest/download/plugin.tar.gz
tar -xzf plugin.tar.gz -C ~/.config/native-launcher/plugins/
**3. Document dependencies**:
```markdown
## Dependencies
- `curl` - For API requests
- `jq` - For JSON parsing
- `python3-requests` - For Python HTTP
Resources
Documentation
- Plugin Development Guide - Complete reference
- Example Plugins - Weather, emoji, color picker
- Plugin System - Overall architecture
Community
- GitHub Discussions - Share your plugins!
- Issue Tracker - Report bugs
- Wiki - This page and more
Tools
- jq - JSON processor for testing
- shellcheck - Bash script linter
- python -m json.tool - Validate JSON
FAQ
Q: Can I use any programming language?
A: Yes! Any language that can output text (JSON/plain) works. Bash, Python, Ruby, Node.js, Go, Rust executables, etc.
Q: How do I handle API keys securely?
A: Use environment variables in [environment] section, or read from ~/.config/my-plugin/config.
Q: Can plugins interact with each other?
A: Not currently. Each plugin runs independently.
Q: What if my script is slow?
A: Set a higher timeout_ms and cache results. Consider background updates.
Q: Can I use external dependencies?
A: Yes! Document them in your README. Python virtual envs and npm packages work fine.
Q: How do I update a plugin?
A: Replace files and restart launcher. Hot reload not yet supported.
Q: Is there a plugin marketplace?
A: Not yet, but planned for future releases!
Q: Can I make GUI plugins?
A: Not currently. Plugins return text results only. Custom UI planned for future.
License
Script plugins are independent projects. Your plugins can use any license.
Native Launcher itself is MIT licensed.