doc(plugin): add guides for Script and Full-featured Plugin development

* Introduced `Script Plugin Development Guide` for lightweight plugin creation.
* Added `Full-featured Plugin Development Guide` detailing comprehensive plugin architecture and features.
* Updated `_sidebar.md` to include links to the new guides.
* Enhanced `plugin_summary.md` with new plugin categories and implementation types.
This commit is contained in:
qianlifeng 2025-05-29 23:11:07 +08:00
parent fda4d4df3a
commit 4b97ebedf0
No known key found for this signature in database
4 changed files with 827 additions and 2 deletions

View File

@ -17,6 +17,8 @@
- Plugin
- [Plugin summary](plugin_summary.md)
- [Script Plugin Guide](script_plugin_guide.md)
- [Full-featured Plugin Guide](full_featured_plugin_guide.md)
- [Plugin store](plugin_store.md)
- [Write plugin](vue.md)
- [Submit plugin](helpers.md)

View File

@ -0,0 +1,370 @@
# Full-featured Plugin Development Guide
Full-featured plugins are comprehensive plugins that run in dedicated host processes and communicate with Wox via WebSocket. They provide the full power of the Wox plugin system with rich APIs, persistent state, and advanced features.
## Overview
Full-featured plugins are designed for complex applications that require:
- Persistent state management
- High-performance query processing
- Advanced Wox API integration (AI, settings, previews)
- Complex async operations
- Professional-grade plugin development
## Supported Languages
### Python Plugins
**Requirements:**
- Python >= 3.8 (Python 3.12 recommended)
- `wox-plugin` SDK
**Installation:**
```bash
# Using pip
pip install wox-plugin
# Using uv (recommended)
uv add wox-plugin
```
**Basic Structure:**
```python
from wox_plugin import Plugin, Query, Result, Context, PluginInitParams
class MyPlugin(Plugin):
async def init(self, ctx: Context, params: PluginInitParams) -> None:
self.api = params.api
self.plugin_dir = params.plugin_directory
async def query(self, ctx: Context, query: Query) -> list[Result]:
# Your plugin logic here
results = []
results.append(
Result(
title="Hello Wox",
subtitle="This is a sample result",
icon="path/to/icon.png",
score=100
)
)
return results
# MUST HAVE! The plugin class will be automatically loaded by Wox
plugin = MyPlugin()
```
### Node.js Plugins
**Requirements:**
- Node.js >= 16
- `@wox-launcher/wox-plugin` SDK
**Installation:**
```bash
npm install @wox-launcher/wox-plugin
# or
pnpm add @wox-launcher/wox-plugin
```
**Basic Structure:**
```javascript
import { Plugin, Query, Result, Context, PluginInitParams } from '@wox-launcher/wox-plugin'
class MyPlugin implements Plugin {
private api: any
private pluginDir: string
async init(ctx: Context, params: PluginInitParams): Promise<void> {
this.api = params.API
this.pluginDir = params.PluginDirectory
}
async query(ctx: Context, query: Query): Promise<Result[]> {
// Your plugin logic here
return [
{
Title: "Hello Wox",
SubTitle: "This is a sample result",
Icon: "path/to/icon.png",
Score: 100
}
]
}
}
// MUST HAVE! Export the plugin instance
export const plugin = new MyPlugin()
```
## Plugin Architecture
### Plugin Host System
Full-featured plugins run in dedicated host processes:
- **Python Host**: `wox.plugin.host.python` manages Python plugins
- **Node.js Host**: `wox.plugin.host.nodejs` manages Node.js plugins
### Communication Flow
1. **Wox Core****Plugin Host** (WebSocket)
2. **Plugin Host****Plugin Instance** (Direct method calls)
### Plugin Lifecycle
1. **Load**: Plugin host loads the plugin module
2. **Init**: Plugin initialization with API access
3. **Query**: Handle user queries (multiple times)
4. **Unload**: Plugin cleanup when disabled/uninstalled
## Rich API Features
### Core APIs
```python
# Change query
await self.api.change_query(ctx, ChangeQueryParam(query="new query"))
# Show/hide app
await self.api.show_app(ctx)
await self.api.hide_app(ctx)
# Notifications
await self.api.notify(ctx, "Hello from plugin!")
# Logging
await self.api.log(ctx, "info", "Plugin message")
# Settings
value = await self.api.get_setting(ctx, "setting_key")
# Translations
text = await self.api.get_translation(ctx, "i18n_key")
```
### AI Integration
```python
# Chat with AI
conversation = [
ai_message("You are a helpful assistant"),
user_message("What is 2+2?")
]
async def stream_callback(data):
# Handle streaming response
print(data.content)
response = await self.api.ai_chat(ctx, conversation, stream_callback)
```
### Advanced Result Features
```python
# Result with preview
Result(
title="Document",
subtitle="Click to preview",
preview=WoxPreview(
preview_type=WoxPreviewType.TEXT,
preview_data="Document content here..."
)
)
# Refreshable result
Result(
title="Live Data",
subtitle="Updates automatically",
on_refresh=self.refresh_data
)
# Result with custom actions
Result(
title="File",
subtitle="Multiple actions available",
actions=[
ResultAction(name="Open", action=self.open_file),
ResultAction(name="Delete", action=self.delete_file)
]
)
```
## Plugin Configuration
### plugin.json
```json
{
"Id": "my-awesome-plugin",
"Name": "My Awesome Plugin",
"Author": "Your Name",
"Version": "1.0.0",
"MinWoxVersion": "2.0.0",
"Runtime": "Python",
"Entry": "main.py",
"TriggerKeywords": ["awesome", "ap"],
"Commands": [
{
"Command": "config",
"Description": "Configure plugin settings"
}
],
"Settings": [
{
"Type": "textbox",
"Key": "api_key",
"Title": "API Key",
"Description": "Enter your API key"
}
]
}
```
### Settings Definition
```python
from wox_plugin import PluginSettingDefinitionItem
settings = [
PluginSettingDefinitionItem(
type="textbox",
key="api_key",
title="API Key",
description="Enter your API key",
value=""
),
PluginSettingDefinitionItem(
type="checkbox",
key="enable_cache",
title="Enable Cache",
description="Cache results for better performance",
value="true"
)
]
```
## Performance Optimization
### Async Operations
```python
import asyncio
import aiohttp
async def query(self, ctx: Context, query: Query) -> list[Result]:
# Concurrent API calls
async with aiohttp.ClientSession() as session:
tasks = [
self.fetch_data(session, url1),
self.fetch_data(session, url2),
self.fetch_data(session, url3)
]
results = await asyncio.gather(*tasks)
return self.process_results(results)
```
### Caching
```python
from functools import lru_cache
import time
class MyPlugin(Plugin):
def __init__(self):
self.cache = {}
self.cache_ttl = 300 # 5 minutes
async def query(self, ctx: Context, query: Query) -> list[Result]:
cache_key = f"query:{query.search}"
# Check cache
if cache_key in self.cache:
data, timestamp = self.cache[cache_key]
if time.time() - timestamp < self.cache_ttl:
return data
# Fetch new data
results = await self.fetch_results(query.search)
# Update cache
self.cache[cache_key] = (results, time.time())
return results
```
## Error Handling
```python
from wox_plugin import WoxPluginError, APIError
async def query(self, ctx: Context, query: Query) -> list[Result]:
try:
# Your plugin logic
return await self.process_query(query)
except APIError as e:
await self.api.log(ctx, "error", f"API error: {e}")
return [Result(
title="API Error",
subtitle="Please check your settings",
score=0
)]
except Exception as e:
await self.api.log(ctx, "error", f"Unexpected error: {e}")
return []
```
## Testing
### Unit Testing
```python
import pytest
from unittest.mock import AsyncMock
from your_plugin import MyPlugin
@pytest.mark.asyncio
async def test_query():
plugin = MyPlugin()
# Mock API
mock_api = AsyncMock()
await plugin.init(mock_context, PluginInitParams(
api=mock_api,
plugin_directory="/test"
))
# Test query
results = await plugin.query(mock_context, Query(search="test"))
assert len(results) > 0
assert results[0].title == "Expected Title"
```
## Best Practices
1. **Use Async/Await**: Leverage async operations for better performance
2. **Handle Errors Gracefully**: Always catch and log exceptions
3. **Implement Caching**: Cache expensive operations when appropriate
4. **Provide Good UX**: Use meaningful titles, subtitles, and icons
5. **Follow Naming Conventions**: Use clear, descriptive names
6. **Document Your Code**: Add docstrings and comments
7. **Test Thoroughly**: Write unit tests for your plugin logic
## Migration from Script Plugin
If you have a script plugin that needs more features:
1. **Create Plugin Structure**: Set up proper plugin directory with `plugin.json`
2. **Install SDK**: Add the appropriate SDK dependency
3. **Convert Logic**: Move your script logic to the plugin class
4. **Add State Management**: Utilize persistent state if needed
5. **Enhance with APIs**: Add AI, settings, or other advanced features
6. **Optimize Performance**: Implement caching and async operations
## Publishing
1. **Test Locally**: Ensure your plugin works correctly
2. **Create Package**: Follow the plugin packaging guidelines
3. **Submit to Store**: Use the plugin submission process
4. **Maintain**: Keep your plugin updated and respond to user feedback

View File

@ -1,14 +1,89 @@
# Wox Plugin Summary
## Plugin Types
## Plugin Categories
Wox plugins are categorized into two types:
Wox plugins are categorized by their installation type:
1. **System Plugin**: These are plugins bundled with Wox and cannot be uninstalled. They provide essential functionalities. For instance, `wpm` is a system plugin used
for plugin management.
2. **User Plugin**: These are plugins installed by the user. They can be installed, uninstalled, updated, or disabled by the user.
## Plugin Implementation Types
Wox supports two different plugin implementation approaches:
### Script Plugin
Script plugins are lightweight, single-file plugins that are perfect for simple automation tasks and quick utilities.
**Features:**
- **Single File Implementation**: Entire plugin logic contained in one script file
- **On-demand Execution**: Scripts are executed per query, no persistent running required
- **Multi-language Support**: Supports Python, JavaScript, Bash and other scripting languages
- **Simplified Development**: Metadata defined through comments, no complex configuration files needed
- **Instant Effect**: Changes take effect immediately after modifying script file, no restart required
- **JSON-RPC Communication**: Communicates with Wox through standard input/output using JSON-RPC
**Use Cases:**
- Simple file operations and system commands
- Quick text processing and format conversion
- Calling external APIs for simple data retrieval
- Personal automation scripts and utilities
- Learning and prototype development
- Functions that don't require complex state management
**Limitations:**
- Scripts are re-executed for each query, relatively lower performance
- No support for complex async operations and state management
- Limited API functionality
- No support for plugin settings interface
- Execution timeout limit (10 seconds)
### Full-featured Plugin
Full-featured plugins are comprehensive plugins designed for complex application scenarios and high-performance requirements.
**Features:**
- **Complete Architecture**: Runs through dedicated plugin host processes
- **Persistent Running**: Plugins remain loaded and running, supporting state management
- **Rich APIs**: Supports AI integration, preview functionality, settings interface and other advanced features
- **WebSocket Communication**: Efficient communication with Wox core through WebSocket
- **Async Support**: Full support for asynchronous operations
- **Lifecycle Management**: Complete plugin initialization, query, and unload lifecycle
**Use Cases:**
- Applications requiring complex state management
- High-frequency queries and real-time data processing
- Smart plugins with AI integration
- Plugins requiring custom settings interface
- Complex async operations and network requests
- Performance-sensitive application scenarios
- Commercial-grade plugin development
**Supported Languages:**
- **Python**: Using `wox-plugin` SDK
- **Node.js**: Using `@wox-launcher/wox-plugin` SDK
## Selection Guide
### Choose Script Plugin when:
- ✅ Functionality is relatively simple with clear logic
- ✅ No complex state management required
- ✅ Query frequency is not high
- ✅ Rapid prototyping and personal tools
- ✅ Learning Wox plugin development
- ✅ Single-execution tasks
### Choose Full-featured Plugin when:
- ✅ Complex business logic required
- ✅ High-frequency queries and real-time responses
- ✅ AI integration functionality needed
- ✅ Custom settings interface required
- ✅ Complex async operations
- ✅ High performance requirements
- ✅ Commercial plugin development
## Plugin Commands
Wox plugins can have commands that provide specific functionalities. For example, the `wpm` plugin has commands like `install` and `remove` for plugin management.

378
docs/script_plugin_guide.md Normal file
View File

@ -0,0 +1,378 @@
# Script Plugin Development Guide
Script plugins are lightweight, single-file plugins that provide a simple way to extend Wox functionality. They are perfect for quick automation tasks, personal utilities, and learning plugin development.
## Overview
Script plugins communicate with Wox using JSON-RPC over stdin/stdout. Each script is executed on-demand when a query is made, making them ideal for simple, stateless operations.
## Getting Started
### Creating a Script Plugin
Use the `wpm` plugin to create a new script plugin:
```
wpm create script <template> <name>
```
Available templates:
- `python` - Python script template
- `javascript` - JavaScript/Node.js script template
- `bash` - Bash script template
Example:
```
wpm create script python my-calculator
```
### Script Plugin Structure
A script plugin consists of a single executable file with metadata defined in comments:
```python
#!/usr/bin/env python3
# Required parameters:
# @wox.id my-calculator
# @wox.name My Calculator
# @wox.keywords calc
# Optional parameters:
# @wox.icon 🧮
# @wox.version 1.0.0
# @wox.author Your Name
# @wox.description A simple calculator plugin
# @wox.minWoxVersion 2.0.0
# Your plugin code here...
```
## Metadata Parameters
### Required Parameters
- `@wox.id` - Unique plugin identifier
- `@wox.name` - Display name of the plugin
- `@wox.keywords` - Trigger keywords (space-separated)
### Optional Parameters
- `@wox.icon` - Plugin icon (emoji or file path)
- `@wox.version` - Plugin version
- `@wox.author` - Plugin author
- `@wox.description` - Plugin description
- `@wox.minWoxVersion` - Minimum required Wox version
## JSON-RPC Communication
Script plugins communicate with Wox using JSON-RPC 2.0 protocol.
### Request Format
Wox sends requests to your script via stdin:
```json
{
"jsonrpc": "2.0",
"method": "query",
"params": {
"search": "user search term",
"trigger_keyword": "calc",
"command": "",
"raw_query": "calc 2+2"
},
"id": "request-id"
}
```
### Response Format
Your script should respond via stdout:
```json
{
"jsonrpc": "2.0",
"result": {
"items": [
{
"title": "Result: 4",
"subtitle": "2 + 2 = 4",
"score": 100,
"action": {
"id": "copy-result",
"data": "4"
}
}
]
},
"id": "request-id"
}
```
## Available Methods
### query Method
Handles user queries and returns search results.
**Parameters:**
- `search` - The search term entered by user
- `trigger_keyword` - The keyword that triggered this plugin
- `command` - Command if using plugin commands
- `raw_query` - The complete raw query string
### action Method
Handles user selection of a result item.
**Parameters:**
- `id` - The action ID from the result item
- `data` - The action data from the result item
## Environment Variables
Script plugins have access to these environment variables:
- `WOX_DIRECTORY_USER_SCRIPT_PLUGINS` - Script plugins directory
- `WOX_DIRECTORY_USER_DATA` - User data directory
- `WOX_DIRECTORY_WOX_DATA` - Wox application data directory
- `WOX_DIRECTORY_PLUGINS` - Plugin directory
- `WOX_DIRECTORY_THEMES` - Theme directory
## Action Types
Script plugins can return actions that Wox will handle:
### open-url
Opens a URL in the default browser:
```json
{
"action": "open-url",
"url": "https://example.com"
}
```
### open-directory
Opens a directory in the file manager:
```json
{
"action": "open-directory",
"path": "/path/to/directory"
}
```
## Example: Simple Calculator
```python
#!/usr/bin/env python3
# @wox.id simple-calculator
# @wox.name Simple Calculator
# @wox.keywords calc
import json
import sys
import re
def handle_query(params, request_id):
search = params.get('search', '').strip()
if not search:
return {
"jsonrpc": "2.0",
"result": {"items": []},
"id": request_id
}
try:
# Simple math evaluation (be careful with eval in real plugins!)
if re.match(r'^[0-9+\-*/().\s]+$', search):
result = eval(search)
return {
"jsonrpc": "2.0",
"result": {
"items": [{
"title": f"Result: {result}",
"subtitle": f"{search} = {result}",
"score": 100,
"action": {
"id": "copy-result",
"data": str(result)
}
}]
},
"id": request_id
}
except:
pass
return {
"jsonrpc": "2.0",
"result": {"items": []},
"id": request_id
}
def handle_action(params, request_id):
# Handle copy action
return {
"jsonrpc": "2.0",
"result": {},
"id": request_id
}
if __name__ == "__main__":
request = json.loads(sys.stdin.read())
method = request.get("method")
params = request.get("params", {})
request_id = request.get("id")
if method == "query":
response = handle_query(params, request_id)
elif method == "action":
response = handle_action(params, request_id)
else:
response = {
"jsonrpc": "2.0",
"error": {"code": -32601, "message": "Method not found"},
"id": request_id
}
print(json.dumps(response))
```
## More Examples
### File Search Plugin
```bash
#!/bin/bash
# @wox.id file-search
# @wox.name File Search
# @wox.keywords fs
# Read JSON-RPC request
read -r request
# Parse request
search=$(echo "$request" | jq -r '.params.search // ""')
id=$(echo "$request" | jq -r '.id')
if [ -z "$search" ]; then
echo '{"jsonrpc":"2.0","result":{"items":[]},"id":"'$id'"}'
exit 0
fi
# Search files
results=()
while IFS= read -r -d '' file; do
basename=$(basename "$file")
results+=("{\"title\":\"$basename\",\"subtitle\":\"$file\",\"score\":90,\"action\":{\"id\":\"open-file\",\"data\":\"$file\"}}")
done < <(find "$HOME" -name "*$search*" -type f -print0 2>/dev/null | head -z -10)
# Build JSON response
items=$(IFS=,; echo "${results[*]}")
echo '{"jsonrpc":"2.0","result":{"items":['$items']},"id":"'$id'"}'
```
### Weather Plugin (JavaScript)
```javascript
#!/usr/bin/env node
// @wox.id weather-plugin
// @wox.name Weather
// @wox.keywords weather
const https = require('https');
function handleQuery(params, requestId) {
const search = params.search || '';
if (!search) {
return {
jsonrpc: "2.0",
result: { items: [] },
id: requestId
};
}
// Mock weather data (replace with real API)
const weatherData = {
temperature: "22°C",
condition: "Sunny",
location: search
};
return {
jsonrpc: "2.0",
result: {
items: [{
title: `${weatherData.temperature} - ${weatherData.condition}`,
subtitle: `Weather in ${weatherData.location}`,
score: 100,
action: {
id: "show-details",
data: JSON.stringify(weatherData)
}
}]
},
id: requestId
};
}
// Main execution
const input = process.stdin.read();
if (input) {
const request = JSON.parse(input.toString());
const response = handleQuery(request.params || {}, request.id);
console.log(JSON.stringify(response));
}
```
## Best Practices
1. **Keep it Simple**: Script plugins are best for simple, stateless operations
2. **Handle Errors**: Always handle exceptions and return proper JSON-RPC responses
3. **Performance**: Remember that scripts are executed for each query
4. **Security**: Be careful with user input, especially when using `eval()` or executing commands
5. **Testing**: Test your script manually with JSON input before using in Wox
6. **Use Environment Variables**: Leverage the provided WOX_DIRECTORY_* variables
7. **Validate Input**: Always validate and sanitize user input
8. **Provide Meaningful Results**: Use descriptive titles and subtitles
## Debugging Tips
### Manual Testing
Test your script manually:
```bash
# Create test input
echo '{"jsonrpc":"2.0","method":"query","params":{"search":"test"},"id":"1"}' | ./your-script.py
# Expected output format
{"jsonrpc":"2.0","result":{"items":[...]},"id":"1"}
```
### Common Issues
1. **Script not executable**: Run `chmod +x your-script.py`
2. **JSON parsing errors**: Validate your JSON output
3. **Timeout issues**: Optimize your script for speed
4. **Missing shebang**: Always include `#!/usr/bin/env python3` or similar
## Limitations
- **Execution Timeout**: Scripts must complete within 10 seconds
- **No Persistent State**: Scripts are executed fresh for each query
- **Limited API**: No access to advanced Wox APIs like AI integration or settings UI
- **Performance**: Not suitable for high-frequency queries or complex operations
- **No Settings UI**: Cannot provide custom settings interface
## Migration to Full-featured Plugin
If your script plugin grows complex, consider migrating to a full-featured plugin:
- Use Python SDK: `wox-plugin`
- Use Node.js SDK: `@wox-launcher/wox-plugin`
- Access to full Wox API
- Persistent state and better performance
- Support for settings UI and advanced features
- AI integration capabilities
- Custom preview support