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

FieldTypeRequiredDescriptionConstraints
idstringyesThe surface id: <product>.<surface>, e.g. bot.controls.
  • pattern ^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$
  • maxLength 80
versionintegeryesThe catalog version. A layout names the version it was made against.
  • minimum 1
namestringyesWhat the surface is called where a person sees it.
  • minLength 1
  • maxLength 80
descriptionstring
  • maxLength 500
devicesarray of enumyesThe device classes this surface offers.
  • minItems 1
  • maxItems 3
  • unique items
  • items: one of "phone", "tablet", "computer"
gridobjectyes
  • no other fields
grid.columnsobjectyesThe 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.
  • no other fields
grid.columns.phoneintegeryes
  • minimum 2
  • maximum 8
grid.columns.tabletintegeryes
  • minimum 2
  • maximum 16
grid.columns.computerintegeryes
  • minimum 2
  • maximum 24
grid.cell_pxobjectyesThe 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.
  • no other fields
grid.cell_px.phoneintegeryes
  • minimum 24
  • maximum 200
grid.cell_px.tabletintegeryes
  • minimum 24
  • maximum 200
grid.cell_px.computerintegeryes
  • minimum 24
  • maximum 200
theme_tokensarray of stringyesThe theme tokens a tint may name (accent, surface, danger, ...). Never a colour value: the theme decides what the token is.
  • minItems 1
  • maxItems 64
  • unique items
  • items: pattern ^[a-z][a-z0-9-]{0,39}$
partshttps://openvibe.network/contracts/ui/surface.v1.json#/$defs/partyes
  • minItems 1
  • maxItems 64

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 }] }