UI slots
Component positions in the host UI that plugins can replace. Declared in
mod.jsonunderui_extensions.
Slot list
✅ Consumed (replaceable)
| Slot | Position | Mutually exclusive |
|---|---|---|
overlay_background | Subtitle overlay backplate | ✅ |
caption_view | Caption content view | ✅ |
audio_level | Bottom-right level meter / waveform bar | ✅ |
alert_capsule | Floating alert capsule | ✅ |
overlay_decorations | Overlay decoration layer | ✅ |
style_bar_actions | Custom action buttons in the caption-bar control row | ❌ |
settings_background | Settings center backplate | ✅ |
history_background | History window backplate | ✅ |
dialog_background | Generic dialog backplate | ✅ |
window_background | Generic window backplate | ✅ |
copilot_window | AI assistant top-level floating window | ✅ |
settings_view | Standalone settings form for a plugin (special, not in the standard list, never exclusive) | ❌ |
❌ Declared but not consumed by any Loader today (do not rely on them)
overlay_rootstyle_bar
Mutual exclusion rules
- Slots marked ✅ above are mutually exclusive per slot: only one plugin can be in effect in the same slot at a time.
- Full themes (
style_layer=full) and local UI slot extensions can coexist — enabling a waveform plugin does not take the user's theme offline. - Mutual exclusion between local widgets of the same class only happens when both are local (neither side is a full theme).
settings_viewis never mutually exclusive.
Cascade fallback: a window looks for an available plugin background in the order dedicated slot → window_background → overlay_background; if it finds none it uses the official native backplate.
Declaration
{
"type": "theme",
"style_layer": "effects",
"ui_extensions": {
"audio_level": "CustomWaveform.qml",
"overlay_background": "MyBackground.qml"
}
}Paths are relative to the plugin directory.
Background slot guidelines
If you declare any of overlay_background / settings_background / history_background / dialog_background / window_background, you must follow these rules:
1) Border drawing is your responsibility (you must draw it yourself)
When the host loads a plugin background it automatically hides the official native 1px crystal highlight outer border (to prevent double overlapping lines). So a plugin background must draw its own window outer edge / border / outer glow (two-tone neon gradient frame, chamfered metal, highlight stroke …). Filling a flat colour without drawing a border → the window becomes a bare, borderless rectangle.
2) Adaptive corner radius
The host injects the target window's native corner radius through customRadius (settings center 16, history window 16, generic dialog 12 …). A component should declare property bool enforceCustomRadius: false and adapt when customRadius changes; do not hard-code a tiny radius (such as customRadius: 2) — you get an abrupt, harsh clipped right angle.
3) Context property pass-through
| Property | Content |
|---|---|
window / targetWindow | Target window instance (you can probe window.activeFocus / window.hovered) |
appState | Global application state (appState.bgColor / bgOpacity / neonBorderColor …) |
hostContext | Overlay-window-specific context (style / subtitle / audio / invoke) |
hovered | Whether the overlay window is currently hovered by the mouse |
4) Live response to config values (the trap people hit most)
⚠️
bridge.getPluginConfig(pluginId)is a transient function call, not a reactive QML property. If you define switches such asneon_glow/hud_bracketsinmod.json, you must listen tobridge.pluginConfigChanged, otherwise flipping a switch in the settings panel does nothing to a window that is already running.
import QtQuick 2.15
Item {
id: root
anchors.fill: parent
readonly property string myPluginId: "community.theme.my_theme"
property var cfg: (typeof bridge !== "undefined" && bridge && bridge.getPluginConfig)
? bridge.getPluginConfig(root.myPluginId) : ({})
readonly property bool enableGlow: (cfg && typeof cfg.neon_glow !== "undefined") ? cfg.neon_glow : true
readonly property bool enableBrackets: (cfg && typeof cfg.hud_brackets !== "undefined") ? cfg.hud_brackets : true
Connections {
target: (typeof bridge !== "undefined") ? bridge : null
function onPluginConfigChanged(pid) {
if (pid === root.myPluginId) {
root.cfg = bridge.getPluginConfig(root.myPluginId) || ({})
}
}
}
Rectangle {
anchors.fill: parent
visible: root.enableGlow
border.color: "#00f0ff"
border.width: 1.5
}
}Window material window_backdrop
Only at the top level of theme.json (writing it into mod.json is ignored): acrylic | mica | mica_alt | blur | none. See theme.
Reference implementations
| Directory | Demonstrates |
|---|---|
extensions/example_theme_cyberpunk/ | full theme + 5 slots (including decorations) |
extensions/community_theme_glassmorphism/ | a complete glass-texture background set |
extensions/example_custom_waveform_ui/ | a local extension that only replaces audio_level (style_layer: effects) |
Related
- theme
- UI extensions (QML)
- Plugin windows — use this for functional always-on windows (multi-instance / positioning)
- config_schema — config switches for background components
