@golemui/gui-mcp 0.16.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/cli.d.ts +1 -0
- package/cli.js +1 -1
- package/get-concept-ChE67L3a.js +2795 -0
- package/lib.js +1 -1
- package/lint/reactive-expressions.d.ts +18 -0
- package/lint/string-interpolation.d.ts +16 -0
- package/mapping/json-schema-to-gui.d.ts +56 -0
- package/mapping/validator.d.ts +29 -0
- package/package.json +15 -6
- package/schemas/ajv.d.ts +5 -0
- package/schemas/index.d.ts +16 -0
- package/schemas/shallow-validators.d.ts +13 -0
- package/tools/generate-from-json-schema.d.ts +48 -0
- package/tools/generate-from-openapi.d.ts +81 -0
- package/tools/get-concept.d.ts +30 -0
- package/tools/get-widget-spec.d.ts +28 -0
- package/tools/validate-form-definition.d.ts +29 -0
- package/utils/errors.d.ts +28 -0
|
@@ -0,0 +1,2795 @@
|
|
|
1
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
2
|
+
import addFormats from "ajv-formats";
|
|
3
|
+
const $schema$u = "https://json-schema.org/draft/2020-12/schema";
|
|
4
|
+
const $id$u = "https://golemui.com/schemas/common.schema.json";
|
|
5
|
+
const title$u = "Golem Common Definitions";
|
|
6
|
+
const $defs$t = { "reactiveExpression": { "type": "string", "description": "A reactive expression string. Use `$form` to access form data e.g. `$form.termsAccepted === true`" }, "dotPath": { "type": "string", "description": "A dot-separated path to a property in the form data" }, "dateRange": { "type": "object", "description": "A single date range used by calendar/date-picker widgets to mark days as disabled.", "properties": { "start": { "type": "string", "description": "ISO-8601 date string for the start of the disabled range (inclusive)" }, "end": { "type": "string", "description": "ISO-8601 date string for the end of the disabled range (inclusive). Omit to disable a single day." } }, "required": ["start"], "additionalProperties": false }, "uid": { "type": "string", "description": "Unique identifier for the widget" }, "localizable": { "description": "A value that can be a plain string or an i18n key object. Use a plain string for static text, or `{key, default, params}` to reference a translation key.", "oneOf": [{ "type": "string" }, { "type": "object", "properties": { "key": { "type": "string", "description": "The i18n translation key" }, "default": { "type": "string", "description": "Fallback text shown when the translation key is not found" }, "params": { "type": "object", "description": "Interpolation parameters passed to the translation function" } }, "required": ["key"], "additionalProperties": false }] }, "include": { "description": "Show this widget only in certain form states (`{in: [stateNames]}`) or when a reactive expression is true (`{when: expr}`). If omitted the widget is always shown.", "oneOf": [{ "type": "object", "properties": { "in": { "type": "array", "items": { "type": "string" }, "description": "List of state names (defined in the form `states` map) in which this widget should be visible" } }, "required": ["in"], "additionalProperties": false }, { "type": "object", "properties": { "when": { "$ref": "#/$defs/reactiveExpression" } }, "required": ["when"], "additionalProperties": false }] }, "exclude": { "description": "Hide this widget in certain form states (`{from: [stateNames]}`) or when a reactive expression is true (`{when: expr}`). If omitted the widget is never hidden.", "oneOf": [{ "type": "object", "properties": { "from": { "type": "array", "items": { "type": "string" }, "description": "List of state names (defined in the form `states` map) in which this widget should be hidden" } }, "required": ["from"], "additionalProperties": false }, { "type": "object", "properties": { "when": { "$ref": "#/$defs/reactiveExpression" } }, "required": ["when"], "additionalProperties": false }] }, "boolOrWhen": { "description": "A static boolean or a conditional `{when: reactiveExpression}` object evaluated at runtime. Use `{when: '$form.someField === true'}` for dynamic behavior.", "oneOf": [{ "type": "boolean" }, { "type": "object", "properties": { "when": { "$ref": "#/$defs/reactiveExpression" } }, "required": ["when"], "additionalProperties": false }] }, "on": { "type": "object", "description": 'Event handlers for this widget. Values are action names dispatched to the form\'s event bus. Each key also accepts a state-suffixed variant (`"click.<stateName>"`) that overrides the base handler when the named state is active.', "properties": { "load": { "type": "string", "description": "Action dispatched when the widget is first mounted" }, "click": { "type": "string", "description": "Action dispatched when the user clicks the widget" }, "change": { "type": "string", "description": "Action dispatched when the widget value changes" }, "filter": { "type": "string", "description": "Action dispatched on search/filter input, typically used with async data sources" }, "blur": { "type": "string", "description": "Action dispatched when the widget loses focus" } }, "patternProperties": { "^load\\.[^.]+$": { "type": "string", "description": 'State-suffixed load handler: used instead of `load` when the named state is active (e.g. `"load.myState"`)' }, "^click\\.[^.]+$": { "type": "string", "description": 'State-suffixed click handler: used instead of `click` when the named state is active (e.g. `"click.myState"`)' }, "^change\\.[^.]+$": { "type": "string", "description": 'State-suffixed change handler: used instead of `change` when the named state is active (e.g. `"change.myState"`)' }, "^filter\\.[^.]+$": { "type": "string", "description": 'State-suffixed filter handler: used instead of `filter` when the named state is active (e.g. `"filter.myState"`)' }, "^blur\\.[^.]+$": { "type": "string", "description": 'State-suffixed blur handler: used instead of `blur` when the named state is active (e.g. `"blur.myState"`)' } }, "additionalProperties": false }, "baseWidget": { "type": "object", "description": 'Base properties shared by all widgets. Root-level props that support state-suffixing (e.g. `"label.myState"`) are: `label`, `disabled`, `readonly`, `validator`, `size`. Any key inside `props` can also be suffixed. State names must be declared in the form `states` map.', "properties": { "$schema": { "type": "string" }, "uid": { "$ref": "#/$defs/uid" }, "type": { "type": "string" }, "size": { "type": "number", "description": "Column span in the form grid (1–12). Defaults to full width when omitted.", "minimum": 1, "maximum": 12 }, "include": { "$ref": "#/$defs/include" }, "exclude": { "$ref": "#/$defs/exclude" } }, "required": ["type"] } };
|
|
7
|
+
const commonSchema = {
|
|
8
|
+
$schema: $schema$u,
|
|
9
|
+
$id: $id$u,
|
|
10
|
+
title: title$u,
|
|
11
|
+
$defs: $defs$t
|
|
12
|
+
};
|
|
13
|
+
const $schema$t = "https://json-schema.org/draft/2020-12/schema";
|
|
14
|
+
const $id$t = "https://golemui.com/schemas/form.schema.json";
|
|
15
|
+
const title$t = "Golem Form DSL";
|
|
16
|
+
const type$r = "object";
|
|
17
|
+
const properties$r = { "$schema": { "type": "string" }, "states": { "type": "object", "description": 'Named boolean conditions keyed by state name, each mapping to a reactive expression. States serve two purposes: (1) gating widget visibility via `include.in` / `exclude.from`; (2) overriding individual widget properties when a state is active by appending `".<stateName>"` to any property key — e.g. `"label.myState": "Active label"` or `"props.hint.myState": "Active hint"`. Root-level suffixable props: `label`, `disabled`, `readonly`, `validator`, `size`. Any key inside `props` can also be suffixed.', "additionalProperties": { "$ref": "common.schema.json#/$defs/reactiveExpression" } }, "form": { "type": "array", "description": "The ordered list of widgets that make up the form body", "items": { "$ref": "#/$defs/formWidget" } }, "autocomplete": { "type": "string", "description": "HTML `autocomplete` attribute applied to the `<form>` element (e.g. `on`, `off`)" } };
|
|
18
|
+
const required$r = ["form"];
|
|
19
|
+
const additionalProperties = false;
|
|
20
|
+
const $defs$s = { "chunkRef": { "type": "object", "description": "A reference to an external chunk file whose widget array will be inlined at this position in the form", "properties": { "$ref": { "type": "string", "description": "Relative path to a JSON chunk file" } }, "required": ["$ref"], "additionalProperties": false }, "formWidget": { "oneOf": [{ "$ref": "./components/accordion.schema.json" }, { "$ref": "./components/alert.schema.json" }, { "$ref": "./components/button.schema.json" }, { "$ref": "./components/calendar.schema.json" }, { "$ref": "./components/checkbox.schema.json" }, { "$ref": "./components/currency.schema.json" }, { "$ref": "./components/dateinput.schema.json" }, { "$ref": "./components/datepicker.schema.json" }, { "$ref": "./components/dropdown.schema.json" }, { "$ref": "./components/list.schema.json" }, { "$ref": "./components/markdown.schema.json" }, { "$ref": "./components/markdowntext.schema.json" }, { "$ref": "./components/number.schema.json" }, { "$ref": "./components/password.schema.json" }, { "$ref": "./components/radiogroup.schema.json" }, { "$ref": "./components/rangecalendar.schema.json" }, { "$ref": "./components/rangedateinput.schema.json" }, { "$ref": "./components/rangedatepicker.schema.json" }, { "$ref": "./components/repeater.schema.json" }, { "$ref": "./components/select.schema.json" }, { "$ref": "./components/flex.schema.json" }, { "$ref": "./components/grid.schema.json" }, { "$ref": "./components/tabs.schema.json" }, { "$ref": "./components/tags.schema.json" }, { "$ref": "./components/textarea.schema.json" }, { "$ref": "./components/textinput.schema.json" }, { "$ref": "./components/toggle.schema.json" }, { "$ref": "#/$defs/chunkRef" }] } };
|
|
21
|
+
const formSchema = {
|
|
22
|
+
$schema: $schema$t,
|
|
23
|
+
$id: $id$t,
|
|
24
|
+
title: title$t,
|
|
25
|
+
type: type$r,
|
|
26
|
+
properties: properties$r,
|
|
27
|
+
required: required$r,
|
|
28
|
+
additionalProperties,
|
|
29
|
+
$defs: $defs$s
|
|
30
|
+
};
|
|
31
|
+
const $schema$s = "https://json-schema.org/draft/2020-12/schema";
|
|
32
|
+
const $id$s = "https://golemui.com/schemas/layout-widget.schema.json";
|
|
33
|
+
const title$s = "Golem Layout Widget";
|
|
34
|
+
const oneOf = [{ "$ref": "./components/accordion.schema.json" }, { "$ref": "./components/flex.schema.json" }, { "$ref": "./components/grid.schema.json" }, { "$ref": "./components/tabs.schema.json" }];
|
|
35
|
+
const layoutWidgetSchema = {
|
|
36
|
+
$schema: $schema$s,
|
|
37
|
+
$id: $id$s,
|
|
38
|
+
title: title$s,
|
|
39
|
+
oneOf
|
|
40
|
+
};
|
|
41
|
+
const $schema$r = "https://json-schema.org/draft/2020-12/schema";
|
|
42
|
+
const $id$r = "https://golemui.com/schemas/validators.schema.json";
|
|
43
|
+
const title$r = "GolemUI Validator Definitions";
|
|
44
|
+
const $defs$r = { "localizable": { "$ref": "./common.schema.json#/$defs/localizable" }, "stringValidator": { "type": "object", "properties": { "type": { "const": "string" }, "required": { "type": "boolean", "description": "Makes the field mandatory — validation fails if the value is empty or absent" }, "minLength": { "type": "number", "description": "Minimum number of characters the string must contain" }, "maxLength": { "type": "number", "description": "Maximum number of characters the string may contain" }, "pattern": { "type": "string", "description": "A regular expression pattern the value must match" }, "format": { "type": "string", "description": "A named format the string must conform to (e.g. `email`, `url`, `date`, `uuid`)", "enum": ["email", "hostname", "ipv4", "ipv6", "url", "uuid", "date", "time", "date-time", "duration"] }, "const": { "description": "The string must equal exactly this value" }, "enum": { "type": "array", "description": "The string must be one of these allowed values" }, "messages": { "type": "object", "description": "Custom error messages that override the default text for each constraint violation", "properties": { "invalid": { "$ref": "#/$defs/localizable", "description": "Shown when the value fails general type validation" }, "required": { "$ref": "#/$defs/localizable", "description": "Shown when a required field is empty" }, "minLength": { "$ref": "#/$defs/localizable", "description": "Shown when the value is shorter than `minLength`" }, "maxLength": { "$ref": "#/$defs/localizable", "description": "Shown when the value is longer than `maxLength`" }, "pattern": { "$ref": "#/$defs/localizable", "description": "Shown when the value does not match the `pattern` regex" }, "format": { "$ref": "#/$defs/localizable", "description": "Shown when the value does not conform to the specified `format`" }, "enum": { "$ref": "#/$defs/localizable", "description": "Shown when the value is not in the `enum` list" }, "const": { "$ref": "#/$defs/localizable", "description": "Shown when the value does not equal `const`" } }, "additionalProperties": false } }, "required": ["type"], "additionalProperties": false }, "numberValidator": { "type": "object", "properties": { "type": { "type": "string", "enum": ["number", "integer"] }, "required": { "type": "boolean", "description": "Makes the field mandatory — validation fails if the value is absent" }, "minimum": { "type": "number", "description": "The value must be greater than or equal to this number (inclusive)" }, "maximum": { "type": "number", "description": "The value must be less than or equal to this number (inclusive)" }, "exclusiveMinimum": { "type": "number", "description": "The value must be strictly greater than this number (exclusive)" }, "exclusiveMaximum": { "type": "number", "description": "The value must be strictly less than this number (exclusive)" }, "multipleOf": { "type": "number", "description": "The value must be a multiple of this number" }, "const": { "description": "The value must equal exactly this number" }, "enum": { "type": "array", "description": "The value must be one of these allowed numbers" }, "messages": { "type": "object", "description": "Custom error messages that override the default text for each constraint violation", "properties": { "invalid": { "$ref": "#/$defs/localizable", "description": "Shown when the value fails general type validation" }, "minimum": { "$ref": "#/$defs/localizable", "description": "Shown when the value is below `minimum`" }, "maximum": { "$ref": "#/$defs/localizable", "description": "Shown when the value exceeds `maximum`" }, "exclusiveMinimum": { "$ref": "#/$defs/localizable", "description": "Shown when the value is not strictly above `exclusiveMinimum`" }, "exclusiveMaximum": { "$ref": "#/$defs/localizable", "description": "Shown when the value is not strictly below `exclusiveMaximum`" }, "multipleOf": { "$ref": "#/$defs/localizable", "description": "Shown when the value is not a multiple of `multipleOf`" }, "enum": { "$ref": "#/$defs/localizable", "description": "Shown when the value is not in the `enum` list" }, "const": { "$ref": "#/$defs/localizable", "description": "Shown when the value does not equal `const`" } }, "additionalProperties": false } }, "required": ["type"], "additionalProperties": false }, "booleanValidator": { "type": "object", "properties": { "type": { "const": "boolean" }, "required": { "type": "boolean", "description": "Makes the field mandatory — validation fails if the value is absent or false (e.g. an unchecked required checkbox)" }, "const": { "description": "The value must equal exactly this boolean (e.g. `true` to require acceptance)" }, "messages": { "type": "object", "description": "Custom error messages that override the default text for each constraint violation", "properties": { "invalid": { "$ref": "#/$defs/localizable", "description": "Shown when the value fails general type validation" }, "const": { "$ref": "#/$defs/localizable", "description": "Shown when the value does not equal `const`" } }, "additionalProperties": false } }, "required": ["type"], "additionalProperties": false }, "arrayValidator": { "type": "object", "properties": { "type": { "const": "array" }, "required": { "type": "boolean", "description": "Makes the field mandatory — validation fails if the array is empty or absent" }, "minItems": { "type": "number", "description": "The array must contain at least this many items" }, "maxItems": { "type": "number", "description": "The array must contain no more than this many items" }, "uniqueItems": { "type": "boolean", "description": "When true, all items in the array must be distinct values" }, "messages": { "type": "object", "description": "Custom error messages that override the default text for each constraint violation", "properties": { "invalid": { "$ref": "#/$defs/localizable", "description": "Shown when the value fails general type validation" }, "required": { "$ref": "#/$defs/localizable", "description": "Shown when a required array is empty or absent" }, "minItems": { "$ref": "#/$defs/localizable", "description": "Shown when the array has fewer items than `minItems`" }, "maxItems": { "$ref": "#/$defs/localizable", "description": "Shown when the array has more items than `maxItems`" } }, "additionalProperties": false } }, "required": ["type"], "additionalProperties": false }, "customValidator": { "type": "object", "description": "A custom validator that delegates validation to application code. The registered validator function receives these constraint keys and must return a Standard Schema V1-compliant schema object (https://standardschema.dev). Add arbitrary constraint keys alongside `type` — they are passed as-is to the registered validator function.", "properties": { "type": { "const": "custom" }, "required": { "type": "boolean", "description": "Makes the field mandatory — evaluation is still delegated to the custom validator" } }, "required": ["type"], "additionalProperties": true }, "validator": { "description": "Validation rules for this field. The `type` discriminant selects the validator: `string`, `number`, `integer`, `boolean`, `array`, or `custom`.", "oneOf": [{ "$ref": "#/$defs/stringValidator" }, { "$ref": "#/$defs/numberValidator" }, { "$ref": "#/$defs/booleanValidator" }, { "$ref": "#/$defs/arrayValidator" }, { "$ref": "#/$defs/customValidator" }] } };
|
|
45
|
+
const validatorsSchema = {
|
|
46
|
+
$schema: $schema$r,
|
|
47
|
+
$id: $id$r,
|
|
48
|
+
title: title$r,
|
|
49
|
+
$defs: $defs$r
|
|
50
|
+
};
|
|
51
|
+
const $schema$q = "https://json-schema.org/draft/2020-12/schema";
|
|
52
|
+
const $id$q = "https://golemui.com/schemas/components/accordion.schema.json";
|
|
53
|
+
const title$q = "Accordion Widget";
|
|
54
|
+
const type$q = "object";
|
|
55
|
+
const $defs$q = { "singleOpenProp": { "type": "boolean", "description": "When true, opening one section automatically closes all others. Defaults to false." }, "defaultOpenProp": { "type": "object", "description": "Map of section UIDs to booleans indicating which sections are open on initial render", "additionalProperties": { "type": "boolean" }, "minProperties": 1 }, "renderModeProp": { "description": "Controls when section content is rendered: `all` keeps all sections in the DOM, `activeOnly` unmounts inactive ones", "enum": ["all", "activeOnly"] }, "sectionsProp": { "type": "array", "description": "Ordered list of section definitions. Each section's `uid` must match the widget UIDs used in `children`.", "minItems": 1, "items": { "type": "object", "properties": { "label": { "$ref": "../common.schema.json#/$defs/localizable" }, "uid": { "type": "string", "description": "Unique identifier linking this section definition to its widgets in `children`" } }, "required": ["label", "uid"], "additionalProperties": false } } };
|
|
56
|
+
const allOf$q = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
57
|
+
const properties$q = { "kind": { "const": "layout" }, "type": { "const": "accordion" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "children": { "type": "array", "description": "Flat list of widgets belonging to all sections. Each widget must have a `uid` matching one of the section UIDs in `props.sections`.", "minItems": 1, "items": { "$ref": "../form.schema.json#/$defs/formWidget" } }, "props": { "type": "object", "properties": { "singleOpen": { "$ref": "#/$defs/singleOpenProp" }, "defaultOpen": { "$ref": "#/$defs/defaultOpenProp" }, "renderMode": { "$ref": "#/$defs/renderModeProp" }, "sections": { "$ref": "#/$defs/sectionsProp" } }, "required": ["sections"], "patternProperties": { "^singleOpen\\.[^.]+$": { "$ref": "#/$defs/singleOpenProp" }, "^defaultOpen\\.[^.]+$": { "$ref": "#/$defs/defaultOpenProp" }, "^renderMode\\.[^.]+$": { "$ref": "#/$defs/renderModeProp" }, "^sections\\.[^.]+$": { "$ref": "#/$defs/sectionsProp" } }, "additionalProperties": false } };
|
|
58
|
+
const required$q = ["kind", "type", "children", "props"];
|
|
59
|
+
const unevaluatedProperties$q = false;
|
|
60
|
+
const accordion = {
|
|
61
|
+
$schema: $schema$q,
|
|
62
|
+
$id: $id$q,
|
|
63
|
+
title: title$q,
|
|
64
|
+
type: type$q,
|
|
65
|
+
$defs: $defs$q,
|
|
66
|
+
allOf: allOf$q,
|
|
67
|
+
properties: properties$q,
|
|
68
|
+
required: required$q,
|
|
69
|
+
unevaluatedProperties: unevaluatedProperties$q
|
|
70
|
+
};
|
|
71
|
+
const $schema$p = "https://json-schema.org/draft/2020-12/schema";
|
|
72
|
+
const $id$p = "https://golemui.com/schemas/components/alert.schema.json";
|
|
73
|
+
const title$p = "Alert Widget";
|
|
74
|
+
const type$p = "object";
|
|
75
|
+
const $defs$p = { "textProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "levelProp": { "enum": ["default", "info", "success", "warning", "error"] } };
|
|
76
|
+
const allOf$p = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
77
|
+
const properties$p = { "kind": { "const": "display" }, "type": { "const": "alert" }, "props": { "type": "object", "properties": { "text": { "$ref": "#/$defs/textProp" }, "level": { "$ref": "#/$defs/levelProp" } }, "patternProperties": { "^text\\.[^.]+$": { "$ref": "#/$defs/textProp" }, "^level\\.[^.]+$": { "$ref": "#/$defs/levelProp" } }, "required": ["text"], "additionalProperties": false } };
|
|
78
|
+
const required$p = ["kind", "type", "props"];
|
|
79
|
+
const unevaluatedProperties$p = false;
|
|
80
|
+
const alert = {
|
|
81
|
+
$schema: $schema$p,
|
|
82
|
+
$id: $id$p,
|
|
83
|
+
title: title$p,
|
|
84
|
+
type: type$p,
|
|
85
|
+
$defs: $defs$p,
|
|
86
|
+
allOf: allOf$p,
|
|
87
|
+
properties: properties$p,
|
|
88
|
+
required: required$p,
|
|
89
|
+
unevaluatedProperties: unevaluatedProperties$p
|
|
90
|
+
};
|
|
91
|
+
const $schema$o = "https://json-schema.org/draft/2020-12/schema";
|
|
92
|
+
const $id$o = "https://golemui.com/schemas/components/button.schema.json";
|
|
93
|
+
const title$o = "Button Widget";
|
|
94
|
+
const type$o = "object";
|
|
95
|
+
const $defs$o = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "variantProp": { "enum": ["filled", "outlined", "link"] } };
|
|
96
|
+
const allOf$o = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
97
|
+
const properties$o = { "kind": { "const": "action" }, "type": { "const": "button" }, "actionType": { "enum": ["button", "submit"] }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "props": { "type": "object", "properties": { "variant": { "$ref": "#/$defs/variantProp" }, "icon": { "type": "string" }, "iconPosition": { "enum": ["left", "right"] } }, "patternProperties": { "^variant\\.[^.]+$": { "$ref": "#/$defs/variantProp" } }, "additionalProperties": false } };
|
|
98
|
+
const patternProperties$k = { "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" } };
|
|
99
|
+
const required$o = ["kind", "type"];
|
|
100
|
+
const unevaluatedProperties$o = false;
|
|
101
|
+
const button = {
|
|
102
|
+
$schema: $schema$o,
|
|
103
|
+
$id: $id$o,
|
|
104
|
+
title: title$o,
|
|
105
|
+
type: type$o,
|
|
106
|
+
$defs: $defs$o,
|
|
107
|
+
allOf: allOf$o,
|
|
108
|
+
properties: properties$o,
|
|
109
|
+
patternProperties: patternProperties$k,
|
|
110
|
+
required: required$o,
|
|
111
|
+
unevaluatedProperties: unevaluatedProperties$o
|
|
112
|
+
};
|
|
113
|
+
const $schema$n = "https://json-schema.org/draft/2020-12/schema";
|
|
114
|
+
const $id$n = "https://golemui.com/schemas/components/calendar.schema.json";
|
|
115
|
+
const title$n = "Calendar Widget";
|
|
116
|
+
const type$n = "object";
|
|
117
|
+
const $defs$n = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "prevMonthIconProp": { "type": "string" }, "nextMonthIconProp": { "type": "string" }, "prevMonthAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "nextMonthAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "dayFormatProp": { "description": "Formatting of day numbers in the calendar grid (`numeric` = 1–31, `2-digit` = 01–31)", "enum": ["numeric", "2-digit"] }, "weekdayFormatProp": { "description": "Formatting of weekday column headers (`short` = Mon, `long` = Monday, `narrow` = M)", "enum": ["short", "long", "narrow"] }, "monthFormatProp": { "description": "Formatting of the month heading (`long` = January, `short` = Jan, `narrow` = J, `numeric` = 1, `2-digit` = 01)", "enum": ["numeric", "2-digit", "long", "short", "narrow"] }, "minDateProp": { "type": "string", "description": "ISO-8601 date string for the earliest selectable date (inclusive)" }, "maxDateProp": { "type": "string", "description": "ISO-8601 date string for the latest selectable date (inclusive)" }, "numberOfMonthsProp": { "type": "number", "description": "Number of month panels to display side by side. Defaults to 1." }, "disabledRangesProp": { "type": "array", "description": "Date ranges that are shown but cannot be selected", "items": { "$ref": "../common.schema.json#/$defs/dateRange" } } };
|
|
118
|
+
const allOf$n = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
119
|
+
const properties$n = { "kind": { "const": "input" }, "type": { "const": "calendar" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "prevMonthIcon": { "$ref": "#/$defs/prevMonthIconProp" }, "nextMonthIcon": { "$ref": "#/$defs/nextMonthIconProp" }, "prevMonthAriaLabel": { "$ref": "#/$defs/prevMonthAriaLabelProp" }, "nextMonthAriaLabel": { "$ref": "#/$defs/nextMonthAriaLabelProp" }, "dayFormat": { "$ref": "#/$defs/dayFormatProp" }, "weekdayFormat": { "$ref": "#/$defs/weekdayFormatProp" }, "monthFormat": { "$ref": "#/$defs/monthFormatProp" }, "minDate": { "$ref": "#/$defs/minDateProp" }, "maxDate": { "$ref": "#/$defs/maxDateProp" }, "numberOfMonths": { "$ref": "#/$defs/numberOfMonthsProp" }, "disabledRanges": { "$ref": "#/$defs/disabledRangesProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^prevMonthIcon\\.[^.]+$": { "$ref": "#/$defs/prevMonthIconProp" }, "^nextMonthIcon\\.[^.]+$": { "$ref": "#/$defs/nextMonthIconProp" }, "^prevMonthAriaLabel\\.[^.]+$": { "$ref": "#/$defs/prevMonthAriaLabelProp" }, "^nextMonthAriaLabel\\.[^.]+$": { "$ref": "#/$defs/nextMonthAriaLabelProp" }, "^dayFormat\\.[^.]+$": { "$ref": "#/$defs/dayFormatProp" }, "^weekdayFormat\\.[^.]+$": { "$ref": "#/$defs/weekdayFormatProp" }, "^monthFormat\\.[^.]+$": { "$ref": "#/$defs/monthFormatProp" }, "^minDate\\.[^.]+$": { "$ref": "#/$defs/minDateProp" }, "^maxDate\\.[^.]+$": { "$ref": "#/$defs/maxDateProp" }, "^numberOfMonths\\.[^.]+$": { "$ref": "#/$defs/numberOfMonthsProp" }, "^disabledRanges\\.[^.]+$": { "$ref": "#/$defs/disabledRangesProp" } }, "additionalProperties": false } };
|
|
120
|
+
const patternProperties$j = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
121
|
+
const required$n = ["kind", "type", "path"];
|
|
122
|
+
const unevaluatedProperties$n = false;
|
|
123
|
+
const calendar = {
|
|
124
|
+
$schema: $schema$n,
|
|
125
|
+
$id: $id$n,
|
|
126
|
+
title: title$n,
|
|
127
|
+
type: type$n,
|
|
128
|
+
$defs: $defs$n,
|
|
129
|
+
allOf: allOf$n,
|
|
130
|
+
properties: properties$n,
|
|
131
|
+
patternProperties: patternProperties$j,
|
|
132
|
+
required: required$n,
|
|
133
|
+
unevaluatedProperties: unevaluatedProperties$n
|
|
134
|
+
};
|
|
135
|
+
const $schema$m = "https://json-schema.org/draft/2020-12/schema";
|
|
136
|
+
const $id$m = "https://golemui.com/schemas/components/checkbox.schema.json";
|
|
137
|
+
const title$m = "Checkbox Widget";
|
|
138
|
+
const type$m = "object";
|
|
139
|
+
const $defs$m = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "checkboxPositionProp": { "enum": ["left", "right"], "default": "left" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" } };
|
|
140
|
+
const allOf$m = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
141
|
+
const properties$m = { "kind": { "const": "input" }, "type": { "const": "checkbox" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "boolean" }, "props": { "type": "object", "properties": { "checkboxPosition": { "$ref": "#/$defs/checkboxPositionProp" }, "hint": { "$ref": "#/$defs/hintProp" } }, "patternProperties": { "^checkboxPosition\\.[^.]+$": { "$ref": "#/$defs/checkboxPositionProp" }, "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" } }, "additionalProperties": false } };
|
|
142
|
+
const patternProperties$i = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
143
|
+
const required$m = ["kind", "type", "path"];
|
|
144
|
+
const unevaluatedProperties$m = false;
|
|
145
|
+
const checkbox = {
|
|
146
|
+
$schema: $schema$m,
|
|
147
|
+
$id: $id$m,
|
|
148
|
+
title: title$m,
|
|
149
|
+
type: type$m,
|
|
150
|
+
$defs: $defs$m,
|
|
151
|
+
allOf: allOf$m,
|
|
152
|
+
properties: properties$m,
|
|
153
|
+
patternProperties: patternProperties$i,
|
|
154
|
+
required: required$m,
|
|
155
|
+
unevaluatedProperties: unevaluatedProperties$m
|
|
156
|
+
};
|
|
157
|
+
const $schema$l = "https://json-schema.org/draft/2020-12/schema";
|
|
158
|
+
const $id$l = "https://golemui.com/schemas/components/currency.schema.json";
|
|
159
|
+
const title$l = "Currency Widget";
|
|
160
|
+
const type$l = "object";
|
|
161
|
+
const $defs$l = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "currencyProp": { "type": "string" }, "stepProp": { "type": "number" }, "maximumFractionDigitsProp": { "type": "number" }, "minimumFractionDigitsProp": { "type": "number" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" }, "autocompleteProp": { "type": "string" } };
|
|
162
|
+
const allOf$l = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
163
|
+
const properties$l = { "kind": { "const": "input" }, "type": { "const": "currency" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "number" }, "props": { "type": "object", "properties": { "currency": { "$ref": "#/$defs/currencyProp" }, "step": { "$ref": "#/$defs/stepProp" }, "maximumFractionDigits": { "$ref": "#/$defs/maximumFractionDigitsProp" }, "minimumFractionDigits": { "$ref": "#/$defs/minimumFractionDigitsProp" }, "hint": { "$ref": "#/$defs/hintProp" }, "placeholder": { "$ref": "#/$defs/placeholderProp" }, "icon": { "$ref": "#/$defs/iconProp" }, "autocomplete": { "$ref": "#/$defs/autocompleteProp" } }, "patternProperties": { "^currency\\.[^.]+$": { "$ref": "#/$defs/currencyProp" }, "^step\\.[^.]+$": { "$ref": "#/$defs/stepProp" }, "^maximumFractionDigits\\.[^.]+$": { "$ref": "#/$defs/maximumFractionDigitsProp" }, "^minimumFractionDigits\\.[^.]+$": { "$ref": "#/$defs/minimumFractionDigitsProp" }, "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" }, "^autocomplete\\.[^.]+$": { "$ref": "#/$defs/autocompleteProp" } }, "additionalProperties": false } };
|
|
164
|
+
const patternProperties$h = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
165
|
+
const required$l = ["kind", "type", "path"];
|
|
166
|
+
const unevaluatedProperties$l = false;
|
|
167
|
+
const currency = {
|
|
168
|
+
$schema: $schema$l,
|
|
169
|
+
$id: $id$l,
|
|
170
|
+
title: title$l,
|
|
171
|
+
type: type$l,
|
|
172
|
+
$defs: $defs$l,
|
|
173
|
+
allOf: allOf$l,
|
|
174
|
+
properties: properties$l,
|
|
175
|
+
patternProperties: patternProperties$h,
|
|
176
|
+
required: required$l,
|
|
177
|
+
unevaluatedProperties: unevaluatedProperties$l
|
|
178
|
+
};
|
|
179
|
+
const $schema$k = "https://json-schema.org/draft/2020-12/schema";
|
|
180
|
+
const $id$k = "https://golemui.com/schemas/components/dateinput.schema.json";
|
|
181
|
+
const title$k = "DateInput Widget";
|
|
182
|
+
const type$k = "object";
|
|
183
|
+
const $defs$k = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" } };
|
|
184
|
+
const allOf$k = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
185
|
+
const properties$k = { "kind": { "const": "input" }, "type": { "const": "dateInput" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "icon": { "$ref": "#/$defs/iconProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" } }, "additionalProperties": false } };
|
|
186
|
+
const patternProperties$g = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
187
|
+
const required$k = ["kind", "type", "path"];
|
|
188
|
+
const unevaluatedProperties$k = false;
|
|
189
|
+
const dateinput = {
|
|
190
|
+
$schema: $schema$k,
|
|
191
|
+
$id: $id$k,
|
|
192
|
+
title: title$k,
|
|
193
|
+
type: type$k,
|
|
194
|
+
$defs: $defs$k,
|
|
195
|
+
allOf: allOf$k,
|
|
196
|
+
properties: properties$k,
|
|
197
|
+
patternProperties: patternProperties$g,
|
|
198
|
+
required: required$k,
|
|
199
|
+
unevaluatedProperties: unevaluatedProperties$k
|
|
200
|
+
};
|
|
201
|
+
const $schema$j = "https://json-schema.org/draft/2020-12/schema";
|
|
202
|
+
const $id$j = "https://golemui.com/schemas/components/datepicker.schema.json";
|
|
203
|
+
const title$j = "DatePicker Widget";
|
|
204
|
+
const type$j = "object";
|
|
205
|
+
const $defs$j = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" }, "prevMonthIconProp": { "type": "string" }, "nextMonthIconProp": { "type": "string" }, "prevMonthAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "nextMonthAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "dayFormatProp": { "enum": ["numeric", "2-digit"] }, "weekdayFormatProp": { "enum": ["short", "long", "narrow"] }, "monthFormatProp": { "enum": ["numeric", "2-digit", "long", "short", "narrow"] }, "minDateProp": { "type": "string" }, "maxDateProp": { "type": "string" }, "numberOfMonthsProp": { "type": "number" }, "disabledRangesProp": { "type": "array", "items": { "$ref": "../common.schema.json#/$defs/dateRange" } } };
|
|
206
|
+
const allOf$j = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
207
|
+
const properties$j = { "kind": { "const": "input" }, "type": { "const": "datePicker" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "icon": { "$ref": "#/$defs/iconProp" }, "prevMonthIcon": { "$ref": "#/$defs/prevMonthIconProp" }, "nextMonthIcon": { "$ref": "#/$defs/nextMonthIconProp" }, "prevMonthAriaLabel": { "$ref": "#/$defs/prevMonthAriaLabelProp" }, "nextMonthAriaLabel": { "$ref": "#/$defs/nextMonthAriaLabelProp" }, "dayFormat": { "$ref": "#/$defs/dayFormatProp" }, "weekdayFormat": { "$ref": "#/$defs/weekdayFormatProp" }, "monthFormat": { "$ref": "#/$defs/monthFormatProp" }, "minDate": { "$ref": "#/$defs/minDateProp" }, "maxDate": { "$ref": "#/$defs/maxDateProp" }, "numberOfMonths": { "$ref": "#/$defs/numberOfMonthsProp" }, "disabledRanges": { "$ref": "#/$defs/disabledRangesProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" }, "^prevMonthIcon\\.[^.]+$": { "$ref": "#/$defs/prevMonthIconProp" }, "^nextMonthIcon\\.[^.]+$": { "$ref": "#/$defs/nextMonthIconProp" }, "^prevMonthAriaLabel\\.[^.]+$": { "$ref": "#/$defs/prevMonthAriaLabelProp" }, "^nextMonthAriaLabel\\.[^.]+$": { "$ref": "#/$defs/nextMonthAriaLabelProp" }, "^dayFormat\\.[^.]+$": { "$ref": "#/$defs/dayFormatProp" }, "^weekdayFormat\\.[^.]+$": { "$ref": "#/$defs/weekdayFormatProp" }, "^monthFormat\\.[^.]+$": { "$ref": "#/$defs/monthFormatProp" }, "^minDate\\.[^.]+$": { "$ref": "#/$defs/minDateProp" }, "^maxDate\\.[^.]+$": { "$ref": "#/$defs/maxDateProp" }, "^numberOfMonths\\.[^.]+$": { "$ref": "#/$defs/numberOfMonthsProp" }, "^disabledRanges\\.[^.]+$": { "$ref": "#/$defs/disabledRangesProp" } }, "additionalProperties": false } };
|
|
208
|
+
const patternProperties$f = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
209
|
+
const required$j = ["kind", "type", "path"];
|
|
210
|
+
const unevaluatedProperties$j = false;
|
|
211
|
+
const datepicker = {
|
|
212
|
+
$schema: $schema$j,
|
|
213
|
+
$id: $id$j,
|
|
214
|
+
title: title$j,
|
|
215
|
+
type: type$j,
|
|
216
|
+
$defs: $defs$j,
|
|
217
|
+
allOf: allOf$j,
|
|
218
|
+
properties: properties$j,
|
|
219
|
+
patternProperties: patternProperties$f,
|
|
220
|
+
required: required$j,
|
|
221
|
+
unevaluatedProperties: unevaluatedProperties$j
|
|
222
|
+
};
|
|
223
|
+
const $schema$i = "https://json-schema.org/draft/2020-12/schema";
|
|
224
|
+
const $id$i = "https://golemui.com/schemas/components/dropdown.schema.json";
|
|
225
|
+
const title$i = "Dropdown Widget";
|
|
226
|
+
const type$i = "object";
|
|
227
|
+
const $defs$i = { "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "itemsProp": { "type": "array", "description": "Items are either primitive values (string/number) or arbitrary record objects (paired with `labelField`/`valueField`/`itemRenderer` for custom rendering). Note: widget templates inside items are only supported on `repeater`, not here.", "items": { "type": ["string", "number", "object"] } }, "labelFieldProp": { "type": "string", "description": 'Object property used as the display label when items are objects (e.g. `"name"` reads `item.name`)' }, "valueFieldProp": { "type": "string", "description": 'Object property used as the stored value when items are objects (e.g. `"id"` stores `item.id`)' }, "searchFieldsProp": { "type": "array", "description": "Object properties searched when the user types in the filter box. Defaults to `labelField` when omitted.", "items": { "type": "string" } }, "heightProp": { "type": "number", "description": "Fixed pixel height of the dropdown list panel (enables virtual scrolling for large lists)" }, "itemHeightProp": { "type": "number", "description": "Fixed pixel height of each item row, required for virtual scrolling when `height` is set" }, "itemRendererProp": { "type": "string", "description": "Name of a registered custom item renderer component used to display each option" }, "inputDebounceProp": { "type": "number", "description": "Milliseconds to debounce the filter input before firing the `on.filter` event (default: 0)" }, "autocompleteProp": { "type": "string", "description": "HTML `autocomplete` attribute applied to the underlying input element" } };
|
|
228
|
+
const allOf$i = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
229
|
+
const properties$i = { "kind": { "const": "input" }, "type": { "const": "dropdown" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "required": ["items"], "properties": { "placeholder": { "$ref": "#/$defs/placeholderProp" }, "hint": { "$ref": "#/$defs/hintProp" }, "items": { "$ref": "#/$defs/itemsProp" }, "labelField": { "$ref": "#/$defs/labelFieldProp" }, "valueField": { "$ref": "#/$defs/valueFieldProp" }, "searchFields": { "$ref": "#/$defs/searchFieldsProp" }, "height": { "$ref": "#/$defs/heightProp" }, "itemHeight": { "$ref": "#/$defs/itemHeightProp" }, "itemRenderer": { "$ref": "#/$defs/itemRendererProp" }, "inputDebounce": { "$ref": "#/$defs/inputDebounceProp" }, "autocomplete": { "$ref": "#/$defs/autocompleteProp" } }, "patternProperties": { "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^items\\.[^.]+$": { "$ref": "#/$defs/itemsProp" }, "^labelField\\.[^.]+$": { "$ref": "#/$defs/labelFieldProp" }, "^valueField\\.[^.]+$": { "$ref": "#/$defs/valueFieldProp" }, "^searchFields\\.[^.]+$": { "$ref": "#/$defs/searchFieldsProp" }, "^height\\.[^.]+$": { "$ref": "#/$defs/heightProp" }, "^itemHeight\\.[^.]+$": { "$ref": "#/$defs/itemHeightProp" }, "^itemRenderer\\.[^.]+$": { "$ref": "#/$defs/itemRendererProp" }, "^inputDebounce\\.[^.]+$": { "$ref": "#/$defs/inputDebounceProp" }, "^autocomplete\\.[^.]+$": { "$ref": "#/$defs/autocompleteProp" } }, "additionalProperties": false } };
|
|
230
|
+
const patternProperties$e = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
231
|
+
const required$i = ["kind", "type", "path", "props"];
|
|
232
|
+
const unevaluatedProperties$i = false;
|
|
233
|
+
const dropdown = {
|
|
234
|
+
$schema: $schema$i,
|
|
235
|
+
$id: $id$i,
|
|
236
|
+
title: title$i,
|
|
237
|
+
type: type$i,
|
|
238
|
+
$defs: $defs$i,
|
|
239
|
+
allOf: allOf$i,
|
|
240
|
+
properties: properties$i,
|
|
241
|
+
patternProperties: patternProperties$e,
|
|
242
|
+
required: required$i,
|
|
243
|
+
unevaluatedProperties: unevaluatedProperties$i
|
|
244
|
+
};
|
|
245
|
+
const $schema$h = "https://json-schema.org/draft/2020-12/schema";
|
|
246
|
+
const $id$h = "https://golemui.com/schemas/components/flex.schema.json";
|
|
247
|
+
const title$h = "Flex Widget";
|
|
248
|
+
const type$h = "object";
|
|
249
|
+
const $defs$h = { "directionProp": { "enum": ["row", "row-reverse", "column", "column-reverse"] }, "justifyProp": { "enum": ["center", "start", "end", "stretch"] }, "alignProp": { "enum": ["center", "start", "end", "space-between", "space-around", "space-evenly"] }, "gapProp": { "type": "number" } };
|
|
250
|
+
const allOf$h = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
251
|
+
const properties$h = { "kind": { "const": "layout" }, "type": { "const": "flex" }, "children": { "type": "array", "minItems": 1, "items": { "$ref": "../form.schema.json#/$defs/formWidget" } }, "props": { "type": "object", "properties": { "direction": { "$ref": "#/$defs/directionProp" }, "justify": { "$ref": "#/$defs/justifyProp" }, "align": { "$ref": "#/$defs/alignProp" }, "gap": { "$ref": "#/$defs/gapProp" } }, "patternProperties": { "^direction\\.[^.]+$": { "$ref": "#/$defs/directionProp" }, "^justify\\.[^.]+$": { "$ref": "#/$defs/justifyProp" }, "^align\\.[^.]+$": { "$ref": "#/$defs/alignProp" }, "^gap\\.[^.]+$": { "$ref": "#/$defs/gapProp" } }, "additionalProperties": false } };
|
|
252
|
+
const required$h = ["kind", "type", "children"];
|
|
253
|
+
const unevaluatedProperties$h = false;
|
|
254
|
+
const flex = {
|
|
255
|
+
$schema: $schema$h,
|
|
256
|
+
$id: $id$h,
|
|
257
|
+
title: title$h,
|
|
258
|
+
type: type$h,
|
|
259
|
+
$defs: $defs$h,
|
|
260
|
+
allOf: allOf$h,
|
|
261
|
+
properties: properties$h,
|
|
262
|
+
required: required$h,
|
|
263
|
+
unevaluatedProperties: unevaluatedProperties$h
|
|
264
|
+
};
|
|
265
|
+
const $schema$g = "https://json-schema.org/draft/2020-12/schema";
|
|
266
|
+
const $id$g = "https://golemui.com/schemas/components/grid.schema.json";
|
|
267
|
+
const title$g = "Grid Widget";
|
|
268
|
+
const type$g = "object";
|
|
269
|
+
const $defs$g = { "directionProp": { "enum": ["row", "column"] }, "columnGapProp": { "type": "number" }, "rowGapProp": { "type": "number" }, "autoFitProp": { "type": "boolean" }, "alignProp": { "enum": ["center", "start", "end", "space-between", "space-around", "space-evenly", "stretch"] }, "justifyProp": { "enum": ["center", "start", "end", "stretch"] } };
|
|
270
|
+
const allOf$g = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
271
|
+
const properties$g = { "kind": { "const": "layout" }, "type": { "const": "grid" }, "children": { "type": "array", "minItems": 1, "items": { "$ref": "../form.schema.json#/$defs/formWidget" } }, "props": { "type": "object", "properties": { "direction": { "$ref": "#/$defs/directionProp" }, "columnGap": { "$ref": "#/$defs/columnGapProp" }, "rowGap": { "$ref": "#/$defs/rowGapProp" }, "autoFit": { "$ref": "#/$defs/autoFitProp" }, "align": { "$ref": "#/$defs/alignProp" }, "justify": { "$ref": "#/$defs/justifyProp" } }, "patternProperties": { "^direction\\.[^.]+$": { "$ref": "#/$defs/directionProp" }, "^columnGap\\.[^.]+$": { "$ref": "#/$defs/columnGapProp" }, "^rowGap\\.[^.]+$": { "$ref": "#/$defs/rowGapProp" }, "^autoFit\\.[^.]+$": { "$ref": "#/$defs/autoFitProp" }, "^align\\.[^.]+$": { "$ref": "#/$defs/alignProp" }, "^justify\\.[^.]+$": { "$ref": "#/$defs/justifyProp" } }, "additionalProperties": false } };
|
|
272
|
+
const required$g = ["kind", "type", "children"];
|
|
273
|
+
const unevaluatedProperties$g = false;
|
|
274
|
+
const grid = {
|
|
275
|
+
$schema: $schema$g,
|
|
276
|
+
$id: $id$g,
|
|
277
|
+
title: title$g,
|
|
278
|
+
type: type$g,
|
|
279
|
+
$defs: $defs$g,
|
|
280
|
+
allOf: allOf$g,
|
|
281
|
+
properties: properties$g,
|
|
282
|
+
required: required$g,
|
|
283
|
+
unevaluatedProperties: unevaluatedProperties$g
|
|
284
|
+
};
|
|
285
|
+
const $schema$f = "https://json-schema.org/draft/2020-12/schema";
|
|
286
|
+
const $id$f = "https://golemui.com/schemas/components/list.schema.json";
|
|
287
|
+
const title$f = "List Widget";
|
|
288
|
+
const type$f = "object";
|
|
289
|
+
const $defs$f = { "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "itemsProp": { "type": "array", "description": "Items are either primitive values (string/number) or arbitrary record objects (paired with `labelField`/`valueField`/`itemRenderer` for custom rendering). Note: widget templates inside items are only supported on `repeater`, not here.", "items": { "type": ["string", "number", "object"] } }, "labelFieldProp": { "type": "string" }, "valueFieldProp": { "type": "string" }, "heightProp": { "type": "number" }, "itemHeightProp": { "type": "number" }, "itemRendererProp": { "type": "string" } };
|
|
290
|
+
const allOf$f = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
291
|
+
const properties$f = { "kind": { "const": "input" }, "type": { "const": "list" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "items": { "$ref": "#/$defs/itemsProp" }, "labelField": { "$ref": "#/$defs/labelFieldProp" }, "valueField": { "$ref": "#/$defs/valueFieldProp" }, "height": { "$ref": "#/$defs/heightProp" }, "itemHeight": { "$ref": "#/$defs/itemHeightProp" }, "itemRenderer": { "$ref": "#/$defs/itemRendererProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^items\\.[^.]+$": { "$ref": "#/$defs/itemsProp" }, "^labelField\\.[^.]+$": { "$ref": "#/$defs/labelFieldProp" }, "^valueField\\.[^.]+$": { "$ref": "#/$defs/valueFieldProp" }, "^height\\.[^.]+$": { "$ref": "#/$defs/heightProp" }, "^itemHeight\\.[^.]+$": { "$ref": "#/$defs/itemHeightProp" }, "^itemRenderer\\.[^.]+$": { "$ref": "#/$defs/itemRendererProp" } }, "additionalProperties": false } };
|
|
292
|
+
const patternProperties$d = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
293
|
+
const required$f = ["kind", "type", "path"];
|
|
294
|
+
const unevaluatedProperties$f = false;
|
|
295
|
+
const list = {
|
|
296
|
+
$schema: $schema$f,
|
|
297
|
+
$id: $id$f,
|
|
298
|
+
title: title$f,
|
|
299
|
+
type: type$f,
|
|
300
|
+
$defs: $defs$f,
|
|
301
|
+
allOf: allOf$f,
|
|
302
|
+
properties: properties$f,
|
|
303
|
+
patternProperties: patternProperties$d,
|
|
304
|
+
required: required$f,
|
|
305
|
+
unevaluatedProperties: unevaluatedProperties$f
|
|
306
|
+
};
|
|
307
|
+
const $schema$e = "https://json-schema.org/draft/2020-12/schema";
|
|
308
|
+
const $id$e = "https://golemui.com/schemas/components/markdown.schema.json";
|
|
309
|
+
const title$e = "Markdown Widget";
|
|
310
|
+
const type$e = "object";
|
|
311
|
+
const $defs$e = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "counterModeProp": { "enum": ["remaining", "current"] }, "minimumHeightProp": { "type": "number" }, "autoGrowProp": { "type": "boolean" }, "defaultOpenPreviewProp": { "type": "boolean" }, "maxLengthProp": { "type": "number" }, "toolsProp": { "type": "array", "items": { "enum": ["H", "B", "I", "Q", "L", "OL", "UL", "|"] } }, "splitViewTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "headingTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "boldTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "italicTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "strikethroughTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "quoteTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "linkTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "orderedListTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "unorderedListTitleProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "autocompleteProp": { "type": "string" } };
|
|
312
|
+
const allOf$e = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
313
|
+
const properties$e = { "kind": { "const": "input" }, "type": { "const": "markdown" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "string" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "placeholder": { "$ref": "#/$defs/placeholderProp" }, "counterMode": { "$ref": "#/$defs/counterModeProp" }, "minimumHeight": { "$ref": "#/$defs/minimumHeightProp" }, "autoGrow": { "$ref": "#/$defs/autoGrowProp" }, "defaultOpenPreview": { "$ref": "#/$defs/defaultOpenPreviewProp" }, "maxLength": { "$ref": "#/$defs/maxLengthProp" }, "tools": { "$ref": "#/$defs/toolsProp" }, "splitViewTitle": { "$ref": "#/$defs/splitViewTitleProp" }, "headingTitle": { "$ref": "#/$defs/headingTitleProp" }, "boldTitle": { "$ref": "#/$defs/boldTitleProp" }, "italicTitle": { "$ref": "#/$defs/italicTitleProp" }, "strikethroughTitle": { "$ref": "#/$defs/strikethroughTitleProp" }, "quoteTitle": { "$ref": "#/$defs/quoteTitleProp" }, "linkTitle": { "$ref": "#/$defs/linkTitleProp" }, "orderedListTitle": { "$ref": "#/$defs/orderedListTitleProp" }, "unorderedListTitle": { "$ref": "#/$defs/unorderedListTitleProp" }, "autocomplete": { "$ref": "#/$defs/autocompleteProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^counterMode\\.[^.]+$": { "$ref": "#/$defs/counterModeProp" }, "^minimumHeight\\.[^.]+$": { "$ref": "#/$defs/minimumHeightProp" }, "^autoGrow\\.[^.]+$": { "$ref": "#/$defs/autoGrowProp" }, "^defaultOpenPreview\\.[^.]+$": { "$ref": "#/$defs/defaultOpenPreviewProp" }, "^maxLength\\.[^.]+$": { "$ref": "#/$defs/maxLengthProp" }, "^tools\\.[^.]+$": { "$ref": "#/$defs/toolsProp" }, "^splitViewTitle\\.[^.]+$": { "$ref": "#/$defs/splitViewTitleProp" }, "^headingTitle\\.[^.]+$": { "$ref": "#/$defs/headingTitleProp" }, "^boldTitle\\.[^.]+$": { "$ref": "#/$defs/boldTitleProp" }, "^italicTitle\\.[^.]+$": { "$ref": "#/$defs/italicTitleProp" }, "^strikethroughTitle\\.[^.]+$": { "$ref": "#/$defs/strikethroughTitleProp" }, "^quoteTitle\\.[^.]+$": { "$ref": "#/$defs/quoteTitleProp" }, "^linkTitle\\.[^.]+$": { "$ref": "#/$defs/linkTitleProp" }, "^orderedListTitle\\.[^.]+$": { "$ref": "#/$defs/orderedListTitleProp" }, "^unorderedListTitle\\.[^.]+$": { "$ref": "#/$defs/unorderedListTitleProp" }, "^autocomplete\\.[^.]+$": { "$ref": "#/$defs/autocompleteProp" } }, "additionalProperties": false } };
|
|
314
|
+
const patternProperties$c = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
315
|
+
const required$e = ["kind", "type", "path"];
|
|
316
|
+
const unevaluatedProperties$e = false;
|
|
317
|
+
const markdown = {
|
|
318
|
+
$schema: $schema$e,
|
|
319
|
+
$id: $id$e,
|
|
320
|
+
title: title$e,
|
|
321
|
+
type: type$e,
|
|
322
|
+
$defs: $defs$e,
|
|
323
|
+
allOf: allOf$e,
|
|
324
|
+
properties: properties$e,
|
|
325
|
+
patternProperties: patternProperties$c,
|
|
326
|
+
required: required$e,
|
|
327
|
+
unevaluatedProperties: unevaluatedProperties$e
|
|
328
|
+
};
|
|
329
|
+
const $schema$d = "https://json-schema.org/draft/2020-12/schema";
|
|
330
|
+
const $id$d = "https://golemui.com/schemas/components/markdowntext.schema.json";
|
|
331
|
+
const title$d = "MarkdownText Widget";
|
|
332
|
+
const type$d = "object";
|
|
333
|
+
const $defs$d = { "mdProp": { "$ref": "../common.schema.json#/$defs/localizable" } };
|
|
334
|
+
const allOf$d = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
335
|
+
const properties$d = { "kind": { "const": "display" }, "type": { "const": "markdownText" }, "props": { "type": "object", "properties": { "md": { "$ref": "#/$defs/mdProp" } }, "patternProperties": { "^md\\.[^.]+$": { "$ref": "#/$defs/mdProp" } }, "required": ["md"], "additionalProperties": false } };
|
|
336
|
+
const required$d = ["kind", "type", "props"];
|
|
337
|
+
const unevaluatedProperties$d = false;
|
|
338
|
+
const markdowntext = {
|
|
339
|
+
$schema: $schema$d,
|
|
340
|
+
$id: $id$d,
|
|
341
|
+
title: title$d,
|
|
342
|
+
type: type$d,
|
|
343
|
+
$defs: $defs$d,
|
|
344
|
+
allOf: allOf$d,
|
|
345
|
+
properties: properties$d,
|
|
346
|
+
required: required$d,
|
|
347
|
+
unevaluatedProperties: unevaluatedProperties$d
|
|
348
|
+
};
|
|
349
|
+
const $schema$c = "https://json-schema.org/draft/2020-12/schema";
|
|
350
|
+
const $id$c = "https://golemui.com/schemas/components/number.schema.json";
|
|
351
|
+
const title$c = "Number Widget";
|
|
352
|
+
const type$c = "object";
|
|
353
|
+
const $defs$c = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "stepProp": { "type": "number" }, "minimumProp": { "type": "number" }, "maximumProp": { "type": "number" }, "autoGrowProp": { "type": "boolean" }, "autocompleteProp": { "type": "string" } };
|
|
354
|
+
const allOf$c = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
355
|
+
const properties$c = { "kind": { "const": "input" }, "type": { "const": "number" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "number" }, "props": { "type": "object", "properties": { "placeholder": { "$ref": "#/$defs/placeholderProp" }, "hint": { "$ref": "#/$defs/hintProp" }, "step": { "$ref": "#/$defs/stepProp" }, "minimum": { "$ref": "#/$defs/minimumProp" }, "maximum": { "$ref": "#/$defs/maximumProp" }, "autoGrow": { "$ref": "#/$defs/autoGrowProp" }, "autocomplete": { "$ref": "#/$defs/autocompleteProp" } }, "patternProperties": { "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^step\\.[^.]+$": { "$ref": "#/$defs/stepProp" }, "^minimum\\.[^.]+$": { "$ref": "#/$defs/minimumProp" }, "^maximum\\.[^.]+$": { "$ref": "#/$defs/maximumProp" }, "^autoGrow\\.[^.]+$": { "$ref": "#/$defs/autoGrowProp" }, "^autocomplete\\.[^.]+$": { "$ref": "#/$defs/autocompleteProp" } }, "additionalProperties": false } };
|
|
356
|
+
const patternProperties$b = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
357
|
+
const required$c = ["kind", "type", "path"];
|
|
358
|
+
const unevaluatedProperties$c = false;
|
|
359
|
+
const numberWidget = {
|
|
360
|
+
$schema: $schema$c,
|
|
361
|
+
$id: $id$c,
|
|
362
|
+
title: title$c,
|
|
363
|
+
type: type$c,
|
|
364
|
+
$defs: $defs$c,
|
|
365
|
+
allOf: allOf$c,
|
|
366
|
+
properties: properties$c,
|
|
367
|
+
patternProperties: patternProperties$b,
|
|
368
|
+
required: required$c,
|
|
369
|
+
unevaluatedProperties: unevaluatedProperties$c
|
|
370
|
+
};
|
|
371
|
+
const $schema$b = "https://json-schema.org/draft/2020-12/schema";
|
|
372
|
+
const $id$b = "https://golemui.com/schemas/components/password.schema.json";
|
|
373
|
+
const title$b = "Password Widget";
|
|
374
|
+
const type$b = "object";
|
|
375
|
+
const $defs$b = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" }, "showPasswordIconProp": { "type": "string" }, "hidePasswordIconProp": { "type": "string" }, "showPasswordLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "hidePasswordLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "autocompleteProp": { "type": "string" } };
|
|
376
|
+
const allOf$b = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
377
|
+
const properties$b = { "kind": { "const": "input" }, "type": { "const": "password" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "string" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "placeholder": { "$ref": "#/$defs/placeholderProp" }, "icon": { "$ref": "#/$defs/iconProp" }, "showPasswordIcon": { "$ref": "#/$defs/showPasswordIconProp" }, "hidePasswordIcon": { "$ref": "#/$defs/hidePasswordIconProp" }, "showPasswordLabel": { "$ref": "#/$defs/showPasswordLabelProp" }, "hidePasswordLabel": { "$ref": "#/$defs/hidePasswordLabelProp" }, "autocomplete": { "$ref": "#/$defs/autocompleteProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" }, "^showPasswordIcon\\.[^.]+$": { "$ref": "#/$defs/showPasswordIconProp" }, "^hidePasswordIcon\\.[^.]+$": { "$ref": "#/$defs/hidePasswordIconProp" }, "^showPasswordLabel\\.[^.]+$": { "$ref": "#/$defs/showPasswordLabelProp" }, "^hidePasswordLabel\\.[^.]+$": { "$ref": "#/$defs/hidePasswordLabelProp" }, "^autocomplete\\.[^.]+$": { "$ref": "#/$defs/autocompleteProp" } }, "additionalProperties": false } };
|
|
378
|
+
const patternProperties$a = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
379
|
+
const required$b = ["kind", "type", "path"];
|
|
380
|
+
const unevaluatedProperties$b = false;
|
|
381
|
+
const password = {
|
|
382
|
+
$schema: $schema$b,
|
|
383
|
+
$id: $id$b,
|
|
384
|
+
title: title$b,
|
|
385
|
+
type: type$b,
|
|
386
|
+
$defs: $defs$b,
|
|
387
|
+
allOf: allOf$b,
|
|
388
|
+
properties: properties$b,
|
|
389
|
+
patternProperties: patternProperties$a,
|
|
390
|
+
required: required$b,
|
|
391
|
+
unevaluatedProperties: unevaluatedProperties$b
|
|
392
|
+
};
|
|
393
|
+
const $schema$a = "https://json-schema.org/draft/2020-12/schema";
|
|
394
|
+
const $id$a = "https://golemui.com/schemas/components/radiogroup.schema.json";
|
|
395
|
+
const title$a = "RadioGroup Widget";
|
|
396
|
+
const type$a = "object";
|
|
397
|
+
const $defs$a = { "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "optionsProp": { "type": "array", "description": "Options may be primitive values (string/number), structured `{ label, value }` records, or arbitrary objects (paired with `labelField`/`valueField` for custom rendering).", "items": { "type": ["string", "number", "object"] } }, "labelFieldProp": { "type": "string", "description": 'Object property used as the display label when options are objects (e.g. `"name"` reads `item.name`)' }, "valueFieldProp": { "type": "string", "description": 'Object property used as the stored value when options are objects (e.g. `"id"` stores `item.id`)' }, "directionProp": { "description": "Layout direction for the radio buttons (`row` = horizontal, `column` = vertical). Defaults to `column`.", "enum": ["row", "column"], "default": "column" } };
|
|
398
|
+
const allOf$a = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
399
|
+
const properties$a = { "kind": { "const": "input" }, "type": { "const": "radiogroup" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "options": { "$ref": "#/$defs/optionsProp" }, "labelField": { "$ref": "#/$defs/labelFieldProp" }, "valueField": { "$ref": "#/$defs/valueFieldProp" }, "direction": { "$ref": "#/$defs/directionProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^options\\.[^.]+$": { "$ref": "#/$defs/optionsProp" }, "^labelField\\.[^.]+$": { "$ref": "#/$defs/labelFieldProp" }, "^valueField\\.[^.]+$": { "$ref": "#/$defs/valueFieldProp" }, "^direction\\.[^.]+$": { "$ref": "#/$defs/directionProp" } }, "additionalProperties": false } };
|
|
400
|
+
const patternProperties$9 = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
401
|
+
const required$a = ["kind", "type", "path"];
|
|
402
|
+
const unevaluatedProperties$a = false;
|
|
403
|
+
const radiogroup = {
|
|
404
|
+
$schema: $schema$a,
|
|
405
|
+
$id: $id$a,
|
|
406
|
+
title: title$a,
|
|
407
|
+
type: type$a,
|
|
408
|
+
$defs: $defs$a,
|
|
409
|
+
allOf: allOf$a,
|
|
410
|
+
properties: properties$a,
|
|
411
|
+
patternProperties: patternProperties$9,
|
|
412
|
+
required: required$a,
|
|
413
|
+
unevaluatedProperties: unevaluatedProperties$a
|
|
414
|
+
};
|
|
415
|
+
const $schema$9 = "https://json-schema.org/draft/2020-12/schema";
|
|
416
|
+
const $id$9 = "https://golemui.com/schemas/components/rangecalendar.schema.json";
|
|
417
|
+
const title$9 = "RangeCalendar Widget";
|
|
418
|
+
const type$9 = "object";
|
|
419
|
+
const $defs$9 = { "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "prevMonthIconProp": { "type": "string" }, "nextMonthIconProp": { "type": "string" }, "prevMonthAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "nextMonthAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "removePillAriaLabelProp": { "description": "Accessible label for the button that clears the selected range pill", "$ref": "../common.schema.json#/$defs/localizable" }, "dayFormatProp": { "description": "Formatting of day numbers in the calendar grid (`numeric` = 1–31, `2-digit` = 01–31)", "enum": ["numeric", "2-digit"] }, "weekdayFormatProp": { "description": "Formatting of weekday column headers (`short` = Mon, `long` = Monday, `narrow` = M)", "enum": ["short", "long", "narrow"] }, "monthFormatProp": { "description": "Formatting of the month heading (`long` = January, `short` = Jan, `narrow` = J, `numeric` = 1, `2-digit` = 01)", "enum": ["numeric", "2-digit", "long", "short", "narrow"] }, "minDateProp": { "type": "string", "description": "ISO-8601 date string for the earliest selectable date (inclusive)" }, "maxDateProp": { "type": "string", "description": "ISO-8601 date string for the latest selectable date (inclusive)" }, "numberOfMonthsProp": { "type": "number", "description": "Number of month panels to display side by side. Defaults to 1." }, "disabledRangesProp": { "type": "array", "description": "Date ranges that are shown but cannot be selected", "items": { "$ref": "../common.schema.json#/$defs/dateRange" } } };
|
|
420
|
+
const allOf$9 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
421
|
+
const properties$9 = { "kind": { "const": "input" }, "type": { "const": "rangeCalendar" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "prevMonthIcon": { "$ref": "#/$defs/prevMonthIconProp" }, "nextMonthIcon": { "$ref": "#/$defs/nextMonthIconProp" }, "prevMonthAriaLabel": { "$ref": "#/$defs/prevMonthAriaLabelProp" }, "nextMonthAriaLabel": { "$ref": "#/$defs/nextMonthAriaLabelProp" }, "removePillAriaLabel": { "$ref": "#/$defs/removePillAriaLabelProp" }, "dayFormat": { "$ref": "#/$defs/dayFormatProp" }, "weekdayFormat": { "$ref": "#/$defs/weekdayFormatProp" }, "monthFormat": { "$ref": "#/$defs/monthFormatProp" }, "minDate": { "$ref": "#/$defs/minDateProp" }, "maxDate": { "$ref": "#/$defs/maxDateProp" }, "numberOfMonths": { "$ref": "#/$defs/numberOfMonthsProp" }, "disabledRanges": { "$ref": "#/$defs/disabledRangesProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^prevMonthIcon\\.[^.]+$": { "$ref": "#/$defs/prevMonthIconProp" }, "^nextMonthIcon\\.[^.]+$": { "$ref": "#/$defs/nextMonthIconProp" }, "^prevMonthAriaLabel\\.[^.]+$": { "$ref": "#/$defs/prevMonthAriaLabelProp" }, "^nextMonthAriaLabel\\.[^.]+$": { "$ref": "#/$defs/nextMonthAriaLabelProp" }, "^removePillAriaLabel\\.[^.]+$": { "$ref": "#/$defs/removePillAriaLabelProp" }, "^dayFormat\\.[^.]+$": { "$ref": "#/$defs/dayFormatProp" }, "^weekdayFormat\\.[^.]+$": { "$ref": "#/$defs/weekdayFormatProp" }, "^monthFormat\\.[^.]+$": { "$ref": "#/$defs/monthFormatProp" }, "^minDate\\.[^.]+$": { "$ref": "#/$defs/minDateProp" }, "^maxDate\\.[^.]+$": { "$ref": "#/$defs/maxDateProp" }, "^numberOfMonths\\.[^.]+$": { "$ref": "#/$defs/numberOfMonthsProp" }, "^disabledRanges\\.[^.]+$": { "$ref": "#/$defs/disabledRangesProp" } }, "additionalProperties": false } };
|
|
422
|
+
const patternProperties$8 = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
423
|
+
const required$9 = ["kind", "type", "path"];
|
|
424
|
+
const unevaluatedProperties$9 = false;
|
|
425
|
+
const rangecalendar = {
|
|
426
|
+
$schema: $schema$9,
|
|
427
|
+
$id: $id$9,
|
|
428
|
+
title: title$9,
|
|
429
|
+
type: type$9,
|
|
430
|
+
$defs: $defs$9,
|
|
431
|
+
allOf: allOf$9,
|
|
432
|
+
properties: properties$9,
|
|
433
|
+
patternProperties: patternProperties$8,
|
|
434
|
+
required: required$9,
|
|
435
|
+
unevaluatedProperties: unevaluatedProperties$9
|
|
436
|
+
};
|
|
437
|
+
const $schema$8 = "https://json-schema.org/draft/2020-12/schema";
|
|
438
|
+
const $id$8 = "https://golemui.com/schemas/components/rangedateinput.schema.json";
|
|
439
|
+
const title$8 = "RangeDateInput Widget";
|
|
440
|
+
const type$8 = "object";
|
|
441
|
+
const $defs$8 = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" }, "separatorProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "removePillAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "startDateAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "endDateAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" } };
|
|
442
|
+
const allOf$8 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
443
|
+
const properties$8 = { "kind": { "const": "input" }, "type": { "const": "rangeDateInput" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "icon": { "$ref": "#/$defs/iconProp" }, "separator": { "$ref": "#/$defs/separatorProp" }, "removePillAriaLabel": { "$ref": "#/$defs/removePillAriaLabelProp" }, "startDateAriaLabel": { "$ref": "#/$defs/startDateAriaLabelProp" }, "endDateAriaLabel": { "$ref": "#/$defs/endDateAriaLabelProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" }, "^separator\\.[^.]+$": { "$ref": "#/$defs/separatorProp" }, "^removePillAriaLabel\\.[^.]+$": { "$ref": "#/$defs/removePillAriaLabelProp" }, "^startDateAriaLabel\\.[^.]+$": { "$ref": "#/$defs/startDateAriaLabelProp" }, "^endDateAriaLabel\\.[^.]+$": { "$ref": "#/$defs/endDateAriaLabelProp" } }, "additionalProperties": false } };
|
|
444
|
+
const patternProperties$7 = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
445
|
+
const required$8 = ["kind", "type", "path"];
|
|
446
|
+
const unevaluatedProperties$8 = false;
|
|
447
|
+
const rangedateinput = {
|
|
448
|
+
$schema: $schema$8,
|
|
449
|
+
$id: $id$8,
|
|
450
|
+
title: title$8,
|
|
451
|
+
type: type$8,
|
|
452
|
+
$defs: $defs$8,
|
|
453
|
+
allOf: allOf$8,
|
|
454
|
+
properties: properties$8,
|
|
455
|
+
patternProperties: patternProperties$7,
|
|
456
|
+
required: required$8,
|
|
457
|
+
unevaluatedProperties: unevaluatedProperties$8
|
|
458
|
+
};
|
|
459
|
+
const $schema$7 = "https://json-schema.org/draft/2020-12/schema";
|
|
460
|
+
const $id$7 = "https://golemui.com/schemas/components/rangedatepicker.schema.json";
|
|
461
|
+
const title$7 = "RangeDatePicker Widget";
|
|
462
|
+
const type$7 = "object";
|
|
463
|
+
const $defs$7 = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" }, "separatorProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "removePillAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "startDateAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "endDateAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "prevMonthIconProp": { "type": "string" }, "nextMonthIconProp": { "type": "string" }, "prevMonthAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "nextMonthAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "dayFormatProp": { "enum": ["numeric", "2-digit"] }, "weekdayFormatProp": { "enum": ["short", "long", "narrow"] }, "monthFormatProp": { "enum": ["numeric", "2-digit", "long", "short", "narrow"] }, "minDateProp": { "type": "string" }, "maxDateProp": { "type": "string" }, "numberOfMonthsProp": { "type": "number" }, "disabledRangesProp": { "type": "array", "items": { "$ref": "../common.schema.json#/$defs/dateRange" } } };
|
|
464
|
+
const allOf$7 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
465
|
+
const properties$7 = { "kind": { "const": "input" }, "type": { "const": "rangeDatePicker" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "icon": { "$ref": "#/$defs/iconProp" }, "separator": { "$ref": "#/$defs/separatorProp" }, "removePillAriaLabel": { "$ref": "#/$defs/removePillAriaLabelProp" }, "startDateAriaLabel": { "$ref": "#/$defs/startDateAriaLabelProp" }, "endDateAriaLabel": { "$ref": "#/$defs/endDateAriaLabelProp" }, "prevMonthIcon": { "$ref": "#/$defs/prevMonthIconProp" }, "nextMonthIcon": { "$ref": "#/$defs/nextMonthIconProp" }, "prevMonthAriaLabel": { "$ref": "#/$defs/prevMonthAriaLabelProp" }, "nextMonthAriaLabel": { "$ref": "#/$defs/nextMonthAriaLabelProp" }, "dayFormat": { "$ref": "#/$defs/dayFormatProp" }, "weekdayFormat": { "$ref": "#/$defs/weekdayFormatProp" }, "monthFormat": { "$ref": "#/$defs/monthFormatProp" }, "minDate": { "$ref": "#/$defs/minDateProp" }, "maxDate": { "$ref": "#/$defs/maxDateProp" }, "numberOfMonths": { "$ref": "#/$defs/numberOfMonthsProp" }, "disabledRanges": { "$ref": "#/$defs/disabledRangesProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" }, "^separator\\.[^.]+$": { "$ref": "#/$defs/separatorProp" }, "^removePillAriaLabel\\.[^.]+$": { "$ref": "#/$defs/removePillAriaLabelProp" }, "^startDateAriaLabel\\.[^.]+$": { "$ref": "#/$defs/startDateAriaLabelProp" }, "^endDateAriaLabel\\.[^.]+$": { "$ref": "#/$defs/endDateAriaLabelProp" }, "^prevMonthIcon\\.[^.]+$": { "$ref": "#/$defs/prevMonthIconProp" }, "^nextMonthIcon\\.[^.]+$": { "$ref": "#/$defs/nextMonthIconProp" }, "^prevMonthAriaLabel\\.[^.]+$": { "$ref": "#/$defs/prevMonthAriaLabelProp" }, "^nextMonthAriaLabel\\.[^.]+$": { "$ref": "#/$defs/nextMonthAriaLabelProp" }, "^dayFormat\\.[^.]+$": { "$ref": "#/$defs/dayFormatProp" }, "^weekdayFormat\\.[^.]+$": { "$ref": "#/$defs/weekdayFormatProp" }, "^monthFormat\\.[^.]+$": { "$ref": "#/$defs/monthFormatProp" }, "^minDate\\.[^.]+$": { "$ref": "#/$defs/minDateProp" }, "^maxDate\\.[^.]+$": { "$ref": "#/$defs/maxDateProp" }, "^numberOfMonths\\.[^.]+$": { "$ref": "#/$defs/numberOfMonthsProp" }, "^disabledRanges\\.[^.]+$": { "$ref": "#/$defs/disabledRangesProp" } }, "additionalProperties": false } };
|
|
466
|
+
const patternProperties$6 = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
467
|
+
const required$7 = ["kind", "type", "path"];
|
|
468
|
+
const unevaluatedProperties$7 = false;
|
|
469
|
+
const rangedatepicker = {
|
|
470
|
+
$schema: $schema$7,
|
|
471
|
+
$id: $id$7,
|
|
472
|
+
title: title$7,
|
|
473
|
+
type: type$7,
|
|
474
|
+
$defs: $defs$7,
|
|
475
|
+
allOf: allOf$7,
|
|
476
|
+
properties: properties$7,
|
|
477
|
+
patternProperties: patternProperties$6,
|
|
478
|
+
required: required$7,
|
|
479
|
+
unevaluatedProperties: unevaluatedProperties$7
|
|
480
|
+
};
|
|
481
|
+
const $schema$6 = "https://json-schema.org/draft/2020-12/schema";
|
|
482
|
+
const $id$6 = "https://golemui.com/schemas/components/repeater.schema.json";
|
|
483
|
+
const title$6 = "Repeater Widget";
|
|
484
|
+
const type$6 = "object";
|
|
485
|
+
const $defs$6 = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "addLabelProp": { "description": "Label for the button that adds a new repeated item", "$ref": "../common.schema.json#/$defs/localizable" }, "removeLabelProp": { "description": "Label for the button that removes an existing repeated item", "$ref": "../common.schema.json#/$defs/localizable" }, "limitProp": { "type": "number", "description": "Maximum number of items the user can add. No limit when omitted." }, "templateProp": { "description": "Widget tree used as the template for each repeated item. Paths inside are resolved relative to the item's index in the array.", "$ref": "../layout-widget.schema.json" }, "titleProp": { "description": "Title displayed in the header of each item panel.", "$ref": "../common.schema.json#/$defs/localizable" }, "addButtonIconProp": { "type": "string", "description": "Icon name or CSS class for the add-item button icon" }, "removeButtonIconProp": { "type": "string", "description": "Icon name or CSS class for the remove-item button icon" } };
|
|
486
|
+
const allOf$6 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
487
|
+
const properties$6 = { "kind": { "const": "input" }, "type": { "const": "repeater" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "addLabel": { "$ref": "#/$defs/addLabelProp" }, "removeLabel": { "$ref": "#/$defs/removeLabelProp" }, "limit": { "$ref": "#/$defs/limitProp" }, "template": { "$ref": "#/$defs/templateProp" }, "title": { "$ref": "#/$defs/titleProp" }, "addButtonIcon": { "$ref": "#/$defs/addButtonIconProp" }, "removeButtonIcon": { "$ref": "#/$defs/removeButtonIconProp" } }, "required": ["template"], "patternProperties": { "^addLabel\\.[^.]+$": { "$ref": "#/$defs/addLabelProp" }, "^removeLabel\\.[^.]+$": { "$ref": "#/$defs/removeLabelProp" }, "^limit\\.[^.]+$": { "$ref": "#/$defs/limitProp" }, "^template\\.[^.]+$": { "$ref": "#/$defs/templateProp" }, "^title\\.[^.]+$": { "$ref": "#/$defs/titleProp" }, "^addButtonIcon\\.[^.]+$": { "$ref": "#/$defs/addButtonIconProp" }, "^removeButtonIcon\\.[^.]+$": { "$ref": "#/$defs/removeButtonIconProp" } }, "additionalProperties": false } };
|
|
488
|
+
const patternProperties$5 = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
489
|
+
const required$6 = ["kind", "type", "path", "props"];
|
|
490
|
+
const unevaluatedProperties$6 = false;
|
|
491
|
+
const repeater = {
|
|
492
|
+
$schema: $schema$6,
|
|
493
|
+
$id: $id$6,
|
|
494
|
+
title: title$6,
|
|
495
|
+
type: type$6,
|
|
496
|
+
$defs: $defs$6,
|
|
497
|
+
allOf: allOf$6,
|
|
498
|
+
properties: properties$6,
|
|
499
|
+
patternProperties: patternProperties$5,
|
|
500
|
+
required: required$6,
|
|
501
|
+
unevaluatedProperties: unevaluatedProperties$6
|
|
502
|
+
};
|
|
503
|
+
const $schema$5 = "https://json-schema.org/draft/2020-12/schema";
|
|
504
|
+
const $id$5 = "https://golemui.com/schemas/components/select.schema.json";
|
|
505
|
+
const title$5 = "Select Widget";
|
|
506
|
+
const type$5 = "object";
|
|
507
|
+
const $defs$5 = { "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" }, "optionsProp": { "type": "array", "description": "Options may be primitive values (string/number), structured `{ label, value }` records, or arbitrary objects (paired with `labelField`/`valueField` for custom rendering).", "items": { "type": ["string", "number", "object"] } }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "labelFieldProp": { "type": "string", "description": 'Object property used as the display label when options are objects (e.g. `"name"` reads `item.name`)' }, "valueFieldProp": { "type": "string", "description": 'Object property used as the stored value when options are objects (e.g. `"id"` stores `item.id`)' }, "autocompleteProp": { "type": "string", "description": "HTML `autocomplete` attribute applied to the underlying input element" } };
|
|
508
|
+
const allOf$5 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
509
|
+
const properties$5 = { "kind": { "const": "input" }, "type": { "const": "select" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "icon": { "$ref": "#/$defs/iconProp" }, "options": { "$ref": "#/$defs/optionsProp" }, "placeholder": { "$ref": "#/$defs/placeholderProp" }, "labelField": { "$ref": "#/$defs/labelFieldProp" }, "valueField": { "$ref": "#/$defs/valueFieldProp" }, "autocomplete": { "$ref": "#/$defs/autocompleteProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" }, "^options\\.[^.]+$": { "$ref": "#/$defs/optionsProp" }, "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^labelField\\.[^.]+$": { "$ref": "#/$defs/labelFieldProp" }, "^valueField\\.[^.]+$": { "$ref": "#/$defs/valueFieldProp" }, "^autocomplete\\.[^.]+$": { "$ref": "#/$defs/autocompleteProp" } }, "additionalProperties": false } };
|
|
510
|
+
const patternProperties$4 = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
511
|
+
const required$5 = ["kind", "type", "path"];
|
|
512
|
+
const unevaluatedProperties$5 = false;
|
|
513
|
+
const select = {
|
|
514
|
+
$schema: $schema$5,
|
|
515
|
+
$id: $id$5,
|
|
516
|
+
title: title$5,
|
|
517
|
+
type: type$5,
|
|
518
|
+
$defs: $defs$5,
|
|
519
|
+
allOf: allOf$5,
|
|
520
|
+
properties: properties$5,
|
|
521
|
+
patternProperties: patternProperties$4,
|
|
522
|
+
required: required$5,
|
|
523
|
+
unevaluatedProperties: unevaluatedProperties$5
|
|
524
|
+
};
|
|
525
|
+
const $schema$4 = "https://json-schema.org/draft/2020-12/schema";
|
|
526
|
+
const $id$4 = "https://golemui.com/schemas/components/tabs.schema.json";
|
|
527
|
+
const title$4 = "Tabs Widget";
|
|
528
|
+
const type$4 = "object";
|
|
529
|
+
const $defs$4 = { "defaultOpenProp": { "type": "string", "description": "UID of the tab that is active on initial render. Defaults to the first tab." }, "renderModeProp": { "description": "Controls when tab content is rendered: `all` keeps all tabs in the DOM, `activeOnly` unmounts inactive ones", "enum": ["all", "activeOnly"] }, "tabsProp": { "type": "array", "description": "Ordered list of tab definitions. Each tab's `uid` must match the widget UIDs used in `children`.", "minItems": 1, "items": { "type": "object", "properties": { "label": { "$ref": "../common.schema.json#/$defs/localizable" }, "uid": { "type": "string", "description": "Unique identifier linking this tab definition to its widgets in `children`" } }, "required": ["label", "uid"], "additionalProperties": false } } };
|
|
530
|
+
const allOf$4 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
531
|
+
const properties$4 = { "kind": { "const": "layout" }, "type": { "const": "tabs" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "children": { "type": "array", "description": "Flat list of widgets belonging to all tabs. Each widget must have a `uid` matching one of the tab UIDs in `props.tabs`.", "minItems": 1, "items": { "$ref": "../form.schema.json#/$defs/formWidget" } }, "props": { "type": "object", "properties": { "defaultOpen": { "$ref": "#/$defs/defaultOpenProp" }, "renderMode": { "$ref": "#/$defs/renderModeProp" }, "tabs": { "$ref": "#/$defs/tabsProp" } }, "required": ["tabs"], "patternProperties": { "^defaultOpen\\.[^.]+$": { "$ref": "#/$defs/defaultOpenProp" }, "^renderMode\\.[^.]+$": { "$ref": "#/$defs/renderModeProp" }, "^tabs\\.[^.]+$": { "$ref": "#/$defs/tabsProp" } }, "additionalProperties": false } };
|
|
532
|
+
const required$4 = ["kind", "type", "children", "props"];
|
|
533
|
+
const unevaluatedProperties$4 = false;
|
|
534
|
+
const tabs = {
|
|
535
|
+
$schema: $schema$4,
|
|
536
|
+
$id: $id$4,
|
|
537
|
+
title: title$4,
|
|
538
|
+
type: type$4,
|
|
539
|
+
$defs: $defs$4,
|
|
540
|
+
allOf: allOf$4,
|
|
541
|
+
properties: properties$4,
|
|
542
|
+
required: required$4,
|
|
543
|
+
unevaluatedProperties: unevaluatedProperties$4
|
|
544
|
+
};
|
|
545
|
+
const $schema$3 = "https://json-schema.org/draft/2020-12/schema";
|
|
546
|
+
const $id$3 = "https://golemui.com/schemas/components/tags.schema.json";
|
|
547
|
+
const title$3 = "Tags Widget";
|
|
548
|
+
const type$3 = "object";
|
|
549
|
+
const $defs$3 = { "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" }, "separatorsProp": { "type": "array", "description": 'Characters or strings that trigger a new tag when typed (e.g. `[",", " "]`). Defaults to Enter key only.', "items": { "type": "string" } }, "allowDuplicatesProp": { "type": "boolean", "description": "When true, the same tag value may be added more than once. Defaults to false." }, "trimProp": { "type": "boolean", "description": "When true, whitespace is trimmed from each tag before it is added. Defaults to true." }, "limitProp": { "type": "number", "description": "Maximum number of tags the user can add. No limit when omitted." }, "removeAriaLabelProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "removeIconProp": { "type": "string" } };
|
|
550
|
+
const allOf$3 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
551
|
+
const properties$3 = { "kind": { "const": "input" }, "type": { "const": "tags" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "array", "items": { "type": "string" } }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "placeholder": { "$ref": "#/$defs/placeholderProp" }, "icon": { "$ref": "#/$defs/iconProp" }, "separators": { "$ref": "#/$defs/separatorsProp" }, "allowDuplicates": { "$ref": "#/$defs/allowDuplicatesProp" }, "trim": { "$ref": "#/$defs/trimProp" }, "limit": { "$ref": "#/$defs/limitProp" }, "removeAriaLabel": { "$ref": "#/$defs/removeAriaLabelProp" }, "removeIcon": { "$ref": "#/$defs/removeIconProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" }, "^separators\\.[^.]+$": { "$ref": "#/$defs/separatorsProp" }, "^allowDuplicates\\.[^.]+$": { "$ref": "#/$defs/allowDuplicatesProp" }, "^trim\\.[^.]+$": { "$ref": "#/$defs/trimProp" }, "^limit\\.[^.]+$": { "$ref": "#/$defs/limitProp" }, "^removeAriaLabel\\.[^.]+$": { "$ref": "#/$defs/removeAriaLabelProp" }, "^removeIcon\\.[^.]+$": { "$ref": "#/$defs/removeIconProp" } }, "additionalProperties": false } };
|
|
552
|
+
const patternProperties$3 = { "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
553
|
+
const required$3 = ["kind", "type", "path"];
|
|
554
|
+
const unevaluatedProperties$3 = false;
|
|
555
|
+
const tags = {
|
|
556
|
+
$schema: $schema$3,
|
|
557
|
+
$id: $id$3,
|
|
558
|
+
title: title$3,
|
|
559
|
+
type: type$3,
|
|
560
|
+
$defs: $defs$3,
|
|
561
|
+
allOf: allOf$3,
|
|
562
|
+
properties: properties$3,
|
|
563
|
+
patternProperties: patternProperties$3,
|
|
564
|
+
required: required$3,
|
|
565
|
+
unevaluatedProperties: unevaluatedProperties$3
|
|
566
|
+
};
|
|
567
|
+
const $schema$2 = "https://json-schema.org/draft/2020-12/schema";
|
|
568
|
+
const $id$2 = "https://golemui.com/schemas/components/textarea.schema.json";
|
|
569
|
+
const title$2 = "TextArea Widget";
|
|
570
|
+
const type$2 = "object";
|
|
571
|
+
const $defs$2 = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "counterModeProp": { "enum": ["remaining", "current"] }, "minimumHeightProp": { "type": "number" }, "autoGrowProp": { "type": "boolean" }, "maxLengthProp": { "type": "number" }, "autocompleteProp": { "type": "string" } };
|
|
572
|
+
const allOf$2 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
573
|
+
const properties$2 = { "kind": { "const": "input" }, "type": { "const": "textarea" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "string" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "placeholder": { "$ref": "#/$defs/placeholderProp" }, "counterMode": { "$ref": "#/$defs/counterModeProp" }, "minimumHeight": { "$ref": "#/$defs/minimumHeightProp" }, "autoGrow": { "$ref": "#/$defs/autoGrowProp" }, "maxLength": { "$ref": "#/$defs/maxLengthProp" }, "autocomplete": { "$ref": "#/$defs/autocompleteProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^counterMode\\.[^.]+$": { "$ref": "#/$defs/counterModeProp" }, "^minimumHeight\\.[^.]+$": { "$ref": "#/$defs/minimumHeightProp" }, "^autoGrow\\.[^.]+$": { "$ref": "#/$defs/autoGrowProp" }, "^maxLength\\.[^.]+$": { "$ref": "#/$defs/maxLengthProp" }, "^autocomplete\\.[^.]+$": { "$ref": "#/$defs/autocompleteProp" } }, "additionalProperties": false } };
|
|
574
|
+
const patternProperties$2 = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
575
|
+
const required$2 = ["kind", "type", "path"];
|
|
576
|
+
const unevaluatedProperties$2 = false;
|
|
577
|
+
const textarea = {
|
|
578
|
+
$schema: $schema$2,
|
|
579
|
+
$id: $id$2,
|
|
580
|
+
title: title$2,
|
|
581
|
+
type: type$2,
|
|
582
|
+
$defs: $defs$2,
|
|
583
|
+
allOf: allOf$2,
|
|
584
|
+
properties: properties$2,
|
|
585
|
+
patternProperties: patternProperties$2,
|
|
586
|
+
required: required$2,
|
|
587
|
+
unevaluatedProperties: unevaluatedProperties$2
|
|
588
|
+
};
|
|
589
|
+
const $schema$1 = "https://json-schema.org/draft/2020-12/schema";
|
|
590
|
+
const $id$1 = "https://golemui.com/schemas/components/textinput.schema.json";
|
|
591
|
+
const title$1 = "TextInput Widget";
|
|
592
|
+
const type$1 = "object";
|
|
593
|
+
const $defs$1 = { "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "placeholderProp": { "$ref": "../common.schema.json#/$defs/localizable" }, "iconProp": { "type": "string" }, "autocompleteProp": { "type": "string" } };
|
|
594
|
+
const allOf$1 = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
595
|
+
const properties$1 = { "kind": { "const": "input" }, "type": { "const": "textinput" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "string" }, "props": { "type": "object", "properties": { "hint": { "$ref": "#/$defs/hintProp" }, "placeholder": { "$ref": "#/$defs/placeholderProp" }, "icon": { "$ref": "#/$defs/iconProp" }, "autocomplete": { "$ref": "#/$defs/autocompleteProp" } }, "patternProperties": { "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" }, "^placeholder\\.[^.]+$": { "$ref": "#/$defs/placeholderProp" }, "^icon\\.[^.]+$": { "$ref": "#/$defs/iconProp" }, "^autocomplete\\.[^.]+$": { "$ref": "#/$defs/autocompleteProp" } }, "additionalProperties": false } };
|
|
596
|
+
const patternProperties$1 = { "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
597
|
+
const required$1 = ["kind", "type", "path"];
|
|
598
|
+
const unevaluatedProperties$1 = false;
|
|
599
|
+
const textinput = {
|
|
600
|
+
$schema: $schema$1,
|
|
601
|
+
$id: $id$1,
|
|
602
|
+
title: title$1,
|
|
603
|
+
type: type$1,
|
|
604
|
+
$defs: $defs$1,
|
|
605
|
+
allOf: allOf$1,
|
|
606
|
+
properties: properties$1,
|
|
607
|
+
patternProperties: patternProperties$1,
|
|
608
|
+
required: required$1,
|
|
609
|
+
unevaluatedProperties: unevaluatedProperties$1
|
|
610
|
+
};
|
|
611
|
+
const $schema = "https://json-schema.org/draft/2020-12/schema";
|
|
612
|
+
const $id = "https://golemui.com/schemas/components/toggle.schema.json";
|
|
613
|
+
const title = "Toggle Widget";
|
|
614
|
+
const type = "object";
|
|
615
|
+
const $defs = { "labelDef": { "$ref": "../common.schema.json#/$defs/localizable" }, "disabledDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "readonlyDef": { "$ref": "../common.schema.json#/$defs/boolOrWhen" }, "togglePositionProp": { "enum": ["left", "right"], "default": "left" }, "hintProp": { "$ref": "../common.schema.json#/$defs/localizable" } };
|
|
616
|
+
const allOf = [{ "$ref": "../common.schema.json#/$defs/baseWidget" }];
|
|
617
|
+
const properties = { "kind": { "const": "input" }, "type": { "const": "toggle" }, "path": { "$ref": "../common.schema.json#/$defs/dotPath" }, "label": { "$ref": "#/$defs/labelDef" }, "disabled": { "$ref": "#/$defs/disabledDef" }, "readonly": { "$ref": "#/$defs/readonlyDef" }, "on": { "$ref": "../common.schema.json#/$defs/on" }, "validator": { "$ref": "../validators.schema.json#/$defs/validator" }, "defaultValue": { "type": "boolean" }, "props": { "type": "object", "properties": { "togglePosition": { "$ref": "#/$defs/togglePositionProp" }, "hint": { "$ref": "#/$defs/hintProp" } }, "patternProperties": { "^togglePosition\\.[^.]+$": { "$ref": "#/$defs/togglePositionProp" }, "^hint\\.[^.]+$": { "$ref": "#/$defs/hintProp" } }, "additionalProperties": false } };
|
|
618
|
+
const patternProperties = { "^label\\.[^.]+$": { "$ref": "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { "$ref": "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { "$ref": "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { "$ref": "../validators.schema.json#/$defs/validator" } };
|
|
619
|
+
const required = ["kind", "type", "path"];
|
|
620
|
+
const unevaluatedProperties = false;
|
|
621
|
+
const toggle = {
|
|
622
|
+
$schema,
|
|
623
|
+
$id,
|
|
624
|
+
title,
|
|
625
|
+
type,
|
|
626
|
+
$defs,
|
|
627
|
+
allOf,
|
|
628
|
+
properties,
|
|
629
|
+
patternProperties,
|
|
630
|
+
required,
|
|
631
|
+
unevaluatedProperties
|
|
632
|
+
};
|
|
633
|
+
const COMMON_SCHEMA = commonSchema;
|
|
634
|
+
const FORM_SCHEMA = formSchema;
|
|
635
|
+
const LAYOUT_WIDGET_SCHEMA = layoutWidgetSchema;
|
|
636
|
+
const VALIDATORS_SCHEMA = validatorsSchema;
|
|
637
|
+
const COMPONENT_SCHEMAS = {
|
|
638
|
+
accordion,
|
|
639
|
+
alert,
|
|
640
|
+
button,
|
|
641
|
+
calendar,
|
|
642
|
+
checkbox,
|
|
643
|
+
currency,
|
|
644
|
+
dateInput: dateinput,
|
|
645
|
+
datePicker: datepicker,
|
|
646
|
+
dropdown,
|
|
647
|
+
flex,
|
|
648
|
+
grid,
|
|
649
|
+
list,
|
|
650
|
+
markdown,
|
|
651
|
+
markdownText: markdowntext,
|
|
652
|
+
number: numberWidget,
|
|
653
|
+
password,
|
|
654
|
+
radiogroup,
|
|
655
|
+
rangeCalendar: rangecalendar,
|
|
656
|
+
rangeDateInput: rangedateinput,
|
|
657
|
+
rangeDatePicker: rangedatepicker,
|
|
658
|
+
repeater,
|
|
659
|
+
select,
|
|
660
|
+
tabs,
|
|
661
|
+
tags,
|
|
662
|
+
textarea,
|
|
663
|
+
textinput,
|
|
664
|
+
toggle
|
|
665
|
+
};
|
|
666
|
+
const ALL_SCHEMAS = [
|
|
667
|
+
COMMON_SCHEMA,
|
|
668
|
+
VALIDATORS_SCHEMA,
|
|
669
|
+
LAYOUT_WIDGET_SCHEMA,
|
|
670
|
+
...Object.values(COMPONENT_SCHEMAS),
|
|
671
|
+
FORM_SCHEMA
|
|
672
|
+
];
|
|
673
|
+
let cachedAjv = null;
|
|
674
|
+
let cachedFormValidator = null;
|
|
675
|
+
function buildAjv() {
|
|
676
|
+
const ajv = new Ajv2020({
|
|
677
|
+
allErrors: true,
|
|
678
|
+
strict: false,
|
|
679
|
+
verbose: true
|
|
680
|
+
});
|
|
681
|
+
addFormats(ajv);
|
|
682
|
+
for (const schema of ALL_SCHEMAS) {
|
|
683
|
+
if (typeof schema.$id === "string" && !ajv.getSchema(schema.$id)) {
|
|
684
|
+
ajv.addSchema(schema);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return ajv;
|
|
688
|
+
}
|
|
689
|
+
function getAjv() {
|
|
690
|
+
if (!cachedAjv) {
|
|
691
|
+
cachedAjv = buildAjv();
|
|
692
|
+
}
|
|
693
|
+
return cachedAjv;
|
|
694
|
+
}
|
|
695
|
+
function getFormValidator() {
|
|
696
|
+
if (!cachedFormValidator) {
|
|
697
|
+
cachedFormValidator = getAjv().compile(FORM_SCHEMA);
|
|
698
|
+
}
|
|
699
|
+
return cachedFormValidator;
|
|
700
|
+
}
|
|
701
|
+
const cache = /* @__PURE__ */ new Map();
|
|
702
|
+
const validatorBranchCache = /* @__PURE__ */ new Map();
|
|
703
|
+
const VALIDATOR_TYPE_TO_DEF = {
|
|
704
|
+
string: "stringValidator",
|
|
705
|
+
number: "numberValidator",
|
|
706
|
+
integer: "numberValidator",
|
|
707
|
+
boolean: "booleanValidator",
|
|
708
|
+
array: "arrayValidator",
|
|
709
|
+
custom: "customValidator"
|
|
710
|
+
};
|
|
711
|
+
function getValidatorBranches() {
|
|
712
|
+
return Object.keys(VALIDATOR_TYPE_TO_DEF);
|
|
713
|
+
}
|
|
714
|
+
function getValidatorBranchValidator(validatorType) {
|
|
715
|
+
const defKey = VALIDATOR_TYPE_TO_DEF[validatorType];
|
|
716
|
+
if (!defKey) return null;
|
|
717
|
+
if (validatorBranchCache.has(defKey)) return validatorBranchCache.get(defKey);
|
|
718
|
+
const defs = VALIDATORS_SCHEMA.$defs ?? {};
|
|
719
|
+
const branch = defs[defKey];
|
|
720
|
+
if (!branch) return null;
|
|
721
|
+
const cloned = {
|
|
722
|
+
...JSON.parse(JSON.stringify(branch)),
|
|
723
|
+
$defs: JSON.parse(JSON.stringify(defs))
|
|
724
|
+
};
|
|
725
|
+
rewriteRefs(cloned, VALIDATORS_SCHEMA.$id);
|
|
726
|
+
const ajv = getAjv();
|
|
727
|
+
const v = ajv.compile(cloned);
|
|
728
|
+
validatorBranchCache.set(defKey, v);
|
|
729
|
+
return v;
|
|
730
|
+
}
|
|
731
|
+
function getShallowWidgetValidator(widgetType) {
|
|
732
|
+
if (cache.has(widgetType)) return cache.get(widgetType);
|
|
733
|
+
const schema = COMPONENT_SCHEMAS[widgetType];
|
|
734
|
+
if (!schema) return null;
|
|
735
|
+
const shallow = makeShallow(schema, schema.$id);
|
|
736
|
+
const ajv = getAjv();
|
|
737
|
+
const validator = ajv.compile(shallow);
|
|
738
|
+
cache.set(widgetType, validator);
|
|
739
|
+
return validator;
|
|
740
|
+
}
|
|
741
|
+
function makeShallow(schema, baseId) {
|
|
742
|
+
const cloned = JSON.parse(JSON.stringify(schema));
|
|
743
|
+
rewriteRefs(cloned, baseId);
|
|
744
|
+
delete cloned["$id"];
|
|
745
|
+
const props = cloned["properties"] ?? {};
|
|
746
|
+
if (props["children"]) {
|
|
747
|
+
props["children"] = { type: "array" };
|
|
748
|
+
}
|
|
749
|
+
if (props["validator"]) {
|
|
750
|
+
props["validator"] = { type: "object" };
|
|
751
|
+
}
|
|
752
|
+
const patternProps = cloned["patternProperties"];
|
|
753
|
+
if (patternProps) {
|
|
754
|
+
for (const key of Object.keys(patternProps)) {
|
|
755
|
+
if (key.startsWith("^validator\\.")) {
|
|
756
|
+
patternProps[key] = { type: "object" };
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
const propsField = props["props"];
|
|
761
|
+
if (propsField?.properties?.["template"]) {
|
|
762
|
+
propsField.properties["template"] = { type: "object" };
|
|
763
|
+
}
|
|
764
|
+
return cloned;
|
|
765
|
+
}
|
|
766
|
+
function rewriteRefs(node, baseId) {
|
|
767
|
+
if (node === null || typeof node !== "object") return;
|
|
768
|
+
if (Array.isArray(node)) {
|
|
769
|
+
for (const item of node) rewriteRefs(item, baseId);
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const obj = node;
|
|
773
|
+
if (typeof obj["$ref"] === "string") {
|
|
774
|
+
const ref = obj["$ref"];
|
|
775
|
+
if (!ref.startsWith("http") && !ref.startsWith("#")) {
|
|
776
|
+
try {
|
|
777
|
+
obj["$ref"] = new URL(ref, baseId).href;
|
|
778
|
+
} catch {
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
for (const v of Object.values(obj)) rewriteRefs(v, baseId);
|
|
783
|
+
}
|
|
784
|
+
const STRING_FORMATS = [
|
|
785
|
+
"email",
|
|
786
|
+
"hostname",
|
|
787
|
+
"ipv4",
|
|
788
|
+
"ipv6",
|
|
789
|
+
"url",
|
|
790
|
+
"uuid",
|
|
791
|
+
"date",
|
|
792
|
+
"time",
|
|
793
|
+
"date-time",
|
|
794
|
+
"duration"
|
|
795
|
+
];
|
|
796
|
+
const WIDGET_TYPES = Object.keys(COMPONENT_SCHEMAS);
|
|
797
|
+
function levenshtein(a, b) {
|
|
798
|
+
if (a === b) return 0;
|
|
799
|
+
if (!a.length) return b.length;
|
|
800
|
+
if (!b.length) return a.length;
|
|
801
|
+
let prev = new Array(b.length + 1);
|
|
802
|
+
let curr = new Array(b.length + 1);
|
|
803
|
+
for (let j = 0; j <= b.length; j++) prev[j] = j;
|
|
804
|
+
for (let i = 1; i <= a.length; i++) {
|
|
805
|
+
curr[0] = i;
|
|
806
|
+
for (let j = 1; j <= b.length; j++) {
|
|
807
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
808
|
+
const del = (prev[j] ?? 0) + 1;
|
|
809
|
+
const ins = (curr[j - 1] ?? 0) + 1;
|
|
810
|
+
const sub = (prev[j - 1] ?? 0) + cost;
|
|
811
|
+
curr[j] = Math.min(del, ins, sub);
|
|
812
|
+
}
|
|
813
|
+
[prev, curr] = [curr, prev];
|
|
814
|
+
}
|
|
815
|
+
return prev[b.length] ?? 0;
|
|
816
|
+
}
|
|
817
|
+
function nearest(target, candidates) {
|
|
818
|
+
let best;
|
|
819
|
+
let bestDist = Infinity;
|
|
820
|
+
for (const c of candidates) {
|
|
821
|
+
const d = levenshtein(target.toLowerCase(), c.toLowerCase());
|
|
822
|
+
if (d < bestDist) {
|
|
823
|
+
bestDist = d;
|
|
824
|
+
best = c;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return bestDist <= Math.max(2, Math.floor(target.length / 2)) ? best : void 0;
|
|
828
|
+
}
|
|
829
|
+
function suggestForAdditional(propertyName, instancePath) {
|
|
830
|
+
if (/\/validator(\/|$)/.test(instancePath)) {
|
|
831
|
+
const validatorKeys = [
|
|
832
|
+
"type",
|
|
833
|
+
"required",
|
|
834
|
+
"minLength",
|
|
835
|
+
"maxLength",
|
|
836
|
+
"minimum",
|
|
837
|
+
"maximum",
|
|
838
|
+
"pattern",
|
|
839
|
+
"format",
|
|
840
|
+
"const",
|
|
841
|
+
"enum",
|
|
842
|
+
"messages",
|
|
843
|
+
"minItems",
|
|
844
|
+
"maxItems",
|
|
845
|
+
"uniqueItems",
|
|
846
|
+
"multipleOf",
|
|
847
|
+
"exclusiveMinimum",
|
|
848
|
+
"exclusiveMaximum"
|
|
849
|
+
];
|
|
850
|
+
const guess = nearest(propertyName, validatorKeys);
|
|
851
|
+
if (guess && guess !== propertyName) return `Did you mean \`${guess}\`?`;
|
|
852
|
+
return void 0;
|
|
853
|
+
}
|
|
854
|
+
return void 0;
|
|
855
|
+
}
|
|
856
|
+
function suggestForEnum(value, allowed) {
|
|
857
|
+
if (typeof value !== "string") return void 0;
|
|
858
|
+
const stringAllowed = allowed.filter((v) => typeof v === "string");
|
|
859
|
+
if (!stringAllowed.length) return void 0;
|
|
860
|
+
const guess = nearest(value, stringAllowed);
|
|
861
|
+
if (guess)
|
|
862
|
+
return `Did you mean \`${guess}\`? Allowed values: ${stringAllowed.map((v) => `\`${v}\``).join(", ")}.`;
|
|
863
|
+
return `Allowed values: ${stringAllowed.map((v) => `\`${v}\``).join(", ")}.`;
|
|
864
|
+
}
|
|
865
|
+
const VALIDATOR_TYPES = ["string", "number", "integer", "boolean", "array", "custom"];
|
|
866
|
+
function suggestForConst(value, allowed, instancePath) {
|
|
867
|
+
if (typeof value !== "string" || typeof allowed !== "string") return void 0;
|
|
868
|
+
if (/^\/form\/\d+(?:\/children\/\d+)*(?:\/props\/template)?\/type$/.test(instancePath)) {
|
|
869
|
+
const guess = nearest(value, WIDGET_TYPES);
|
|
870
|
+
return guess ? `Did you mean \`type: '${guess}'\`?` : `Valid widget types: ${WIDGET_TYPES.map((v) => `\`${v}\``).join(", ")}.`;
|
|
871
|
+
}
|
|
872
|
+
if (instancePath.endsWith("/validator/type")) {
|
|
873
|
+
const guess = nearest(value, VALIDATOR_TYPES);
|
|
874
|
+
return guess ? `Did you mean \`type: '${guess}'\`?` : `Allowed validator types: ${VALIDATOR_TYPES.map((v) => `\`${v}\``).join(", ")}.`;
|
|
875
|
+
}
|
|
876
|
+
return void 0;
|
|
877
|
+
}
|
|
878
|
+
function describe(value) {
|
|
879
|
+
if (value === null) return "null";
|
|
880
|
+
if (Array.isArray(value)) return "array";
|
|
881
|
+
if (typeof value === "object") return "object";
|
|
882
|
+
return JSON.stringify(value);
|
|
883
|
+
}
|
|
884
|
+
function formatAjvErrors(errors, dataRoot) {
|
|
885
|
+
if (!errors?.length) return { errors: [], warnings: [] };
|
|
886
|
+
const collapsed = dataRoot !== void 0 ? collapseOneOfErrors(errors, dataRoot) : { errors, warnings: [] };
|
|
887
|
+
const filtered = collapsed.errors;
|
|
888
|
+
const out = [];
|
|
889
|
+
for (const err of filtered) {
|
|
890
|
+
const path = err.instancePath || "/";
|
|
891
|
+
let message = err.message ?? "invalid value";
|
|
892
|
+
let suggestion;
|
|
893
|
+
switch (err.keyword) {
|
|
894
|
+
case "additionalProperties":
|
|
895
|
+
case "unevaluatedProperties": {
|
|
896
|
+
const prop = err.params.additionalProperty;
|
|
897
|
+
if (prop) {
|
|
898
|
+
message = `Unknown property \`${prop}\` at \`${path}\``;
|
|
899
|
+
suggestion = suggestForAdditional(prop, path);
|
|
900
|
+
}
|
|
901
|
+
break;
|
|
902
|
+
}
|
|
903
|
+
case "required": {
|
|
904
|
+
const prop = err.params.missingProperty;
|
|
905
|
+
if (prop) {
|
|
906
|
+
message = `Missing required property \`${prop}\` at \`${path}\``;
|
|
907
|
+
}
|
|
908
|
+
break;
|
|
909
|
+
}
|
|
910
|
+
case "enum": {
|
|
911
|
+
const allowed = err.params.allowedValues ?? [];
|
|
912
|
+
const v = describe(err.data);
|
|
913
|
+
message = `Value ${v} at \`${path}\` is not one of the allowed values`;
|
|
914
|
+
suggestion = suggestForEnum(err.data, allowed);
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
case "const": {
|
|
918
|
+
const allowed = err.params.allowedValue;
|
|
919
|
+
message = `Expected \`${describe(allowed)}\` at \`${path}\`, got \`${describe(err.data)}\``;
|
|
920
|
+
suggestion = suggestForConst(err.data, allowed, path);
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
case "type": {
|
|
924
|
+
const expected = err.params.type;
|
|
925
|
+
message = `Expected type ${Array.isArray(expected) ? expected.join("|") : expected} at \`${path}\`, got ${describe(err.data)}`;
|
|
926
|
+
break;
|
|
927
|
+
}
|
|
928
|
+
case "oneOf":
|
|
929
|
+
case "anyOf": {
|
|
930
|
+
message = `Value at \`${path}\` does not match any allowed variant`;
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
default:
|
|
934
|
+
message = `${err.message ?? "invalid value"} at \`${path}\``;
|
|
935
|
+
}
|
|
936
|
+
if (path.endsWith("/validator/format") && err.keyword === "enum") {
|
|
937
|
+
suggestion = `Allowed string formats: ${STRING_FORMATS.map((f) => `\`${f}\``).join(", ")}.`;
|
|
938
|
+
}
|
|
939
|
+
out.push({
|
|
940
|
+
path,
|
|
941
|
+
message,
|
|
942
|
+
suggestion,
|
|
943
|
+
keyword: err.keyword,
|
|
944
|
+
params: err.params
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
return { errors: dedupe(out), warnings: collapsed.warnings };
|
|
948
|
+
}
|
|
949
|
+
function extractWidgetPath(instancePath) {
|
|
950
|
+
const m = instancePath.match(/^(\/form\/\d+(?:\/children\/\d+)*)/);
|
|
951
|
+
return m ? m[1] : null;
|
|
952
|
+
}
|
|
953
|
+
function pickIntendedBranch(widget) {
|
|
954
|
+
if (!widget || typeof widget !== "object") return null;
|
|
955
|
+
const w = widget;
|
|
956
|
+
const widgetType = typeof w.type === "string" ? w.type : void 0;
|
|
957
|
+
const widgetKind = typeof w.kind === "string" ? w.kind : void 0;
|
|
958
|
+
if (widgetType && COMPONENT_SCHEMAS[widgetType]) {
|
|
959
|
+
return { $id: COMPONENT_SCHEMAS[widgetType].$id, type: widgetType };
|
|
960
|
+
}
|
|
961
|
+
if (!widgetType) return null;
|
|
962
|
+
const kindMatched = Object.entries(COMPONENT_SCHEMAS).filter(([, schema]) => {
|
|
963
|
+
const k = schema["properties"]?.["kind"]?.const;
|
|
964
|
+
return k === widgetKind;
|
|
965
|
+
});
|
|
966
|
+
const pool = kindMatched.length ? kindMatched : Object.entries(COMPONENT_SCHEMAS);
|
|
967
|
+
const candidateTypes = pool.map(([t]) => t);
|
|
968
|
+
const match = nearest(widgetType, candidateTypes);
|
|
969
|
+
if (!match) return null;
|
|
970
|
+
return { $id: COMPONENT_SCHEMAS[match].$id, type: match };
|
|
971
|
+
}
|
|
972
|
+
function collapseOneOfErrors(errors, dataRoot) {
|
|
973
|
+
const topLevel = [];
|
|
974
|
+
for (const err of errors) {
|
|
975
|
+
if (extractWidgetPath(err.instancePath) === null) topLevel.push(err);
|
|
976
|
+
}
|
|
977
|
+
const widgetErrors = [];
|
|
978
|
+
const warnings = [];
|
|
979
|
+
const form = dataRoot?.form;
|
|
980
|
+
if (Array.isArray(form)) {
|
|
981
|
+
form.forEach((widget, i) => {
|
|
982
|
+
collectWidgetErrors(widget, `/form/${i}`, widgetErrors, warnings);
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
return { errors: [...topLevel, ...widgetErrors], warnings };
|
|
986
|
+
}
|
|
987
|
+
function collectWidgetErrors(widget, widgetPath, out, warnings) {
|
|
988
|
+
const intended = pickIntendedBranch(widget);
|
|
989
|
+
if (!intended) {
|
|
990
|
+
const widgetType = widget?.type;
|
|
991
|
+
if (typeof widgetType !== "string" || !widgetType) {
|
|
992
|
+
out.push({
|
|
993
|
+
keyword: "required",
|
|
994
|
+
instancePath: widgetPath,
|
|
995
|
+
schemaPath: "",
|
|
996
|
+
params: { missingProperty: "type" },
|
|
997
|
+
message: `Widget at ${widgetPath} is missing or has an invalid \`type\``
|
|
998
|
+
});
|
|
999
|
+
} else {
|
|
1000
|
+
warnings.push({
|
|
1001
|
+
path: `${widgetPath}/type`,
|
|
1002
|
+
keyword: "customWidget",
|
|
1003
|
+
message: `Widget type \`${widgetType}\` at \`${widgetPath}\` is not a built-in GolemUI widget — assumed custom. Its props were not validated.`,
|
|
1004
|
+
suggestion: "Built-in widget types: " + Object.keys(COMPONENT_SCHEMAS).join(", ") + ". If this is intentional (a custom widget registered via the framework loader), you can ignore this warning.",
|
|
1005
|
+
params: { type: widgetType }
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
recurseIntoChildren(widget, widgetPath, out, warnings);
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
const validator = getShallowWidgetValidator(intended.type);
|
|
1012
|
+
if (validator) {
|
|
1013
|
+
validator(widget);
|
|
1014
|
+
for (const e of validator.errors ?? []) {
|
|
1015
|
+
out.push({
|
|
1016
|
+
...e,
|
|
1017
|
+
instancePath: widgetPath + e.instancePath
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
const w = widget;
|
|
1022
|
+
if (w?.validator && typeof w.validator === "object") {
|
|
1023
|
+
collectValidatorErrors(w.validator, `${widgetPath}/validator`, out);
|
|
1024
|
+
}
|
|
1025
|
+
if (widget && typeof widget === "object" && !Array.isArray(widget)) {
|
|
1026
|
+
for (const [key, value] of Object.entries(widget)) {
|
|
1027
|
+
if (key.startsWith("validator.") && value && typeof value === "object") {
|
|
1028
|
+
collectValidatorErrors(value, `${widgetPath}/${key}`, out);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
recurseIntoChildren(widget, widgetPath, out, warnings);
|
|
1033
|
+
}
|
|
1034
|
+
function recurseIntoChildren(widget, widgetPath, out, warnings) {
|
|
1035
|
+
const w = widget;
|
|
1036
|
+
if (Array.isArray(w?.children)) {
|
|
1037
|
+
w.children.forEach((child, i) => {
|
|
1038
|
+
collectWidgetErrors(child, `${widgetPath}/children/${i}`, out, warnings);
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
if (w?.type === "repeater" && w.props?.template) {
|
|
1042
|
+
collectWidgetErrors(w.props.template, `${widgetPath}/props/template`, out, warnings);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
function collectValidatorErrors(validator, path, out) {
|
|
1046
|
+
const v = validator;
|
|
1047
|
+
const t = typeof v.type === "string" ? v.type : null;
|
|
1048
|
+
const branches = getValidatorBranches();
|
|
1049
|
+
const matchedType = t && branches.includes(t) ? t : t ? nearest(t, branches) : void 0;
|
|
1050
|
+
if (!matchedType) {
|
|
1051
|
+
out.push({
|
|
1052
|
+
keyword: "enum",
|
|
1053
|
+
instancePath: `${path}/type`,
|
|
1054
|
+
schemaPath: "",
|
|
1055
|
+
params: { allowedValues: branches },
|
|
1056
|
+
message: `Validator type is not one of ${branches.join(", ")}`,
|
|
1057
|
+
data: t
|
|
1058
|
+
});
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
const compiled = getValidatorBranchValidator(matchedType);
|
|
1062
|
+
if (!compiled) return;
|
|
1063
|
+
compiled(validator);
|
|
1064
|
+
for (const e of compiled.errors ?? []) {
|
|
1065
|
+
out.push({ ...e, instancePath: path + e.instancePath });
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
function dedupe(errors) {
|
|
1069
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1070
|
+
const result = [];
|
|
1071
|
+
for (const e of errors) {
|
|
1072
|
+
const key = `${e.path}|${e.keyword}|${e.message}`;
|
|
1073
|
+
if (seen.has(key)) continue;
|
|
1074
|
+
seen.add(key);
|
|
1075
|
+
result.push(e);
|
|
1076
|
+
}
|
|
1077
|
+
return result;
|
|
1078
|
+
}
|
|
1079
|
+
function lintReactiveExpressions(formDefinition) {
|
|
1080
|
+
const findings = [];
|
|
1081
|
+
walk$1(formDefinition, "", findings);
|
|
1082
|
+
return findings;
|
|
1083
|
+
}
|
|
1084
|
+
function walk$1(node, path, out) {
|
|
1085
|
+
if (node === null || typeof node !== "object") return;
|
|
1086
|
+
if (Array.isArray(node)) {
|
|
1087
|
+
node.forEach((item, i) => walk$1(item, `${path}/${i}`, out));
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const obj = node;
|
|
1091
|
+
for (const key of ["include", "exclude"]) {
|
|
1092
|
+
const child = obj[key];
|
|
1093
|
+
if (child && typeof child === "object" && "when" in child) {
|
|
1094
|
+
const expr = child.when;
|
|
1095
|
+
if (typeof expr === "string") {
|
|
1096
|
+
checkExpression(expr, `${path}/${key}/when`, out);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
if (path === "" && obj["states"] && typeof obj["states"] === "object") {
|
|
1101
|
+
for (const [name, expr] of Object.entries(obj["states"])) {
|
|
1102
|
+
if (typeof expr === "string") {
|
|
1103
|
+
checkExpression(expr, `/states/${name}`, out);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
1108
|
+
if (v && typeof v === "object" && !Array.isArray(v) && "when" in v) {
|
|
1109
|
+
const expr = v.when;
|
|
1110
|
+
if (typeof expr === "string" && k !== "include" && k !== "exclude") {
|
|
1111
|
+
checkExpression(expr, `${path}/${k}/when`, out);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (typeof v === "object" && v !== null) {
|
|
1115
|
+
walk$1(v, `${path}/${k}`, out);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
function checkExpression(expr, path, out) {
|
|
1120
|
+
const trimmed = expr.trim();
|
|
1121
|
+
if (!trimmed) {
|
|
1122
|
+
out.push({ path, expression: expr, message: "Expression is empty." });
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
const stack = [];
|
|
1126
|
+
const pairs = { ")": "(", "]": "[", "}": "{" };
|
|
1127
|
+
for (const ch of trimmed) {
|
|
1128
|
+
if ("([{".includes(ch)) stack.push(ch);
|
|
1129
|
+
else if (")]}".includes(ch)) {
|
|
1130
|
+
if (stack.pop() !== pairs[ch]) {
|
|
1131
|
+
out.push({
|
|
1132
|
+
path,
|
|
1133
|
+
expression: expr,
|
|
1134
|
+
message: `Unbalanced \`${ch}\` in reactive expression.`
|
|
1135
|
+
});
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if (stack.length) {
|
|
1141
|
+
out.push({
|
|
1142
|
+
path,
|
|
1143
|
+
expression: expr,
|
|
1144
|
+
message: `Unclosed \`${stack[stack.length - 1]}\` in reactive expression.`
|
|
1145
|
+
});
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
if (!/\$form\b|\$meta\b|\$formIsInvalid\b/.test(trimmed)) {
|
|
1149
|
+
out.push({
|
|
1150
|
+
path,
|
|
1151
|
+
expression: expr,
|
|
1152
|
+
message: "Expression does not reference `$form`, `$meta`, or `$formIsInvalid`.",
|
|
1153
|
+
suggestion: "GolemUI expressions read form data via `$form.fieldName`, form metadata via `$meta.key`, or the built-in `$formIsInvalid` boolean. Did you forget the prefix?"
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
if (/(?<![=!<>])=(?![=>])/.test(trimmed)) {
|
|
1157
|
+
out.push({
|
|
1158
|
+
path,
|
|
1159
|
+
expression: expr,
|
|
1160
|
+
message: "Expression contains a single `=` (assignment). Reactive expressions are read-only.",
|
|
1161
|
+
suggestion: "Use `===` for equality comparison."
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
if (/(?<![&])&(?![&])/.test(trimmed) || /(?<![|])\|(?![|])/.test(trimmed)) {
|
|
1165
|
+
out.push({
|
|
1166
|
+
path,
|
|
1167
|
+
expression: expr,
|
|
1168
|
+
message: "Expression contains a single `&` or `|` (bitwise).",
|
|
1169
|
+
suggestion: "Use `&&` for logical AND, `||` for logical OR."
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
if (/(?<![=!])==(?!=)/.test(trimmed) || /(?<![!])!=(?!=)/.test(trimmed)) {
|
|
1173
|
+
out.push({
|
|
1174
|
+
path,
|
|
1175
|
+
expression: expr,
|
|
1176
|
+
message: "Expression uses loose equality (`==` or `!=`).",
|
|
1177
|
+
suggestion: "Use strict equality `===` / `!==` to avoid type coercion (e.g. `$form.x !== undefined` rather than `$form.x != null`)."
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
if (/(?<![=!])!\s*\$(?:form|meta)\b/.test(trimmed)) {
|
|
1181
|
+
out.push({
|
|
1182
|
+
path,
|
|
1183
|
+
expression: expr,
|
|
1184
|
+
message: "Expression negates a `$form`/`$meta` reference (relies on truthy/falsy coercion).",
|
|
1185
|
+
suggestion: 'Form data values can be `undefined`. Pick the case you actually mean and write it explicitly — `$form.x === undefined`, `$form.x === null`, `$form.x === 0`, `$form.x === ""` — instead of `!$form.x`.'
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
const refChainRe = /\$(?:form|meta)\b((?:\.[\w?]+)*)/g;
|
|
1189
|
+
let chainFlagged = false;
|
|
1190
|
+
let chainMatch;
|
|
1191
|
+
while ((chainMatch = refChainRe.exec(trimmed)) !== null) {
|
|
1192
|
+
const chain = chainMatch[1];
|
|
1193
|
+
if (!chain) continue;
|
|
1194
|
+
const segments = chain.split(".").filter(Boolean);
|
|
1195
|
+
let unsafe = false;
|
|
1196
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1197
|
+
const segment = segments[i];
|
|
1198
|
+
if (!segment || !segment.endsWith("?")) {
|
|
1199
|
+
unsafe = true;
|
|
1200
|
+
break;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
if (unsafe && !chainFlagged) {
|
|
1204
|
+
out.push({
|
|
1205
|
+
path,
|
|
1206
|
+
expression: expr,
|
|
1207
|
+
message: "Expression chains nested property access without optional chaining (e.g. `$form.user.name`).",
|
|
1208
|
+
suggestion: "Treat every nested property as possibly `undefined`. Use `?.` between segments: `$form.user?.name` instead of `$form.user.name`. The runtime throws if `$form.user` is undefined."
|
|
1209
|
+
});
|
|
1210
|
+
chainFlagged = true;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
const refOnly = /^\$(?:form|meta)(?:\.[\w?]+)*$/;
|
|
1214
|
+
const refBeforeBool = /\$(?:form|meta)(?:\.[\w?]+)*\s*(?:&&|\|\||\?(?![.?]))/;
|
|
1215
|
+
const refAfterBool = /(?:&&|\|\|)\s*\$(?:form|meta)(?:\.[\w?]+)*\s*$/;
|
|
1216
|
+
if (refOnly.test(trimmed) || refBeforeBool.test(trimmed) || refAfterBool.test(trimmed)) {
|
|
1217
|
+
out.push({
|
|
1218
|
+
path,
|
|
1219
|
+
expression: expr,
|
|
1220
|
+
message: "Expression uses `$form`/`$meta` directly as a boolean (relies on truthy/falsy coercion).",
|
|
1221
|
+
suggestion: 'Form data values can be `undefined`. Compare explicitly: `$form.x !== undefined`, `$form.x === "value"`, `$form.items?.length > 0`. For default values use nullish coalescing: `$form.x ?? defaultValue`.'
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
const refForCmpRe = /\$(?:form|meta)(?:\.[\w?]+)+/g;
|
|
1225
|
+
let r5Flagged = false;
|
|
1226
|
+
let cmpMatch;
|
|
1227
|
+
while ((cmpMatch = refForCmpRe.exec(trimmed)) !== null) {
|
|
1228
|
+
const start = cmpMatch.index;
|
|
1229
|
+
const end = start + cmpMatch[0].length;
|
|
1230
|
+
const before = trimmed.slice(0, start).trimEnd();
|
|
1231
|
+
const after = trimmed.slice(end).trimStart();
|
|
1232
|
+
const opAfter = /^(?:<=?|>=?|[+\-*/%])(?!=)/.test(after);
|
|
1233
|
+
const opBefore = /(?<![=!])[<>+\-*/%]$/.test(before);
|
|
1234
|
+
if (!(opAfter || opBefore)) continue;
|
|
1235
|
+
if (/&&|\|\|/.test(before)) continue;
|
|
1236
|
+
if (!r5Flagged) {
|
|
1237
|
+
out.push({
|
|
1238
|
+
path,
|
|
1239
|
+
expression: expr,
|
|
1240
|
+
message: "Expression applies a comparison or arithmetic operator to a `$form`/`$meta` reference whose leaf may be `undefined`.",
|
|
1241
|
+
suggestion: "Guard the value first: `$form.x !== undefined && $form.x > 180`. Or default it: `($form.x ?? 0) > 180`. Strict equality (`$form.x === 180`) is also safe since it evaluates to `false` when undefined."
|
|
1242
|
+
});
|
|
1243
|
+
r5Flagged = true;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
function lintStringInterpolations(formDefinition) {
|
|
1248
|
+
const findings = [];
|
|
1249
|
+
walk(formDefinition, "", findings);
|
|
1250
|
+
return findings;
|
|
1251
|
+
}
|
|
1252
|
+
const SLOT_REGEX = /\{\{([^}]*(?:\}[^}]+)*)\}\}/g;
|
|
1253
|
+
function walk(node, path, out) {
|
|
1254
|
+
if (node === null || typeof node !== "object") {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
if (Array.isArray(node)) {
|
|
1258
|
+
node.forEach((item, i) => walk(item, `${path}/${i}`, out));
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
const obj = node;
|
|
1262
|
+
const isTranslationConfig = typeof obj["key"] === "string" && obj["params"] !== null && typeof obj["params"] === "object" && !Array.isArray(obj["params"]);
|
|
1263
|
+
if (isTranslationConfig) {
|
|
1264
|
+
const params = obj["params"];
|
|
1265
|
+
for (const [paramKey, paramValue] of Object.entries(params)) {
|
|
1266
|
+
if (typeof paramValue === "string") {
|
|
1267
|
+
checkParamExpression(paramValue, `${path}/params/${paramKey}`, out);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1272
|
+
if (key === "params" && isTranslationConfig) {
|
|
1273
|
+
continue;
|
|
1274
|
+
}
|
|
1275
|
+
if (key === "defaultValue" || key.startsWith("defaultValue.")) {
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
const childPath = `${path}/${key}`;
|
|
1279
|
+
if (typeof value === "string") {
|
|
1280
|
+
checkTemplate(value, childPath, out);
|
|
1281
|
+
} else {
|
|
1282
|
+
walk(value, childPath, out);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
function checkParamExpression(value, path, out) {
|
|
1287
|
+
if (value.includes("{{") || value.includes("}}")) {
|
|
1288
|
+
out.push({
|
|
1289
|
+
path,
|
|
1290
|
+
slot: value,
|
|
1291
|
+
message: "i18n param expression should not use `{{` / `}}` delimiters.",
|
|
1292
|
+
suggestion: 'Use a bare expression: `"$form.fieldName"` not `"{{$form.fieldName}}"`.'
|
|
1293
|
+
});
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
if (value.startsWith("$") && /(?<![=!<>])=(?![=>])/.test(value)) {
|
|
1297
|
+
out.push({
|
|
1298
|
+
path,
|
|
1299
|
+
slot: value,
|
|
1300
|
+
message: "i18n param expression contains a single `=` (assignment).",
|
|
1301
|
+
suggestion: "Param expressions are read-only. Did you mean `===` for equality?"
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
function checkTemplate(value, path, out) {
|
|
1306
|
+
const openCount = (value.match(/\{\{/g) ?? []).length;
|
|
1307
|
+
const closeCount = (value.match(/\}\}/g) ?? []).length;
|
|
1308
|
+
if (openCount !== closeCount) {
|
|
1309
|
+
out.push({
|
|
1310
|
+
path,
|
|
1311
|
+
slot: value,
|
|
1312
|
+
message: "String template has unbalanced `{{` / `}}` delimiters.",
|
|
1313
|
+
suggestion: "Every `{{` must have a matching `}}`."
|
|
1314
|
+
});
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
if (openCount === 0) return;
|
|
1318
|
+
let match;
|
|
1319
|
+
SLOT_REGEX.lastIndex = 0;
|
|
1320
|
+
while ((match = SLOT_REGEX.exec(value)) !== null) {
|
|
1321
|
+
const slot = match[0];
|
|
1322
|
+
const expr = match[1];
|
|
1323
|
+
checkSlot(expr, slot, path, out);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
function checkSlot(expr, slot, path, out) {
|
|
1327
|
+
const trimmed = expr.trim();
|
|
1328
|
+
if (!trimmed) {
|
|
1329
|
+
out.push({
|
|
1330
|
+
path,
|
|
1331
|
+
slot,
|
|
1332
|
+
message: "String interpolation slot is empty.",
|
|
1333
|
+
suggestion: "Add an expression, e.g. `{{$form.fieldName}}`."
|
|
1334
|
+
});
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
if (trimmed.includes("{{")) {
|
|
1338
|
+
out.push({
|
|
1339
|
+
path,
|
|
1340
|
+
slot,
|
|
1341
|
+
message: "Interpolation slot contains nested `{{`.",
|
|
1342
|
+
suggestion: "Slots cannot be nested. Check for a copy-paste error."
|
|
1343
|
+
});
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
if (!/\$form\b|\$meta\b|\$errors\b|\$formIsInvalid\b/.test(trimmed)) {
|
|
1347
|
+
out.push({
|
|
1348
|
+
path,
|
|
1349
|
+
slot,
|
|
1350
|
+
message: "Interpolation slot does not reference `$form`, `$meta`, `$errors`, or `$formIsInvalid`.",
|
|
1351
|
+
suggestion: "GolemUI template slots read data via `$form.fieldName`, metadata via `$meta.key`, validation errors via `$errors.fieldName`, or the built-in `$formIsInvalid` boolean."
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
if (/(?<![=!<>])=(?![=>])/.test(trimmed)) {
|
|
1355
|
+
out.push({
|
|
1356
|
+
path,
|
|
1357
|
+
slot,
|
|
1358
|
+
message: "Interpolation slot contains a single `=` (assignment).",
|
|
1359
|
+
suggestion: "Template slots are read-only. Did you mean `===` for equality?"
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
function validateFormDefinition(input) {
|
|
1364
|
+
const validate = getFormValidator();
|
|
1365
|
+
const ajvOk = validate(input.formDefinition);
|
|
1366
|
+
const { errors, warnings } = formatAjvErrors(validate.errors, input.formDefinition);
|
|
1367
|
+
const expressionWarnings = lintReactiveExpressions(input.formDefinition);
|
|
1368
|
+
const interpolationWarnings = lintStringInterpolations(input.formDefinition);
|
|
1369
|
+
if (!ajvOk && errors.length === 0 && warnings.length === 0) {
|
|
1370
|
+
errors.push({
|
|
1371
|
+
path: "/",
|
|
1372
|
+
keyword: "oneOf",
|
|
1373
|
+
message: "Form failed schema validation but no specific error could be localized. The form may contain a widget at a position the form schema does not allow, or a structural shape that our targeted validator missed. Verify each widget against its `get_widget_spec` entry."
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
valid: errors.length === 0,
|
|
1378
|
+
errors,
|
|
1379
|
+
warnings,
|
|
1380
|
+
expressionWarnings,
|
|
1381
|
+
interpolationWarnings
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
const VALIDATE_FORM_DEFINITION_TOOL = {
|
|
1385
|
+
name: "validate_form_definition",
|
|
1386
|
+
description: "Validate a GolemUI form definition against the bundled JSON Schemas. Use this AFTER generating or modifying a form definition to guarantee it is correct before the user pastes it into their codebase. Returns `{ valid, errors, warnings, expressionWarnings, interpolationWarnings }`. Hard mistakes (typos in widget `type`, missing required props, invalid validator shapes) show up in `errors` and flip `valid` to false. Likely-custom widgets (a `type` value that isn't a built-in and isn't close to one) show up in `warnings` instead — they don't affect `valid`. Reactive expressions (`include.when`, `disabled.when`, etc.) are linted separately into `expressionWarnings`. String interpolation templates (`{{$form.x}}`, `{{$meta.y}}`, expressions like `{{$form.count + 1}}`, etc.) in widget props, and bare expressions inside i18n `params` objects, are linted into `interpolationWarnings`.",
|
|
1387
|
+
inputSchema: {
|
|
1388
|
+
type: "object",
|
|
1389
|
+
properties: {
|
|
1390
|
+
formDefinition: {
|
|
1391
|
+
type: "object",
|
|
1392
|
+
additionalProperties: true,
|
|
1393
|
+
description: "The full GolemUI form definition object, shaped as `{ form: [...widgets], states?: {...} }`. Pass the JSON object, not a stringified version."
|
|
1394
|
+
}
|
|
1395
|
+
},
|
|
1396
|
+
required: ["formDefinition"]
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
const SUPPORTED_STRING_FORMATS = /* @__PURE__ */ new Set([
|
|
1400
|
+
"email",
|
|
1401
|
+
"hostname",
|
|
1402
|
+
"ipv4",
|
|
1403
|
+
"ipv6",
|
|
1404
|
+
"url",
|
|
1405
|
+
"uuid",
|
|
1406
|
+
"date",
|
|
1407
|
+
"time",
|
|
1408
|
+
"date-time",
|
|
1409
|
+
"duration"
|
|
1410
|
+
]);
|
|
1411
|
+
function buildStringValidator(s, required2) {
|
|
1412
|
+
const v = { type: "string" };
|
|
1413
|
+
let used = false;
|
|
1414
|
+
if (required2) {
|
|
1415
|
+
v.required = true;
|
|
1416
|
+
used = true;
|
|
1417
|
+
}
|
|
1418
|
+
if (typeof s.minLength === "number") {
|
|
1419
|
+
v.minLength = s.minLength;
|
|
1420
|
+
used = true;
|
|
1421
|
+
}
|
|
1422
|
+
if (typeof s.maxLength === "number") {
|
|
1423
|
+
v.maxLength = s.maxLength;
|
|
1424
|
+
used = true;
|
|
1425
|
+
}
|
|
1426
|
+
if (typeof s.pattern === "string") {
|
|
1427
|
+
v.pattern = s.pattern;
|
|
1428
|
+
used = true;
|
|
1429
|
+
}
|
|
1430
|
+
if (typeof s.format === "string" && SUPPORTED_STRING_FORMATS.has(s.format)) {
|
|
1431
|
+
if (s.format !== "date" && s.format !== "date-time") {
|
|
1432
|
+
v.format = s.format;
|
|
1433
|
+
used = true;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (s.const !== void 0) {
|
|
1437
|
+
v.const = s.const;
|
|
1438
|
+
used = true;
|
|
1439
|
+
}
|
|
1440
|
+
if (Array.isArray(s.enum)) {
|
|
1441
|
+
v.enum = s.enum;
|
|
1442
|
+
used = true;
|
|
1443
|
+
}
|
|
1444
|
+
return used ? v : void 0;
|
|
1445
|
+
}
|
|
1446
|
+
function buildNumberValidator(s, required2, t) {
|
|
1447
|
+
const v = { type: t };
|
|
1448
|
+
let used = false;
|
|
1449
|
+
if (required2) {
|
|
1450
|
+
v.required = true;
|
|
1451
|
+
used = true;
|
|
1452
|
+
}
|
|
1453
|
+
if (typeof s.minimum === "number") {
|
|
1454
|
+
v.minimum = s.minimum;
|
|
1455
|
+
used = true;
|
|
1456
|
+
}
|
|
1457
|
+
if (typeof s.maximum === "number") {
|
|
1458
|
+
v.maximum = s.maximum;
|
|
1459
|
+
used = true;
|
|
1460
|
+
}
|
|
1461
|
+
if (typeof s.exclusiveMinimum === "number") {
|
|
1462
|
+
v.exclusiveMinimum = s.exclusiveMinimum;
|
|
1463
|
+
used = true;
|
|
1464
|
+
}
|
|
1465
|
+
if (typeof s.exclusiveMaximum === "number") {
|
|
1466
|
+
v.exclusiveMaximum = s.exclusiveMaximum;
|
|
1467
|
+
used = true;
|
|
1468
|
+
}
|
|
1469
|
+
if (typeof s.multipleOf === "number") {
|
|
1470
|
+
v.multipleOf = s.multipleOf;
|
|
1471
|
+
used = true;
|
|
1472
|
+
}
|
|
1473
|
+
return used ? v : void 0;
|
|
1474
|
+
}
|
|
1475
|
+
function buildBooleanValidator(s, required2) {
|
|
1476
|
+
const v = { type: "boolean" };
|
|
1477
|
+
let used = false;
|
|
1478
|
+
if (required2) {
|
|
1479
|
+
v.required = true;
|
|
1480
|
+
used = true;
|
|
1481
|
+
}
|
|
1482
|
+
if (s.const !== void 0) {
|
|
1483
|
+
v.const = s.const;
|
|
1484
|
+
used = true;
|
|
1485
|
+
}
|
|
1486
|
+
return used ? v : void 0;
|
|
1487
|
+
}
|
|
1488
|
+
const SECRET_HINTS = /(password|secret|api[_-]?key|token)/i;
|
|
1489
|
+
const LONG_TEXT_MIN_LENGTH = 200;
|
|
1490
|
+
const SELECT_THRESHOLD = 6;
|
|
1491
|
+
function jsonSchemaToGui(schema, opts = {}) {
|
|
1492
|
+
const unmapped = [];
|
|
1493
|
+
const root = unwrap(schema);
|
|
1494
|
+
const fields = [];
|
|
1495
|
+
if (root.type === "object" || root.properties) {
|
|
1496
|
+
const required2 = new Set(root.required ?? []);
|
|
1497
|
+
for (const [name, propSchema] of Object.entries(root.properties ?? {})) {
|
|
1498
|
+
const widget = mapProperty(name, unwrap(propSchema), required2.has(name), "", unmapped);
|
|
1499
|
+
if (widget) fields.push(widget);
|
|
1500
|
+
}
|
|
1501
|
+
} else {
|
|
1502
|
+
unmapped.push({
|
|
1503
|
+
path: "",
|
|
1504
|
+
reason: "Top-level JSON Schema is not an object — only object schemas map to forms."
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
if (opts.submitAction !== false) {
|
|
1508
|
+
fields.push({
|
|
1509
|
+
kind: "action",
|
|
1510
|
+
type: "button",
|
|
1511
|
+
actionType: "submit",
|
|
1512
|
+
label: opts.submitLabel ?? "Submit",
|
|
1513
|
+
props: { variant: "filled" }
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
const wrapped = opts.layout && opts.layout !== "vertical" ? [
|
|
1517
|
+
{
|
|
1518
|
+
kind: "layout",
|
|
1519
|
+
type: opts.layout === "grid" ? "grid" : "flex",
|
|
1520
|
+
props: opts.layout === "grid" ? { columnGap: 12, rowGap: 12 } : { direction: "row", gap: 12 },
|
|
1521
|
+
children: fields
|
|
1522
|
+
}
|
|
1523
|
+
] : fields;
|
|
1524
|
+
return {
|
|
1525
|
+
formDefinition: { $schema: "https://golemui.com/schemas/form.schema.json", form: wrapped },
|
|
1526
|
+
unmapped
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
function unwrap(schema) {
|
|
1530
|
+
if (schema.oneOf?.length === 2) {
|
|
1531
|
+
const nonNull = schema.oneOf.find((s) => s.type !== "null");
|
|
1532
|
+
if (nonNull) return { ...nonNull, ...stripBranches(schema) };
|
|
1533
|
+
}
|
|
1534
|
+
if (schema.anyOf?.length === 2) {
|
|
1535
|
+
const nonNull = schema.anyOf.find((s) => s.type !== "null");
|
|
1536
|
+
if (nonNull) return { ...nonNull, ...stripBranches(schema) };
|
|
1537
|
+
}
|
|
1538
|
+
return schema;
|
|
1539
|
+
}
|
|
1540
|
+
function stripBranches(s) {
|
|
1541
|
+
const { oneOf: oneOf2, anyOf, ...rest } = s;
|
|
1542
|
+
return rest;
|
|
1543
|
+
}
|
|
1544
|
+
function mapProperty(name, schema, required2, parentPath, unmapped) {
|
|
1545
|
+
const path = parentPath ? `${parentPath}.${name}` : name;
|
|
1546
|
+
const label = humanLabel(schema.title ?? name);
|
|
1547
|
+
const t = Array.isArray(schema.type) ? schema.type.find((x) => x !== "null") : schema.type;
|
|
1548
|
+
if (Array.isArray(schema.enum) && schema.enum.every((v) => typeof v === "string")) {
|
|
1549
|
+
return buildEnumField(path, name, label, schema, required2);
|
|
1550
|
+
}
|
|
1551
|
+
switch (t) {
|
|
1552
|
+
case "string":
|
|
1553
|
+
return buildStringField(path, name, label, schema, required2);
|
|
1554
|
+
case "number":
|
|
1555
|
+
case "integer":
|
|
1556
|
+
return buildNumberField(path, label, schema, required2, t);
|
|
1557
|
+
case "boolean":
|
|
1558
|
+
return buildBooleanField(path, label, schema, required2);
|
|
1559
|
+
case "object":
|
|
1560
|
+
return buildObjectGroup(path, name, label, schema, unmapped);
|
|
1561
|
+
case "array":
|
|
1562
|
+
return buildArrayField(path, name, label, schema, required2, unmapped);
|
|
1563
|
+
default:
|
|
1564
|
+
unmapped.push({
|
|
1565
|
+
path,
|
|
1566
|
+
reason: `Unsupported JSON Schema type \`${schema.type ?? "undefined"}\`.`
|
|
1567
|
+
});
|
|
1568
|
+
return null;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
function buildStringField(path, name, label, schema, required2) {
|
|
1572
|
+
const validator = buildStringValidator(schema, required2);
|
|
1573
|
+
const isSecret = SECRET_HINTS.test(name);
|
|
1574
|
+
const isLong = (schema.maxLength ?? 0) >= LONG_TEXT_MIN_LENGTH;
|
|
1575
|
+
if (schema.format === "date") {
|
|
1576
|
+
return cleanFields({ kind: "input", type: "dateInput", path, label, validator });
|
|
1577
|
+
}
|
|
1578
|
+
if (schema.format === "date-time") {
|
|
1579
|
+
return cleanFields({ kind: "input", type: "datePicker", path, label, validator });
|
|
1580
|
+
}
|
|
1581
|
+
if (isSecret) {
|
|
1582
|
+
return cleanFields({ kind: "input", type: "password", path, label, validator });
|
|
1583
|
+
}
|
|
1584
|
+
if (isLong) {
|
|
1585
|
+
return cleanFields({
|
|
1586
|
+
kind: "input",
|
|
1587
|
+
type: "textarea",
|
|
1588
|
+
path,
|
|
1589
|
+
label,
|
|
1590
|
+
validator,
|
|
1591
|
+
props: schema.description ? { hint: schema.description } : void 0
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
return cleanFields({
|
|
1595
|
+
kind: "input",
|
|
1596
|
+
type: "textinput",
|
|
1597
|
+
path,
|
|
1598
|
+
label,
|
|
1599
|
+
validator,
|
|
1600
|
+
props: schema.description ? { hint: schema.description } : void 0
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
function buildEnumField(path, name, label, schema, required2) {
|
|
1604
|
+
const values = schema.enum ?? [];
|
|
1605
|
+
const validator = required2 ? { type: "string", required: true, enum: values } : { type: "string", enum: values };
|
|
1606
|
+
if (values.length <= SELECT_THRESHOLD) {
|
|
1607
|
+
return cleanFields({
|
|
1608
|
+
kind: "input",
|
|
1609
|
+
type: "select",
|
|
1610
|
+
path,
|
|
1611
|
+
label,
|
|
1612
|
+
validator,
|
|
1613
|
+
props: {
|
|
1614
|
+
options: values.map((v) => ({ label: humanLabel(v), value: v }))
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
return cleanFields({
|
|
1619
|
+
kind: "input",
|
|
1620
|
+
type: "dropdown",
|
|
1621
|
+
path,
|
|
1622
|
+
label,
|
|
1623
|
+
validator,
|
|
1624
|
+
props: {
|
|
1625
|
+
labelField: "label",
|
|
1626
|
+
valueField: "value",
|
|
1627
|
+
items: values.map((v) => ({ label: humanLabel(v), value: v }))
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
function buildNumberField(path, label, schema, required2, t) {
|
|
1632
|
+
const validator = buildNumberValidator(schema, required2, t);
|
|
1633
|
+
return cleanFields({
|
|
1634
|
+
kind: "input",
|
|
1635
|
+
type: "number",
|
|
1636
|
+
path,
|
|
1637
|
+
label,
|
|
1638
|
+
validator,
|
|
1639
|
+
props: schema.description ? { hint: schema.description } : void 0
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
function buildBooleanField(path, label, schema, required2) {
|
|
1643
|
+
const validator = buildBooleanValidator(schema, required2);
|
|
1644
|
+
return cleanFields({ kind: "input", type: "checkbox", path, label, validator });
|
|
1645
|
+
}
|
|
1646
|
+
function buildObjectGroup(path, _name, _label, schema, unmapped) {
|
|
1647
|
+
const required2 = new Set(schema.required ?? []);
|
|
1648
|
+
const children = [];
|
|
1649
|
+
for (const [childName, childSchema] of Object.entries(schema.properties ?? {})) {
|
|
1650
|
+
const w = mapProperty(childName, unwrap(childSchema), required2.has(childName), path, unmapped);
|
|
1651
|
+
if (w) children.push(w);
|
|
1652
|
+
}
|
|
1653
|
+
if (!children.length) {
|
|
1654
|
+
unmapped.push({ path, reason: "Object has no mappable properties." });
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
return {
|
|
1658
|
+
kind: "layout",
|
|
1659
|
+
type: "flex",
|
|
1660
|
+
props: { direction: "column", gap: 8 },
|
|
1661
|
+
children
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function buildArrayField(path, _name, label, schema, required2, unmapped) {
|
|
1665
|
+
const items = schema.items ? unwrap(schema.items) : void 0;
|
|
1666
|
+
if (!items) {
|
|
1667
|
+
unmapped.push({ path, reason: "Array without `items` schema cannot be mapped." });
|
|
1668
|
+
return null;
|
|
1669
|
+
}
|
|
1670
|
+
if (items.type === "object") {
|
|
1671
|
+
const templateChildren = [];
|
|
1672
|
+
const required22 = new Set(items.required ?? []);
|
|
1673
|
+
const childParent = `${path}.items`;
|
|
1674
|
+
for (const [childName, childSchema] of Object.entries(items.properties ?? {})) {
|
|
1675
|
+
const w = mapProperty(
|
|
1676
|
+
childName,
|
|
1677
|
+
unwrap(childSchema),
|
|
1678
|
+
required22.has(childName),
|
|
1679
|
+
childParent,
|
|
1680
|
+
unmapped
|
|
1681
|
+
);
|
|
1682
|
+
if (w) templateChildren.push(w);
|
|
1683
|
+
}
|
|
1684
|
+
if (!templateChildren.length) {
|
|
1685
|
+
unmapped.push({ path, reason: "Array of objects has no mappable item properties." });
|
|
1686
|
+
return null;
|
|
1687
|
+
}
|
|
1688
|
+
return {
|
|
1689
|
+
kind: "input",
|
|
1690
|
+
type: "repeater",
|
|
1691
|
+
path,
|
|
1692
|
+
label,
|
|
1693
|
+
props: {
|
|
1694
|
+
addLabel: `Add ${singular(label).toLowerCase()}`,
|
|
1695
|
+
removeLabel: "Remove",
|
|
1696
|
+
template: {
|
|
1697
|
+
kind: "layout",
|
|
1698
|
+
type: "flex",
|
|
1699
|
+
props: { direction: "column", gap: 8 },
|
|
1700
|
+
children: templateChildren
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
if (items.type === "string" || items.type === "number" || items.type === "integer") {
|
|
1706
|
+
const validator = buildArrayValidator(schema, required2);
|
|
1707
|
+
return cleanFields({
|
|
1708
|
+
kind: "input",
|
|
1709
|
+
type: "tags",
|
|
1710
|
+
path,
|
|
1711
|
+
label,
|
|
1712
|
+
validator,
|
|
1713
|
+
props: schema.description ? { placeholder: "Add and press Enter", hint: schema.description } : { placeholder: "Add and press Enter" }
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
unmapped.push({
|
|
1717
|
+
path,
|
|
1718
|
+
reason: "Array shape is not mappable (only arrays of objects → repeater, or arrays of strings/numbers → tags)."
|
|
1719
|
+
});
|
|
1720
|
+
return null;
|
|
1721
|
+
}
|
|
1722
|
+
function buildArrayValidator(s, required2) {
|
|
1723
|
+
const v = { type: "array" };
|
|
1724
|
+
let used = false;
|
|
1725
|
+
if (required2) {
|
|
1726
|
+
v.required = true;
|
|
1727
|
+
used = true;
|
|
1728
|
+
}
|
|
1729
|
+
if (typeof s.minItems === "number") {
|
|
1730
|
+
v.minItems = s.minItems;
|
|
1731
|
+
used = true;
|
|
1732
|
+
}
|
|
1733
|
+
if (typeof s.maxItems === "number") {
|
|
1734
|
+
v.maxItems = s.maxItems;
|
|
1735
|
+
used = true;
|
|
1736
|
+
}
|
|
1737
|
+
if (s.uniqueItems === true) {
|
|
1738
|
+
v.uniqueItems = true;
|
|
1739
|
+
used = true;
|
|
1740
|
+
}
|
|
1741
|
+
return used ? v : void 0;
|
|
1742
|
+
}
|
|
1743
|
+
function humanLabel(name) {
|
|
1744
|
+
if (!name) return "";
|
|
1745
|
+
return name.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/\s+/g, " ").trim().replace(/^./, (c) => c.toUpperCase());
|
|
1746
|
+
}
|
|
1747
|
+
function singular(label) {
|
|
1748
|
+
if (label.endsWith("ies") && label.length > 3) return label.slice(0, -3) + "y";
|
|
1749
|
+
if (label.endsWith("s") && !label.endsWith("ss")) return label.slice(0, -1);
|
|
1750
|
+
return label;
|
|
1751
|
+
}
|
|
1752
|
+
function cleanFields(o) {
|
|
1753
|
+
for (const k of Object.keys(o)) {
|
|
1754
|
+
if (o[k] === void 0) delete o[k];
|
|
1755
|
+
}
|
|
1756
|
+
return o;
|
|
1757
|
+
}
|
|
1758
|
+
function generateFromJsonSchema(input) {
|
|
1759
|
+
const opts = {
|
|
1760
|
+
submitAction: input.submitAction,
|
|
1761
|
+
submitLabel: input.submitLabel,
|
|
1762
|
+
layout: input.layout
|
|
1763
|
+
};
|
|
1764
|
+
const { formDefinition, unmapped } = jsonSchemaToGui(input.jsonSchema, opts);
|
|
1765
|
+
const validation = validateFormDefinition({ formDefinition });
|
|
1766
|
+
return { formDefinition, unmapped, validation };
|
|
1767
|
+
}
|
|
1768
|
+
const GENERATE_FROM_JSON_SCHEMA_TOOL = {
|
|
1769
|
+
name: "generate_from_json_schema",
|
|
1770
|
+
description: "Generate a GolemUI form definition from a JSON Schema describing the form data shape (typically an API request body or a Zod-derived schema). The result is validated against the GolemUI JSON Schemas before being returned, so it is guaranteed to be syntactically correct. Anything the mapper cannot handle is reported in `unmapped` rather than silently dropped — use that list to surface remaining work to the user.",
|
|
1771
|
+
inputSchema: {
|
|
1772
|
+
type: "object",
|
|
1773
|
+
properties: {
|
|
1774
|
+
jsonSchema: {
|
|
1775
|
+
type: "object",
|
|
1776
|
+
additionalProperties: true,
|
|
1777
|
+
description: "A standard JSON Schema. The top level must be an `object` schema with a `properties` map. Supports primitive types (string/number/integer/boolean) with formats and constraints, enums, nested objects, and arrays of objects (rendered as repeaters)."
|
|
1778
|
+
},
|
|
1779
|
+
submitAction: {
|
|
1780
|
+
type: "boolean",
|
|
1781
|
+
description: "Append a submit button. Defaults to true."
|
|
1782
|
+
},
|
|
1783
|
+
submitLabel: {
|
|
1784
|
+
type: "string",
|
|
1785
|
+
description: 'Label for the submit button when `submitAction` is true. Defaults to "Submit".'
|
|
1786
|
+
},
|
|
1787
|
+
layout: {
|
|
1788
|
+
type: "string",
|
|
1789
|
+
enum: ["vertical", "horizontal", "grid"],
|
|
1790
|
+
description: "Top-level layout. Defaults to vertical (one field per row)."
|
|
1791
|
+
}
|
|
1792
|
+
},
|
|
1793
|
+
required: ["jsonSchema"]
|
|
1794
|
+
}
|
|
1795
|
+
};
|
|
1796
|
+
const METHODS = ["get", "post", "put", "patch", "delete"];
|
|
1797
|
+
async function generateFromOpenapi(input) {
|
|
1798
|
+
if (!input.document && !input.documentUrl) {
|
|
1799
|
+
throw new Error("Provide either `document` (parsed OpenAPI object) or `documentUrl`.");
|
|
1800
|
+
}
|
|
1801
|
+
const doc = input.document ?? await fetchDoc(input.documentUrl);
|
|
1802
|
+
const resolved = findOperation(doc, input.operation);
|
|
1803
|
+
if (!resolved) {
|
|
1804
|
+
throw new Error(
|
|
1805
|
+
`Operation \`${input.operation}\` not found. Use the form "METHOD /path" (e.g. "POST /users") or an exact operationId.`
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
const { method, path, operation } = resolved;
|
|
1809
|
+
let bodySchema = operation.requestBody?.content?.["application/json"]?.schema;
|
|
1810
|
+
if (bodySchema) bodySchema = derefSchema(bodySchema, doc);
|
|
1811
|
+
let assembled;
|
|
1812
|
+
if (bodySchema && (bodySchema.type === "object" || bodySchema.properties)) {
|
|
1813
|
+
assembled = bodySchema;
|
|
1814
|
+
} else if (operation.parameters?.length) {
|
|
1815
|
+
assembled = paramsToSchema(operation.parameters, doc);
|
|
1816
|
+
} else {
|
|
1817
|
+
throw new Error(
|
|
1818
|
+
`Operation \`${method.toUpperCase()} ${path}\` has no JSON request body and no parameters — nothing to render as a form.`
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
const { formDefinition, unmapped } = jsonSchemaToGui(assembled, {
|
|
1822
|
+
submitAction: input.submitAction ?? true,
|
|
1823
|
+
submitLabel: input.submitLabel ?? defaultSubmitLabel(method, operation)
|
|
1824
|
+
});
|
|
1825
|
+
const validation = validateFormDefinition({ formDefinition });
|
|
1826
|
+
return {
|
|
1827
|
+
resolvedOperation: { method: method.toUpperCase(), path, operationId: operation.operationId },
|
|
1828
|
+
formDefinition,
|
|
1829
|
+
unmapped,
|
|
1830
|
+
validation
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
async function fetchDoc(url) {
|
|
1834
|
+
const res = await fetch(url);
|
|
1835
|
+
if (!res.ok) {
|
|
1836
|
+
throw new Error(
|
|
1837
|
+
`Failed to fetch OpenAPI document from ${url}: ${res.status} ${res.statusText}`
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
const text = await res.text();
|
|
1841
|
+
try {
|
|
1842
|
+
return JSON.parse(text);
|
|
1843
|
+
} catch {
|
|
1844
|
+
throw new Error(
|
|
1845
|
+
`Document at ${url} is not JSON. v1 only supports JSON OpenAPI docs — convert YAML before passing.`
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
function findOperation(doc, ref) {
|
|
1850
|
+
const trimmed = ref.trim();
|
|
1851
|
+
const methodPathMatch = trimmed.match(/^([A-Za-z]+)\s+(\/\S*)$/);
|
|
1852
|
+
if (methodPathMatch) {
|
|
1853
|
+
const method = methodPathMatch[1].toLowerCase();
|
|
1854
|
+
const path = methodPathMatch[2];
|
|
1855
|
+
if (!METHODS.includes(method)) {
|
|
1856
|
+
throw new Error(`Unsupported HTTP method \`${method.toUpperCase()}\`.`);
|
|
1857
|
+
}
|
|
1858
|
+
const op = doc.paths?.[path]?.[method];
|
|
1859
|
+
if (op) return { method, path, operation: op };
|
|
1860
|
+
return null;
|
|
1861
|
+
}
|
|
1862
|
+
for (const [path, methods] of Object.entries(doc.paths ?? {})) {
|
|
1863
|
+
for (const [method, op] of Object.entries(methods)) {
|
|
1864
|
+
if (METHODS.includes(method) && op.operationId === trimmed) {
|
|
1865
|
+
return { method, path, operation: op };
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return null;
|
|
1870
|
+
}
|
|
1871
|
+
function paramsToSchema(parameters, doc) {
|
|
1872
|
+
const properties2 = {};
|
|
1873
|
+
const required2 = [];
|
|
1874
|
+
for (const p of parameters) {
|
|
1875
|
+
const s = p.schema ? derefSchema(p.schema, doc) : { type: "string" };
|
|
1876
|
+
properties2[p.name] = { ...s, description: p.description ?? s.description };
|
|
1877
|
+
if (p.required) required2.push(p.name);
|
|
1878
|
+
}
|
|
1879
|
+
return { type: "object", properties: properties2, required: required2 };
|
|
1880
|
+
}
|
|
1881
|
+
function defaultSubmitLabel(method, op) {
|
|
1882
|
+
if (op.summary) return op.summary;
|
|
1883
|
+
const verbs = {
|
|
1884
|
+
post: "Create",
|
|
1885
|
+
put: "Update",
|
|
1886
|
+
patch: "Update",
|
|
1887
|
+
delete: "Delete",
|
|
1888
|
+
get: "Submit"
|
|
1889
|
+
};
|
|
1890
|
+
return verbs[method.toLowerCase()] ?? "Submit";
|
|
1891
|
+
}
|
|
1892
|
+
function derefSchema(schema, doc, depth = 0) {
|
|
1893
|
+
if (depth > 16) return schema;
|
|
1894
|
+
if (typeof schema.$ref === "string") {
|
|
1895
|
+
const target = resolveLocalRef(schema.$ref, doc);
|
|
1896
|
+
if (target) return derefSchema(target, doc, depth + 1);
|
|
1897
|
+
return schema;
|
|
1898
|
+
}
|
|
1899
|
+
const result = { ...schema };
|
|
1900
|
+
if (result.properties) {
|
|
1901
|
+
result.properties = Object.fromEntries(
|
|
1902
|
+
Object.entries(result.properties).map(([k, v]) => [k, derefSchema(v, doc, depth + 1)])
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
if (result.items) result.items = derefSchema(result.items, doc, depth + 1);
|
|
1906
|
+
if (result.oneOf) result.oneOf = result.oneOf.map((s) => derefSchema(s, doc, depth + 1));
|
|
1907
|
+
if (result.anyOf) result.anyOf = result.anyOf.map((s) => derefSchema(s, doc, depth + 1));
|
|
1908
|
+
return result;
|
|
1909
|
+
}
|
|
1910
|
+
function resolveLocalRef(ref, doc) {
|
|
1911
|
+
if (!ref.startsWith("#/")) return null;
|
|
1912
|
+
const parts = ref.slice(2).split("/");
|
|
1913
|
+
let cur = doc;
|
|
1914
|
+
for (const p of parts) {
|
|
1915
|
+
if (cur && typeof cur === "object") {
|
|
1916
|
+
cur = cur[p];
|
|
1917
|
+
} else {
|
|
1918
|
+
return null;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
return cur ?? null;
|
|
1922
|
+
}
|
|
1923
|
+
const GENERATE_FROM_OPENAPI_TOOL = {
|
|
1924
|
+
name: "generate_from_openapi",
|
|
1925
|
+
description: 'Generate a GolemUI form for a specific OpenAPI 3.x operation (e.g. "POST /users"). Resolves the operation\'s JSON request body, dereferences `$ref`s, then maps it to a form definition that is validated against the GolemUI JSON Schemas before being returned, so it is guaranteed syntactically correct. Falls back to operation parameters when no request body is present. Anything the mapper cannot handle is reported in `unmapped` rather than silently dropped — use that list to surface remaining work to the user. Pass either a parsed `document` or a `documentUrl` to fetch.',
|
|
1926
|
+
inputSchema: {
|
|
1927
|
+
type: "object",
|
|
1928
|
+
properties: {
|
|
1929
|
+
documentUrl: {
|
|
1930
|
+
type: "string",
|
|
1931
|
+
description: "URL of a JSON OpenAPI document. Either this or `document` is required."
|
|
1932
|
+
},
|
|
1933
|
+
document: {
|
|
1934
|
+
type: "object",
|
|
1935
|
+
additionalProperties: true,
|
|
1936
|
+
description: "Parsed OpenAPI document (JSON object). Either this or `documentUrl` is required."
|
|
1937
|
+
},
|
|
1938
|
+
operation: {
|
|
1939
|
+
type: "string",
|
|
1940
|
+
description: 'The operation to generate a form for. Either "METHOD /path" (e.g. "POST /users") or an exact operationId.'
|
|
1941
|
+
},
|
|
1942
|
+
submitAction: {
|
|
1943
|
+
type: "boolean",
|
|
1944
|
+
description: "Append a submit button. Defaults to true."
|
|
1945
|
+
},
|
|
1946
|
+
submitLabel: {
|
|
1947
|
+
type: "string",
|
|
1948
|
+
description: "Label for the submit button. Defaults to the operation summary or a verb derived from the HTTP method."
|
|
1949
|
+
}
|
|
1950
|
+
},
|
|
1951
|
+
required: ["operation"]
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1954
|
+
const EXAMPLES = {
|
|
1955
|
+
accordion: {
|
|
1956
|
+
kind: "layout",
|
|
1957
|
+
type: "accordion",
|
|
1958
|
+
props: {
|
|
1959
|
+
sections: [
|
|
1960
|
+
{ uid: "personal", label: "Personal" },
|
|
1961
|
+
{ uid: "preferences", label: "Preferences" }
|
|
1962
|
+
]
|
|
1963
|
+
},
|
|
1964
|
+
children: [
|
|
1965
|
+
{ kind: "input", type: "textinput", path: "firstName", label: "First name" },
|
|
1966
|
+
{ kind: "input", type: "checkbox", path: "newsletter", label: "Newsletter" }
|
|
1967
|
+
]
|
|
1968
|
+
},
|
|
1969
|
+
textinput: {
|
|
1970
|
+
kind: "input",
|
|
1971
|
+
type: "textinput",
|
|
1972
|
+
path: "firstName",
|
|
1973
|
+
label: "First name",
|
|
1974
|
+
validator: { type: "string", required: true, minLength: 1 },
|
|
1975
|
+
props: { placeholder: "Jane" }
|
|
1976
|
+
},
|
|
1977
|
+
textarea: {
|
|
1978
|
+
kind: "input",
|
|
1979
|
+
type: "textarea",
|
|
1980
|
+
path: "bio",
|
|
1981
|
+
label: "Bio",
|
|
1982
|
+
props: { minimumHeight: 120, autoGrow: true }
|
|
1983
|
+
},
|
|
1984
|
+
tags: {
|
|
1985
|
+
kind: "input",
|
|
1986
|
+
type: "tags",
|
|
1987
|
+
path: "keywords",
|
|
1988
|
+
label: "Keywords",
|
|
1989
|
+
defaultValue: [],
|
|
1990
|
+
props: {
|
|
1991
|
+
placeholder: "Add a keyword and press Enter",
|
|
1992
|
+
separators: ["Enter", ","],
|
|
1993
|
+
trim: true,
|
|
1994
|
+
allowDuplicates: false,
|
|
1995
|
+
limit: 10
|
|
1996
|
+
},
|
|
1997
|
+
validator: {
|
|
1998
|
+
type: "array",
|
|
1999
|
+
minItems: 1,
|
|
2000
|
+
maxItems: 10
|
|
2001
|
+
}
|
|
2002
|
+
},
|
|
2003
|
+
password: {
|
|
2004
|
+
kind: "input",
|
|
2005
|
+
type: "password",
|
|
2006
|
+
path: "password",
|
|
2007
|
+
label: "Password",
|
|
2008
|
+
validator: { type: "string", required: true, minLength: 8 }
|
|
2009
|
+
},
|
|
2010
|
+
number: {
|
|
2011
|
+
kind: "input",
|
|
2012
|
+
type: "number",
|
|
2013
|
+
path: "age",
|
|
2014
|
+
label: "Age",
|
|
2015
|
+
validator: { type: "integer", required: true, minimum: 0 }
|
|
2016
|
+
},
|
|
2017
|
+
currency: {
|
|
2018
|
+
kind: "input",
|
|
2019
|
+
type: "currency",
|
|
2020
|
+
path: "salary",
|
|
2021
|
+
label: "Salary",
|
|
2022
|
+
props: { currency: "USD" }
|
|
2023
|
+
},
|
|
2024
|
+
checkbox: {
|
|
2025
|
+
kind: "input",
|
|
2026
|
+
type: "checkbox",
|
|
2027
|
+
path: "termsAccepted",
|
|
2028
|
+
label: "I accept the terms",
|
|
2029
|
+
validator: { type: "boolean", required: true, const: true }
|
|
2030
|
+
},
|
|
2031
|
+
toggle: {
|
|
2032
|
+
kind: "input",
|
|
2033
|
+
type: "toggle",
|
|
2034
|
+
path: "notifications",
|
|
2035
|
+
label: "Email notifications"
|
|
2036
|
+
},
|
|
2037
|
+
dropdown: {
|
|
2038
|
+
kind: "input",
|
|
2039
|
+
type: "dropdown",
|
|
2040
|
+
path: "country",
|
|
2041
|
+
label: "Country",
|
|
2042
|
+
props: {
|
|
2043
|
+
labelField: "label",
|
|
2044
|
+
valueField: "value",
|
|
2045
|
+
items: [
|
|
2046
|
+
{ label: "United States", value: "us", flag: "🇺🇸" },
|
|
2047
|
+
{ label: "Canada", value: "ca", flag: "🇨🇦" }
|
|
2048
|
+
]
|
|
2049
|
+
}
|
|
2050
|
+
},
|
|
2051
|
+
select: {
|
|
2052
|
+
kind: "input",
|
|
2053
|
+
type: "select",
|
|
2054
|
+
path: "plan",
|
|
2055
|
+
label: "Plan",
|
|
2056
|
+
props: {
|
|
2057
|
+
options: [
|
|
2058
|
+
{ label: "Free", value: "free" },
|
|
2059
|
+
{ label: "Pro", value: "pro" }
|
|
2060
|
+
]
|
|
2061
|
+
}
|
|
2062
|
+
},
|
|
2063
|
+
radiogroup: {
|
|
2064
|
+
kind: "input",
|
|
2065
|
+
type: "radiogroup",
|
|
2066
|
+
path: "shippingSpeed",
|
|
2067
|
+
label: "Shipping",
|
|
2068
|
+
props: {
|
|
2069
|
+
options: [
|
|
2070
|
+
{ label: "Standard", value: "standard" },
|
|
2071
|
+
{ label: "Express", value: "express" }
|
|
2072
|
+
]
|
|
2073
|
+
}
|
|
2074
|
+
},
|
|
2075
|
+
dateInput: {
|
|
2076
|
+
kind: "input",
|
|
2077
|
+
type: "dateInput",
|
|
2078
|
+
path: "birthDate",
|
|
2079
|
+
label: "Birth date"
|
|
2080
|
+
},
|
|
2081
|
+
datePicker: {
|
|
2082
|
+
kind: "input",
|
|
2083
|
+
type: "datePicker",
|
|
2084
|
+
path: "startDate",
|
|
2085
|
+
label: "Start date"
|
|
2086
|
+
},
|
|
2087
|
+
calendar: {
|
|
2088
|
+
kind: "input",
|
|
2089
|
+
type: "calendar",
|
|
2090
|
+
path: "eventDate",
|
|
2091
|
+
label: "Event date"
|
|
2092
|
+
},
|
|
2093
|
+
button: {
|
|
2094
|
+
kind: "action",
|
|
2095
|
+
type: "button",
|
|
2096
|
+
actionType: "submit",
|
|
2097
|
+
label: "Submit",
|
|
2098
|
+
props: { variant: "filled" }
|
|
2099
|
+
},
|
|
2100
|
+
alert: {
|
|
2101
|
+
kind: "display",
|
|
2102
|
+
type: "alert",
|
|
2103
|
+
props: { level: "info", text: "Heads up!" }
|
|
2104
|
+
},
|
|
2105
|
+
markdownText: {
|
|
2106
|
+
kind: "display",
|
|
2107
|
+
type: "markdownText",
|
|
2108
|
+
props: { md: "Welcome to the form." }
|
|
2109
|
+
},
|
|
2110
|
+
flex: {
|
|
2111
|
+
kind: "layout",
|
|
2112
|
+
type: "flex",
|
|
2113
|
+
props: { direction: "column", gap: 12 },
|
|
2114
|
+
children: [
|
|
2115
|
+
{
|
|
2116
|
+
kind: "input",
|
|
2117
|
+
type: "textinput",
|
|
2118
|
+
path: "firstName",
|
|
2119
|
+
label: "First name"
|
|
2120
|
+
}
|
|
2121
|
+
]
|
|
2122
|
+
},
|
|
2123
|
+
grid: {
|
|
2124
|
+
kind: "layout",
|
|
2125
|
+
type: "grid",
|
|
2126
|
+
props: { columnGap: 12, rowGap: 12 },
|
|
2127
|
+
children: [
|
|
2128
|
+
{ kind: "input", type: "textinput", path: "firstName", label: "First name" },
|
|
2129
|
+
{ kind: "input", type: "textinput", path: "lastName", label: "Last name" }
|
|
2130
|
+
]
|
|
2131
|
+
},
|
|
2132
|
+
tabs: {
|
|
2133
|
+
kind: "layout",
|
|
2134
|
+
type: "tabs",
|
|
2135
|
+
props: {
|
|
2136
|
+
tabs: [
|
|
2137
|
+
{ uid: "personal", label: "Personal" },
|
|
2138
|
+
{ uid: "address", label: "Address" }
|
|
2139
|
+
]
|
|
2140
|
+
},
|
|
2141
|
+
children: [
|
|
2142
|
+
{ kind: "input", type: "textinput", path: "firstName", label: "First name" },
|
|
2143
|
+
{ kind: "input", type: "textinput", path: "street", label: "Street" }
|
|
2144
|
+
]
|
|
2145
|
+
},
|
|
2146
|
+
repeater: {
|
|
2147
|
+
kind: "input",
|
|
2148
|
+
type: "repeater",
|
|
2149
|
+
path: "addresses",
|
|
2150
|
+
label: "Addresses",
|
|
2151
|
+
props: {
|
|
2152
|
+
addLabel: "Add address",
|
|
2153
|
+
removeLabel: "Remove",
|
|
2154
|
+
template: {
|
|
2155
|
+
kind: "layout",
|
|
2156
|
+
type: "flex",
|
|
2157
|
+
props: { direction: "column" },
|
|
2158
|
+
children: [
|
|
2159
|
+
// Child paths inside a repeater template MUST be `<repeater.path>.items.<field>` —
|
|
2160
|
+
// `items` is the reserved segment that the runtime expands per array entry.
|
|
2161
|
+
{ kind: "input", type: "textinput", path: "addresses.items.street", label: "Street" },
|
|
2162
|
+
{ kind: "input", type: "textinput", path: "addresses.items.city", label: "City" }
|
|
2163
|
+
]
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
const NOTES = {
|
|
2169
|
+
textinput: [
|
|
2170
|
+
"`path` is the dot-path into form data this field writes to.",
|
|
2171
|
+
"`validator.format` supports: `email`, `hostname`, `ipv4`, `ipv6`, `url`, `uuid`, `date`, `time`, `date-time`, `duration`.",
|
|
2172
|
+
'Root props `label`, `disabled`, `readonly`, `validator`, and `size` accept state suffixes — e.g. `"label.<stateName>": "New label"` overrides the label only when that named state is active. Props inside `props` (e.g. `hint`, `placeholder`) also accept suffixes as `"hint.<stateName>"`. Call `get_concept({ concept: "states" })` for the full pattern.',
|
|
2173
|
+
'`props.icon` accepts a Google Material Icons ligature name (e.g. `"search"`, `"email"`, `"lock"`) to display a decorative icon inside the input field. Supports state suffix: `"icon.<stateName>": "check"` to swap the icon when a state is active. Call `get_concept({ concept: "icons" })` for setup and the full list of icon-capable widgets.'
|
|
2174
|
+
],
|
|
2175
|
+
markdownText: [
|
|
2176
|
+
"Display-only widget for rendering markdown. Can be used as a top-level form widget (inside any layout) or inside templates like `dropdown.props.items[].template`.",
|
|
2177
|
+
"The required prop is `md` (the markdown string), not `text`."
|
|
2178
|
+
],
|
|
2179
|
+
tags: [
|
|
2180
|
+
"Use `tags` for free-form arrays of primitive values (typically `string[]`) — e.g. keywords, email lists, hashtags. Backing data is `string[]`. For arrays of structured objects, use `repeater` instead.",
|
|
2181
|
+
'`props.separators` controls which keys/characters commit a new tag. Defaults emit on `Enter` and `,`; you can also include `"Tab"` or `"blur"`. `props.trim` strips whitespace from each tag; `props.allowDuplicates: false` rejects repeats; `props.limit` caps the array length.',
|
|
2182
|
+
'Validate with an `arrayValidator`: `{ type: "array", minItems, maxItems, uniqueItems }`. The `tags` widget complements that with UI-level enforcement (`allowDuplicates`, `limit`) but the validator is still authoritative for form-level required/min/max constraints.'
|
|
2183
|
+
],
|
|
2184
|
+
dropdown: [
|
|
2185
|
+
"PREFER `select` for plain label/value lists (countries, plans, sizes). `dropdown` is for richer item shapes: when each item has extra fields (icons, flags, metadata) that an `itemRenderer` or `labelField`/`valueField` can use.",
|
|
2186
|
+
'`props.items` is EITHER primitives `["one", 2, ...]` OR arbitrary objects `[{ label, value, ...extras }]`. Pair the object form with `labelField`/`valueField` to tell the dropdown which fields to render as label and which to use as the value. **Widget templates inside items (the `{template, value}` shape) are NOT supported — only `repeater` accepts widget templates.**'
|
|
2187
|
+
],
|
|
2188
|
+
select: [
|
|
2189
|
+
'`props.options[]` is the simple form: `[{ label: "United States", value: "us" }, ...]`. Use this for ANY plain text list — countries, plans, sizes, status enums. Only switch to `dropdown` if you need custom per-item rendering (icons, flags, rich layouts).',
|
|
2190
|
+
"For very large lists (>50 items) consider `dropdown` for its virtualization (`height`, `itemHeight`, `searchFields`)."
|
|
2191
|
+
],
|
|
2192
|
+
flex: [
|
|
2193
|
+
"Use `flex` for page scaffolding and directional grouping: a column stack of sections, a row of side-by-side panels, a row of action buttons. For field-level layout (inputs that should sit in columns with aligned labels), prefer `grid` instead.",
|
|
2194
|
+
'`props.direction`: `"row"` (default) | `"column"` | `"row-reverse"` | `"column-reverse"` — controls the main axis. `"column"` stacks children vertically; `"row"` places them side by side.',
|
|
2195
|
+
"`props.gap`: number (pixels) — uniform spacing between all children along the main axis.",
|
|
2196
|
+
'`props.justify`: `"center"` | `"start"` | `"end"` | `"stretch"` — aligns children along the cross-axis (perpendicular to `direction`).',
|
|
2197
|
+
'`props.align`: `"center"` | `"start"` | `"end"` | `"space-between"` | `"space-around"` | `"space-evenly"` — distributes children along the main axis.',
|
|
2198
|
+
'All `flex` props support state suffixes: `"direction.<stateName>": "column"` swaps the direction when a state is active. Call `get_concept({ concept: "states" })` for the full pattern.'
|
|
2199
|
+
],
|
|
2200
|
+
grid: [
|
|
2201
|
+
"Use `grid` when you want multiple form fields to sit side by side with their labels and inputs aligned across columns. GolemUI's `grid` uses CSS subgrid internally: each child widget gets two implicit sub-tracks (one for its label, one for its input) that align to the parent grid columns, giving consistent label/input alignment across all rows without manual sizing.",
|
|
2202
|
+
"For loose page scaffolding — stacking sections, wrapping a group in a header — use `flex` instead.",
|
|
2203
|
+
"`props.columnGap`: number (pixels) — horizontal gap between columns.",
|
|
2204
|
+
"`props.rowGap`: number (pixels) — vertical gap between rows.",
|
|
2205
|
+
"`props.autoFit`: boolean — when `true`, the grid auto-fits as many columns as will fit in the available container width, using `columnGap` as the gutter. Useful for responsive layouts where the number of columns should adapt to the viewport.",
|
|
2206
|
+
'`props.direction`: `"row"` | `"column"` — controls how children flow into the grid tracks.',
|
|
2207
|
+
"`props.align` and `props.justify` accept the same values as `flex`.",
|
|
2208
|
+
'All `grid` props support state suffixes. Call `get_concept({ concept: "states" })` for the full pattern.'
|
|
2209
|
+
],
|
|
2210
|
+
tabs: [
|
|
2211
|
+
"Children are associated with tabs by **`uid` matching**, not by array order: each direct child must have a `uid` field at the widget level (alongside `kind`/`type`) whose string value equals one of the `props.tabs[].uid` entries. There is no `tag` property — that is not a real GolemUI field.",
|
|
2212
|
+
"Children are typically written in display order for readability, but rearranging them does not change which tab they belong to — only the `uid` string match does."
|
|
2213
|
+
],
|
|
2214
|
+
accordion: [
|
|
2215
|
+
"Children are associated with sections by **`uid` matching** (same pattern as `tabs`): each direct child must have a `uid` field at the widget level whose value equals one of the `props.sections[].uid` entries. There is no `tag` property.",
|
|
2216
|
+
"`props.defaultOpen` is a map of `{ <sectionUid>: boolean }` controlling which sections start expanded."
|
|
2217
|
+
],
|
|
2218
|
+
repeater: [
|
|
2219
|
+
"`props.template` must be a layout widget (flex/grid/tabs/accordion) whose children are the per-item fields.",
|
|
2220
|
+
'Child paths inside the template MUST follow the form `<repeater.path>.items.<fieldName>`. The `items` segment is reserved — the runtime substitutes it with the current array index per row. For example, a repeater at `path: "users"` with a child `firstName` uses `path: "users.items.firstName"`. Plain `firstName` will NOT bind to the array.',
|
|
2221
|
+
'Nested repeaters chain the convention: a repeater at `path: "teams"` whose template contains a repeater at `path: "teams.items.members"` whose children use `path: "teams.items.members.items.<field>"`.',
|
|
2222
|
+
'`addLabel` supports state suffixes: `"addLabel.<stateName>": "Limit reached"` swaps the add-button label when a named state is active — useful for capping array length. Call `get_concept({ concept: "states" })` for the full pattern.'
|
|
2223
|
+
],
|
|
2224
|
+
button: [
|
|
2225
|
+
"`actionType` controls the button's role. `actionType: \"submit\"` makes the button fire the form's `formSubmit` event natively — the host listens for it via `(formSubmit)` (Angular), `@formSubmit` (Vue), `onFormSubmit` (React), or the `form-submit` event (Lit). No custom handler needed. Use this for the primary submit button on a form.",
|
|
2226
|
+
'`actionType: "button"` (the default, can be omitted) is a regular action button. Wire it via `on.click: "<handlerName>"` where `<handlerName>` is registered in the form config\'s event handlers.',
|
|
2227
|
+
'Supports state-suffixed props: `"label.<stateName>"` and `"disabled.<stateName>"` swap the label or disabled state when a named state is active — e.g. disable the submit button until terms are accepted, then re-enable it. Call `get_concept({ concept: "states" })` for the full pattern.',
|
|
2228
|
+
'`props.icon` accepts a Google Material Icons ligature name (e.g. `"send"`, `"check"`, `"arrow_forward"`) to render an icon on the button. `props.iconPosition` controls placement: `"left"` (default) or `"right"`. Both support state suffixes: `"icon.<stateName>": "hourglass_empty"` swaps the icon while a state is active. Call `get_concept({ concept: "icons" })` for setup instructions and the full list of icon-capable widgets.'
|
|
2229
|
+
],
|
|
2230
|
+
checkbox: ["Set `validator.const: true` to require the user to tick it (e.g. terms acceptance)."],
|
|
2231
|
+
alert: [
|
|
2232
|
+
'Use `include: { in: ["stateName"] }` to show this alert only when a named state is active, or `exclude: { from: ["stateName"] }` to hide it when a state is active. This is cleaner than `include: { when: "..." }` when the same condition is reused across multiple widgets. Call `get_concept({ concept: "states" })` for the full states pattern.',
|
|
2233
|
+
'Props inside `props` (e.g. `text`, `level`) accept state suffixes: `"text.<stateName>": "New message"` swaps the message when that state is active.'
|
|
2234
|
+
]
|
|
2235
|
+
};
|
|
2236
|
+
function synthesizeExample(widgetType, schema) {
|
|
2237
|
+
const props = schema["properties"];
|
|
2238
|
+
const kindProp = props?.["kind"]?.const ?? "input";
|
|
2239
|
+
const ex = { kind: kindProp, type: widgetType };
|
|
2240
|
+
if (props?.["path"]) ex["path"] = "field";
|
|
2241
|
+
if (props?.["label"]) ex["label"] = "Field";
|
|
2242
|
+
return ex;
|
|
2243
|
+
}
|
|
2244
|
+
function getWidgetSpec(input) {
|
|
2245
|
+
const schema = COMPONENT_SCHEMAS[input.widgetType];
|
|
2246
|
+
if (!schema) {
|
|
2247
|
+
const known = Object.keys(COMPONENT_SCHEMAS).sort().join(", ");
|
|
2248
|
+
throw new Error(`Unknown widget type \`${input.widgetType}\`. Known: ${known}.`);
|
|
2249
|
+
}
|
|
2250
|
+
const props = schema["properties"];
|
|
2251
|
+
const kindProp = props?.["kind"]?.const ?? "unknown";
|
|
2252
|
+
return {
|
|
2253
|
+
widgetType: input.widgetType,
|
|
2254
|
+
schema,
|
|
2255
|
+
kind: kindProp,
|
|
2256
|
+
example: EXAMPLES[input.widgetType] ?? synthesizeExample(input.widgetType, schema),
|
|
2257
|
+
notes: NOTES[input.widgetType] ?? []
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
const GET_WIDGET_SPEC_TOOL = {
|
|
2261
|
+
name: "get_widget_spec",
|
|
2262
|
+
description: "Look up the JSON Schema and a minimal working example for a single GolemUI widget. Use this when you need to know which `props` a widget accepts, what `kind` value it uses, or what shape its `validator` takes. Cheaper than dumping the whole API into context.",
|
|
2263
|
+
inputSchema: {
|
|
2264
|
+
type: "object",
|
|
2265
|
+
properties: {
|
|
2266
|
+
widgetType: {
|
|
2267
|
+
type: "string",
|
|
2268
|
+
description: "The widget `type` constant. One of: " + Object.keys(COMPONENT_SCHEMAS).sort().map((t) => `\`${t}\``).join(", ") + "."
|
|
2269
|
+
}
|
|
2270
|
+
},
|
|
2271
|
+
required: ["widgetType"]
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2274
|
+
const STATES_CONCEPT = {
|
|
2275
|
+
concept: "states",
|
|
2276
|
+
summary: "States are named boolean conditions declared at the form root. Each state name maps to a reactive expression string (using `$form`, `$meta`, or `$formIsInvalid`) that the runtime evaluates continuously as the user interacts with the form. Once declared, state names can gate widget visibility (include / exclude) and swap individual widget properties per-state — a capability unique to named states that has no inline `when` equivalent.",
|
|
2277
|
+
patterns: [
|
|
2278
|
+
{
|
|
2279
|
+
name: "Declare states at the form root",
|
|
2280
|
+
description: 'Add a `"states"` object to the top-level form definition. Each key is a state name; each value is a reactive expression string. Expressions are evaluated at runtime — they have access to `$form` (all current form values), `$meta` (host-supplied metadata), and `$formIsInvalid`. State names can contain letters, numbers, hyphens, and underscores. Colons enable hierarchical composition — see the "Composed sub-states (colon notation)" pattern below.',
|
|
2281
|
+
example: {
|
|
2282
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2283
|
+
states: {
|
|
2284
|
+
termsAccepted: "$form.terms === true",
|
|
2285
|
+
hasDiscount: "$form.hasDiscountCode === true",
|
|
2286
|
+
limitReached: "$form.users?.length === 5"
|
|
2287
|
+
},
|
|
2288
|
+
form: ["/* ... widgets ... */"]
|
|
2289
|
+
}
|
|
2290
|
+
},
|
|
2291
|
+
{
|
|
2292
|
+
name: "Composed sub-states (colon notation)",
|
|
2293
|
+
description: 'Colons in a state name denote hierarchy: `"register"`, `"register:adult"`, `"register:minor:tall"`. At runtime the form engine rewrites every child expression by ANDing the full ancestor chain in front of it. This means you write only the *incremental* condition in a child — the parent conditions are inherited automatically. A child state is active only when ALL of its ancestors are also active.',
|
|
2294
|
+
example: {
|
|
2295
|
+
// What you write in the form definition:
|
|
2296
|
+
statesAsAuthored: {
|
|
2297
|
+
register: "$form.agreeTerms === true",
|
|
2298
|
+
"register:adult": "$form.user?.age >= 18",
|
|
2299
|
+
"register:minor": "$form.user?.age < 18",
|
|
2300
|
+
"register:minor:tall": "$form.user?.height > 180"
|
|
2301
|
+
},
|
|
2302
|
+
// What the runtime actually evaluates (expandStateExpressions output):
|
|
2303
|
+
statesAtRuntime: {
|
|
2304
|
+
register: "($form.agreeTerms === true)",
|
|
2305
|
+
"register:adult": "($form.agreeTerms === true) && ($form.user?.age >= 18)",
|
|
2306
|
+
"register:minor": "($form.agreeTerms === true) && ($form.user?.age < 18)",
|
|
2307
|
+
"register:minor:tall": "($form.agreeTerms === true) && ($form.user?.age < 18) && ($form.user?.height > 180)"
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
name: "Conditional rendering with include / exclude (named-state form)",
|
|
2313
|
+
description: 'Use `"include": { "in": ["stateName"] }` on any widget to render it only when the named state is active. Use `"exclude": { "from": ["stateName"] }` to render it only when the state is NOT active. Both `in` and `from` are arrays — a widget can be gated on multiple states simultaneously.',
|
|
2314
|
+
example: {
|
|
2315
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2316
|
+
states: {
|
|
2317
|
+
hasDiscount: "$form.hasDiscountCode === true"
|
|
2318
|
+
},
|
|
2319
|
+
form: [
|
|
2320
|
+
{
|
|
2321
|
+
kind: "input",
|
|
2322
|
+
type: "checkbox",
|
|
2323
|
+
path: "hasDiscountCode",
|
|
2324
|
+
label: "I have a discount code"
|
|
2325
|
+
},
|
|
2326
|
+
{
|
|
2327
|
+
kind: "input",
|
|
2328
|
+
type: "textinput",
|
|
2329
|
+
path: "discountCode",
|
|
2330
|
+
label: "Discount code",
|
|
2331
|
+
include: { in: ["hasDiscount"] }
|
|
2332
|
+
},
|
|
2333
|
+
{
|
|
2334
|
+
kind: "display",
|
|
2335
|
+
type: "alert",
|
|
2336
|
+
props: { text: "No discount applied." },
|
|
2337
|
+
exclude: { from: ["hasDiscount"] }
|
|
2338
|
+
}
|
|
2339
|
+
]
|
|
2340
|
+
}
|
|
2341
|
+
},
|
|
2342
|
+
{
|
|
2343
|
+
name: "Inline conditional rendering with `when`",
|
|
2344
|
+
description: 'For a one-off condition that applies to a single widget only, use the inline `when` form: `"include": { "when": "expression" }` or `"exclude": { "when": "expression" }`. The expression is evaluated exactly like a state expression — it has access to `$form`, `$meta`, and `$formIsInvalid`, uses the same safe operator subset, and must use optional chaining for nested fields. No entry in the root `states` map is required. Use this form when the condition is not shared with any other widget; use the named-state form (`in`/`from`) when the same condition gates two or more widgets.',
|
|
2345
|
+
example: {
|
|
2346
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2347
|
+
form: [
|
|
2348
|
+
{
|
|
2349
|
+
kind: "input",
|
|
2350
|
+
type: "checkbox",
|
|
2351
|
+
path: "agreeTerms",
|
|
2352
|
+
label: "I agree to the terms"
|
|
2353
|
+
},
|
|
2354
|
+
{
|
|
2355
|
+
kind: "action",
|
|
2356
|
+
type: "button",
|
|
2357
|
+
actionType: "submit",
|
|
2358
|
+
label: "Continue",
|
|
2359
|
+
// Show only after terms are accepted — one-off condition, no state needed.
|
|
2360
|
+
include: { when: "$form.agreeTerms === true" }
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
kind: "input",
|
|
2364
|
+
type: "textinput",
|
|
2365
|
+
path: "companyName",
|
|
2366
|
+
label: "Company name",
|
|
2367
|
+
// Hide when the user selects "individual" account type — one-off condition.
|
|
2368
|
+
exclude: { when: "$form.accountType === 'individual'" }
|
|
2369
|
+
}
|
|
2370
|
+
]
|
|
2371
|
+
}
|
|
2372
|
+
},
|
|
2373
|
+
{
|
|
2374
|
+
name: "State-suffixed props",
|
|
2375
|
+
description: 'Override individual widget properties when a named state is active by appending `".<stateName>"` to the property key. The unsuffixed key holds the default value; each suffixed key supplies the override for that state. This works on root-level widget properties (`label`, `disabled`, `readonly`, `validator`, `size`) AND on any key inside the `props` object (`hint`, `placeholder`, `items`, `addLabel`, etc.). There is NO inline `when` equivalent for this — state-suffixed props REQUIRE a named state. Multiple suffixes can coexist on the same property; when more than one state is active, the last matching suffix in document order wins.',
|
|
2376
|
+
example: {
|
|
2377
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2378
|
+
states: {
|
|
2379
|
+
termsAccepted: "$form.terms === true",
|
|
2380
|
+
busy: "$meta.submitting === true"
|
|
2381
|
+
},
|
|
2382
|
+
form: [
|
|
2383
|
+
{
|
|
2384
|
+
kind: "input",
|
|
2385
|
+
type: "checkbox",
|
|
2386
|
+
path: "terms",
|
|
2387
|
+
label: "I accept the terms"
|
|
2388
|
+
},
|
|
2389
|
+
{
|
|
2390
|
+
kind: "action",
|
|
2391
|
+
type: "button",
|
|
2392
|
+
label: "Submit",
|
|
2393
|
+
"label.termsAccepted": "Submit ✓",
|
|
2394
|
+
"label.busy": "Submitting…",
|
|
2395
|
+
disabled: true,
|
|
2396
|
+
"disabled.termsAccepted": false,
|
|
2397
|
+
"disabled.busy": true
|
|
2398
|
+
},
|
|
2399
|
+
{
|
|
2400
|
+
kind: "display",
|
|
2401
|
+
type: "alert",
|
|
2402
|
+
props: {
|
|
2403
|
+
level: "warning",
|
|
2404
|
+
text: "Please accept the terms to continue.",
|
|
2405
|
+
"text.termsAccepted": "Ready to submit!"
|
|
2406
|
+
},
|
|
2407
|
+
exclude: { from: ["busy"] }
|
|
2408
|
+
},
|
|
2409
|
+
{
|
|
2410
|
+
kind: "input",
|
|
2411
|
+
type: "repeater",
|
|
2412
|
+
path: "users",
|
|
2413
|
+
label: "Users",
|
|
2414
|
+
addLabel: "Add user",
|
|
2415
|
+
"addLabel.limitReached": "Limit reached — can't add more",
|
|
2416
|
+
props: {
|
|
2417
|
+
removeLabel: "Remove",
|
|
2418
|
+
template: {
|
|
2419
|
+
kind: "layout",
|
|
2420
|
+
type: "flex",
|
|
2421
|
+
props: { direction: "column" },
|
|
2422
|
+
children: [
|
|
2423
|
+
{ kind: "input", type: "textinput", path: "users.items.name", label: "Name" }
|
|
2424
|
+
]
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
]
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
],
|
|
2432
|
+
rules: [
|
|
2433
|
+
'Every state name used in `include.in`, `exclude.from`, or as a property suffix MUST be declared in the root `"states"` map.',
|
|
2434
|
+
"Child state expressions must contain only the *additional* (incremental) condition — ancestor conditions are ANDed in automatically by the runtime. Duplicating a parent condition in a child expression is wrong and redundant.",
|
|
2435
|
+
'A sub-state is only ever active when all of its ancestor states are also active. When using `include.in: ["register:adult"]` you do NOT need to also add `"register"` to the array.',
|
|
2436
|
+
"State-suffixed root props — only these support suffixes at the widget root level: `label`, `disabled`, `readonly`, `validator`, `size`. All other overridable properties live inside `props`.",
|
|
2437
|
+
'State-suffixed props inside `props` — any key inside the `props` object can be suffixed: `"hint.<state>"`, `"placeholder.<state>"`, `"items.<state>"`, `"addLabel.<state>"`, etc.',
|
|
2438
|
+
'Suffix names must not contain dots (the dot is the separator between property and state name): `"label.myState"` ✅, `"label.register:adult"` ✅ — `"label.my.state"` ❌.',
|
|
2439
|
+
"Reactive expressions must reference `$form`, `$meta`, or `$formIsInvalid`. A bare identifier like `termsAccepted` without a root reference is invalid.",
|
|
2440
|
+
"Use `===` / `!==` for equality, `&&` / `||` for logic. Avoid `=` (assignment), `==`/`!=` (loose equality), or bitwise `&`/`|`.",
|
|
2441
|
+
'When multiple states are active at the same time and a property has more than one matching suffix, the longest state name wins (most specific takes priority). Example: if both `register` and `register:adult` are active, `"label.register:adult"` overrides `"label.register"`.',
|
|
2442
|
+
'`include.in` and `exclude.from` each accept an Array of state names. A widget included `in: ["a", "b"]` renders when state `a` OR state `b` is active.',
|
|
2443
|
+
"Use optional chaining (`?.`) when accessing nested fields that may not yet exist in the form data: `$form.user?.age >= 18` not `$form.user.age >= 18`."
|
|
2444
|
+
]
|
|
2445
|
+
};
|
|
2446
|
+
const STRING_INTERPOLATION_CONCEPT = {
|
|
2447
|
+
concept: "string-interpolation",
|
|
2448
|
+
summary: "GolemUI supports live data binding in text props via `{{expression}}` template slots. Any string-valued property (e.g. `props.text`, `props.hint`, `label`) can embed one or more `{{...}}` slots. Each slot is a JavaScript-like expression evaluated against the live form state using a safe subset of JavaScript (no side effects, no function calls). i18n translation `params` objects accept a matching bare-expression format — the same expressions but without the `{{}}` delimiters.",
|
|
2449
|
+
patterns: [
|
|
2450
|
+
{
|
|
2451
|
+
name: "Template slots in display text",
|
|
2452
|
+
description: "Embed `{{expression}}` in any string prop to inject live values. Available scopes: `$form` (all current field values), `$meta` (host-supplied metadata), `$errors` (current validation error messages keyed by field uid), `$formIsInvalid` (boolean — `true` when any field currently fails validation). Use optional chaining (`?.`) when accessing nested fields that may not yet exist. Multiple slots can appear in a single string.",
|
|
2453
|
+
example: {
|
|
2454
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2455
|
+
form: [
|
|
2456
|
+
{
|
|
2457
|
+
uid: "userName",
|
|
2458
|
+
kind: "input",
|
|
2459
|
+
type: "textinput",
|
|
2460
|
+
path: "userName",
|
|
2461
|
+
validator: { type: "string", required: true }
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
uid: "submitBtn",
|
|
2465
|
+
kind: "action",
|
|
2466
|
+
type: "button",
|
|
2467
|
+
label: "Submit",
|
|
2468
|
+
actionType: "submit"
|
|
2469
|
+
},
|
|
2470
|
+
{
|
|
2471
|
+
uid: "greeting",
|
|
2472
|
+
kind: "display",
|
|
2473
|
+
type: "alert",
|
|
2474
|
+
props: { text: "Hello {{$form.userName}}" }
|
|
2475
|
+
},
|
|
2476
|
+
{
|
|
2477
|
+
uid: "status",
|
|
2478
|
+
kind: "display",
|
|
2479
|
+
type: "alert",
|
|
2480
|
+
props: {
|
|
2481
|
+
text: "Error: {{$errors.userName}} | Form invalid: {{$formIsInvalid}}"
|
|
2482
|
+
}
|
|
2483
|
+
},
|
|
2484
|
+
{
|
|
2485
|
+
uid: "meta-info",
|
|
2486
|
+
kind: "display",
|
|
2487
|
+
type: "alert",
|
|
2488
|
+
props: { text: "Connected as {{$meta.role}} on {{$meta.server}}" }
|
|
2489
|
+
}
|
|
2490
|
+
]
|
|
2491
|
+
}
|
|
2492
|
+
},
|
|
2493
|
+
{
|
|
2494
|
+
name: "Expressions in slots",
|
|
2495
|
+
description: "Slots support full JavaScript-like expressions: arithmetic, string concatenation, ternary conditionals, and optional chaining. The expression is evaluated against the same scope object (`$form`, `$meta`, `$errors`, `$formIsInvalid`). If the expression evaluates to `null` or `undefined`, the slot renders as an empty string.",
|
|
2496
|
+
example: {
|
|
2497
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2498
|
+
data: { firstName: "Jane", lastName: "Doe", count: 4, role: "admin" },
|
|
2499
|
+
form: [
|
|
2500
|
+
{
|
|
2501
|
+
uid: "full-name",
|
|
2502
|
+
kind: "display",
|
|
2503
|
+
type: "alert",
|
|
2504
|
+
props: { text: "Full name: {{$form.firstName + ' ' + $form.lastName}}" }
|
|
2505
|
+
},
|
|
2506
|
+
{
|
|
2507
|
+
uid: "next-count",
|
|
2508
|
+
kind: "display",
|
|
2509
|
+
type: "alert",
|
|
2510
|
+
props: { text: "Next item: {{$form.count + 1}}" }
|
|
2511
|
+
},
|
|
2512
|
+
{
|
|
2513
|
+
uid: "role-label",
|
|
2514
|
+
kind: "display",
|
|
2515
|
+
type: "alert",
|
|
2516
|
+
props: { text: "Role: {{$form.role === 'admin' ? 'Administrator' : 'User'}}" }
|
|
2517
|
+
},
|
|
2518
|
+
{
|
|
2519
|
+
uid: "nested",
|
|
2520
|
+
kind: "display",
|
|
2521
|
+
type: "alert",
|
|
2522
|
+
props: { text: "City: {{$form.address?.city}}" }
|
|
2523
|
+
}
|
|
2524
|
+
]
|
|
2525
|
+
}
|
|
2526
|
+
},
|
|
2527
|
+
{
|
|
2528
|
+
name: "i18n param expressions",
|
|
2529
|
+
description: "When using i18n translations, `params` values support the same expression language as `{{}}` slots, but as **bare expressions** without the `{{}}` delimiters. Params that start with a `$` scope prefix are evaluated; others are passed as static strings. The expression has access to `$form`, `$meta`, `$errors`, and `$formIsInvalid`.",
|
|
2530
|
+
example: {
|
|
2531
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2532
|
+
data: { firstName: "Jane", lastName: "Doe", count: 4 },
|
|
2533
|
+
meta: { connectionStatus: "online" },
|
|
2534
|
+
form: [
|
|
2535
|
+
{
|
|
2536
|
+
uid: "greeting",
|
|
2537
|
+
kind: "display",
|
|
2538
|
+
type: "alert",
|
|
2539
|
+
props: {
|
|
2540
|
+
text: {
|
|
2541
|
+
key: "user.greeting",
|
|
2542
|
+
params: {
|
|
2543
|
+
hello: "Hola",
|
|
2544
|
+
fullName: "$form.firstName + ' ' + $form.lastName",
|
|
2545
|
+
n: "$form.count + 1",
|
|
2546
|
+
status: "$meta.connectionStatus"
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
]
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
],
|
|
2555
|
+
rules: [
|
|
2556
|
+
"Slots must reference at least one of `$form`, `$meta`, `$errors`, or `$formIsInvalid`. A bare identifier without a scope prefix is invalid inside `{{}}`: use `{{$form.name}}` not `{{name}}`. `$formIsInvalid` is a bare boolean — do not chain properties onto it.",
|
|
2557
|
+
"If an expression evaluates to `null` or `undefined`, the slot renders as an empty string in display text.",
|
|
2558
|
+
"Use optional chaining (`?.`) when accessing nested fields that may not yet exist: `{{$form.address?.city}}` not `{{$form.address.city}}`.",
|
|
2559
|
+
"Do not use assignment `=` inside a slot — slots are read-only. Use `===` for equality checks.",
|
|
2560
|
+
"Slots cannot be nested: `{{$form.a {{$form.b}}}}` is invalid.",
|
|
2561
|
+
"Every `{{` must have a matching `}}`. Unbalanced delimiters cause a lint warning.",
|
|
2562
|
+
'i18n `params` values are bare expressions — do NOT wrap them in `{{}}`. Write `"$form.name"` not `"{{$form.name}}"`.',
|
|
2563
|
+
'Static string params (not starting with `$`) are passed through as-is — use them for constant values like `"Hola"` or `"px"`.',
|
|
2564
|
+
'The expression language is the same as reactive expressions — see `get_concept({ concept: "reactive-scope" })` for the operator reference and safe-expression constraints.'
|
|
2565
|
+
]
|
|
2566
|
+
};
|
|
2567
|
+
const REACTIVE_SCOPE_CONCEPT = {
|
|
2568
|
+
concept: "reactive-scope",
|
|
2569
|
+
summary: "GolemUI reactive expressions (used in `states`, `include.when`, `exclude.when`, and `{{}}` template slots) share a common read-only scope object. The scope exposes four variables: `$form` (live form data), `$meta` (host-supplied metadata), `$errors` (validation error messages), and `$formIsInvalid` (built-in boolean). All variables are read-only — expressions can only read from them, never write to them.",
|
|
2570
|
+
patterns: [
|
|
2571
|
+
{
|
|
2572
|
+
name: "$form — live form data",
|
|
2573
|
+
description: '`$form` is a plain object whose keys are the field paths currently registered in the form. Each key equals the `path` property of an input widget. A simple field `path: "firstName"` is accessed as `$form.firstName`. A nested field `path: "address.city"` is accessed as `$form.address?.city` — the dot segments in the path become dot-property accesses, and optional chaining (`?.`) is required at each intermediate segment because the parent object may not yet exist while the user is filling in the form. `$form` itself is always a defined object; only leaf values may be `undefined` before the user fills them in.',
|
|
2574
|
+
example: {
|
|
2575
|
+
states: {
|
|
2576
|
+
// path: "firstName" -> $form.firstName
|
|
2577
|
+
hasName: '$form.firstName !== undefined && $form.firstName !== ""',
|
|
2578
|
+
// path: "address.city" -> $form.address?.city (optional chaining at "address" segment)
|
|
2579
|
+
hasCity: '$form.address?.city !== undefined && $form.address?.city !== ""',
|
|
2580
|
+
// path: "user.profile.age" -> $form.user?.profile?.age
|
|
2581
|
+
isAdult: "$form.user?.profile?.age >= 18"
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
},
|
|
2585
|
+
{
|
|
2586
|
+
name: "$meta — host-supplied metadata",
|
|
2587
|
+
description: "`$meta` is a free-form key/value object supplied by the host application at mount time. It is useful for passing server-side flags, the current user's role, locale, or any context that is not part of the form data itself. The host sets the `meta` prop on the GolemUI form component; the MCP tool has no knowledge of which keys the host will supply. Access values with `$meta.keyName`. Use optional chaining if a key may not always be present.",
|
|
2588
|
+
example: {
|
|
2589
|
+
states: {
|
|
2590
|
+
isAdmin: "$meta.role === 'admin'",
|
|
2591
|
+
isOnline: '$meta.connectionStatus === "online"'
|
|
2592
|
+
},
|
|
2593
|
+
form: [
|
|
2594
|
+
{
|
|
2595
|
+
kind: "input",
|
|
2596
|
+
type: "textinput",
|
|
2597
|
+
path: "apiKey",
|
|
2598
|
+
label: "API key",
|
|
2599
|
+
// Show only for admin users — the role comes from $meta, not from $form.
|
|
2600
|
+
include: { when: "$meta.role === 'admin'" }
|
|
2601
|
+
}
|
|
2602
|
+
]
|
|
2603
|
+
}
|
|
2604
|
+
},
|
|
2605
|
+
{
|
|
2606
|
+
name: "$errors — validation error messages",
|
|
2607
|
+
description: '`$errors` is a plain object keyed by widget `uid`. `$errors.myField` is the current validation error message string for the widget with `uid: "myField"`, or `undefined` if that field is currently valid. Use it to display a field\'s error message inside another widget (e.g. an `alert`). Note: a widget must have an explicit `uid` value set for its errors to appear in `$errors`; auto-generated uids are not predictable.',
|
|
2608
|
+
example: {
|
|
2609
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2610
|
+
form: [
|
|
2611
|
+
{
|
|
2612
|
+
uid: "emailField",
|
|
2613
|
+
kind: "input",
|
|
2614
|
+
type: "textinput",
|
|
2615
|
+
path: "email",
|
|
2616
|
+
label: "Email",
|
|
2617
|
+
validator: { type: "string", required: true, format: "email" }
|
|
2618
|
+
},
|
|
2619
|
+
{
|
|
2620
|
+
kind: "display",
|
|
2621
|
+
type: "alert",
|
|
2622
|
+
props: {
|
|
2623
|
+
level: "error",
|
|
2624
|
+
// Render the live validation error message for the emailField widget.
|
|
2625
|
+
text: "{{$errors.emailField}}"
|
|
2626
|
+
},
|
|
2627
|
+
// Only show this alert when the field actually has an error.
|
|
2628
|
+
include: { when: "$errors.emailField !== undefined" }
|
|
2629
|
+
}
|
|
2630
|
+
]
|
|
2631
|
+
}
|
|
2632
|
+
},
|
|
2633
|
+
{
|
|
2634
|
+
name: "$formIsInvalid — whole-form validity flag",
|
|
2635
|
+
description: "`$formIsInvalid` is a built-in boolean that the runtime maintains automatically. It is `true` when ANY field in the form currently fails its validator; `false` otherwise. Use it to disable the submit button, show a banner, or guard a navigation step. It is NOT a property of `$form` — use it as a bare identifier. Do NOT chain properties onto it: `$formIsInvalid.something` is invalid.",
|
|
2636
|
+
example: {
|
|
2637
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2638
|
+
form: [
|
|
2639
|
+
{
|
|
2640
|
+
kind: "action",
|
|
2641
|
+
type: "button",
|
|
2642
|
+
actionType: "submit",
|
|
2643
|
+
label: "Submit",
|
|
2644
|
+
// Disable the submit button while any field is invalid.
|
|
2645
|
+
disabled: true,
|
|
2646
|
+
"disabled.formValid": false
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
kind: "display",
|
|
2650
|
+
type: "alert",
|
|
2651
|
+
props: { level: "warning", text: "Please fix the errors above before submitting." },
|
|
2652
|
+
include: { when: "$formIsInvalid" }
|
|
2653
|
+
}
|
|
2654
|
+
],
|
|
2655
|
+
states: {
|
|
2656
|
+
formValid: "!$formIsInvalid"
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
],
|
|
2661
|
+
rules: [
|
|
2662
|
+
"All four scope variables are read-only. Expressions may only read values, never assign them.",
|
|
2663
|
+
"`$form` is always a defined object. Its leaf values (`$form.someField`) may be `undefined` until the user fills them in.",
|
|
2664
|
+
"Use optional chaining (`?.`) at every intermediate segment of a nested path. `$form.address?.city` is safe; `$form.address.city` throws if `address` is undefined.",
|
|
2665
|
+
'A widget\'s `path` value maps directly to a `$form` key path using the same dot segments. `path: "shipping.address.zip"` -> `$form.shipping?.address?.zip`.',
|
|
2666
|
+
'`$formIsInvalid` is a built-in bare boolean — use it directly: `disabled: { when: "$formIsInvalid" }` or `states: { formValid: "!$formIsInvalid" }`. Never chain: `$formIsInvalid.value` is invalid.',
|
|
2667
|
+
"`$errors` keys are widget `uid` values, not field path values. A widget must have an explicit `uid` for its errors to be addressable.",
|
|
2668
|
+
"`$meta` keys are arbitrary — they depend entirely on what the host application passes to the form component. Use optional chaining if a key may be absent.",
|
|
2669
|
+
"Supported operators across all expressions: arithmetic (`+`, `-`, `*`, `/`, `%`), comparison (`===`, `!==`, `<`, `>`, `<=`, `>=`), logical (`&&`, `||`, `!`), ternary (`? :`), optional chaining (`?.`), nullish coalescing (`??`).",
|
|
2670
|
+
"Use `===` / `!==` for equality. Avoid `==` / `!=` (loose equality causes unexpected coercions with `undefined`).",
|
|
2671
|
+
"No function calls, no `eval`, no side effects. The expression must be a pure read."
|
|
2672
|
+
]
|
|
2673
|
+
};
|
|
2674
|
+
const ICONS_CONCEPT = {
|
|
2675
|
+
concept: "icons",
|
|
2676
|
+
summary: 'GolemUI uses Google Material Icons for all icon props. An icon value is a Material Icons ligature name — a lowercase string with underscores, such as `"search"`, `"home"`, `"arrow_forward"`, or `"check_circle"`. The icon font must be loaded by the host page; without it, the icon value renders as raw text. Several widgets accept a `props.icon` property; `button` additionally has `props.iconPosition`. All icon props support state suffixes for reactive icon swapping.',
|
|
2677
|
+
patterns: [
|
|
2678
|
+
{
|
|
2679
|
+
name: "Widget icon props",
|
|
2680
|
+
description: "The following widgets accept `props.icon` (a Material Icons ligature name string): `button`, `textinput`, `password`, `select`, `currency`, `tags`, `dateInput`, `datePicker`, `rangeCalendar`, `rangedateinput`, `rangedatepicker`. The `button` widget additionally accepts `props.iconPosition` to control where the icon appears relative to the label. All other icon-capable widgets render the icon at a default position determined by the component.",
|
|
2681
|
+
example: {
|
|
2682
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2683
|
+
form: [
|
|
2684
|
+
{
|
|
2685
|
+
kind: "action",
|
|
2686
|
+
type: "button",
|
|
2687
|
+
actionType: "submit",
|
|
2688
|
+
label: "Send",
|
|
2689
|
+
props: {
|
|
2690
|
+
// "send" is the Google Material Icons ligature name for the send icon.
|
|
2691
|
+
icon: "send",
|
|
2692
|
+
iconPosition: "right"
|
|
2693
|
+
}
|
|
2694
|
+
},
|
|
2695
|
+
{
|
|
2696
|
+
kind: "input",
|
|
2697
|
+
type: "textinput",
|
|
2698
|
+
path: "email",
|
|
2699
|
+
label: "Email",
|
|
2700
|
+
props: {
|
|
2701
|
+
icon: "email",
|
|
2702
|
+
placeholder: "you@example.com"
|
|
2703
|
+
}
|
|
2704
|
+
},
|
|
2705
|
+
{
|
|
2706
|
+
kind: "input",
|
|
2707
|
+
type: "select",
|
|
2708
|
+
path: "country",
|
|
2709
|
+
label: "Country",
|
|
2710
|
+
props: {
|
|
2711
|
+
icon: "public",
|
|
2712
|
+
options: [
|
|
2713
|
+
{ label: "United States", value: "us" },
|
|
2714
|
+
{ label: "Canada", value: "ca" }
|
|
2715
|
+
]
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
]
|
|
2719
|
+
}
|
|
2720
|
+
},
|
|
2721
|
+
{
|
|
2722
|
+
name: "Reactive icon swapping with state suffixes",
|
|
2723
|
+
description: 'Like other widget props, `icon` supports state suffixes: `"icon.<stateName>": "checkName"` swaps the icon when the named state is active. This is useful for toggling between a default and a confirmation icon, or for indicating a loading state on a button.',
|
|
2724
|
+
example: {
|
|
2725
|
+
$schema: "https://golemui.com/schemas/form.schema.json",
|
|
2726
|
+
states: {
|
|
2727
|
+
submitted: "$meta.submitting === true"
|
|
2728
|
+
},
|
|
2729
|
+
form: [
|
|
2730
|
+
{
|
|
2731
|
+
kind: "action",
|
|
2732
|
+
type: "button",
|
|
2733
|
+
actionType: "submit",
|
|
2734
|
+
label: "Submit",
|
|
2735
|
+
"label.submitted": "Submitting...",
|
|
2736
|
+
props: {
|
|
2737
|
+
icon: "send",
|
|
2738
|
+
// Swap to a spinner/hourglass icon while the form is being submitted.
|
|
2739
|
+
"icon.submitted": "hourglass_empty"
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
]
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
],
|
|
2746
|
+
rules: [
|
|
2747
|
+
'Icon values are Google Material Icons ligature names — lowercase strings, words separated by underscores: `"search"`, `"arrow_forward"`, `"check_circle"`. Wrong casing or spaces will not render correctly.',
|
|
2748
|
+
'The Material Icons font MUST be loaded in the host page\'s `<head>`. Without it, `props.icon` renders as the raw string (e.g. the word "search") rather than an icon glyph.',
|
|
2749
|
+
"Widgets that support `props.icon`: `button`, `textinput`, `password`, `select`, `currency`, `tags`, `dateInput`, `datePicker`, `rangeCalendar`, `rangedateinput`, `rangedatepicker`.",
|
|
2750
|
+
'`button` is the only widget with `props.iconPosition`. Allowed values: `"left"` (default) and `"right"`.',
|
|
2751
|
+
'All icon props support state suffixes: `"icon.<stateName>": "check"` swaps the icon when that state is active. Call `get_concept({ concept: "states" })` for the full state-suffix pattern.',
|
|
2752
|
+
"Do not set `props.icon` on widgets that do not support it — the schema will reject it and `validate_form_definition` will report an error."
|
|
2753
|
+
]
|
|
2754
|
+
};
|
|
2755
|
+
const CONCEPTS = {
|
|
2756
|
+
states: STATES_CONCEPT,
|
|
2757
|
+
"string-interpolation": STRING_INTERPOLATION_CONCEPT,
|
|
2758
|
+
"reactive-scope": REACTIVE_SCOPE_CONCEPT,
|
|
2759
|
+
icons: ICONS_CONCEPT
|
|
2760
|
+
};
|
|
2761
|
+
function getConcept(input) {
|
|
2762
|
+
const result = CONCEPTS[input.concept];
|
|
2763
|
+
if (!result) {
|
|
2764
|
+
const known = Object.keys(CONCEPTS).sort().map((c) => `\`${c}\``).join(", ");
|
|
2765
|
+
throw new Error(`Unknown concept \`${input.concept}\`. Known concepts: ${known}.`);
|
|
2766
|
+
}
|
|
2767
|
+
return result;
|
|
2768
|
+
}
|
|
2769
|
+
const GET_CONCEPT_TOOL = {
|
|
2770
|
+
name: "get_concept",
|
|
2771
|
+
description: 'Return a detailed guide for a cross-cutting GolemUI form concept — things that span multiple widgets and affect the whole form, rather than the API of a single widget. Call this when you need to: (1) conditionally show or hide widgets (`include`/`exclude`) — both the named-state form (`in`/`from`) and the inline `when` expression form are covered under the `states` concept; (2) change a widget\'s props based on form state (state-suffixed props like `"label.stateName": "…"`); (3) understand what `$form`, `$meta`, `$errors`, and `$formIsInvalid` are and how to reference form data in reactive expressions — use the `reactive-scope` concept; (4) add icons to widgets — use the `icons` concept. Currently supported concepts: `states`, `string-interpolation`, `reactive-scope`, `icons`.',
|
|
2772
|
+
inputSchema: {
|
|
2773
|
+
type: "object",
|
|
2774
|
+
properties: {
|
|
2775
|
+
concept: {
|
|
2776
|
+
type: "string",
|
|
2777
|
+
description: 'The concept to explain. Currently supported: `"states"`, `"string-interpolation"`.',
|
|
2778
|
+
enum: Object.keys(CONCEPTS)
|
|
2779
|
+
}
|
|
2780
|
+
},
|
|
2781
|
+
required: ["concept"]
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2784
|
+
export {
|
|
2785
|
+
GENERATE_FROM_JSON_SCHEMA_TOOL as G,
|
|
2786
|
+
VALIDATE_FORM_DEFINITION_TOOL as V,
|
|
2787
|
+
generateFromOpenapi as a,
|
|
2788
|
+
GENERATE_FROM_OPENAPI_TOOL as b,
|
|
2789
|
+
getWidgetSpec as c,
|
|
2790
|
+
GET_WIDGET_SPEC_TOOL as d,
|
|
2791
|
+
getConcept as e,
|
|
2792
|
+
GET_CONCEPT_TOOL as f,
|
|
2793
|
+
generateFromJsonSchema as g,
|
|
2794
|
+
validateFormDefinition as v
|
|
2795
|
+
};
|