diff --git a/schemas/layout.json b/schemas/layout.json index 2c7a42b1..ada46f33 100644 --- a/schemas/layout.json +++ b/schemas/layout.json @@ -20,612 +20,91 @@ "items": { "anyOf": [ { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "description": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback.", - "markdownDescription": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback." - }, - "rect": { - "type": "array", - "minItems": 4, - "items": [ - { - "type": "number", - "description": "X coordinate of the rectangle.", - "minimum": 0, - "maximum": 200, - "title": "x", - "markdownDescription": "X coordinate of the rectangle." - }, - { - "type": "number", - "description": "Y coordinate of the rectangle.", - "minimum": 0, - "maximum": 100, - "title": "y", - "markdownDescription": "Y coordinate of the rectangle." - }, - { - "type": "number", - "description": "Width of the rectangle.", - "minimum": 0, - "maximum": 200, - "title": "width", - "markdownDescription": "Width of the rectangle." - }, - { - "type": "number", - "description": "Height of the rectangle.", - "minimum": 0, - "maximum": 100, - "title": "height", - "markdownDescription": "Height of the rectangle." - } - ], - "maxItems": 4, - "description": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`.", - "markdownDescription": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`." - }, - "type": { - "type": "string", - "const": "bar", - "description": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc.", - "markdownDescription": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc." - }, - "bar_bg_c": { - "type": "string", - "description": "Bar background color represented as a named color, hexadecimal value, or gradient. Default is `darkGray`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", - "examples": [ - "darkGray" - ], - "markdownDescription": "Bar background color represented as a named color, hexadecimal value, or gradient. Default is `darkGray`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" - }, - "bar_border_c": { - "type": "string", - "description": "Border color represented as a named color, or hexadecimal value. Default is `white`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)", - "examples": [ - "white" - ], - "markdownDescription": "Border color represented as a named color, or hexadecimal value. Default is `white`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)" - }, - "bar_fill_c": { - "type": "string", - "description": "Fill color of the bar represented as a named color, hexadecimal value, or gradient. Default is `white`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", - "examples": [ - "white" - ], - "markdownDescription": "Fill color of the bar represented as a named color, hexadecimal value, or gradient. Default is `white`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" - }, - "border_w": { - "type": "number", - "description": "Width of the border around the bar, as a whole number. Default is `2`.", - "examples": [ - 2 - ], - "markdownDescription": "Width of the border around the bar, as a whole number. Default is `2`." - }, - "range": { - "type": "object", - "properties": { - "min": { - "type": "number", - "description": "Minimum value of the bar.", - "markdownDescription": "Minimum value of the bar." - }, - "max": { - "type": "number", - "description": "Maximum value of the bar.", - "markdownDescription": "Maximum value of the bar." - } - }, - "required": [ - "min", - "max" - ], - "additionalProperties": false, - "description": "Defines the range of the value the bar represents, e.g. 0-20, 0-100, etc.", - "markdownDescription": "Defines the range of the value the bar represents, e.g. 0-20, 0-100, etc." - }, - "subtype": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4 - ], - "description": "Sub-type used to determine the type of bar to render. Default is {@link BarSubType.Groove } (4).\n\n**Options**\n- Rectangle (0)\n- DoubleRectangle (1)\n- Trapezoid (2)\n- DoubleTrapezoid (3)\n- Groove (4)", - "markdownDescription": "Sub-type used to determine the type of bar to render. Default is {@link BarSubType.Groove } (4).\n\n**Options**\n- Rectangle (0)\n- DoubleRectangle (1)\n- Trapezoid (2)\n- DoubleTrapezoid (3)\n- Groove (4)" - }, - "value": { - "type": "number", - "description": "Value used to determine how much of the bar is filled. Correlates with the item's `range` if specified in the layout's JSON definition; default range is `0..100`.", - "markdownDescription": "Value used to determine how much of the bar is filled. Correlates with the item's `range` if specified in the layout's JSON definition; default range is `0..100`." - }, - "background": { - "type": "string", - "description": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", - "markdownDescription": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" - }, - "enabled": { - "type": "boolean", - "description": "Determines whether the item is enabled (i.e. visible); default is `true`.", - "markdownDescription": "Determines whether the item is enabled (i.e. visible); default is `true`." - }, - "opacity": { - "type": "number", - "enum": [ - 0, - 0.1, - 0.2, - 0.3, - 0.4, - 0.5, - 0.6, - 0.7, - 0.8, - 0.9, - 1 - ], - "description": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`.", - "markdownDescription": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`." - }, - "zOrder": { - "type": "number", - "description": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`.", - "minimum": 0, - "maximum": 700, - "markdownDescription": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`." + "if": { + "properties": { + "type": { + "const": "bar", + "enum": [ + "bar", + "gbar", + "pixmap", + "text" + ] + } } }, - "required": [ - "key", - "rect", - "type", - "value" - ], - "description": "Extended information used to define a layout item within a layout's JSON file.", - "markdownDescription": "Extended information used to define a layout item within a layout's JSON file." - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "description": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback.", - "markdownDescription": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback." - }, - "rect": { - "type": "array", - "minItems": 4, - "items": [ - { - "type": "number", - "description": "X coordinate of the rectangle.", - "minimum": 0, - "maximum": 200, - "title": "x", - "markdownDescription": "X coordinate of the rectangle." - }, - { - "type": "number", - "description": "Y coordinate of the rectangle.", - "minimum": 0, - "maximum": 100, - "title": "y", - "markdownDescription": "Y coordinate of the rectangle." - }, - { - "type": "number", - "description": "Width of the rectangle.", - "minimum": 0, - "maximum": 200, - "title": "width", - "markdownDescription": "Width of the rectangle." - }, - { - "type": "number", - "description": "Height of the rectangle.", - "minimum": 0, - "maximum": 100, - "title": "height", - "markdownDescription": "Height of the rectangle." - } - ], - "maxItems": 4, - "description": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`.", - "markdownDescription": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`." - }, - "type": { - "type": "string", - "const": "gbar", - "description": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc.", - "markdownDescription": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc." - }, - "bar_h": { - "type": "number", - "description": "Height of the bar's indicator. Default is `10`.", - "examples": [ - 10 - ], - "markdownDescription": "Height of the bar's indicator. Default is `10`." - }, - "bar_bg_c": { - "type": "string", - "description": "Bar background color represented as a named color, hexadecimal value, or gradient. Default is `darkGray`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", - "examples": [ - "darkGray" - ], - "markdownDescription": "Bar background color represented as a named color, hexadecimal value, or gradient. Default is `darkGray`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" - }, - "bar_border_c": { - "type": "string", - "description": "Border color represented as a named color, or hexadecimal value. Default is `white`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)", - "examples": [ - "white" - ], - "markdownDescription": "Border color represented as a named color, or hexadecimal value. Default is `white`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)" - }, - "bar_fill_c": { - "type": "string", - "description": "Fill color of the bar represented as a named color, hexadecimal value, or gradient. Default is `white`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", - "examples": [ - "white" - ], - "markdownDescription": "Fill color of the bar represented as a named color, hexadecimal value, or gradient. Default is `white`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" - }, - "border_w": { - "type": "number", - "description": "Width of the border around the bar, as a whole number. Default is `2`.", - "examples": [ - 2 - ], - "markdownDescription": "Width of the border around the bar, as a whole number. Default is `2`." - }, - "range": { - "type": "object", + "then": { + "$ref": "#/definitions/bar" + }, + "else": { + "if": { "properties": { - "min": { - "type": "number", - "description": "Minimum value of the bar.", - "markdownDescription": "Minimum value of the bar." - }, - "max": { - "type": "number", - "description": "Maximum value of the bar.", - "markdownDescription": "Maximum value of the bar." + "type": { + "const": "gbar", + "enum": [ + "bar", + "gbar", + "pixmap", + "text" + ] } - }, - "required": [ - "min", - "max" - ], - "additionalProperties": false, - "description": "Defines the range of the value the bar represents, e.g. 0-20, 0-100, etc.", - "markdownDescription": "Defines the range of the value the bar represents, e.g. 0-20, 0-100, etc." - }, - "subtype": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4 - ], - "description": "Sub-type used to determine the type of bar to render. Default is {@link BarSubType.Groove } (4).\n\n**Options**\n- Rectangle (0)\n- DoubleRectangle (1)\n- Trapezoid (2)\n- DoubleTrapezoid (3)\n- Groove (4)", - "markdownDescription": "Sub-type used to determine the type of bar to render. Default is {@link BarSubType.Groove } (4).\n\n**Options**\n- Rectangle (0)\n- DoubleRectangle (1)\n- Trapezoid (2)\n- DoubleTrapezoid (3)\n- Groove (4)" - }, - "value": { - "type": "number", - "description": "Value used to determine how much of the bar is filled. Correlates with the item's `range` if specified in the layout's JSON definition; default range is `0..100`.", - "markdownDescription": "Value used to determine how much of the bar is filled. Correlates with the item's `range` if specified in the layout's JSON definition; default range is `0..100`." - }, - "background": { - "type": "string", - "description": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", - "markdownDescription": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" - }, - "enabled": { - "type": "boolean", - "description": "Determines whether the item is enabled (i.e. visible); default is `true`.", - "markdownDescription": "Determines whether the item is enabled (i.e. visible); default is `true`." - }, - "opacity": { - "type": "number", - "enum": [ - 0, - 0.1, - 0.2, - 0.3, - 0.4, - 0.5, - 0.6, - 0.7, - 0.8, - 0.9, - 1 - ], - "description": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`.", - "markdownDescription": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`." - }, - "zOrder": { - "type": "number", - "description": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`.", - "minimum": 0, - "maximum": 700, - "markdownDescription": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`." - } - }, - "required": [ - "key", - "rect", - "type", - "value" - ], - "description": "Extended information used to define a layout item within a layout's JSON file.", - "markdownDescription": "Extended information used to define a layout item within a layout's JSON file." - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "description": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback.", - "markdownDescription": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback." - }, - "rect": { - "type": "array", - "minItems": 4, - "items": [ - { - "type": "number", - "description": "X coordinate of the rectangle.", - "minimum": 0, - "maximum": 200, - "title": "x", - "markdownDescription": "X coordinate of the rectangle." - }, - { - "type": "number", - "description": "Y coordinate of the rectangle.", - "minimum": 0, - "maximum": 100, - "title": "y", - "markdownDescription": "Y coordinate of the rectangle." - }, - { - "type": "number", - "description": "Width of the rectangle.", - "minimum": 0, - "maximum": 200, - "title": "width", - "markdownDescription": "Width of the rectangle." - }, - { - "type": "number", - "description": "Height of the rectangle.", - "minimum": 0, - "maximum": 100, - "title": "height", - "markdownDescription": "Height of the rectangle." + } + }, + "then": { + "$ref": "#/definitions/gbar" + }, + "else": { + "if": { + "properties": { + "type": { + "const": "pixmap", + "enum": [ + "bar", + "gbar", + "pixmap", + "text" + ] + } } - ], - "maxItems": 4, - "description": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`.", - "markdownDescription": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`." - }, - "type": { - "type": "string", - "const": "pixmap", - "description": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc.", - "markdownDescription": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc." - }, - "value": { - "type": "string", - "description": "Image to render; this can be either a path to a local file within the plugin's folder, a base64 encoded `string` with the mime type declared (e.g. PNG, JPEG, etc.), or an SVG `string`.\n\n**Examples:**\n- \"imgs/Logo.png\"\n- \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MHB0IiBoZWlnaHQ9…\"", - "markdownDescription": "Image to render; this can be either a path to a local file within the plugin's folder, a base64 encoded `string` with the mime type declared (e.g. PNG, JPEG, etc.), or an SVG `string`.\n\n**Examples:**\n- \"imgs/Logo.png\"\n- \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MHB0IiBoZWlnaHQ9…\"" - }, - "background": { - "type": "string", - "description": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", - "markdownDescription": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" - }, - "enabled": { - "type": "boolean", - "description": "Determines whether the item is enabled (i.e. visible); default is `true`.", - "markdownDescription": "Determines whether the item is enabled (i.e. visible); default is `true`." - }, - "opacity": { - "type": "number", - "enum": [ - 0, - 0.1, - 0.2, - 0.3, - 0.4, - 0.5, - 0.6, - 0.7, - 0.8, - 0.9, - 1 - ], - "description": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`.", - "markdownDescription": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`." - }, - "zOrder": { - "type": "number", - "description": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`.", - "minimum": 0, - "maximum": 700, - "markdownDescription": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`." - } - }, - "required": [ - "key", - "rect", - "type" - ], - "description": "Extended information used to define a layout item within a layout's JSON file.", - "markdownDescription": "Extended information used to define a layout item within a layout's JSON file." - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "description": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback.", - "markdownDescription": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback." - }, - "rect": { - "type": "array", - "minItems": 4, - "items": [ - { - "type": "number", - "description": "X coordinate of the rectangle.", - "minimum": 0, - "maximum": 200, - "title": "x", - "markdownDescription": "X coordinate of the rectangle." - }, - { - "type": "number", - "description": "Y coordinate of the rectangle.", - "minimum": 0, - "maximum": 100, - "title": "y", - "markdownDescription": "Y coordinate of the rectangle." + }, + "then": { + "$ref": "#/definitions/pixmap" + }, + "else": { + "if": { + "properties": { + "type": { + "const": "text", + "enum": [ + "bar", + "gbar", + "pixmap", + "text" + ] + } + } }, - { - "type": "number", - "description": "Width of the rectangle.", - "minimum": 0, - "maximum": 200, - "title": "width", - "markdownDescription": "Width of the rectangle." + "then": { + "$ref": "#/definitions/text" }, - { - "type": "number", - "description": "Height of the rectangle.", - "minimum": 0, - "maximum": 100, - "title": "height", - "markdownDescription": "Height of the rectangle." + "else": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "bar", + "gbar", + "pixmap", + "text" + ] + } + } } - ], - "maxItems": 4, - "description": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`.", - "markdownDescription": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`." - }, - "type": { - "type": "string", - "const": "text", - "description": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc.", - "markdownDescription": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc." - }, - "alignment": { - "type": "string", - "enum": [ - "center", - "left", - "right" - ], - "description": "Alignment of the text. Default is `\"center\"`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector.", - "markdownDescription": "Alignment of the text. Default is `\"center\"`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector." - }, - "color": { - "type": "string", - "description": "Color of the font represented as a named color, or hexadecimal value. Default is `white`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)", - "markdownDescription": "Color of the font represented as a named color, or hexadecimal value. Default is `white`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)" - }, - "font": { - "type": "object", - "properties": { - "size": { - "type": "number", - "description": "Size of the font. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, this value will be ignored in favour of the user's preferred title settings, as set in property inspector.", - "markdownDescription": "Size of the font. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, this value will be ignored in favour of the user's preferred title settings, as set in property inspector." - }, - "weight": { - "type": "number", - "description": "Weight of the font; value must be a whole `number` in the range of `100..1000`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, this value will be ignored in favour of the user's preferred title settings, as set in property inspector.", - "minimum": 100, - "maximum": 1000, - "markdownDescription": "Weight of the font; value must be a whole `number` in the range of `100..1000`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, this value will be ignored in favour of the user's preferred title settings, as set in property inspector." - } - }, - "additionalProperties": false, - "description": "Defines how the font should be rendered. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector.", - "markdownDescription": "Defines how the font should be rendered. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector." - }, - "text-overflow": { - "type": "string", - "enum": [ - "clip", - "ellipsis", - "fade" - ], - "description": "Defines how overflowing text should be rendered on the layout.\n- clip, truncates the text at the boundary of the element (default).\n- ellipsis, truncates the text prior to the boundary of the element, and adds an ellipsis (…) to the end.\n- fade, applies a fade-gradient over the end of the text.", - "default": "ellipsis", - "markdownDescription": "Defines how overflowing text should be rendered on the layout.\n- clip, truncates the text at the boundary of the element (default).\n- ellipsis, truncates the text prior to the boundary of the element, and adds an ellipsis (…) to the end.\n- fade, applies a fade-gradient over the end of the text." - }, - "value": { - "type": "string", - "description": "Text to be displayed.", - "markdownDescription": "Text to be displayed." - }, - "background": { - "type": "string", - "description": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", - "markdownDescription": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" - }, - "enabled": { - "type": "boolean", - "description": "Determines whether the item is enabled (i.e. visible); default is `true`.", - "markdownDescription": "Determines whether the item is enabled (i.e. visible); default is `true`." - }, - "opacity": { - "type": "number", - "enum": [ - 0, - 0.1, - 0.2, - 0.3, - 0.4, - 0.5, - 0.6, - 0.7, - 0.8, - 0.9, - 1 - ], - "description": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`.", - "markdownDescription": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`." - }, - "zOrder": { - "type": "number", - "description": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`.", - "minimum": 0, - "maximum": 700, - "markdownDescription": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`." + } } - }, - "required": [ - "key", - "rect", - "type" - ], - "description": "Extended information used to define a layout item within a layout's JSON file.", - "markdownDescription": "Extended information used to define a layout item within a layout's JSON file." + } } ] }, @@ -640,6 +119,622 @@ "additionalProperties": false, "description": "Defines the structure of a custom layout file.", "markdownDescription": "Defines the structure of a custom layout file." + }, + "bar": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "type": "string", + "description": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback.", + "pattern": "^[A-Za-z0-9\\-_]+$", + "errorMessage": "String must only contain alphanumeric characters (A-z, 0-9), hyphens (-), and underscores (_)", + "markdownDescription": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback." + }, + "rect": { + "type": "array", + "minItems": 4, + "items": [ + { + "type": "number", + "description": "X coordinate of the rectangle.", + "minimum": 0, + "maximum": 200, + "title": "x", + "markdownDescription": "X coordinate of the rectangle." + }, + { + "type": "number", + "description": "Y coordinate of the rectangle.", + "minimum": 0, + "maximum": 100, + "title": "y", + "markdownDescription": "Y coordinate of the rectangle." + }, + { + "type": "number", + "description": "Width of the rectangle.", + "minimum": 0, + "maximum": 200, + "title": "width", + "markdownDescription": "Width of the rectangle." + }, + { + "type": "number", + "description": "Height of the rectangle.", + "minimum": 0, + "maximum": 100, + "title": "height", + "markdownDescription": "Height of the rectangle." + } + ], + "maxItems": 4, + "description": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`.", + "markdownDescription": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`." + }, + "type": { + "type": "string", + "const": "bar", + "description": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc.", + "markdownDescription": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc." + }, + "bar_bg_c": { + "type": "string", + "description": "Bar background color represented as a named color, hexadecimal value, or gradient. Default is `darkGray`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", + "examples": [ + "darkGray" + ], + "markdownDescription": "Bar background color represented as a named color, hexadecimal value, or gradient. Default is `darkGray`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" + }, + "bar_border_c": { + "type": "string", + "description": "Border color represented as a named color, or hexadecimal value. Default is `white`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)", + "examples": [ + "white" + ], + "markdownDescription": "Border color represented as a named color, or hexadecimal value. Default is `white`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)" + }, + "bar_fill_c": { + "type": "string", + "description": "Fill color of the bar represented as a named color, hexadecimal value, or gradient. Default is `white`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", + "examples": [ + "white" + ], + "markdownDescription": "Fill color of the bar represented as a named color, hexadecimal value, or gradient. Default is `white`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" + }, + "border_w": { + "type": "number", + "description": "Width of the border around the bar, as a whole number. Default is `2`.", + "examples": [ + 2 + ], + "markdownDescription": "Width of the border around the bar, as a whole number. Default is `2`." + }, + "range": { + "type": "object", + "properties": { + "min": { + "type": "number", + "description": "Minimum value of the bar.", + "markdownDescription": "Minimum value of the bar." + }, + "max": { + "type": "number", + "description": "Maximum value of the bar.", + "markdownDescription": "Maximum value of the bar." + } + }, + "required": [ + "min", + "max" + ], + "additionalProperties": false, + "description": "Defines the range of the value the bar represents, e.g. 0-20, 0-100, etc.", + "markdownDescription": "Defines the range of the value the bar represents, e.g. 0-20, 0-100, etc." + }, + "subtype": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "description": "Sub-type used to determine the type of bar to render. Default is {@link BarSubType.Groove } (4).\n\n**Options**\n- Rectangle (0)\n- DoubleRectangle (1)\n- Trapezoid (2)\n- DoubleTrapezoid (3)\n- Groove (4)", + "markdownDescription": "Sub-type used to determine the type of bar to render. Default is {@link BarSubType.Groove } (4).\n\n**Options**\n- Rectangle (0)\n- DoubleRectangle (1)\n- Trapezoid (2)\n- DoubleTrapezoid (3)\n- Groove (4)" + }, + "value": { + "type": "number", + "description": "Value used to determine how much of the bar is filled. Correlates with the item's `range` if specified in the layout's JSON definition; default range is `0..100`.", + "markdownDescription": "Value used to determine how much of the bar is filled. Correlates with the item's `range` if specified in the layout's JSON definition; default range is `0..100`." + }, + "background": { + "type": "string", + "description": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", + "markdownDescription": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" + }, + "enabled": { + "type": "boolean", + "description": "Determines whether the item is enabled (i.e. visible); default is `true`.", + "markdownDescription": "Determines whether the item is enabled (i.e. visible); default is `true`." + }, + "opacity": { + "type": "number", + "enum": [ + 0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "description": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`.", + "markdownDescription": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`." + }, + "zOrder": { + "type": "number", + "description": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`.", + "minimum": 0, + "maximum": 700, + "markdownDescription": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`." + } + }, + "required": [ + "key", + "rect", + "type", + "value" + ], + "description": "Extended information used to define a layout item within a layout's JSON file.", + "markdownDescription": "Extended information used to define a layout item within a layout's JSON file." + }, + "gbar": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "type": "string", + "description": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback.", + "pattern": "^[A-Za-z0-9\\-_]+$", + "errorMessage": "String must only contain alphanumeric characters (A-z, 0-9), hyphens (-), and underscores (_)", + "markdownDescription": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback." + }, + "rect": { + "type": "array", + "minItems": 4, + "items": [ + { + "type": "number", + "description": "X coordinate of the rectangle.", + "minimum": 0, + "maximum": 200, + "title": "x", + "markdownDescription": "X coordinate of the rectangle." + }, + { + "type": "number", + "description": "Y coordinate of the rectangle.", + "minimum": 0, + "maximum": 100, + "title": "y", + "markdownDescription": "Y coordinate of the rectangle." + }, + { + "type": "number", + "description": "Width of the rectangle.", + "minimum": 0, + "maximum": 200, + "title": "width", + "markdownDescription": "Width of the rectangle." + }, + { + "type": "number", + "description": "Height of the rectangle.", + "minimum": 0, + "maximum": 100, + "title": "height", + "markdownDescription": "Height of the rectangle." + } + ], + "maxItems": 4, + "description": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`.", + "markdownDescription": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`." + }, + "type": { + "type": "string", + "const": "gbar", + "description": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc.", + "markdownDescription": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc." + }, + "bar_h": { + "type": "number", + "description": "Height of the bar's indicator. Default is `10`.", + "examples": [ + 10 + ], + "markdownDescription": "Height of the bar's indicator. Default is `10`." + }, + "bar_bg_c": { + "type": "string", + "description": "Bar background color represented as a named color, hexadecimal value, or gradient. Default is `darkGray`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", + "examples": [ + "darkGray" + ], + "markdownDescription": "Bar background color represented as a named color, hexadecimal value, or gradient. Default is `darkGray`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" + }, + "bar_border_c": { + "type": "string", + "description": "Border color represented as a named color, or hexadecimal value. Default is `white`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)", + "examples": [ + "white" + ], + "markdownDescription": "Border color represented as a named color, or hexadecimal value. Default is `white`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)" + }, + "bar_fill_c": { + "type": "string", + "description": "Fill color of the bar represented as a named color, hexadecimal value, or gradient. Default is `white`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", + "examples": [ + "white" + ], + "markdownDescription": "Fill color of the bar represented as a named color, hexadecimal value, or gradient. Default is `white`. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" + }, + "border_w": { + "type": "number", + "description": "Width of the border around the bar, as a whole number. Default is `2`.", + "examples": [ + 2 + ], + "markdownDescription": "Width of the border around the bar, as a whole number. Default is `2`." + }, + "range": { + "type": "object", + "properties": { + "min": { + "type": "number", + "description": "Minimum value of the bar.", + "markdownDescription": "Minimum value of the bar." + }, + "max": { + "type": "number", + "description": "Maximum value of the bar.", + "markdownDescription": "Maximum value of the bar." + } + }, + "required": [ + "min", + "max" + ], + "additionalProperties": false, + "description": "Defines the range of the value the bar represents, e.g. 0-20, 0-100, etc.", + "markdownDescription": "Defines the range of the value the bar represents, e.g. 0-20, 0-100, etc." + }, + "subtype": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "description": "Sub-type used to determine the type of bar to render. Default is {@link BarSubType.Groove } (4).\n\n**Options**\n- Rectangle (0)\n- DoubleRectangle (1)\n- Trapezoid (2)\n- DoubleTrapezoid (3)\n- Groove (4)", + "markdownDescription": "Sub-type used to determine the type of bar to render. Default is {@link BarSubType.Groove } (4).\n\n**Options**\n- Rectangle (0)\n- DoubleRectangle (1)\n- Trapezoid (2)\n- DoubleTrapezoid (3)\n- Groove (4)" + }, + "value": { + "type": "number", + "description": "Value used to determine how much of the bar is filled. Correlates with the item's `range` if specified in the layout's JSON definition; default range is `0..100`.", + "markdownDescription": "Value used to determine how much of the bar is filled. Correlates with the item's `range` if specified in the layout's JSON definition; default range is `0..100`." + }, + "background": { + "type": "string", + "description": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", + "markdownDescription": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" + }, + "enabled": { + "type": "boolean", + "description": "Determines whether the item is enabled (i.e. visible); default is `true`.", + "markdownDescription": "Determines whether the item is enabled (i.e. visible); default is `true`." + }, + "opacity": { + "type": "number", + "enum": [ + 0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "description": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`.", + "markdownDescription": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`." + }, + "zOrder": { + "type": "number", + "description": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`.", + "minimum": 0, + "maximum": 700, + "markdownDescription": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`." + } + }, + "required": [ + "key", + "rect", + "type", + "value" + ], + "description": "Extended information used to define a layout item within a layout's JSON file.", + "markdownDescription": "Extended information used to define a layout item within a layout's JSON file." + }, + "pixmap": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "type": "string", + "description": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback.", + "pattern": "^[A-Za-z0-9\\-_]+$", + "errorMessage": "String must only contain alphanumeric characters (A-z, 0-9), hyphens (-), and underscores (_)", + "markdownDescription": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback." + }, + "rect": { + "type": "array", + "minItems": 4, + "items": [ + { + "type": "number", + "description": "X coordinate of the rectangle.", + "minimum": 0, + "maximum": 200, + "title": "x", + "markdownDescription": "X coordinate of the rectangle." + }, + { + "type": "number", + "description": "Y coordinate of the rectangle.", + "minimum": 0, + "maximum": 100, + "title": "y", + "markdownDescription": "Y coordinate of the rectangle." + }, + { + "type": "number", + "description": "Width of the rectangle.", + "minimum": 0, + "maximum": 200, + "title": "width", + "markdownDescription": "Width of the rectangle." + }, + { + "type": "number", + "description": "Height of the rectangle.", + "minimum": 0, + "maximum": 100, + "title": "height", + "markdownDescription": "Height of the rectangle." + } + ], + "maxItems": 4, + "description": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`.", + "markdownDescription": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`." + }, + "type": { + "type": "string", + "const": "pixmap", + "description": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc.", + "markdownDescription": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc." + }, + "value": { + "type": "string", + "description": "Image to render; this can be either a path to a local file within the plugin's folder, a base64 encoded `string` with the mime type declared (e.g. PNG, JPEG, etc.), or an SVG `string`.\n\n**Examples:**\n- \"imgs/Logo.png\"\n- \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MHB0IiBoZWlnaHQ9…\"", + "markdownDescription": "Image to render; this can be either a path to a local file within the plugin's folder, a base64 encoded `string` with the mime type declared (e.g. PNG, JPEG, etc.), or an SVG `string`.\n\n**Examples:**\n- \"imgs/Logo.png\"\n- \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MHB0IiBoZWlnaHQ9…\"" + }, + "background": { + "type": "string", + "description": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", + "markdownDescription": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" + }, + "enabled": { + "type": "boolean", + "description": "Determines whether the item is enabled (i.e. visible); default is `true`.", + "markdownDescription": "Determines whether the item is enabled (i.e. visible); default is `true`." + }, + "opacity": { + "type": "number", + "enum": [ + 0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "description": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`.", + "markdownDescription": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`." + }, + "zOrder": { + "type": "number", + "description": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`.", + "minimum": 0, + "maximum": 700, + "markdownDescription": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`." + } + }, + "required": [ + "key", + "rect", + "type" + ], + "description": "Extended information used to define a layout item within a layout's JSON file.", + "markdownDescription": "Extended information used to define a layout item within a layout's JSON file." + }, + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "type": "string", + "description": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback.", + "pattern": "^[A-Za-z0-9\\-_]+$", + "errorMessage": "String must only contain alphanumeric characters (A-z, 0-9), hyphens (-), and underscores (_)", + "markdownDescription": "Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback." + }, + "rect": { + "type": "array", + "minItems": 4, + "items": [ + { + "type": "number", + "description": "X coordinate of the rectangle.", + "minimum": 0, + "maximum": 200, + "title": "x", + "markdownDescription": "X coordinate of the rectangle." + }, + { + "type": "number", + "description": "Y coordinate of the rectangle.", + "minimum": 0, + "maximum": 100, + "title": "y", + "markdownDescription": "Y coordinate of the rectangle." + }, + { + "type": "number", + "description": "Width of the rectangle.", + "minimum": 0, + "maximum": 200, + "title": "width", + "markdownDescription": "Width of the rectangle." + }, + { + "type": "number", + "description": "Height of the rectangle.", + "minimum": 0, + "maximum": 100, + "title": "height", + "markdownDescription": "Height of the rectangle." + } + ], + "maxItems": 4, + "description": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`.", + "markdownDescription": "Array defining the items coordinates in the format `[x, y, width, height]`; coordinates must be within canvas size of 200 x 100, e.g. [0, 0, 200, 100]. Items with the same `zOrder` must **not** have an overlapping `rect`." + }, + "type": { + "type": "string", + "const": "text", + "description": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc.", + "markdownDescription": "Type of layout item this instance represents, e.g. \"pixmap\", \"bar\", etc." + }, + "alignment": { + "type": "string", + "enum": [ + "center", + "left", + "right" + ], + "description": "Alignment of the text. Default is `\"center\"`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector.", + "markdownDescription": "Alignment of the text. Default is `\"center\"`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector." + }, + "color": { + "type": "string", + "description": "Color of the font represented as a named color, or hexadecimal value. Default is `white`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)", + "markdownDescription": "Color of the font represented as a named color, or hexadecimal value. Default is `white`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)" + }, + "font": { + "type": "object", + "properties": { + "size": { + "type": "number", + "description": "Size of the font. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, this value will be ignored in favour of the user's preferred title settings, as set in property inspector.", + "markdownDescription": "Size of the font. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, this value will be ignored in favour of the user's preferred title settings, as set in property inspector." + }, + "weight": { + "type": "number", + "description": "Weight of the font; value must be a whole `number` in the range of `100..1000`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, this value will be ignored in favour of the user's preferred title settings, as set in property inspector.", + "minimum": 100, + "maximum": 1000, + "markdownDescription": "Weight of the font; value must be a whole `number` in the range of `100..1000`. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, this value will be ignored in favour of the user's preferred title settings, as set in property inspector." + } + }, + "additionalProperties": false, + "description": "Defines how the font should be rendered. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector.", + "markdownDescription": "Defines how the font should be rendered. **Note**, when the `key` of this layout item is set to `\"title\"` within the layout's JSON definition, these values will be ignored in favour of the user's preferred title settings, as set in property inspector." + }, + "text-overflow": { + "type": "string", + "enum": [ + "clip", + "ellipsis", + "fade" + ], + "description": "Defines how overflowing text should be rendered on the layout.\n- clip, truncates the text at the boundary of the element (default).\n- ellipsis, truncates the text prior to the boundary of the element, and adds an ellipsis (…) to the end.\n- fade, applies a fade-gradient over the end of the text.", + "default": "ellipsis", + "markdownDescription": "Defines how overflowing text should be rendered on the layout.\n- clip, truncates the text at the boundary of the element (default).\n- ellipsis, truncates the text prior to the boundary of the element, and adds an ellipsis (…) to the end.\n- fade, applies a fade-gradient over the end of the text." + }, + "value": { + "type": "string", + "description": "Text to be displayed.", + "markdownDescription": "Text to be displayed." + }, + "background": { + "type": "string", + "description": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)", + "markdownDescription": "Background color represented as a named color, hexadecimal value, or gradient. Gradients can be defined by specifying multiple color-stops separated by commas, in the following format `[{offset}:{color}[,]]`.\n\n**Examples:**\n- \"pink\"\n- \"#204cfe\" (Elgato blue)\n- \"0:#ff0000,0.5:yellow,1:#00ff00\" (Gradient)" + }, + "enabled": { + "type": "boolean", + "description": "Determines whether the item is enabled (i.e. visible); default is `true`.", + "markdownDescription": "Determines whether the item is enabled (i.e. visible); default is `true`." + }, + "opacity": { + "type": "number", + "enum": [ + 0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1 + ], + "description": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`.", + "markdownDescription": "Defines the opacity of the item being shown based on a single-decimal value ranging from `0..1`, e.g. `0.1`, `0.2`, etc. with `0` being invisible and `1` being fully visible. Default is `1`." + }, + "zOrder": { + "type": "number", + "description": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`.", + "minimum": 0, + "maximum": 700, + "markdownDescription": "Z-order of the item, used to layer items within a layout; must be between 0-700. Items with the same `zOrder` must **not** have an overlapping `rect`. Default is `0`." + } + }, + "required": [ + "key", + "rect", + "type" + ], + "description": "Extended information used to define a layout item within a layout's JSON file.", + "markdownDescription": "Extended information used to define a layout item within a layout's JSON file." } } } \ No newline at end of file diff --git a/schemas/manifest.json b/schemas/manifest.json index bef12234..0745894c 100644 --- a/schemas/manifest.json +++ b/schemas/manifest.json @@ -301,8 +301,8 @@ "UUID": { "type": "string", "description": "Unique identifier of the action, represented in reverse-DNS format. This value is supplied by Stream Deck when events are emitted that relate to the action enabling you to identify the source of the event.\n\n**Allowed characters:**\n- Lowercase alphanumeric characters (a-z, 0-9)\n- Hyphens (-)\n- Underscores (_)\n- Periods (.)\n\nNB: `UUID` must be unique, and should be prefixed with the plugin's UUID.\n\n\n**Examples:**\n- com.elgato.wavelink.toggle-mute\n- com.elgato.discord.join-voice\n- tv.twitch.go-live", - "pattern": "^([a-z0-9\\-_]+)(\\.[a-z0-9\\-_]+)+$", - "errorMessage": "String must use reverse DNS format, and must only contain lowercase alphanumeric characters (a-z, 0-9), hyphens (-), underscores (_), and periods (.)", + "pattern": "^([a-z0-9\\-]+)(\\.[a-z0-9\\-]+)+$", + "errorMessage": "String must only contain alphanumeric characters (A-z, 0-9), hyphens (-), and periods (.), and be in reverse DNS format", "markdownDescription": "Unique identifier of the action, represented in reverse-DNS format. This value is supplied by Stream Deck when events are emitted that relate to the action enabling you to identify the source of the event.\n\n**Allowed characters:**\n- Lowercase alphanumeric characters (a-z, 0-9)\n- Hyphens (-)\n- Underscores (_)\n- Periods (.)\n\nNB: `UUID` must be unique, and should be prefixed with the plugin's UUID.\n\n\n**Examples:**\n- com.elgato.wavelink.toggle-mute\n- com.elgato.discord.join-voice\n- tv.twitch.go-live" }, "UserTitleEnabled": { @@ -612,8 +612,8 @@ "UUID": { "type": "string", "description": "Unique identifier of the plugin, represented in reverse-DNS format.\n\n**Allowed characters:**\n- Lowercase alphanumeric characters (a-z, 0-9)\n- Hyphens (-)\n- Underscores (_)\n- Periods (.)\n\n**Examples:**\n- com.elgato.wavelink\n- com.elgato.discord\n- tv.twitch", - "pattern": "^([a-z0-9\\-_]+)(\\.[a-z0-9\\-_]+)+$", - "errorMessage": "String must use reverse DNS format, and must only contain lowercase alphanumeric characters (a-z, 0-9), hyphens (-), underscores (_), and periods (.)", + "pattern": "^([a-z0-9\\-]+)(\\.[a-z0-9\\-]+)+$", + "errorMessage": "String must only contain alphanumeric characters (A-z, 0-9), hyphens (-), and periods (.), and be in reverse DNS format", "markdownDescription": "Unique identifier of the plugin, represented in reverse-DNS format.\n\n**Allowed characters:**\n- Lowercase alphanumeric characters (a-z, 0-9)\n- Hyphens (-)\n- Underscores (_)\n- Periods (.)\n\n**Examples:**\n- com.elgato.wavelink\n- com.elgato.discord\n- tv.twitch" }, "Version": { diff --git a/scripts/generate-schemas.ts b/scripts/generate-schemas.ts index 097813d5..c7dcf7e0 100644 --- a/scripts/generate-schemas.ts +++ b/scripts/generate-schemas.ts @@ -1,7 +1,10 @@ /* eslint-disable no-useless-escape */ +import type { JSONSchema7 } from "json-schema"; import { existsSync, mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; -import { Schema, createGenerator } from "ts-json-schema-generator"; +import { createGenerator } from "ts-json-schema-generator"; +import { customKeywordTransformer } from "./schema-transformers/custom-keywords"; +import { layoutTransformer } from "./schema-transformers/layout"; // Create a generator so we're able to produce multiple schemas. const generator = createGenerator({ @@ -18,173 +21,27 @@ if (!existsSync(outputDir)) { } generateAndWriteSchema("Manifest"); -generateAndWriteSchema("Layout"); +generateAndWriteSchema("Layout", layoutTransformer); /** * Generates the JSON schema for the specified TypeScript `type`, and writes it locally to `{type}.json`. * @param type TypeScript type whose schema should be generated. + * @param transform Optional function used to transform the schema. */ -function generateAndWriteSchema(type: string): void { +function generateAndWriteSchema(type: string, transform?: (schema: JSONSchema7) => void): void { const schema = generator.createSchema(type); - applyCustomKeywords(schema); + if (transform) { + transform(schema); + } + + // Apply the custom keyword transformer to all schemas + customKeywordTransformer(schema); + // Determine the output path, and serialize the schema. const outputPath = join(outputDir, `${type.toLowerCase()}.json`); const contents = JSON.stringify(schema, null, "\t"); + // Finally write the schema. writeFileSync(outputPath, contents); console.log(`Successfully generated schema for ${type}.`); } - -/** - * Applies the custom keywords, aggregating the schema to form a valid structure. - * @param schema Schema to apply the custom keywords to. - */ -function applyCustomKeywords(schema: ExtendedSchema): void { - visitNode(schema, (node, keyword, value) => { - switch (keyword) { - case "description": - node.markdownDescription = value?.toString(); - break; - - case "filePath": - validateFilePathOptions(value); - node.pattern = generatePathPattern(value); - node.errorMessage = generatePathErrorMessage(value); - - break; - } - }); -} - -/** - * Validates the specified {@link options} are an instance of {@link FilePathOptions}. - * @param options Options to validate. - */ -function validateFilePathOptions(options: unknown): asserts options is FilePathOptions { - if (options === null) { - throw new TypeError(`"filePath" options must not be null`); - } - - if (typeof options === "boolean") { - if (options === false) { - throw new TypeError(`"false" is not a valid value for "filePath", expected: "true"`); - } - - return; - } - - if (typeof options !== "object" || !("extensions" in options) || !("includeExtension" in options)) { - throw new TypeError(`${JSON.stringify(options)} is not a complete set of "filePath" options, expected: { "extensions": string[], "includeExtension": boolean }`); - } -} - -/** - * Generates the regular expression pattern of a property based file path's {@link options}. - * - {@link https://regexr.com/7qpi6 File path, with unknown extension} - * - {@link https://regexr.com/7qpj7 File path, with extension} - * - {@link https://regexr.com/7qp5k File path, without extension} - * @param options Options used to determine how the pattern should be generated. - * @returns Regular expression pattern. - */ -function generatePathPattern(options: FilePathOptions): string { - let pattern = "^(?![~\\.]*[\\\\\\/]+)"; // ensure the value doesn't start with a slash, or period followed by a slash. - - // When the file path's extension is unknown, we simply ensure the start of the string. - if (typeof options === "boolean") { - return (pattern += ".*$"); - } - - // Otherwise, construct the pattern based on the valid extensions. - const exts = options.extensions - .map((extension) => { - const chars = Array.from(extension) - .slice(1) - .map((c) => `[${c.toUpperCase()}${c.toLowerCase()}]`) - .join(""); - - return `(${chars})`; - }) - .join("|"); - - if (options.includeExtension) { - // Ensure the value ends with a valid extension - pattern += `.*\\.(${exts})$`; - } else { - // Use a negative look-ahead to ensure the extension isn't specified. - pattern += `(?!.*\\.(${exts})$).*$`; - } - - return pattern; -} - -/** - * Generates the custom error message associated with a file path. - * @param options Options that define the valid file path. - * @returns Custom error message. - */ -function generatePathErrorMessage(options: FilePathOptions): string { - if (typeof options === "boolean") { - return "String must reference file in the plugin directory."; - } - - const exts = options.extensions.reduce((prev, current, index) => { - return index === 0 ? current : index === options.extensions.length - 1 ? prev + `, or ${current}` : prev + `, ${current}`; - }, ""); - - const errorMessage = `String must reference ${exts} file in the plugin directory`; - return options.includeExtension ? `${errorMessage}.` : `${errorMessage}, with the file extension omitted.`; -} - -/** - * Traverses the specified {@link schema} and applies the visitor to each property. - * @param schema Schema to traverse - * @param visitor Visitor to each of the schema's properties. - */ -function visitNode(schema: ExtendedSchema, visitor: (schema: ExtendedSchema, keyword: keyof ExtendedSchema, value: unknown) => void): void { - if (typeof schema === "object") { - for (const [keyword, value] of Object.entries(schema)) { - if (typeof value === "object") { - visitNode(value, visitor); - } - - visitor(schema, keyword as keyof ExtendedSchema, value); - } - } -} - -/** - * Provides an extended JSON schema that includes the `markdownDescription` property. - */ -type ExtendedSchema = Schema & { - /** - * Custom error message shown when the value does not confirm to the defined schemas. - */ - errorMessage?: string; - - /** - * Determines whether the value must represent a file path. - */ - filePath?: FilePathOptions; - - /** - * Markdown representation of the description. - */ - markdownDescription?: string; -}; - -/** - * Options used to determine a valid file path, used to generate the regular expression pattern. - */ -type FilePathOptions = - | true - | { - /** - * Collection of valid file extensions. - */ - extensions: string[]; - - /** - * Determines whether the extension must be present, or omitted, from the file path. - */ - includeExtension: boolean; - }; diff --git a/scripts/schema-transformers/custom-keywords.ts b/scripts/schema-transformers/custom-keywords.ts new file mode 100644 index 00000000..5f90cbd5 --- /dev/null +++ b/scripts/schema-transformers/custom-keywords.ts @@ -0,0 +1,156 @@ +import type { JSONSchema7 } from "json-schema"; +import { Schema } from "ts-json-schema-generator"; + +/** + * Applies the custom keywords, aggregating the schema to form a valid structure. + * @param schema Schema to apply the custom keywords to. + */ +export function customKeywordTransformer(schema: JSONSchema7): void { + visitNode(schema, (node, keyword, value) => { + switch (keyword) { + case "description": + node.markdownDescription = value?.toString(); + break; + + case "filePath": + validateFilePathOptions(value); + node.pattern = generatePathPattern(value); + node.errorMessage = generatePathErrorMessage(value); + + break; + } + }); +} + +/** + * Validates the specified {@link options} are an instance of {@link FilePathOptions}. + * @param options Options to validate. + */ +function validateFilePathOptions(options: unknown): asserts options is FilePathOptions { + if (options === null) { + throw new TypeError(`"filePath" options must not be null`); + } + + if (typeof options === "boolean") { + if (options === false) { + throw new TypeError(`"false" is not a valid value for "filePath", expected: "true"`); + } + + return; + } + + if (typeof options !== "object" || !("extensions" in options) || !("includeExtension" in options)) { + throw new TypeError(`${JSON.stringify(options)} is not a complete set of "filePath" options, expected: { "extensions": string[], "includeExtension": boolean }`); + } +} + +/** + * Generates the regular expression pattern of a property based file path's {@link options}. + * - {@link https://regexr.com/7qpi6 File path, with unknown extension} + * - {@link https://regexr.com/7qpj7 File path, with extension} + * - {@link https://regexr.com/7qp5k File path, without extension} + * @param options Options used to determine how the pattern should be generated. + * @returns Regular expression pattern. + */ +function generatePathPattern(options: FilePathOptions): string { + let pattern = "^(?![~\\.]*[\\\\\\/]+)"; // ensure the value doesn't start with a slash, or period followed by a slash. + + // When the file path's extension is unknown, we simply ensure the start of the string. + if (typeof options === "boolean") { + return (pattern += ".*$"); + } + + // Otherwise, construct the pattern based on the valid extensions. + const exts = options.extensions + .map((extension) => { + const chars = Array.from(extension) + .slice(1) + .map((c) => `[${c.toUpperCase()}${c.toLowerCase()}]`) + .join(""); + + return `(${chars})`; + }) + .join("|"); + + if (options.includeExtension) { + // Ensure the value ends with a valid extension + pattern += `.*\\.(${exts})$`; + } else { + // Use a negative look-ahead to ensure the extension isn't specified. + pattern += `(?!.*\\.(${exts})$).*$`; + } + + return pattern; +} + +/** + * Generates the custom error message associated with a file path. + * @param options Options that define the valid file path. + * @returns Custom error message. + */ +function generatePathErrorMessage(options: FilePathOptions): string { + if (typeof options === "boolean") { + return "String must reference file in the plugin directory."; + } + + const exts = options.extensions.reduce((prev, current, index) => { + return index === 0 ? current : index === options.extensions.length - 1 ? prev + `, or ${current}` : prev + `, ${current}`; + }, ""); + + const errorMessage = `String must reference ${exts} file in the plugin directory`; + return options.includeExtension ? `${errorMessage}.` : `${errorMessage}, with the file extension omitted.`; +} + +/** + * Traverses the specified {@link schema} and applies the visitor to each property. + * @param schema Schema to traverse + * @param visitor Visitor to each of the schema's properties. + */ +function visitNode(schema: ExtendedSchema, visitor: (schema: ExtendedSchema, keyword: keyof ExtendedSchema, value: unknown) => void): void { + if (typeof schema === "object") { + for (const [keyword, value] of Object.entries(schema)) { + if (typeof value === "object") { + visitNode(value, visitor); + } + + visitor(schema, keyword as keyof ExtendedSchema, value); + } + } +} + +/** + * Provides an extended JSON schema that includes the `markdownDescription` property. + */ +type ExtendedSchema = Schema & { + /** + * Custom error message shown when the value does not confirm to the defined schemas. + */ + errorMessage?: string; + + /** + * Determines whether the value must represent a file path. + */ + filePath?: FilePathOptions; + + /** + * Markdown representation of the description. + */ + markdownDescription?: string; +}; + +/** + * Options used to determine a valid file path, used to generate the regular expression pattern. + */ +type FilePathOptions = + | true + | { + /** + * Collection of valid file extensions. + */ + extensions: string[]; + + /** + * Determines whether the extension must be present, or omitted, from the file path. + */ + includeExtension: boolean; + }; diff --git a/scripts/schema-transformers/layout.ts b/scripts/schema-transformers/layout.ts new file mode 100644 index 00000000..8eaf0893 --- /dev/null +++ b/scripts/schema-transformers/layout.ts @@ -0,0 +1,124 @@ +import type { JSONSchema7, JSONSchema7Definition } from "json-schema"; + +/** + * Transforms the specified {@link schema} so that the layout's `items` property provides a better structure for validation. + * @param schema Schema to transform. + */ +export function layoutTransformer(schema: JSONSchema7): void { + schema.definitions ??= {}; + + validateSchema(schema.definitions.Layout); + validateSchema(schema.definitions.Layout.properties?.items); + const itemsProp = schema.definitions.Layout.properties.items; + + validateSchema(itemsProp.items); + + // Get the layout item types, and assign them to the top-level definitions object. + const types = getTypes(itemsProp.items); + for (const { type, definition } of types) { + schema.definitions[type] = definition; + } + + // Then replace the previous items schema with an if-then-else structure for better validation. + itemsProp.items = { + anyOf: [generateRecursiveLayoutTypeSchema(types.map(({ type }) => type))] + }; +} + +/** + * Parses the layout item types from the layout's JSON schema. NB. This is referencing the `/Layout/properties/items/items` value, whereby the first `items` is the property on the + * layout, and the second `items` is the JSON schema that defines the valid items... items on items. + * @param items JSON schema `items` value, of the layout's `items` property. + * @returns Layout item definitions. + */ +function getTypes(items: JSONSchema7): LayoutItemDefinition[] { + if (items.anyOf === undefined) { + throw new Error("Unexpected items schema, expected 'anyOf'"); + } + + return items.anyOf.map((definition: JSONSchema7Definition) => { + validateSchema(definition); + + if (definition.properties?.type === undefined || typeof definition.properties.type === "boolean") { + throw new TypeError("Layout item does not contain a 'type' identifier"); + } + + const { + properties: { + type: { const: type } + } + } = definition; + + if (type === undefined || typeof type !== "string") { + throw new TypeError("Layout item has a type that is not a string"); + } + + return { + type, + definition + }; + }); +} + +/** + * Recursively generates an if-then-else statement from the available layout item types. This is later assigned to the layout's `items` property to provide better validation. + * @param types Layout item types. + * @param index Current index. + * @returns The JSON schema that references the current type definition; otherwise the next. + */ +function generateRecursiveLayoutTypeSchema(types: string[], index: number = 0): JSONSchema7 { + const fallback: JSONSchema7 = { + type: "object", + additionalProperties: false, + properties: { + type: { + type: "string", + enum: types + } + } + }; + + return { + if: { + properties: { + type: { + const: types[index], + enum: types + } + } + }, + then: { + $ref: `#/definitions/${types[index]}` + }, + else: index < types.length - 1 ? generateRecursiveLayoutTypeSchema(types, index + 1) : fallback + }; +} + +/** + * Validates the {@link schema} is a {@link JSONSchema7}. + * @param schema The schema to validate. + */ +function validateSchema(schema: JSONSchema7Definition | JSONSchema7Definition[] | undefined): asserts schema is JSONSchema7 { + if (schema === undefined) { + throw new TypeError("Schema is undefined"); + } + + if (typeof schema === "boolean") { + throw new TypeError("Schema is boolean, expected schema"); + } +} + +/** + * Layout item JSON schema definition. + */ +type LayoutItemDefinition = { + /** + * Type of the layout item, parsed from the layout type. + */ + type: string; + + /** + * JSON schema that represents the layout item structure. + */ + definition: JSONSchema7; +}; diff --git a/src/api/layout.ts b/src/api/layout.ts index c072d87a..25fad8cd 100644 --- a/src/api/layout.ts +++ b/src/api/layout.ts @@ -31,6 +31,10 @@ export type Layout = { type LayoutItemDefinition = TItem & { /** * Unique name used to identify the layout item. When calling `setFeedback` this value should be used as the key as part of the object that represents the feedback. + * @pattern + * ^[A-Za-z0-9\-_]+$ + * @errorMessage + * String must only contain alphanumeric characters (A-z, 0-9), hyphens (-), and underscores (_) */ key: string; diff --git a/src/api/manifest.ts b/src/api/manifest.ts index 06eb6dd9..eecac644 100644 --- a/src/api/manifest.ts +++ b/src/api/manifest.ts @@ -556,9 +556,9 @@ type OS = { /** * Unique identifier, in reverse DNS format. * @pattern - * ^([a-z0-9\-_]+)(\.[a-z0-9\-_]+)+$ + * ^([a-z0-9\-]+)(\.[a-z0-9\-]+)+$ * @errorMessage - * String must use reverse DNS format, and must only contain lowercase alphanumeric characters (a-z, 0-9), hyphens (-), underscores (_), and periods (.) + * String must only contain alphanumeric characters (A-z, 0-9), hyphens (-), and periods (.), and be in reverse DNS format */ type Identifier = string;