tool — tools / text sources / select-to-translate
Does not attach to the ASR→MT→TTS pipeline; it provides capability through global hotkeys +
action_*: select-to-translate, word capture, text injection, external data sources, and so on. Multiple instances allowed.⚠️ Tools must use the real
type: "tool"— do not hangaction_*offmiddleware: that makes both the Hub category and the hotkey semantics wrong.
Declaration
{
"id": "example.select_translate",
"version": "1.0.0",
"target_app_version": "0.2",
"type": "tool",
"entrypoint": "select_translate.py:SelectTranslatePlugin",
"hotkeys": [
{ "action": "translate_selection", "default": "Alt+T", "name": "划词翻译" }
],
"config_schema": [
{ "key": "capture_mode", "label": "捕获方式", "type": "select",
"choices": ["auto", "clipboard"], "default": "auto" },
{ "key": "speak_translation", "label": "划词后朗读译文", "type": "boolean", "default": false }
]
}hotkeys[].action maps to the action_<action> method in your implementation.
Enabled plugins with
type=toolare preloaded during the scan phase (they must register their hotkeys), soinitialize()runs at startup — don't do heavy work in it.
Minimal skeleton
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="我的工具")
def action_run(self, **kwargs) -> dict:
ok = self.host.feed_direct_caption("hello", "auto", tts=False, drop_noise=False)
return {"ok": ok}
def handle_host_event(self, event_name: str, payload: dict) -> None:
if event_name == "pipeline.state":
self.host.log("info", f"pipeline: {payload.get('running')}")
def shutdown(self) -> None:
self.host.unregister_plugin_hotkeys(self.id)
super().shutdown()hotkeysregisters declaratively (the host callsRegisterHotKeyon the Qt main thread); you can also register at runtime withhost.register_plugin_hotkey(...), see Global hotkeys.- Action dispatch:
handle_action(action, params)→action_<action>(**params); on the QML side call it withbridge.invokePluginAction/invokePluginActionAsync. - The returned dict is handed back to the caller as-is.
Feeding text into the captions
Two paths (see Captions & translation):
| Use | When |
|---|---|
host.feed_direct_caption(text, lang, tts=…, drop_noise=…) | Source text only; let the host run the full chain (SI running = full chain; not running = lightweight MT + captions + history) |
host.feed_caption(text, translation, lang, source=…) | You already have the translation (select-to-translate, caption-bridge frame patches) and don't want MT to run again |
For tools, drop_noise must be False (never swallow short words).
Typical scenarios
| Scenario | Key point |
|---|---|
| Select-to-translate | Read the clipboard/selection → host.translate() or feed_direct_caption → optionally host.speak() |
| External data source | A background thread polls/subscribes → feed_direct_caption |
| Text-injection HUD | host.create_plugin_window(...) opens a click-through + capture-safe floating window (Plugin windows) |
| Batch processing | host.begin_progress() + is_progress_cancelled() (File pickers & long tasks) |
Reference implementation
extensions/community_select_translate/ (select-to-translate; the canonical type=tool example).
