Skip to content

Quick start ​

Goal: within 15 minutes, get a minimal plugin to appear in the Extension Workshop, be enableable, and speak in the log.


1. What the smallest plugin looks like ​

A plugin = one standalone folder whose root must contain mod.json:

text
my_tool/
├── mod.json                 # required (or manifest.json)
├── my_tool.py               # Python entry point (not needed for data plugins)
├── locales/                 # recommended: plugin self-contained i18n strings
│   ├── zh-CN.json
│   └── en-US.json
├── icon.svg                 # recommended; icon.png/webp/jpg are auto-detected too
└── README.md                # optional, shown in the hub listing

Minimal mod.json:

json
{
  "id": "myteam.tool.hello",
  "name": "Hello Tool",
  "version": "1.0.0",
  "target_app_version": "0.2",
  "author": "MyTeam",
  "description": "最小示例:按热键把一句话送进字幕",
  "type": "tool",
  "entrypoint": "my_tool.py:HelloTool"
}

⚠️ target_app_version is mandatory: without it the plugin never even shows up in the Extension Workshop (log: [registry] 拒绝识别…未适配 0.2.0). See Compatibility & version gates.

my_tool.py:

python
from transbox.plugins.base import BasePlugin


class HelloTool(BasePlugin):
    def initialize(self, config=None) -> None:
        super().initialize(config)
        self.host.register_plugin_hotkey(self.id, "run", "Alt+T", name="我的工具")
        self.host.log("info", "Hello Tool 已就绪")

    def action_run(self, **kwargs) -> dict:
        ok = self.host.feed_direct_caption("hello", "auto", tts=False, drop_noise=False)
        return {"ok": ok}

    def shutdown(self) -> None:
        self.host.unregister_plugin_hotkeys(self.id)
        super().shutdown()

2. Make the host find it ​

The host scans these directories (the 4th one only in the self-published build):

Scan rootWho puts it thereNotes
<install dir>/extensions/Shipped with the appOfficial built-ins / examples
%APPDATA%/transbox/extensions/The userThe most convenient place while developing
Steam Workshop content/<AppID>/Steam subscriptionSynced automatically by the Steam client
<data dir>/workshop/<id>/Web Plugin HubSelf-published build only; only a root-level mod.json is recognised

Recommended development loop:

  1. Drop the whole my_tool/ folder into %APPDATA%/transbox/extensions/
  2. Open Settings → Extension Workshop → Refresh
  3. Flip the switch on the plugin card to enable it
  4. Engine types (asr / mt / tts / theme / interpretation) must also be selected as the active engine on their own settings page

Source priority when ids collide: local > steam > web; a bundled plugin shipped with the app is only overridden when the workshop version has a higher semver.


3. Where to look when something breaks ​

  • Settings → Extension Workshop: card status, error messages, missing prerequisite plugin and the like all live here
  • Application log: <install dir>/logs/app.log (lines prefixed [registry], [plugin:xxx])
  • The plugin speaking for itself: self.host.log("info", "…")
  • User-facing notice: self.host.show_toast("…")

The host never silently swallows a load failure: a module import error, a missing entry class, a too-new min_app_version and a missing prerequisite dependency all leave a trace in the log and on the hub card.

See Hot debugging.


4. Hand the whole doc set to an AI ​

If you want a large model to write a compliant plugin without access to the main program's source, paste Layout & mod.json, the card for the matching type from Type overview, and Provider signatures into the AI, and require it to obey:

  1. Compliant layout: one standalone folder + a valid root mod.json, a globally unique id, builtin.* forbidden;
  2. Complete contract inheritance: a Python plugin inherits the matching base class from transbox.plugins.base, implements every abstract method, and matches the signatures exactly;
  3. Thread safety: network requests / inference / expensive computation must use self.run_in_background(), events are dispatched with self.emit_event(), and blocking the Qt main thread is strictly forbidden;
  4. Configure with config_schema; when you need "get a key / test connection" style UX, ship ui_extensions.settings_view; always read config via self._config.get(key), never hard-code private keys or personal paths;
  5. Self-contained i18n: locales/zh-CN.json + locales/en-US.json, keys prefixed plugin.<plugin_id>.*, and never modify the host's core string tables;
  6. Visual rules: QML uses ThemeTokens and PluggableWindowBackground to adapt to the theme automatically; Emoji / character icons are forbidden, icons must be vector SVG;
  7. MT plugins must declare target_langs: list every language the model can translate into (including Chinese and English); do not rely on supported_langs alone; see Language codes & translatable languages.
  8. Local-service plugins follow the Local-service engine contract (lazy startup / port fallback / kill child processes in shutdown).
  9. Output: the complete directory tree + the complete mod.json + the complete source (never elide core logic with an ellipsis) + local testing and publishing instructions.

TransBox — empowering borderless cross-language communication for everyone