UiSurface ui.surface@1
Generated at from
openvibe-contracts v0.419.0 and
openvibe-sdk v0.161.0.
- Version
- 1.0.0
- Owner
- contracts
- Visibility
- public
- Status
- active
- Compatibility
- backward
- Schema
https://openvibe.network/contracts/ui/surface.v1.json
ui.surface@1: the catalog a product publishes for one surface people may arrange (bot.controls, bot.overlay, live.channel, dock, chat.composer, navbar.shortcuts). It names the surface, its device classes and grid, the theme tokens its styles draw from, and every part a layout may place. A part says what it is (stick, button, slider, readout, video, list), whether it is required or pressed, its minimum cells and the minimum pixels the product guarantees (44 px for anything pressed, larger for Stop), the sizes and styles it allows, the bindings it accepts, and the parts it must never cover. A layout is validated against this catalog (openvibe-sdk/ui-layout); a layout can arrange only what the catalog has and can never add a power. The catalog is data: a layout or an AI answer never carries code or a URL.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
id | string | yes | The surface id: <product>.<surface>, e.g. bot.controls. |
|
version | integer | yes | The catalog version. A layout names the version it was made against. |
|
name | string | yes | What the surface is called where a person sees it. |
|
description | string |
| ||
devices | array of enum | yes | The device classes this surface offers. |
|
grid | object | yes |
| |
grid.columns | object | yes | The grid's columns per device class: 4 on a phone and 12 on a computer in the design; a product may choose otherwise within these bounds. |
|
grid.columns.phone | integer | yes |
| |
grid.columns.tablet | integer | yes |
| |
grid.columns.computer | integer | yes |
| |
grid.cell_px | object | yes | The smallest a cell is rendered at that device class, in CSS pixels, at the narrowest supported width. The validator multiplies a part's cells by this to check the 44 px rule. |
|
grid.cell_px.phone | integer | yes |
| |
grid.cell_px.tablet | integer | yes |
| |
grid.cell_px.computer | integer | yes |
| |
theme_tokens | array of string | yes | The theme tokens a tint may name (accent, surface, danger, ...). Never a colour value: the theme decides what the token is. |
|
parts | https://openvibe.network/contracts/ui/surface.v1.json#/$defs/part | yes |
|
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: bot-controls
{
"id": "bot.controls",
"version": 1,
"name": "Robot controls",
"description": "The robot's own panel: an optional plain Stop, the stick, the head slider and the readouts.",
"devices": [
"phone",
"tablet",
"computer"
],
"grid": {
"columns": {
"phone": 4,
"tablet": 8,
"computer": 12
},
"cell_px": {
"phone": 44,
"tablet": 44,
"computer": 44
}
},
"theme_tokens": [
"accent",
"surface",
"surface-raised",
"text",
"muted",
"danger",
"warning",
"success"
],
"parts": [
{
"id": "stop",
"name": "Stop",
"kind": "button",
"required": false,
"pressed": true,
"min": {
"w": 1,
"h": 1,
"px": 44
},
"sizes": [
{
"w": 1,
"h": 1
},
{
"w": 2,
"h": 2
}
],
"styles": {
"tints": [
"surface"
],
"shapes": [
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"Space"
],
"gamepad": [
"south"
]
},
"never_covers": []
},
{
"id": "stick",
"name": "Drive stick",
"kind": "stick",
"required": false,
"pressed": true,
"min": {
"w": 3,
"h": 3
},
"sizes": [
{
"w": 3,
"h": 3
},
{
"w": 4,
"h": 4
}
],
"styles": {
"tints": [
"surface",
"surface-raised"
],
"shapes": [
"round"
],
"opacity": {
"min": 0.6,
"max": 1
},
"label": false
},
"bindings": {
"keys": [
"W",
"A",
"S",
"D"
],
"gamepad": [
"left-stick"
]
},
"never_covers": [
"stop"
]
},
{
"id": "head",
"name": "Head",
"kind": "slider",
"required": false,
"pressed": true,
"min": {
"w": 2,
"h": 2
},
"styles": {
"tints": [
"accent",
"surface-raised"
],
"shapes": [
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"ArrowUp",
"ArrowDown"
],
"gamepad": [
"right-stick-y"
]
},
"never_covers": [
"stop"
]
},
{
"id": "battery",
"name": "Battery",
"kind": "readout",
"required": false,
"pressed": false,
"min": {
"w": 1,
"h": 1
},
"styles": {
"tints": [
"text",
"muted",
"success"
],
"shapes": [
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [],
"gamepad": []
},
"never_covers": [
"stop"
]
},
{
"id": "latency",
"name": "Latency",
"kind": "readout",
"required": false,
"pressed": false,
"min": {
"w": 1,
"h": 1
},
"styles": {
"tints": [
"text",
"muted"
],
"shapes": [
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [],
"gamepad": []
},
"never_covers": [
"stop"
]
}
]
}Valid: looi-controls
{
"id": "bot.controls",
"version": 1,
"name": "Robot controls",
"description": "LOOI's panel from its manifest (LOOI 1.0.0): each part says what it sends or shows, for the AI that arranges it.",
"devices": [
"phone",
"tablet",
"computer"
],
"grid": {
"columns": {
"phone": 4,
"tablet": 8,
"computer": 12
},
"cell_px": {
"phone": 44,
"tablet": 44,
"computer": 44
}
},
"theme_tokens": [
"accent",
"surface",
"surface-raised",
"text",
"muted",
"danger",
"warning",
"success"
],
"parts": [
{
"id": "stick",
"name": "Drive stick",
"kind": "stick",
"required": false,
"pressed": true,
"min": {
"w": 2,
"h": 2
},
"styles": {
"tints": [
"surface",
"accent"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"ArrowUp",
"ArrowDown",
"ArrowLeft",
"ArrowRight"
],
"gamepad": [
"left-stick"
]
},
"never_covers": [
"stop"
],
"command": {
"kind": "drive",
"class": "motion",
"label": "Drive",
"value": {
"throttle": [
-1,
1
],
"steer": [
-1,
1
]
},
"lifetime_ms": 500,
"delivery_ms": {
"min": 1,
"max": 300
},
"release": "stop",
"control": "stick"
}
},
{
"id": "head",
"name": "Head",
"kind": "slider",
"required": false,
"pressed": true,
"min": {
"w": 2,
"h": 1
},
"styles": {
"tints": [
"accent",
"surface"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"q",
"e"
],
"gamepad": []
},
"never_covers": [
"stop"
],
"command": {
"kind": "actuator",
"name": "head",
"class": "motion",
"label": "Head",
"value": {
"head": [
-1,
1
]
},
"lifetime_ms": 500,
"delivery_ms": {
"min": 1,
"max": 300
},
"release": "hold",
"control": "slider"
}
},
{
"id": "headlight",
"name": "Headlights",
"kind": "button",
"required": false,
"pressed": true,
"min": {
"w": 2,
"h": 1
},
"styles": {
"tints": [
"surface",
"accent"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"l"
],
"gamepad": []
},
"never_covers": [
"stop"
],
"command": {
"kind": "headlight",
"class": "state",
"label": "Headlights",
"value": {
"on": "boolean"
},
"delivery_ms": {
"min": 1,
"max": 250
},
"min_left_ms": 50,
"rate_per_s": 2,
"release": "none",
"latched": true,
"confirm": "p7_headlight",
"control": "toggle"
}
},
{
"id": "battery",
"name": "Battery",
"kind": "readout",
"required": false,
"pressed": false,
"min": {
"w": 1,
"h": 1
},
"styles": {
"tints": [
"text",
"surface"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [],
"gamepad": []
},
"reads": {
"key": "battery",
"label": "Battery",
"unit": "%",
"range": [
0,
100
],
"source": "poll",
"control": "meter"
}
},
{
"id": "commander",
"name": "Who is driving",
"kind": "readout",
"required": false,
"pressed": false,
"min": {
"w": 2,
"h": 1
},
"styles": {
"tints": [
"text",
"surface"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [],
"gamepad": []
},
"reads": {
"key": "p7_commander",
"label": "Who is driving",
"fields": {
"subject": "string",
"role": [
"owner",
"mod",
"operator"
],
"channel": [
"drive",
"head",
"headlight"
],
"at": "ms"
},
"source": "push",
"control": "badge"
}
},
{
"id": "stop",
"name": "Stop",
"kind": "button",
"required": false,
"pressed": true,
"min": {
"w": 1,
"h": 1,
"px": 44
},
"styles": {
"tints": [
"surface"
],
"shapes": [
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"Space"
],
"gamepad": [
"south"
]
}
}
]
}Rejected: colour-tint
{
"id": "bot.controls",
"version": 1,
"name": "Robot controls",
"devices": [
"phone"
],
"grid": {
"columns": {
"phone": 4,
"tablet": 8,
"computer": 12
},
"cell_px": {
"phone": 44,
"tablet": 44,
"computer": 44
}
},
"theme_tokens": [
"danger"
],
"parts": [
{
"id": "stop",
"name": "Stop",
"kind": "button",
"required": true,
"pressed": true,
"min": {
"w": 2,
"h": 2
},
"styles": {
"tints": [
"#ff0000"
],
"shapes": [
"round"
]
},
"bindings": {
"keys": [
"Space"
],
"gamepad": [
"south"
]
}
}
]
}Rejected: command-motion-without-release
{
"id": "bot.controls",
"version": 1,
"name": "Robot controls",
"description": "LOOI's panel from its manifest (LOOI 1.0.0): each part says what it sends or shows, for the AI that arranges it.",
"devices": [
"phone",
"tablet",
"computer"
],
"grid": {
"columns": {
"phone": 4,
"tablet": 8,
"computer": 12
},
"cell_px": {
"phone": 44,
"tablet": 44,
"computer": 44
}
},
"theme_tokens": [
"accent",
"surface",
"surface-raised",
"text",
"muted",
"danger",
"warning",
"success"
],
"parts": [
{
"id": "stick",
"name": "Drive stick",
"kind": "stick",
"required": false,
"pressed": true,
"min": {
"w": 2,
"h": 2
},
"styles": {
"tints": [
"surface",
"accent"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"ArrowUp",
"ArrowDown",
"ArrowLeft",
"ArrowRight"
],
"gamepad": [
"left-stick"
]
},
"never_covers": [
"stop"
],
"command": {
"kind": "drive",
"class": "motion",
"label": "Drive",
"value": {
"throttle": [
-1,
1
],
"steer": [
-1,
1
]
},
"lifetime_ms": 500,
"delivery_ms": {
"min": 1,
"max": 300
},
"control": "stick"
}
},
{
"id": "head",
"name": "Head",
"kind": "slider",
"required": false,
"pressed": true,
"min": {
"w": 2,
"h": 1
},
"styles": {
"tints": [
"accent",
"surface"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"q",
"e"
],
"gamepad": []
},
"never_covers": [
"stop"
],
"command": {
"kind": "actuator",
"name": "head",
"class": "motion",
"label": "Head",
"value": {
"head": [
-1,
1
]
},
"lifetime_ms": 500,
"delivery_ms": {
"min": 1,
"max": 300
},
"release": "hold",
"control": "slider"
}
},
{
"id": "headlight",
"name": "Headlights",
"kind": "button",
"required": false,
"pressed": true,
"min": {
"w": 2,
"h": 1
},
"styles": {
"tints": [
"surface",
"accent"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"l"
],
"gamepad": []
},
"never_covers": [
"stop"
],
"command": {
"kind": "headlight",
"class": "state",
"label": "Headlights",
"value": {
"on": "boolean"
},
"delivery_ms": {
"min": 1,
"max": 250
},
"min_left_ms": 50,
"rate_per_s": 2,
"release": "none",
"latched": true,
"confirm": "p7_headlight",
"control": "toggle"
}
},
{
"id": "battery",
"name": "Battery",
"kind": "readout",
"required": false,
"pressed": false,
"min": {
"w": 1,
"h": 1
},
"styles": {
"tints": [
"text",
"surface"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [],
"gamepad": []
},
"reads": {
"key": "battery",
"label": "Battery",
"unit": "%",
"range": [
0,
100
],
"source": "poll",
"control": "meter"
}
},
{
"id": "commander",
"name": "Who is driving",
"kind": "readout",
"required": false,
"pressed": false,
"min": {
"w": 2,
"h": 1
},
"styles": {
"tints": [
"text",
"surface"
],
"shapes": [
"round",
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [],
"gamepad": []
},
"reads": {
"key": "p7_commander",
"label": "Who is driving",
"fields": {
"subject": "string",
"role": [
"owner",
"mod",
"operator"
],
"channel": [
"drive",
"head",
"headlight"
],
"at": "ms"
},
"source": "push",
"control": "badge"
}
},
{
"id": "stop",
"name": "Stop",
"kind": "button",
"required": false,
"pressed": true,
"min": {
"w": 1,
"h": 1,
"px": 44
},
"styles": {
"tints": [
"surface"
],
"shapes": [
"pill",
"square"
],
"opacity": {
"min": 0.4,
"max": 1
},
"label": true
},
"bindings": {
"keys": [
"Space"
],
"gamepad": [
"south"
]
}
}
]
}Rejected: unknown-kind
{
"id": "bot.controls",
"version": 1,
"name": "Robot controls",
"devices": [
"phone"
],
"grid": {
"columns": {
"phone": 4,
"tablet": 8,
"computer": 12
},
"cell_px": {
"phone": 44,
"tablet": 44,
"computer": 44
}
},
"theme_tokens": [
"accent"
],
"parts": [
{
"id": "stop",
"name": "Stop",
"kind": "power",
"required": true,
"pressed": true,
"min": {
"w": 2,
"h": 2
},
"styles": {
"tints": [
"danger"
],
"shapes": [
"round"
]
},
"bindings": {
"keys": [
"Space"
],
"gamepad": [
"south"
]
}
}
]
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('ui.surface@1', value); // { valid, errors: [{ path, message }] }