Wox Plugin Creator
Quick Start
- Scaffold a Node.js plugin (clones template repo):
python3 scripts/scaffold_wox_plugin.py --type nodejs --output-dir ./MyPlugin --name "My Plugin" --trigger-keywords my
- Scaffold a Python plugin (clones template repo):
python3 scripts/scaffold_wox_plugin.py --type python --output-dir ./MyPlugin --name "My Plugin" --trigger-keywords my
- Scaffold a single-file SDK plugin (uses local templates; plugin-id auto-generated; writes one file into the live user plugin directory so a running Wox instance can load it immediately). Omit
--output-dir; default is~/.wox/wox-user/plugins/single-file/(Windows:%USERPROFILE%\.wox\wox-user\plugins\single-file\):- Auto-detect this machine:
python3 scripts/scaffold_wox_plugin.py --type singlefile --name "Weather" --trigger-keywords weather - Explicit Node.js:
python3 scripts/scaffold_wox_plugin.py --type singlefile-nodejs --name "Weather" --trigger-keywords weather - Explicit Python:
python3 scripts/scaffold_wox_plugin.py --type singlefile-python --name "Weather" --trigger-keywords weather
- Auto-detect this machine:
Choose a plugin type
- Single-file SDK plugin: one
.pyor CommonJS.jsfile with full Public API, loaded into the existing Python/Node runtime host. No extra process per query. Create and edit only~/.wox/wox-user/plugins/single-file/Wox.Plugin.<Name>.js(or.py); Wox watches that directory and reloads on save. Requires Wox 2.4.2+; headerMinWoxVersionmust be"2.4.2". - SDK plugin (
.wox): multi-file package with dependencies, resources, TypeScript, andplugin.json.
Node.js first version must stay CommonJS (module.exports.plugin) and must not import @wox-launcher/wox-plugin.
Choose a runtime language
Honor an explicit Python or Node.js request from the user, including a .py or .js output filename.
When the user does not specify a language, detect this machine before scaffolding. Do not default to Python.
- Run
python3 scripts/detect_local_runtime.py(usepythonifpython3is missing). It printsnodejs,python, ornone. - If that script cannot run, check the floors yourself:
node --versionfor Node.js 20+, andpython3 --versionfor Python 3.10+. On Windows also trypy -3 --versionandpython --version. - Decision:
- Only one runtime meets the floor → use that runtime
- Both meet the floor → use Node.js
- Neither meets the floor → ask the user which language they want
- Map the choice to
--type:singlefile-nodejs/singlefile-python, ornodejs/python.--type singlefileapplies the same detection inside the scaffold.
Workflow
1) Scaffold plugin files
- Use
scripts/scaffold_wox_plugin.pyfornodejs,python,singlefile,singlefile-python, orsinglefile-nodejs. - Pass
--nameand--trigger-keywordsfor every runtime. The scaffold exits without them.--output-diris required for packaged SDK plugins; omit it for single-file SDK plugins so the file lands in~/.wox/wox-user/plugins/single-file/. - For Node.js and Python packages, the scaffold clones the official template repos and replaces placeholders like
{{.ID}},{{.Name}},{{.Description}},{{.TriggerKeywordsJSON}},{{.Author}}. - Before starting work in a new SDK plugin project, run
make initin the project root when the project has not been initialized yet. - Single-file SDK plugins are single-file host-loaded plugins. Prefer filenames like
Wox.Plugin.<Name>.jsorWox.Plugin.<Name>.py. - Single-file SDK destination: create and implement the plugin as one file in
~/.wox/wox-user/plugins/single-file/(Windows:%USERPROFILE%\.wox\wox-user\plugins\single-file\). That directory is what a running Wox instance loads; saving reloads after about 500ms, so the user can query the trigger keyword immediately. Omit--output-dirwhen using the scaffold, or write the file there directly fromassets/single_file_plugin_templates/. Do not also create a copy under the current repository (plugins/, workspace root, or a new folder). Pass--output-dironly when the user asks for a different path. - Do not put companion files (
*.test.js, README, extra modules) in the live directory. Wox loads every.jsand.pythere as a plugin. - Single-file SDK plugins must set header
MinWoxVersionto"2.4.2". The scaffold applies this default when--min-wox-versionis omitted. Do not lower it; Wox 2.4.2 is the first release that can load this plugin type, and store/CI reject older floors. - For single-file SDK plugins, the scaffold copies templates from
~/.wox/ai/skills/wox-plugin-creator/assets/single_file_plugin_templates/(or the repo.agents/skills/wox-plugin-creator/assets/single_file_plugin_templates/fallback). - Prefer standard library features; avoid third-party dependencies unless absolutely necessary. Single-file SDK plugins cannot use pip/npm packages.
- For SDK usage and API details, read
references/sdk_nodejs.mdorreferences/sdk_python.md. - For plugins declaring
querySelection, return results only for selection types and content the plugin can process. Return an empty results list for unsupported, missing, or empty selection data instead of showing usage or help rows for unrelated selections. - Keep a result on the row.
Titleis the name to scan.SubTitleis one short identity line, such as a code, place, or source.Tailsare the few facts that must stay visible, such as a price and its change. Use at most three tail tags on one result. A fourth tag can be clipped, so part of a tag is not shown. Put any further fact in the subtitle, the copied text, or a preview. Do not addPreviewfor a quote, status, short record, or anything that fits on that row. - A text tail is already a capsule. Use an SVG image tail only when that capsule must also contain an icon. Match the launcher metrics below, and see Simulated tail tags.
- Refresh a visible row in place with
UpdateResult/update_result. Remember the results returned by the latestquery(). Replace that list on the next query. When a background refresh or an action changes a row that is still on screen, callUpdateResultwith the same resultIdand only the fields that changed (Title,SubTitle,Icon,Tails,Actions). The query text stays put and the list does not reload. UseRefreshQuery/refresh_queryonly when rows must be added or removed andUpdateResultcannot express that. Do not useChangeQueryto redraw results. - Add
Previewonly for a large body that cannot fit the row: a long document, many fields, a chart, a gallery, or syntax highlighting. When a preview is required, usemarkdownfor prose, lists, links, and images. Usetextorimagewhen that is the whole preview. UsewebviewHTML only after markdown cannot express it, such as syntax highlighting, folding, or an interactive layout. HTML is the last option because the webview can steal query focus, miss launcher theme colors, and hit layout bugs. There is no separatehtmlpreview type; HTML useswebviewwith a JSON-encodedhtmlfield and no local HTTP server. See the HTML preview examples in the SDK references. - Do not rasterize documents as SVG/
imagepreviews; those scale as pictures, cannot select text, and do not follow theme colors. - When HTML is required, follow the current Wox theme. Call
GetThemeColors/get_theme_colors(Wox >= 2.4.5) when building the HTML and paint opaqueBackground,Text,SecondaryText,Border,Accent,AccentText, andSelection. UseDarkto choose a light or dark syntax palette. Do not hardcode only a dark page, and do not rely ontransparentorprefers-color-schemeas a substitute for the launcher palette. Include a theme color incacheKeyso the preview refreshes after a theme change. If the API is missing, fall back to a dark and a light default. - For inline command arguments or atomic query blocks, read QueryHint. Command declarations contain suffix templates;
ChangeQuerycontains a complete instance. Keep legacy text parsing when structure is absent. - For query-scoped filters or sort controls, return
QueryResponse.Refinementsand readreferences/refinements.mdbefore assigning hotkeys. - For
plugin.json,SettingDefinitions,QueryRequirements, validators, dynamic settings, and feature flags, readreferences/plugin_json_schema.mdfirst. - When implementing an SDK or single-file SDK plugin, also register Plugin Tools in
init()for capabilities other plugins should be able to call. Query results and actions stay for the user; tools expose the same work as structured operations. Readreferences/plugin_tools.mdfirst. Requires Wox >= 2.4.5 (MinWoxVersion"2.4.5"or newer). Do not declare tools inplugin.json. - SDK and single-file SDK plugins should support MRU unless the plugin is clearly unsuitable. Declare the
mrufeature, put restore identity on actionContextData, and registerOnMRURestore/on_mru_restoreininit(). The restore callback must return immediately from memory or local cache. Wox waits 300ms for each start-page MRU restore, then discards that item, logs the timeout, and shows the next MRU item. Do not fetch, scan disk, or call host APIs inside the callback. Skip MRU only for context-dependent, one-shot, diagnostic, or inbox-style plugins, and say why in the implementation notes. - Persist user settings through the Public API (
GetSetting/SaveSetting/SetSetting/OnSettingChanged, or Pythonget_setting/save_setting/set_setting/on_setting_changed). A normal setting write is cloud-synced and follows the user across machines. Use these APIs for preferences, API keys, favorites, and account configuration.
Cache stays out of the settings API
Anything the plugin can rebuild is cache: fetched JSON, subject or entity snapshots, search indexes, downloaded files, and thumbnails. Write that data under the plugin cache folder. Do not pass it to SaveSetting, SetSetting, save_setting, or set_setting.
Plugin settings are cloud-synced. Each settings write becomes a sync record and is copied to the user's other devices, including a hidden key that is absent from SettingDefinitions. A cache blob stored this way is uploaded on every refresh and fills sync history. IsPlatformSpecific still syncs; it only keeps a separate value per operating system. IsLocal / is_local is for a small machine-local preference that must remain in the settings store, such as a window position. It is not a place for cache.
- Call
GetCacheFolder(ctx)/get_cache_folder(ctx)once ininit(), keep the path, and write cache files under it. - The folder is
~/.wox/cache/plugins/<plugin-id>/. Wox creates it and deletes it when the plugin is uninstalled. - Do not invent a
cache/,tmp/,downloads/, ordata/directory beside the plugin file, under user data, or under a hardcoded name. - When authoring
SettingDefinitions, always decide whether each setting is platform-specific before shipping it. Wox cloud sync replicates normal plugin settings across devices, so local paths, executable paths, shell commands, hotkeys, system integrations, browser profiles, and application paths should usually setIsPlatformSpecific: true. Account IDs, API keys, remote service hosts, and cross-platform user preferences should usually keepIsPlatformSpecific: false. - Use
DisabledInPlatformsonly to disable a setting on selected platforms. It does not isolate stored values; useIsPlatformSpecificwhen the value must differ per platform after cloud sync. - When a plugin cannot run a query without required settings such as access keys, declare those requirements in metadata
QueryRequirementsinstead of returning ad hoc setup results fromquery(). - Query refinements (type filters, sort modes, and similar query-scoped chips) belong on
QueryResponse.Refinements, not in command syntax. Every refinementHotkeymust use the platform primary modifier:cmd+<key>on macOS andctrl+<key>on Windows/Linux (for examplecmd+t/ctrl+t). Detect the OS at runtime and emit the matching string. - For ready-to-copy patterns such as validated textbox/select fields, editable tables, AI model selectors, and dynamic preview settings, read
references/settings_patterns.md. - For Python settings APIs, note that helper builders are limited; advanced settings are often created by constructing
PluginSettingDefinitionItemand value objects directly.
2) Author result and action icons
- Read
references/icons.mdbefore choosing any glyph. Result-row and plugin-identity icons may be colorful; Action Panel leading icons must not. - Scaffold templates use inline SVGs for the default plugin and result icons. Keep those defaults as SVG; use emoji or other formats only when deliberately choosing a result identity icon.
- Prefer a bundled monochrome verb from
assets/iconify/action/(copy, open, execute/lightning, delete, edit, paste, add, search, settings). These SVGs already usevar(--wox-theme-icon-color)so the Action Panel can tint them to the row label. - Do not use emoji, brand logos, the plugin mark, or mixed-color result art as the leading action icon. The panel only tints SVGs that contain the theme variable; anything else stays authored and looks inconsistent next to system actions.
- Execute actions use the lightning verb (
action/execute.svg), not a gear or play triangle. Settings actions use the gear. - For a new action metaphor, fetch a monochrome Iconify outline with
scripts/search_iconify.py(it rewritescurrentColorto the theme variable by default). Use--no-wox-themeonly for colorful result icons. - Single-file SDK plugins cannot use relative image paths. Inline the SVG for actions; emoji/URL/base64 are acceptable for result identity only.
3) Package and submit plugin
- For SDK plugins cloned from templates, run
make packageinside the template repo. - Single-file SDK plugins embed JSON metadata in the file header. Keep
MinWoxVersionas"2.4.2". - Publish a single-file SDK plugin on a public GitHub gist. That is the simplest store host. Reference: https://gist.github.com/qianlifeng/04a9609de66eaa582a473f5852450ede
- Create one public gist file named
Wox.Plugin.<Name>.jsorWox.Plugin.<Name>.py. The storeDownloadUrlpath must end with.jsor.py(a gist URL without that suffix will not install). Example:gh gist create --public --desc "Wox.Plugin.YouTube" Wox.Plugin.YouTube.js - Upload the screenshot and icon to GitHub (drag them onto the gist page or a gist comment). Copy the resulting
https://gist.github.com/user-attachments/assets/<id>URLs, for examplehttps://gist.github.com/user-attachments/assets/7502acdc-1ea5-4ef6-a3fb-31875353dabe. - Then follow
wox-plugin-submit2store: clone Wox and add astore-plugin.jsonentry.Websiteis the gist HTML URL,DownloadUrlis the gist raw URL including the filename (https://gist.githubusercontent.com/<user>/<gist-id>/raw/Wox.Plugin.<Name>.js),IconUrlandScreenshotUrlsare the user-attachments URLs. Runtime isnodejsorpython. Do not ship a.woxfor this plugin type.
- Create one public gist file named
- For packaged SDK plugins, use
wox-plugin-submit2storewith a GitHub repository and.woxrelease. Ask the user before submitting to the store.
Simulated tail tags
Text tails are capsules drawn by the launcher. Use at most three on one result. More than three can leave a tag partly hidden. Copy these unscaled metrics when an SVG has to imitate one. Density scale multiplies them; at 100% they are:
| | |
| --- | --- |
| Height | 22 |
| Corner radius | half the height, 11. A 1px stroke inset by 0.5 uses a 21px rect with rx="10.5". |
| Side inset | 8 on the left and 8 on the right. Tag width is measured text width plus 16. |
| Font size | 11 |
| Border | 1 |
| Success | fill #027A48, label #FFFFFF |
| Danger | fill #B42318, label #FFFFFF |
| Warning | fill #B54708, label #FFFFFF |
| Default | no fill, border #FFFFFF at alpha 51 (#FFFFFF33), label is the row foreground |
Prefer a real text tail whenever the label is only text. Wox then applies this capsule, including the selected-row color.
Use Type: "image" only to put an icon and a label in the same capsule. Set ImageWidth to the capsule width and ImageHeight to 22, or the launcher squares it into a 20px icon. Width is 8 + icon + gap + label + 8.
Draw the label as filled glyph paths inside the SVG. The SVG rasterizer does not draw <text>. One <text text-anchor="middle"> whose x is the viewBox center is pulled out and painted centered on the whole image. With an icon on the left, that extra centering adds the icon width again as right inset. Do not use that centered text when the icon and the label sit side by side.
QueryHint
QueryHint is optional semantic background guidance for input queries. It can
carry actual argument values, but must never turn continuous input into a mandatory
form. Preserve normal caret movement, cross-element selection, deletion, clipboard,
undo and IME behavior. Tab is optional. When editing invalidates a semantic boundary,
keep the user's text and discard unreliable metadata rather than blocking input.
- Declare suffix elements in
Commands[].QueryHint;Aliasesuse the same trigger. Wox inserts the matched command prefix with reserved IDcommand. Static metadata andRegisterQueryCommands/register_query_commandsuse the same model. ChangeQueryalways requiresQueryTypeand completeQueryTextfor input, or completeQuerySelectionfor selection.QueryHintis optional visual enhancement, never a replacement for query content. An input hint must describe exactly the supplied text, including trigger keyword, command, separators and values. An invalid or mismatching hint is ignored; the suppliedQueryTextis used unchanged. ExplicitQueryText: ""remains valid for clearing. Python usesChangeQueryParam(query_type=QueryType.INPUT, query_text="set volume 50", query_hint=QueryHint(elements=[...]));QueryHintandQueryElementare SDK exports.- Elements have nonempty, unique
Idvalues.textusesText(including explicit separators);argumentusesValue, optionalPlaceholderandRequired;blockuses atomicValue. A highlighted argument remains freely editable. - Placeholders support
i18n:and never enter the query value or clipboard.Requiredis descriptive: validate empty values and business constraints before offering or executing actions. Querying itself must not perform the action. - Read values by ID from
query.QueryHint.Elements(Node.js) orquery.query_hint.elements(Python). Keep legacySearch/searchparsing when the hint is absent; an empty argument is not an absent hint. Wox supplies a lossy plain-text projection in existing query fields; do not reconstruct boundaries from it. - Complete commands preview hints; space or Tab activates the template. Whole-command paste into ordinary text is not parsed into arguments. Reopening selects the entire query for replacement; undo and history preserve hints when possible.
- Keep the list flat; no nested elements, dropdowns, custom rendering or inline markup.
Hints never carry plugin identity or create a scope. Routing uses the normal query
text and explicit
QueryScope; complete instances must include the trigger keyword when needed (for examplegh issues). If both text and a hint are passed toChangeQuery, the explicit query text remains authoritative; the hint must match it. - Use SDK or single-file SDK APIs. Verify the first supporting Wox/SDK release before setting distribution requirements; the single-file runtime version floor alone is insufficient.
- Validate blank/valid/invalid arguments, multiple arguments, whole-query replacement after reopen, undo and the legacy path. Set Volume is the first built-in example.
Example command suffix (Wox adds the command text):
{
"Command": "set-volume",
"Aliases": ["set volume", "volume"],
"QueryHint": {
"Elements": [
{ "Id": "volume", "Kind": "argument", "Placeholder": "Volume (0–100)", "Required": true }
]
}
}
Runtime Requirements
Wox enforces the same interpreter floors for SDK plugins and single-file SDK plugins. Store install fails, and queries show a setup result, when the machine is below these versions:
- Python: 3.10 or later
- Node.js: 20 or later
Do not target older interpreters. Single-file SDK plugins run inside Wox's existing Python/Node runtime host and require Wox 2.4.2 or later (MinWoxVersion: "2.4.2").
Resources
- scripts:
scripts/scaffold_wox_plugin.py,scripts/detect_local_runtime.py,scripts/search_iconify.py - references:
references/plugin_overview.md,references/scaffold_nodejs.md,references/scaffold_python.md,references/sdk_nodejs.md,references/sdk_python.md,references/plugin_json_schema.md,references/settings_patterns.md,references/plugin_i18n.md,references/icons.md,references/refinements.md,references/plugin_tools.md - assets:
assets/single_file_plugin_templates/,assets/iconify/action/