Skip to content

UI hint vocabulary

The platform auto-generates the Reward-config UI from your JSON Schema, but a bare schema produces bland inputs (a text box where a dropdown belongs). UI hints let you upgrade that generated UI (a picker, a colour swatch, a slider) without writing any UI code. Reach for them when you want the broadcaster’s config experience to feel designed, not generic.

JSON Schema is fine for validation but says nothing about presentation. mobrule defines a small vocabulary of x-mobrule-* keys that steer the auto-generated UI. The validator ignores the whole x-mobrule-* namespace, so hints never affect whether a value is accepted, only how it is asked for.

KeyTypeEffect
x-mobrule-viewer-overridebooleanSurface this property as a viewer-controlled input.
x-mobrule-widgetstringForce a specific widget (see Available widgets below).
x-mobrule-labelstringDisplay name shown instead of the prettified key. Set it when the schema key isn’t the friendly name you want broadcasters to see.
x-mobrule-optionsarrayChoice list: [{ "value": …, "label": "…", "color": "…"? }, …].
x-mobrule-sourcestringName of a pack-shipped option dataset to source choices from, instead of inlining x-mobrule-options.
x-mobrule-unitstringUnit suffix rendered inside number, text, and slider widgets (e.g. "s").
x-mobrule-unit-scalenumberFor a number widget, how many stored units make one displayed unit (e.g. 1000 keeps milliseconds on the wire but shows and edits seconds). minimum / maximum are shown in the displayed unit too.
x-mobrule-toggle-labelstringFirst line of a toggle widget’s label.
x-mobrule-toggle-substringSecond, smaller line under a toggle widget’s label.
x-mobrule-visible-whenobject{ "field": "...", "equals": … }: show this field only while another field equals a value. A hidden field is also excluded from required-validation.

Standard JSON Schema keywords are honoured alongside the hints: description renders as help text, minimum / maximum bound and clamp numeric widgets, and maxLength gives text widgets a live character counter plus validation.

The UI must degrade gracefully when an unknown hint appears, so Pack authors are free to ship forward-compatible hints.

x-mobrule-widget accepts one of the following values. Each demo below is interactive and renders exactly as the platform UI does.

WidgetValue typeRequires
chipsany option valuex-mobrule-options
segmentedany option valuex-mobrule-options
dropdownany option valuex-mobrule-options or x-mobrule-source
comboboxany option valuex-mobrule-options or x-mobrule-source
multiselectarray of valuesx-mobrule-options
checkboxbooleannone
togglebooleannone
numbernumber / integernone
steppernumber / integernone
slidernumber / integerminimum and maximum
textstringnone

One pill button per option; single-select, click again to deselect. An option color shows as a swatch on the chip.

pack.toml
[events.example.params_schema.properties.mood]
type = "string"
"x-mobrule-widget" = "chips"
"x-mobrule-options" = [
{ value = "chaos", label = "Chaos", color = "#e8748a" },
{ value = "calm", label = "Calm", color = "#74d2e0" },
{ value = "gold", label = "Gold", color = "#e8b76a" },
{ value = "plain", label = "Plain" },
]

A joined segmented-button row; single-select, click again to deselect. Best for short ordered scales.

pack.toml
[events.example.params_schema.properties.intensity]
type = "string"
"x-mobrule-widget" = "segmented"
"x-mobrule-options" = [
{ value = "low", label = "Low" },
{ value = "medium", label = "Medium" },
{ value = "high", label = "High" },
]

A picker popover; click the field to open, pick one option.

Select…
pack.toml
[events.example.params_schema.properties.monster]
type = "string"
"x-mobrule-widget" = "dropdown"
"x-mobrule-options" = [
{ value = "imp", label = "Imp" },
{ value = "cacodemon", label = "Cacodemon" },
{ value = "pinky", label = "Pinky" },
{ value = "revenant", label = "Revenant" },
{ value = "mancubus", label = "Mancubus" },
{ value = "archvile", label = "Arch-vile" },
{ value = "baron", label = "Baron of Hell" },
{ value = "cyberdemon", label = "Cyberdemon" },
]

The same picker, searchable: opens on focus and filters as you type. Use for long option lists. For a list long enough that you wouldn’t inline it, source it from an option dataset instead.

pack.toml
[events.example.params_schema.properties.monster]
type = "string"
"x-mobrule-widget" = "combobox"
"x-mobrule-options" = [
{ value = "imp", label = "Imp" },
{ value = "cacodemon", label = "Cacodemon" },
{ value = "pinky", label = "Pinky" },
{ value = "revenant", label = "Revenant" },
{ value = "mancubus", label = "Mancubus" },
{ value = "archvile", label = "Arch-vile" },
{ value = "baron", label = "Baron of Hell" },
{ value = "cyberdemon", label = "Cyberdemon" },
]

Chips that accumulate into an array; toggling every chip off yields unset.

pack.toml
[events.example.params_schema.properties.modifiers]
"x-mobrule-widget" = "multiselect"
"x-mobrule-options" = [
{ value = "chaos", label = "Chaos", color = "#e8748a" },
{ value = "calm", label = "Calm", color = "#74d2e0" },
{ value = "gold", label = "Gold", color = "#e8b76a" },
{ value = "plain", label = "Plain" },
]

A plain checkbox for booleans.

pack.toml
[events.example.params_schema.properties.announce]
type = "boolean"
"x-mobrule-widget" = "checkbox"

A switch with a two-line label from x-mobrule-toggle-label / x-mobrule-toggle-sub; without a label it reads “Enabled” / “Disabled”.

Friendly fire
Effects can hit the streamer's allies too.
pack.toml
[events.example.params_schema.properties.friendly_fire]
type = "boolean"
"x-mobrule-widget" = "toggle"
"x-mobrule-toggle-label" = "Friendly fire"
"x-mobrule-toggle-sub" = "Effects can hit the streamer's allies too."

A numeric input; clamped to minimum / maximum, with an optional x-mobrule-unit suffix. Pair that suffix with x-mobrule-unit-scale to store one unit while the broadcaster reads another: "x-mobrule-unit-scale" = 1000 with "x-mobrule-unit" = "s" keeps milliseconds on the wire but shows and edits seconds.

ms
pack.toml
[events.example.params_schema.properties.delay]
type = "integer"
minimum = 0
maximum = 500
"x-mobrule-widget" = "number"
"x-mobrule-unit" = "ms"

A −/+ stepper around a numeric input; steps by 1, clamps to minimum / maximum, and disables the button at the bound.

pack.toml
[events.example.params_schema.properties.count]
type = "integer"
minimum = 1
maximum = 10
"x-mobrule-widget" = "stepper"

A range slider with a live readout (plus x-mobrule-unit) and min/max tick labels. Requires both minimum and maximum.

30s
5 120
pack.toml
[events.example.params_schema.properties.duration]
type = "integer"
minimum = 5
maximum = 120
"x-mobrule-widget" = "slider"
"x-mobrule-unit" = "s"

A text input; maxLength adds a live counter, x-mobrule-unit a suffix. This is also the fallback for unknown schemas.

7/40
pack.toml
[events.example.params_schema.properties.message]
type = "string"
maxLength = 40
"x-mobrule-widget" = "text"
"x-mobrule-viewer-override" = true

A property without x-mobrule-widget (or with an unrecognised value) is inferred from its schema:

  • "type": "boolean"checkbox
  • has x-mobrule-optionschips for up to 6 options, dropdown beyond
  • has x-mobrule-source (and no x-mobrule-options) → combobox, since a sourced param typically backs a large dataset worth searching
  • "type": "number" or "integer"number
  • everything else (including nested / $ref / anyOf schemas) → text

A duration slider and a mode picker that reveals a dependent field:

{
"type": "object",
"required": ["mode"],
"properties": {
"duration": {
"type": "integer",
"minimum": 5,
"maximum": 120,
"x-mobrule-widget": "slider",
"x-mobrule-unit": "s"
},
"mode": {
"type": "string",
"x-mobrule-widget": "segmented",
"x-mobrule-options": [
{ "value": "chaos", "label": "Chaos", "color": "#e0564b" },
{ "value": "calm", "label": "Calm" }
]
},
"chaos_level": {
"type": "integer",
"x-mobrule-widget": "stepper",
"minimum": 1,
"maximum": 10,
"x-mobrule-visible-when": { "field": "mode", "equals": "chaos" }
}
}
}