mirror of https://github.com/Wox-launcher/Wox
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:
parent
fda4d4df3a
commit
4b97ebedf0
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
Loading…
Reference in New Issue