mirror of https://github.com/Wox-launcher/Wox
8.8 KiB
8.8 KiB
Full-featured Plugin Development Guide (Generated by AI)
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:
# Using pip
pip install wox-plugin
# Using uv (recommended)
uv add wox-plugin
Basic Structure:
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:
npm install @wox-launcher/wox-plugin
# or
pnpm add @wox-launcher/wox-plugin
Basic Structure:
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
- Wox Core ↔ Plugin Host (WebSocket)
- Plugin Host ↔ Plugin Instance (Direct method calls)
Plugin Lifecycle
- Load: Plugin host loads the plugin module
- Init: Plugin initialization with API access
- Query: Handle user queries (multiple times)
- Unload: Plugin cleanup when disabled/uninstalled
Rich API Features
Core APIs
# 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
# 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
# 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
{
"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
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
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
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
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
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
- Use Async/Await: Leverage async operations for better performance
- Handle Errors Gracefully: Always catch and log exceptions
- Implement Caching: Cache expensive operations when appropriate
- Provide Good UX: Use meaningful titles, subtitles, and icons
- Follow Naming Conventions: Use clear, descriptive names
- Document Your Code: Add docstrings and comments
- Test Thoroughly: Write unit tests for your plugin logic
Migration from Script Plugin
If you have a script plugin that needs more features:
- Create Plugin Structure: Set up proper plugin directory with
plugin.json
- Install SDK: Add the appropriate SDK dependency
- Convert Logic: Move your script logic to the plugin class
- Add State Management: Utilize persistent state if needed
- Enhance with APIs: Add AI, settings, or other advanced features
- Optimize Performance: Implement caching and async operations
Publishing
- Test Locally: Ensure your plugin works correctly
- Create Package: Follow the plugin packaging guidelines
- Submit to Store: Use the plugin submission process
- Maintain: Keep your plugin updated and respond to user feedback