UI hint vocabulary
JSON Schema is fine for validation but says nothing about presentation.
mobrule defines a small vocabulary of x-mobrule-* keys that let Pack
authors steer the auto-generated UI without writing UI code. The validator
ignores the whole x-mobrule-* namespace, so hints never affect whether a
value is accepted — only how it is asked for.
Recognised hint keywords
Section titled “Recognised hint keywords”| Key | Type | Effect |
|---|---|---|
x-mobrule-viewer-override | boolean | Surface this property as a viewer-controlled input. |
x-mobrule-widget | string | Force a specific widget (see Available widgets below). |
x-mobrule-options | array | Choice list: [{ "value": …, "label": "…", "color": "…"? }, …]. |
x-mobrule-unit | string | Unit suffix rendered inside number, text, and slider widgets (e.g. "s"). |
x-mobrule-toggle-label | string | First line of a toggle widget’s label. |
x-mobrule-toggle-sub | string | Second, smaller line under a toggle widget’s label. |
x-mobrule-visible-when | object | { "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 — Pack authors are free to ship forward-compatible hints.
Available widgets
Section titled “Available widgets”x-mobrule-widget accepts one of the following values. Each demo below is
interactive and renders exactly as the platform UI does.
| Widget | Value type | Requires |
|---|---|---|
chips | any option value | x-mobrule-options |
segmented | any option value | x-mobrule-options |
dropdown | any option value | x-mobrule-options |
combobox | any option value | x-mobrule-options |
multiselect | array of values | x-mobrule-options |
checkbox | boolean | — |
toggle | boolean | — |
number | number / integer | — |
stepper | number / integer | — |
slider | number / integer | minimum and maximum |
text | string | — |
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" },]segmented
Section titled “segmented”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" },]dropdown
Section titled “dropdown”A picker popover; click the field to open, pick one option.
pack.toml
[events.example.params_schema.properties.starter]type = "string""x-mobrule-widget" = "dropdown""x-mobrule-options" = [ { value = "bulbasaur", label = "Bulbasaur" }, { value = "charmander", label = "Charmander" }, { value = "squirtle", label = "Squirtle" }, { value = "pikachu", label = "Pikachu" }, { value = "eevee", label = "Eevee" }, { value = "snorlax", label = "Snorlax" }, { value = "gengar", label = "Gengar" }, { value = "dragonite", label = "Dragonite" },]combobox
Section titled “combobox”The same picker, searchable — opens on focus and filters as you type. Use for long option lists.
pack.toml
[events.example.params_schema.properties.starter]type = "string""x-mobrule-widget" = "combobox""x-mobrule-options" = [ { value = "bulbasaur", label = "Bulbasaur" }, { value = "charmander", label = "Charmander" }, { value = "squirtle", label = "Squirtle" }, { value = "pikachu", label = "Pikachu" }, { value = "eevee", label = "Eevee" }, { value = "snorlax", label = "Snorlax" }, { value = "gengar", label = "Gengar" }, { value = "dragonite", label = "Dragonite" },]multiselect
Section titled “multiselect”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" },]checkbox
Section titled “checkbox”A plain checkbox for booleans.
pack.toml
[events.example.params_schema.properties.announce]type = "boolean""x-mobrule-widget" = "checkbox"toggle
Section titled “toggle”A switch with a two-line label from x-mobrule-toggle-label /
x-mobrule-toggle-sub; without a label it reads “Enabled” / “Disabled”.
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."number
Section titled “number”A numeric input; clamped to minimum / maximum, with an optional
x-mobrule-unit suffix.
pack.toml
[events.example.params_schema.properties.delay]type = "integer"minimum = 0maximum = 500"x-mobrule-widget" = "number""x-mobrule-unit" = "ms"stepper
Section titled “stepper”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 = 1maximum = 10"x-mobrule-widget" = "stepper"slider
Section titled “slider”A range slider with a live readout (plus x-mobrule-unit) and min/max tick
labels. Requires both minimum and maximum.
pack.toml
[events.example.params_schema.properties.duration]type = "integer"minimum = 5maximum = 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.
pack.toml
[events.example.params_schema.properties.message]type = "string"maxLength = 40"x-mobrule-widget" = "text""x-mobrule-viewer-override" = trueDefaults when no widget is declared
Section titled “Defaults when no widget is declared”A property without x-mobrule-widget (or with an unrecognised value) is
inferred from its schema:
"type": "boolean"→checkbox- has
x-mobrule-options→chipsfor up to 6 options,dropdownbeyond "type": "number"or"integer"→number- everything else (including nested /
$ref/anyOfschemas) →text
Example
Section titled “Example”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" } } }}See also
Section titled “See also”- Manifest reference — where hints live in the schema tree.
- Events — most viewer-facing hints attach to Event params.