Full-featured Plugin Development Guide
Full-featured plugins run inside dedicated hosts (Python or Node.js) and talk to wox.core over WebSocket. They stay loaded, can keep state, and can use the richer Wox API surface such as previews, settings UI, toolbar messages, MRU restore, screenshot capture, AI streaming, and deep links.
When to choose a full-featured plugin
Choose this model when your plugin needs one or more of these:
- persistent state across queries
- async/network-heavy work
- custom settings UI
- richer previews and actions
- plugin-driven screenshot or clipboard workflows
- AI or MRU integration
If your plugin is a small one-file automation script, start with the Script Plugin guide instead.
Quickstart
- Create a folder under
~/.wox/plugins/<your-plugin-id>/ - Add
plugin.jsonand your entry file (main.py,index.js, or built output such asdist/index.js) - Install the SDK
- Reload the plugin from Wox settings or restart Wox
SDK install commands:
- Python:
uv add wox-plugin - Node.js:
pnpm add @wox-launcher/wox-plugin
If you use Codex, see AI Skills For Plugin Development.
Minimal examples
Python
from wox_plugin import Plugin, Query, Result, Context, PluginInitParams
from wox_plugin.models.image import WoxImage
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]:
return [
Result(
title="Hello Wox",
sub_title="This is a sample result",
icon=WoxImage.new_emoji("👋"),
score=100,
)
]
plugin = MyPlugin()Node.js
import { Plugin, Query, Result, Context, PluginInitParams } from "@wox-launcher/wox-plugin"
class MyPlugin implements Plugin {
private api!: PluginInitParams["API"]
private pluginDir = ""
async init(ctx: Context, params: PluginInitParams): Promise<void> {
this.api = params.API
this.pluginDir = params.PluginDirectory
}
async query(ctx: Context, query: Query): Promise<Result[]> {
return [
{
Title: "Hello Wox",
SubTitle: "This is a sample result",
Icon: { ImageType: "emoji", ImageData: "👋" },
Score: 100,
},
]
}
}
export const plugin = new MyPlugin()plugin.json essentials
- Follow the full schema in Specification
- Use
Runtime=PYTHONorNODEJS - Point
Entryat the file Wox should execute - Add
Featuresonly for capabilities you actually use
Example:
{
"Id": "my-awesome-plugin",
"Name": "My Awesome Plugin",
"Description": "Do awesome things",
"Author": "You",
"Version": "1.0.0",
"MinWoxVersion": "2.0.0",
"Runtime": "NODEJS",
"Entry": "dist/index.js",
"TriggerKeywords": ["awesome", "ap"],
"Features": [{ "Name": "querySelection" }, { "Name": "ai" }],
"SettingDefinitions": [
{
"Type": "textbox",
"Value": { "Key": "api_key", "Label": "API Key", "DefaultValue": "" }
}
]
}Query handling
Wox sends a normalized Query object into query():
Query.TypeisinputorselectionQuery.RawQuerykeeps the original inputQuery.TriggerKeyword,Query.Command, andQuery.Searchgive you the parsed partsQuery.Idis the identifier you should keep when doing async follow-up updatesQuery.Envcarries optional environment context when thequeryEnvfeature is enabled
See Query Model for the exact split and feature-dependent fields.
Building results
Each Result can include:
IconPreviewTailsActionsGroupandGroupScore
Useful patterns:
- use
Previewfor markdown, text, image, URL, or file previews - use
Tailsfor badges or small metadata - use
PreventHideAfterActionwhen an action continues to update the same result in place
If you need to update a visible result after an action starts, use:
GetUpdatableResultUpdateResult
If you need to stream or append additional results for the same active query, use:
PushResults
Settings
Define your settings UI in plugin.json with SettingDefinitions.
Common setting types:
textboxcheckboxselectselectAIModeltabledynamicheadlabelnewline
At runtime:
- read values with
GetSetting - persist values with
SaveSetting - react to changes with
OnSettingChanged - provide runtime-generated settings with
OnGetDynamicSetting
Feature flags you will likely use
querySelection: receive text/file selection queriesqueryEnv: receive active-window or browser contextai: use Wox-configured AI APIsdeepLink: register plugin deep linksmru: restore items from Wox MRU storageresultPreviewWidthRatio: widen or narrow the preview areagridLayout: render results in a grid instead of a list
Only enable features you actually need. They change how Wox routes queries and builds plugin context.
Screenshot API
Wox now exposes a built-in screenshot workflow to full-featured plugins.
Use it when your plugin needs the user to draw a region and then continue processing the resulting PNG path itself, for example:
- OCR
- image upload
- bug reporting
- visual annotation pipelines outside Wox
What the API returns
Screenshot() returns:
Success: whether the capture completed successfullyScreenshotPath: exported PNG path when successfulErrMsg: failure reason, or a warning message when the capture completed with caveats
Options
ScreenshotOption supports:
HideAnnotationToolbar: keep the flow focused on raw area selectionAutoConfirm: finish immediately after the user completes a valid selection
Node.js example
const capture = await this.api.Screenshot(ctx, {
HideAnnotationToolbar: true,
AutoConfirm: true,
})
if (!capture.Success) {
await this.api.Notify(ctx, `Screenshot failed: ${capture.ErrMsg}`)
return
}
await this.api.Notify(ctx, `Saved to ${capture.ScreenshotPath}`)Behavior notes:
- the exported file path is returned to the plugin; clipboard handling is left to the plugin
- third-party plugins automatically show their own plugin icon in the floating screenshot toolbox
- if you need Wox's built-in annotation UI, leave
HideAnnotationToolbarunset
AI, deep links, and MRU
- AI APIs require the
aifeature - deep-link callbacks require the
deepLinkfeature andOnDeepLink - MRU restore requires the
mrufeature andOnMRURestore
These are optional capabilities. Keep the initial version of your plugin smaller if you do not need them yet.
Local development loop
- keep your plugin directory under
~/.wox/plugins/, or symlink your working directory there - after changing
plugin.json, reload the plugin from Wox settings or restart Wox - after changing built TypeScript output, rebuild your plugin and reload it
If your plugin touches core/host contracts, rebuild Wox itself instead of assuming the host will pick up type changes automatically.
Recommended debugging approach
When something fails:
- verify
plugin.jsonfirst - confirm the right runtime host is being used
- add plugin-side logging through the SDK API
- inspect Wox logs under
~/.wox/log/ - if the problem crosses layers, rebuild from the repository root with
make build