@lynx-js/genui 0.0.1-rc.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/LICENSE +202 -0
- package/README.md +170 -0
- package/a2ui/README.md +140 -0
- package/a2ui/dist/catalog/Button/catalog.json +156 -0
- package/a2ui/dist/catalog/Button/index.d.ts +35 -0
- package/a2ui/dist/catalog/Button/index.js +35 -0
- package/a2ui/dist/catalog/Button/index.js.map +1 -0
- package/a2ui/dist/catalog/Card/catalog.json +24 -0
- package/a2ui/dist/catalog/Card/index.d.ts +11 -0
- package/a2ui/dist/catalog/Card/index.js +19 -0
- package/a2ui/dist/catalog/Card/index.js.map +1 -0
- package/a2ui/dist/catalog/CheckBox/catalog.json +170 -0
- package/a2ui/dist/catalog/CheckBox/index.d.ts +32 -0
- package/a2ui/dist/catalog/CheckBox/index.js +24 -0
- package/a2ui/dist/catalog/CheckBox/index.js.map +1 -0
- package/a2ui/dist/catalog/Column/catalog.json +57 -0
- package/a2ui/dist/catalog/Column/index.d.ts +15 -0
- package/a2ui/dist/catalog/Column/index.js +55 -0
- package/a2ui/dist/catalog/Column/index.js.map +1 -0
- package/a2ui/dist/catalog/Divider/catalog.json +14 -0
- package/a2ui/dist/catalog/Divider/index.d.ts +9 -0
- package/a2ui/dist/catalog/Divider/index.js +8 -0
- package/a2ui/dist/catalog/Divider/index.js.map +1 -0
- package/a2ui/dist/catalog/Icon/catalog.json +45 -0
- package/a2ui/dist/catalog/Icon/index.d.ts +14 -0
- package/a2ui/dist/catalog/Icon/index.js +11 -0
- package/a2ui/dist/catalog/Icon/index.js.map +1 -0
- package/a2ui/dist/catalog/Image/catalog.json +62 -0
- package/a2ui/dist/catalog/Image/index.d.ts +16 -0
- package/a2ui/dist/catalog/Image/index.js +30 -0
- package/a2ui/dist/catalog/Image/index.js.map +1 -0
- package/a2ui/dist/catalog/LineChart/catalog.json +98 -0
- package/a2ui/dist/catalog/LineChart/index.d.ts +31 -0
- package/a2ui/dist/catalog/LineChart/index.js +195 -0
- package/a2ui/dist/catalog/LineChart/index.js.map +1 -0
- package/a2ui/dist/catalog/List/catalog.json +52 -0
- package/a2ui/dist/catalog/List/index.d.ts +15 -0
- package/a2ui/dist/catalog/List/index.js +53 -0
- package/a2ui/dist/catalog/List/index.js.map +1 -0
- package/a2ui/dist/catalog/Modal/catalog.json +18 -0
- package/a2ui/dist/catalog/Modal/index.d.ts +12 -0
- package/a2ui/dist/catalog/Modal/index.js +33 -0
- package/a2ui/dist/catalog/Modal/index.js.map +1 -0
- package/a2ui/dist/catalog/PieChart/catalog.json +87 -0
- package/a2ui/dist/catalog/PieChart/index.d.ts +37 -0
- package/a2ui/dist/catalog/PieChart/index.js +131 -0
- package/a2ui/dist/catalog/PieChart/index.js.map +1 -0
- package/a2ui/dist/catalog/RadioGroup/catalog.json +184 -0
- package/a2ui/dist/catalog/RadioGroup/index.d.ts +36 -0
- package/a2ui/dist/catalog/RadioGroup/index.js +36 -0
- package/a2ui/dist/catalog/RadioGroup/index.js.map +1 -0
- package/a2ui/dist/catalog/Row/catalog.json +57 -0
- package/a2ui/dist/catalog/Row/index.d.ts +15 -0
- package/a2ui/dist/catalog/Row/index.js +28 -0
- package/a2ui/dist/catalog/Row/index.js.map +1 -0
- package/a2ui/dist/catalog/Slider/catalog.json +183 -0
- package/a2ui/dist/catalog/Slider/index.d.ts +41 -0
- package/a2ui/dist/catalog/Slider/index.js +39 -0
- package/a2ui/dist/catalog/Slider/index.js.map +1 -0
- package/a2ui/dist/catalog/Slider/utils.d.ts +11 -0
- package/a2ui/dist/catalog/Slider/utils.js +58 -0
- package/a2ui/dist/catalog/Slider/utils.js.map +1 -0
- package/a2ui/dist/catalog/Tabs/catalog.json +28 -0
- package/a2ui/dist/catalog/Tabs/index.d.ts +12 -0
- package/a2ui/dist/catalog/Tabs/index.js +32 -0
- package/a2ui/dist/catalog/Tabs/index.js.map +1 -0
- package/a2ui/dist/catalog/Text/catalog.json +74 -0
- package/a2ui/dist/catalog/Text/index.d.ts +18 -0
- package/a2ui/dist/catalog/Text/index.js +27 -0
- package/a2ui/dist/catalog/Text/index.js.map +1 -0
- package/a2ui/dist/catalog/TextField/catalog.json +147 -0
- package/a2ui/dist/catalog/TextField/index.d.ts +35 -0
- package/a2ui/dist/catalog/TextField/index.js +43 -0
- package/a2ui/dist/catalog/TextField/index.js.map +1 -0
- package/a2ui/dist/catalog/TextField/utils.d.ts +7 -0
- package/a2ui/dist/catalog/TextField/utils.js +51 -0
- package/a2ui/dist/catalog/TextField/utils.js.map +1 -0
- package/a2ui/dist/catalog/defineCatalog.d.ts +119 -0
- package/a2ui/dist/catalog/defineCatalog.js +196 -0
- package/a2ui/dist/catalog/defineCatalog.js.map +1 -0
- package/a2ui/dist/catalog/index.d.ts +20 -0
- package/a2ui/dist/catalog/index.js +26 -0
- package/a2ui/dist/catalog/index.js.map +1 -0
- package/a2ui/dist/catalog/utils/chart.d.ts +3 -0
- package/a2ui/dist/catalog/utils/chart.js +28 -0
- package/a2ui/dist/catalog/utils/chart.js.map +1 -0
- package/a2ui/dist/functions/index.d.ts +19 -0
- package/a2ui/dist/functions/index.js +87 -0
- package/a2ui/dist/functions/index.js.map +1 -0
- package/a2ui/dist/index.d.ts +11 -0
- package/a2ui/dist/index.js +35 -0
- package/a2ui/dist/index.js.map +1 -0
- package/a2ui/dist/react/A2UI.d.ts +77 -0
- package/a2ui/dist/react/A2UI.js +159 -0
- package/a2ui/dist/react/A2UI.js.map +1 -0
- package/a2ui/dist/react/A2UIProvider.d.ts +25 -0
- package/a2ui/dist/react/A2UIProvider.js +20 -0
- package/a2ui/dist/react/A2UIProvider.js.map +1 -0
- package/a2ui/dist/react/A2UIRenderer.d.ts +34 -0
- package/a2ui/dist/react/A2UIRenderer.js +161 -0
- package/a2ui/dist/react/A2UIRenderer.js.map +1 -0
- package/a2ui/dist/react/FormContext.d.ts +10 -0
- package/a2ui/dist/react/FormContext.js +12 -0
- package/a2ui/dist/react/FormContext.js.map +1 -0
- package/a2ui/dist/react/index.d.ts +8 -0
- package/a2ui/dist/react/index.js +23 -0
- package/a2ui/dist/react/index.js.map +1 -0
- package/a2ui/dist/react/useA2UIContext.d.ts +7 -0
- package/a2ui/dist/react/useA2UIContext.js +19 -0
- package/a2ui/dist/react/useA2UIContext.js.map +1 -0
- package/a2ui/dist/react/useAction.d.ts +9 -0
- package/a2ui/dist/react/useAction.js +38 -0
- package/a2ui/dist/react/useAction.js.map +1 -0
- package/a2ui/dist/react/useCatalog.d.ts +7 -0
- package/a2ui/dist/react/useCatalog.js +13 -0
- package/a2ui/dist/react/useCatalog.js.map +1 -0
- package/a2ui/dist/react/useChecks.d.ts +27 -0
- package/a2ui/dist/react/useChecks.js +76 -0
- package/a2ui/dist/react/useChecks.js.map +1 -0
- package/a2ui/dist/react/useDataBinding.d.ts +10 -0
- package/a2ui/dist/react/useDataBinding.js +175 -0
- package/a2ui/dist/react/useDataBinding.js.map +1 -0
- package/a2ui/dist/store/FormController.d.ts +23 -0
- package/a2ui/dist/store/FormController.js +40 -0
- package/a2ui/dist/store/FormController.js.map +1 -0
- package/a2ui/dist/store/FunctionRegistry.d.ts +47 -0
- package/a2ui/dist/store/FunctionRegistry.js +23 -0
- package/a2ui/dist/store/FunctionRegistry.js.map +1 -0
- package/a2ui/dist/store/MessageProcessor.d.ts +28 -0
- package/a2ui/dist/store/MessageProcessor.js +408 -0
- package/a2ui/dist/store/MessageProcessor.js.map +1 -0
- package/a2ui/dist/store/MessageStore.d.ts +38 -0
- package/a2ui/dist/store/MessageStore.js +37 -0
- package/a2ui/dist/store/MessageStore.js.map +1 -0
- package/a2ui/dist/store/Resource.d.ts +45 -0
- package/a2ui/dist/store/Resource.js +80 -0
- package/a2ui/dist/store/Resource.js.map +1 -0
- package/a2ui/dist/store/SignalStore.d.ts +10 -0
- package/a2ui/dist/store/SignalStore.js +29 -0
- package/a2ui/dist/store/SignalStore.js.map +1 -0
- package/a2ui/dist/store/index.d.ts +14 -0
- package/a2ui/dist/store/index.js +15 -0
- package/a2ui/dist/store/index.js.map +1 -0
- package/a2ui/dist/store/payloadNormalizer.d.ts +27 -0
- package/a2ui/dist/store/payloadNormalizer.js +179 -0
- package/a2ui/dist/store/payloadNormalizer.js.map +1 -0
- package/a2ui/dist/store/resolveFunctionCall.d.ts +18 -0
- package/a2ui/dist/store/resolveFunctionCall.js +131 -0
- package/a2ui/dist/store/resolveFunctionCall.js.map +1 -0
- package/a2ui/dist/store/types.d.ts +68 -0
- package/a2ui/dist/store/types.js +2 -0
- package/a2ui/dist/store/types.js.map +1 -0
- package/a2ui/dist/tsconfig.build.tsbuildinfo +1 -0
- package/a2ui/styles/catalog/Button.css +83 -0
- package/a2ui/styles/catalog/Card.css +49 -0
- package/a2ui/styles/catalog/CheckBox.css +46 -0
- package/a2ui/styles/catalog/Column.css +89 -0
- package/a2ui/styles/catalog/Divider.css +20 -0
- package/a2ui/styles/catalog/Icon.css +39 -0
- package/a2ui/styles/catalog/Image.css +54 -0
- package/a2ui/styles/catalog/LineChart.css +116 -0
- package/a2ui/styles/catalog/List.css +38 -0
- package/a2ui/styles/catalog/Modal.css +60 -0
- package/a2ui/styles/catalog/PieChart.css +109 -0
- package/a2ui/styles/catalog/RadioGroup.css +123 -0
- package/a2ui/styles/catalog/Row.css +83 -0
- package/a2ui/styles/catalog/Slider.css +96 -0
- package/a2ui/styles/catalog/Tabs.css +46 -0
- package/a2ui/styles/catalog/Text.css +121 -0
- package/a2ui/styles/catalog/TextField.css +48 -0
- package/a2ui/styles/theme.css +62 -0
- package/a2ui-catalog-extractor/README.md +605 -0
- package/a2ui-catalog-extractor/bin/a2ui-catalog-extractor.js +6 -0
- package/a2ui-catalog-extractor/dist/cli.d.ts +12 -0
- package/a2ui-catalog-extractor/dist/cli.js +171 -0
- package/a2ui-catalog-extractor/dist/cli.js.map +1 -0
- package/a2ui-catalog-extractor/dist/index.d.ts +140 -0
- package/a2ui-catalog-extractor/dist/index.js +755 -0
- package/a2ui-catalog-extractor/dist/index.js.map +1 -0
- package/a2ui-catalog-extractor/dist/tsconfig.build.tsbuildinfo +1 -0
- package/a2ui-catalog-extractor/skills/a2ui-catalog-extractor/SKILL.md +30 -0
- package/a2ui-prompt/README.md +65 -0
- package/a2ui-prompt/dist/index.d.ts +91 -0
- package/a2ui-prompt/dist/index.js +767 -0
- package/cli/README.md +88 -0
- package/cli/bin/cli.js +271 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/index.ts +114 -0
- package/openui/README.md +211 -0
- package/openui/dist/catalog/Action/index.d.ts +20 -0
- package/openui/dist/catalog/Action/index.js +23 -0
- package/openui/dist/catalog/Action/index.js.map +1 -0
- package/openui/dist/catalog/Button/index.d.ts +64 -0
- package/openui/dist/catalog/Button/index.js +75 -0
- package/openui/dist/catalog/Button/index.js.map +1 -0
- package/openui/dist/catalog/Card/index.d.ts +34 -0
- package/openui/dist/catalog/Card/index.js +69 -0
- package/openui/dist/catalog/Card/index.js.map +1 -0
- package/openui/dist/catalog/CardHeader/index.d.ts +5 -0
- package/openui/dist/catalog/CardHeader/index.js +18 -0
- package/openui/dist/catalog/CardHeader/index.js.map +1 -0
- package/openui/dist/catalog/Separator/index.d.ts +2 -0
- package/openui/dist/catalog/Separator/index.js +13 -0
- package/openui/dist/catalog/Separator/index.js.map +1 -0
- package/openui/dist/catalog/Stack/index.d.ts +29 -0
- package/openui/dist/catalog/Stack/index.js +61 -0
- package/openui/dist/catalog/Stack/index.js.map +1 -0
- package/openui/dist/catalog/Tag/index.d.ts +4 -0
- package/openui/dist/catalog/Tag/index.js +15 -0
- package/openui/dist/catalog/Tag/index.js.map +1 -0
- package/openui/dist/catalog/TextContent/index.d.ts +11 -0
- package/openui/dist/catalog/TextContent/index.js +33 -0
- package/openui/dist/catalog/TextContent/index.js.map +1 -0
- package/openui/dist/catalog/index.d.ts +7 -0
- package/openui/dist/catalog/index.js +11 -0
- package/openui/dist/catalog/index.js.map +1 -0
- package/openui/dist/catalog/utils.d.ts +2 -0
- package/openui/dist/catalog/utils.js +17 -0
- package/openui/dist/catalog/utils.js.map +1 -0
- package/openui/dist/core/context.d.ts +112 -0
- package/openui/dist/core/context.js +99 -0
- package/openui/dist/core/context.js.map +1 -0
- package/openui/dist/core/createLibrary.d.ts +10 -0
- package/openui/dist/core/createLibrary.js +36 -0
- package/openui/dist/core/createLibrary.js.map +1 -0
- package/openui/dist/core/hooks/index.d.ts +4 -0
- package/openui/dist/core/hooks/index.js +6 -0
- package/openui/dist/core/hooks/index.js.map +1 -0
- package/openui/dist/core/hooks/useFormValidation.d.ts +13 -0
- package/openui/dist/core/hooks/useFormValidation.js +76 -0
- package/openui/dist/core/hooks/useFormValidation.js.map +1 -0
- package/openui/dist/core/hooks/useOpenUIState.d.ts +33 -0
- package/openui/dist/core/hooks/useOpenUIState.js +413 -0
- package/openui/dist/core/hooks/useOpenUIState.js.map +1 -0
- package/openui/dist/core/hooks/useStateField.d.ts +2 -0
- package/openui/dist/core/hooks/useStateField.js +11 -0
- package/openui/dist/core/hooks/useStateField.js.map +1 -0
- package/openui/dist/core/index.d.ts +7 -0
- package/openui/dist/core/index.js +8 -0
- package/openui/dist/core/index.js.map +1 -0
- package/openui/dist/core/library.d.ts +20 -0
- package/openui/dist/core/library.js +13 -0
- package/openui/dist/core/library.js.map +1 -0
- package/openui/dist/core/renderer.css +271 -0
- package/openui/dist/core/renderer.d.ts +9 -0
- package/openui/dist/core/renderer.js +139 -0
- package/openui/dist/core/renderer.js.map +1 -0
- package/openui/dist/core/utils.d.ts +1 -0
- package/openui/dist/core/utils.js +76 -0
- package/openui/dist/core/utils.js.map +1 -0
- package/package.json +120 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
const BASIC_CATALOG_ID = 'https://a2ui.org/specification/v0_9/basic_catalog.json';
|
|
4
|
+
const BASIC_CATALOG_EXAMPLES = [
|
|
5
|
+
{
|
|
6
|
+
name: 'login-card',
|
|
7
|
+
user: 'Generate a login card with email, password, and a submit button.',
|
|
8
|
+
messages: [
|
|
9
|
+
{
|
|
10
|
+
version: 'v0.9',
|
|
11
|
+
createSurface: {
|
|
12
|
+
surfaceId: 'main',
|
|
13
|
+
catalogId: BASIC_CATALOG_ID
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
version: 'v0.9',
|
|
18
|
+
updateComponents: {
|
|
19
|
+
surfaceId: 'main',
|
|
20
|
+
components: [
|
|
21
|
+
{
|
|
22
|
+
id: 'root',
|
|
23
|
+
component: 'Card',
|
|
24
|
+
child: 'form-column'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'form-column',
|
|
28
|
+
component: 'Column',
|
|
29
|
+
children: [
|
|
30
|
+
'title',
|
|
31
|
+
'email',
|
|
32
|
+
'password',
|
|
33
|
+
'submit'
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'title',
|
|
38
|
+
component: 'Text',
|
|
39
|
+
text: 'Sign in',
|
|
40
|
+
variant: 'h2'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'email',
|
|
44
|
+
component: 'TextField',
|
|
45
|
+
label: 'Email',
|
|
46
|
+
value: {
|
|
47
|
+
path: '/form/email'
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'password',
|
|
52
|
+
component: 'TextField',
|
|
53
|
+
label: 'Password',
|
|
54
|
+
variant: 'obscured',
|
|
55
|
+
value: {
|
|
56
|
+
path: '/form/password'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'submit',
|
|
61
|
+
component: 'Button',
|
|
62
|
+
variant: 'primary',
|
|
63
|
+
child: 'submit-label',
|
|
64
|
+
action: {
|
|
65
|
+
event: {
|
|
66
|
+
name: 'submit_login',
|
|
67
|
+
context: {
|
|
68
|
+
email: {
|
|
69
|
+
path: '/form/email'
|
|
70
|
+
},
|
|
71
|
+
password: {
|
|
72
|
+
path: '/form/password'
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'submit-label',
|
|
80
|
+
component: 'Text',
|
|
81
|
+
text: 'Sign in'
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
version: 'v0.9',
|
|
88
|
+
updateDataModel: {
|
|
89
|
+
surfaceId: 'main',
|
|
90
|
+
value: {
|
|
91
|
+
form: {
|
|
92
|
+
email: '',
|
|
93
|
+
password: ''
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'dynamic-list',
|
|
102
|
+
user: 'Show three trip ideas as a compact list.',
|
|
103
|
+
messages: [
|
|
104
|
+
{
|
|
105
|
+
version: 'v0.9',
|
|
106
|
+
createSurface: {
|
|
107
|
+
surfaceId: 'main',
|
|
108
|
+
catalogId: BASIC_CATALOG_ID
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
version: 'v0.9',
|
|
113
|
+
updateComponents: {
|
|
114
|
+
surfaceId: 'main',
|
|
115
|
+
components: [
|
|
116
|
+
{
|
|
117
|
+
id: 'root',
|
|
118
|
+
component: 'Column',
|
|
119
|
+
children: [
|
|
120
|
+
'title',
|
|
121
|
+
'trip-list'
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: 'title',
|
|
126
|
+
component: 'Text',
|
|
127
|
+
text: 'Trip ideas',
|
|
128
|
+
variant: 'h2'
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: 'trip-list',
|
|
132
|
+
component: 'List',
|
|
133
|
+
direction: 'vertical',
|
|
134
|
+
children: {
|
|
135
|
+
path: '/items',
|
|
136
|
+
componentId: 'trip-row'
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'trip-row',
|
|
141
|
+
component: 'Row',
|
|
142
|
+
children: [
|
|
143
|
+
'trip-icon',
|
|
144
|
+
'trip-copy'
|
|
145
|
+
],
|
|
146
|
+
align: 'center'
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: 'trip-icon',
|
|
150
|
+
component: 'Icon',
|
|
151
|
+
name: 'location_on'
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: 'trip-copy',
|
|
155
|
+
component: 'Column',
|
|
156
|
+
children: [
|
|
157
|
+
'trip-name',
|
|
158
|
+
'trip-detail'
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'trip-name',
|
|
163
|
+
component: 'Text',
|
|
164
|
+
text: {
|
|
165
|
+
path: '/items/*/name'
|
|
166
|
+
},
|
|
167
|
+
variant: 'h3'
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: 'trip-detail',
|
|
171
|
+
component: 'Text',
|
|
172
|
+
text: {
|
|
173
|
+
path: '/items/*/detail'
|
|
174
|
+
},
|
|
175
|
+
variant: 'body'
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
version: 'v0.9',
|
|
182
|
+
updateDataModel: {
|
|
183
|
+
surfaceId: 'main',
|
|
184
|
+
path: '/items',
|
|
185
|
+
value: [
|
|
186
|
+
{
|
|
187
|
+
name: 'Canal walk',
|
|
188
|
+
detail: 'Morning coffee and quiet bridges'
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'Museum loop',
|
|
192
|
+
detail: 'Design exhibits plus lunch nearby'
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'Sunset hill',
|
|
196
|
+
detail: 'Short climb with skyline views'
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'chart-card',
|
|
205
|
+
user: 'Show weekly active users as a line chart.',
|
|
206
|
+
messages: [
|
|
207
|
+
{
|
|
208
|
+
version: 'v0.9',
|
|
209
|
+
createSurface: {
|
|
210
|
+
surfaceId: 'main',
|
|
211
|
+
catalogId: BASIC_CATALOG_ID
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
version: 'v0.9',
|
|
216
|
+
updateComponents: {
|
|
217
|
+
surfaceId: 'main',
|
|
218
|
+
components: [
|
|
219
|
+
{
|
|
220
|
+
id: 'root',
|
|
221
|
+
component: 'Card',
|
|
222
|
+
child: 'chart-column'
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'chart-column',
|
|
226
|
+
component: 'Column',
|
|
227
|
+
children: [
|
|
228
|
+
'title',
|
|
229
|
+
'chart'
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'title',
|
|
234
|
+
component: 'Text',
|
|
235
|
+
text: 'Weekly active users',
|
|
236
|
+
variant: 'h2'
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'chart',
|
|
240
|
+
component: 'LineChart',
|
|
241
|
+
labels: {
|
|
242
|
+
path: '/chart/labels'
|
|
243
|
+
},
|
|
244
|
+
series: {
|
|
245
|
+
path: '/chart/series'
|
|
246
|
+
},
|
|
247
|
+
xLabel: 'Day',
|
|
248
|
+
yLabel: 'Users',
|
|
249
|
+
showGrid: true,
|
|
250
|
+
showLegend: true
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
version: 'v0.9',
|
|
257
|
+
updateDataModel: {
|
|
258
|
+
surfaceId: 'main',
|
|
259
|
+
value: {
|
|
260
|
+
chart: {
|
|
261
|
+
labels: [
|
|
262
|
+
'Mon',
|
|
263
|
+
'Tue',
|
|
264
|
+
'Wed',
|
|
265
|
+
'Thu',
|
|
266
|
+
'Fri'
|
|
267
|
+
],
|
|
268
|
+
series: [
|
|
269
|
+
{
|
|
270
|
+
name: 'Users',
|
|
271
|
+
values: [
|
|
272
|
+
120,
|
|
273
|
+
148,
|
|
274
|
+
132,
|
|
275
|
+
171,
|
|
276
|
+
190
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
]
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: 'action-update',
|
|
288
|
+
user: 'A2UI_USER_ACTION: {"surfaceId":"main","action":{"name":"submit_login","context":{"email":"me@example.com"}}}',
|
|
289
|
+
messages: [
|
|
290
|
+
{
|
|
291
|
+
version: 'v0.9',
|
|
292
|
+
updateDataModel: {
|
|
293
|
+
surfaceId: 'main',
|
|
294
|
+
path: '/status',
|
|
295
|
+
value: {
|
|
296
|
+
kind: 'success',
|
|
297
|
+
message: 'Signed in as me@example.com'
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
version: 'v0.9',
|
|
303
|
+
updateComponents: {
|
|
304
|
+
surfaceId: 'main',
|
|
305
|
+
components: [
|
|
306
|
+
{
|
|
307
|
+
id: 'root',
|
|
308
|
+
component: 'Card',
|
|
309
|
+
child: 'status-column'
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
id: 'status-column',
|
|
313
|
+
component: 'Column',
|
|
314
|
+
children: [
|
|
315
|
+
'status-title',
|
|
316
|
+
'status-message'
|
|
317
|
+
]
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
id: 'status-title',
|
|
321
|
+
component: 'Text',
|
|
322
|
+
text: 'Success',
|
|
323
|
+
variant: 'h2'
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: 'status-message',
|
|
327
|
+
component: 'Text',
|
|
328
|
+
text: {
|
|
329
|
+
path: '/status/message'
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
]
|
|
336
|
+
}
|
|
337
|
+
];
|
|
338
|
+
var catalog_namespaceObject = JSON.parse('{"Button":{"properties":{"child":{"type":"string"},"variant":{"type":"string","enum":["primary","borderless"]},"isValid":{"type":"boolean"},"action":{"oneOf":[{"type":"object","properties":{"event":{"type":"object","properties":{"name":{"type":"string"},"context":{"type":"object","additionalProperties":true,"description":"Context is a JSON object map in v0.9."}},"required":["name"],"additionalProperties":false}},"required":["event"],"additionalProperties":false},{"type":"object","properties":{"functionCall":{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}},"required":["functionCall"],"additionalProperties":false}],"description":"v0.9 actions should use the `event` wrapper for server-dispatched clicks."},"checks":{"type":"array","items":{"type":"object","properties":{"condition":{"oneOf":[{"type":"boolean"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}]},"message":{"type":"string"}},"required":["condition","message"],"additionalProperties":false}}},"required":["child","action"]}}');
|
|
339
|
+
var Card_catalog_namespaceObject = JSON.parse('{"Card":{"properties":{"child":{"type":"string"},"variant":{"type":"string","enum":["elevated","outlined","filled","ghost"]},"weight":{"type":"number"}},"required":["child"]}}');
|
|
340
|
+
var CheckBox_catalog_namespaceObject = JSON.parse('{"CheckBox":{"properties":{"label":{"oneOf":[{"type":"string"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}]},"value":{"oneOf":[{"type":"boolean"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}]},"checks":{"type":"array","items":{"type":"object","properties":{"condition":{"oneOf":[{"type":"boolean"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}]},"message":{"type":"string"}},"required":["condition","message"],"additionalProperties":false}}},"required":["label","value"]}}');
|
|
341
|
+
var Column_catalog_namespaceObject = JSON.parse('{"Column":{"properties":{"children":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","properties":{"componentId":{"type":"string"},"path":{"type":"string"}},"required":["componentId","path"],"additionalProperties":false}],"description":"Static child IDs array or template object."},"align":{"type":"string","enum":["start","center","end","stretch"]},"justify":{"type":"string","enum":["start","center","end","stretch","spaceBetween","spaceAround","spaceEvenly"]}},"required":["children"]}}');
|
|
342
|
+
var Divider_catalog_namespaceObject = JSON.parse('{"Divider":{"properties":{"axis":{"type":"string","enum":["horizontal","vertical"]}},"required":[]}}');
|
|
343
|
+
var Icon_catalog_namespaceObject = JSON.parse('{"Icon":{"properties":{"name":{"oneOf":[{"type":"string"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}],"description":"Material icon name (camelCase or snake_case), e.g. \\"info\\", \\"skipNext\\", \\"play_arrow\\"."},"size":{"type":"string","enum":["sm","md","lg"]},"color":{"type":"string","enum":["primary","muted","inherit"]}},"required":["name"]}}');
|
|
344
|
+
var Image_catalog_namespaceObject = JSON.parse('{"Image":{"properties":{"url":{"oneOf":[{"type":"string"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}],"description":"Image URL or path binding."},"fit":{"type":"string","enum":["contain","cover","fill","none","scale-down"]},"mode":{"type":"string","enum":["center","scaleToFill","aspectFit","aspectFill"]},"variant":{"type":"string","enum":["icon","avatar","smallFeature","mediumFeature","largeFeature","header"]},"weight":{"type":"number"}},"required":["url"]}}');
|
|
345
|
+
var LineChart_catalog_namespaceObject = JSON.parse('{"LineChart":{"properties":{"labels":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}],"description":"Category labels shown along the x axis."},"series":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"values":{"type":"array","items":{"type":"number"}},"color":{"type":"string"}},"required":["name","values"],"additionalProperties":false}},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}],"description":"One or more line series to render over the shared labels."},"variant":{"type":"string","enum":["linear","natural","step"]},"xLabel":{"type":"string"},"yLabel":{"type":"string"},"showGrid":{"type":"boolean"},"showLegend":{"type":"boolean"},"height":{"type":"number"}},"required":["labels","series"]}}');
|
|
346
|
+
var List_catalog_namespaceObject = JSON.parse('{"List":{"properties":{"children":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","properties":{"componentId":{"type":"string"},"path":{"type":"string"}},"required":["componentId","path"],"additionalProperties":false}],"description":"Static child IDs array or template object."},"direction":{"type":"string","enum":["horizontal","vertical"]},"align":{"type":"string","enum":["start","center","end","stretch"]}},"required":["children"]}}');
|
|
347
|
+
var Modal_catalog_namespaceObject = JSON.parse('{"Modal":{"properties":{"trigger":{"type":"string","description":"The ID of the component that opens the modal when interacted with."},"content":{"type":"string","description":"The ID of the component to display inside the modal."}},"required":["trigger","content"]}}');
|
|
348
|
+
var RadioGroup_catalog_namespaceObject = JSON.parse('{"RadioGroup":{"properties":{"items":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}],"description":"The list of string options to display."},"value":{"oneOf":[{"type":"string"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}],"description":"The currently selected value."},"usageHint":{"type":"string","enum":["default","card","row"],"description":"A hint for the visual style of the radio group."},"checks":{"type":"array","items":{"type":"object","properties":{"condition":{"oneOf":[{"type":"boolean"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}]},"message":{"type":"string"}},"required":["condition","message"],"additionalProperties":false}}},"required":["items","value"]}}');
|
|
349
|
+
var Row_catalog_namespaceObject = JSON.parse('{"Row":{"properties":{"children":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","properties":{"componentId":{"type":"string"},"path":{"type":"string"}},"required":["componentId","path"],"additionalProperties":false}],"description":"Static child IDs array or template object."},"justify":{"type":"string","enum":["start","center","end","stretch","spaceBetween","spaceAround","spaceEvenly"]},"align":{"type":"string","enum":["start","center","end","stretch"]}},"required":["children"]}}');
|
|
350
|
+
var Slider_catalog_namespaceObject = JSON.parse('{"Slider":{"properties":{"label":{"oneOf":[{"type":"string"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}],"description":"The label for the slider."},"min":{"type":"number","description":"The minimum value of the slider."},"max":{"type":"number","description":"The maximum value of the slider."},"value":{"oneOf":[{"type":"number"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}],"description":"The current value of the slider."},"checks":{"type":"array","items":{"type":"object","properties":{"condition":{"oneOf":[{"type":"boolean"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}],"description":"The condition that indicates whether the check passes."},"message":{"type":"string","description":"The error message to display if the check fails."}},"required":["condition","message"],"additionalProperties":false},"description":"A list of checks to perform."}},"required":["max","value"]}}');
|
|
351
|
+
var Tabs_catalog_namespaceObject = JSON.parse('{"Tabs":{"properties":{"tabs":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"child":{"type":"string"}},"required":["title","child"],"additionalProperties":false}}},"required":["tabs"]}}');
|
|
352
|
+
var Text_catalog_namespaceObject = JSON.parse('{"Text":{"properties":{"text":{"oneOf":[{"type":"string"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":true},"returnType":{"type":"string","enum":["string","number","boolean","object","array","any","void"]}},"required":["call","args"],"additionalProperties":false}],"description":"Literal text, path binding, or function call."},"variant":{"type":"string","enum":["h1","h2","h3","h4","h5","caption","body","markdown"]},"weight":{"type":"number"}},"required":["text"]}}');
|
|
353
|
+
var TextField_catalog_namespaceObject = JSON.parse('{"TextField":{"properties":{"label":{"oneOf":[{"type":"string"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}],"description":"The text label for the input field."},"value":{"oneOf":[{"type":"string"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}],"description":"The value of the text field."},"variant":{"type":"string","enum":["number","longText","shortText","obscured"],"description":"The type of input field to display."},"validationRegexp":{"type":"string","description":"A regular expression used for client-side validation of the input."},"checks":{"type":"array","items":{"type":"object","properties":{"condition":{"oneOf":[{"type":"boolean"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false},{"type":"object","properties":{"call":{"type":"string"},"args":{"type":"object","additionalProperties":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}]}},"returnType":{"type":"string","enum":["boolean"]}},"required":["call"],"additionalProperties":false}],"description":"The condition that indicates whether the check passes."},"message":{"type":"string","description":"The error message to display if the check fails."}},"required":["condition","message"],"additionalProperties":false},"description":"A list of checks to perform."}},"required":["label"]}}');
|
|
354
|
+
const a2ui_catalog_BASIC_CATALOG_ID = 'https://a2ui.org/specification/v0_9/basic_catalog.json';
|
|
355
|
+
const CATALOG_MANIFESTS = [
|
|
356
|
+
Text_catalog_namespaceObject,
|
|
357
|
+
Image_catalog_namespaceObject,
|
|
358
|
+
Icon_catalog_namespaceObject,
|
|
359
|
+
Divider_catalog_namespaceObject,
|
|
360
|
+
LineChart_catalog_namespaceObject,
|
|
361
|
+
Row_catalog_namespaceObject,
|
|
362
|
+
Column_catalog_namespaceObject,
|
|
363
|
+
List_catalog_namespaceObject,
|
|
364
|
+
Card_catalog_namespaceObject,
|
|
365
|
+
Tabs_catalog_namespaceObject,
|
|
366
|
+
Modal_catalog_namespaceObject,
|
|
367
|
+
catalog_namespaceObject,
|
|
368
|
+
TextField_catalog_namespaceObject,
|
|
369
|
+
CheckBox_catalog_namespaceObject,
|
|
370
|
+
RadioGroup_catalog_namespaceObject,
|
|
371
|
+
Slider_catalog_namespaceObject
|
|
372
|
+
];
|
|
373
|
+
const COMPONENT_SUMMARIES = {
|
|
374
|
+
Button: 'Clickable button. MUST always include an action. Has no "label" prop; use a child Text component for the visible label.',
|
|
375
|
+
Card: 'Card container with exactly one child. Wrap multiple elements in a Column/Row/List first.',
|
|
376
|
+
CheckBox: 'Boolean checkbox with a label and optional validation checks.',
|
|
377
|
+
Column: 'Vertical layout container.',
|
|
378
|
+
Divider: 'Horizontal or vertical separator line.',
|
|
379
|
+
Icon: 'Display an icon by name.',
|
|
380
|
+
Image: 'Display an image by URL.',
|
|
381
|
+
LineChart: 'Display one or more numeric line series over shared labels.',
|
|
382
|
+
List: 'Repeating layout container, commonly bound to a data path.',
|
|
383
|
+
Modal: 'Modal dialog with a trigger component and a content component. The trigger opens the modal locally when tapped.',
|
|
384
|
+
RadioGroup: 'Single-choice selector for a list of string options.',
|
|
385
|
+
Row: 'Horizontal layout container.',
|
|
386
|
+
Slider: 'Numeric slider with an optional label and validation checks.',
|
|
387
|
+
Tabs: 'Tabbed container; each tab references a child component id.',
|
|
388
|
+
Text: 'Display styled text. Supports literal text, data bindings, and function calls.',
|
|
389
|
+
TextField: 'Single-line or multi-line text input.'
|
|
390
|
+
};
|
|
391
|
+
const CONTAINER_SHAPES = {
|
|
392
|
+
Button: 'child',
|
|
393
|
+
Card: 'child',
|
|
394
|
+
Column: 'children',
|
|
395
|
+
List: 'children',
|
|
396
|
+
Modal: 'trigger-content',
|
|
397
|
+
Row: 'children',
|
|
398
|
+
Tabs: 'tabs'
|
|
399
|
+
};
|
|
400
|
+
function isRecord(value) {
|
|
401
|
+
return null !== value && 'object' == typeof value && !Array.isArray(value);
|
|
402
|
+
}
|
|
403
|
+
function inferType(schema) {
|
|
404
|
+
if (!schema) return 'unknown';
|
|
405
|
+
if (schema.oneOf && schema.oneOf.length > 0) return schema.oneOf.map((item)=>inferType(item)).join(' | ');
|
|
406
|
+
if ('array' === schema.type) return `${inferType(schema.items)}[]`;
|
|
407
|
+
if ('object' === schema.type) {
|
|
408
|
+
const properties = schema.properties ?? {};
|
|
409
|
+
const keys = Object.keys(properties);
|
|
410
|
+
if (0 === keys.length) return 'object';
|
|
411
|
+
const required = new Set(schema.required ?? []);
|
|
412
|
+
const fields = keys.map((key)=>{
|
|
413
|
+
const optional = required.has(key) ? '' : '?';
|
|
414
|
+
return `${key}${optional}: ${inferType(properties[key])}`;
|
|
415
|
+
});
|
|
416
|
+
return `{ ${fields.join('; ')} }`;
|
|
417
|
+
}
|
|
418
|
+
if ('string' === schema.type) {
|
|
419
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) return schema.enum.filter((item)=>'string' == typeof item).map((item)=>`"${item}"`).join(' | ');
|
|
420
|
+
return 'string';
|
|
421
|
+
}
|
|
422
|
+
if ('number' === schema.type) return 'number';
|
|
423
|
+
if ('boolean' === schema.type) return 'boolean';
|
|
424
|
+
return schema.type ?? 'unknown';
|
|
425
|
+
}
|
|
426
|
+
function inferEnums(schema) {
|
|
427
|
+
if (!schema) return;
|
|
428
|
+
if (Array.isArray(schema.enum)) {
|
|
429
|
+
const values = schema.enum.filter((item)=>'string' == typeof item);
|
|
430
|
+
return values.length > 0 ? values : void 0;
|
|
431
|
+
}
|
|
432
|
+
if (!schema.oneOf) return;
|
|
433
|
+
const nested = schema.oneOf.flatMap((item)=>inferEnums(item) ?? []);
|
|
434
|
+
return nested.length > 0 ? [
|
|
435
|
+
...new Set(nested)
|
|
436
|
+
] : void 0;
|
|
437
|
+
}
|
|
438
|
+
function componentFromManifest(manifest) {
|
|
439
|
+
const [name, schema] = Object.entries(manifest)[0] ?? [];
|
|
440
|
+
if (!name || !schema) return null;
|
|
441
|
+
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
442
|
+
const required = new Set(schema.required ?? []);
|
|
443
|
+
const props = Object.entries(properties).map(([propName, propSchema])=>{
|
|
444
|
+
const enums = inferEnums(propSchema);
|
|
445
|
+
return {
|
|
446
|
+
name: propName,
|
|
447
|
+
type: inferType(propSchema),
|
|
448
|
+
description: propSchema.description ?? '',
|
|
449
|
+
required: required.has(propName),
|
|
450
|
+
schema: propSchema,
|
|
451
|
+
...enums ? {
|
|
452
|
+
enums
|
|
453
|
+
} : {}
|
|
454
|
+
};
|
|
455
|
+
});
|
|
456
|
+
return {
|
|
457
|
+
name,
|
|
458
|
+
summary: COMPONENT_SUMMARIES[name] ?? `${name} component.`,
|
|
459
|
+
props,
|
|
460
|
+
...'Button' === name ? {
|
|
461
|
+
requiresAction: true
|
|
462
|
+
} : {},
|
|
463
|
+
...CONTAINER_SHAPES[name] ? {
|
|
464
|
+
containerShape: CONTAINER_SHAPES[name]
|
|
465
|
+
} : {}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function createA2UICatalogFromManifests(options) {
|
|
469
|
+
return {
|
|
470
|
+
id: options.catalogId,
|
|
471
|
+
label: options.label ?? `A2UI catalog (${options.catalogId})`,
|
|
472
|
+
...options.version ? {
|
|
473
|
+
version: options.version
|
|
474
|
+
} : {},
|
|
475
|
+
components: options.componentManifests.map((manifest)=>componentFromManifest(manifest)).filter((component)=>null !== component),
|
|
476
|
+
...options.extraRules ? {
|
|
477
|
+
extraRules: options.extraRules
|
|
478
|
+
} : {},
|
|
479
|
+
...options.examples ? {
|
|
480
|
+
examples: options.examples
|
|
481
|
+
} : {},
|
|
482
|
+
...options.functions ? {
|
|
483
|
+
functions: options.functions
|
|
484
|
+
} : {}
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const BASIC_CATALOG = {
|
|
488
|
+
id: a2ui_catalog_BASIC_CATALOG_ID,
|
|
489
|
+
label: 'Lynx A2UI basic catalog (v0.9)',
|
|
490
|
+
version: 'v0.9',
|
|
491
|
+
components: CATALOG_MANIFESTS.map((manifest)=>componentFromManifest(manifest)).filter((component)=>null !== component),
|
|
492
|
+
extraRules: [
|
|
493
|
+
'Use only components listed in this catalog; unsupported examples such as Video, AudioPlayer, DatePicker, or Checkbox are not available unless they appear here.',
|
|
494
|
+
'The implemented checkbox component is named "CheckBox" with a capital B.'
|
|
495
|
+
],
|
|
496
|
+
examples: BASIC_CATALOG_EXAMPLES
|
|
497
|
+
};
|
|
498
|
+
function renderCatalogReference(catalog) {
|
|
499
|
+
const lines = [];
|
|
500
|
+
lines.push(`## Component catalog (${catalog.label}; catalogId=${catalog.id})`);
|
|
501
|
+
lines.push('');
|
|
502
|
+
for (const c of catalog.components){
|
|
503
|
+
const required = c.props.filter((p)=>p.required).map((p)=>p.name).join(', ');
|
|
504
|
+
const header = required ? `### ${c.name} (required: ${required})` : `### ${c.name}`;
|
|
505
|
+
lines.push(header);
|
|
506
|
+
lines.push(`- ${c.summary}`);
|
|
507
|
+
for (const p of c.props){
|
|
508
|
+
const req = p.required ? ' [required]' : '';
|
|
509
|
+
const en = p.enums ? ` enum: ${p.enums.join(' | ')}` : '';
|
|
510
|
+
const desc = p.description ? ` — ${p.description}` : '';
|
|
511
|
+
lines.push(` · ${p.name}: ${p.type}${req}${desc}${en}`);
|
|
512
|
+
}
|
|
513
|
+
if (c.requiresAction) lines.push(' · NOTE: this component MUST include a non-empty `action`.');
|
|
514
|
+
lines.push('');
|
|
515
|
+
}
|
|
516
|
+
if (void 0 !== catalog.extraRules && catalog.extraRules.length > 0) {
|
|
517
|
+
lines.push('### Additional catalog rules');
|
|
518
|
+
for (const r of catalog.extraRules)lines.push(`- ${r}`);
|
|
519
|
+
lines.push('');
|
|
520
|
+
}
|
|
521
|
+
if (void 0 !== catalog.functions && catalog.functions.length > 0) {
|
|
522
|
+
lines.push('### Available functions');
|
|
523
|
+
for (const fn of catalog.functions){
|
|
524
|
+
lines.push(`- ${fn.name}: returns ${fn.returnType}`);
|
|
525
|
+
if (fn.description) lines.push(` ${fn.description}`);
|
|
526
|
+
lines.push(` parameters: ${JSON.stringify(fn.parameters)}`);
|
|
527
|
+
}
|
|
528
|
+
lines.push('');
|
|
529
|
+
}
|
|
530
|
+
return lines.join('\n');
|
|
531
|
+
}
|
|
532
|
+
const A2UI_PROTOCOL_VERSION = 'v0.9';
|
|
533
|
+
const PROTOCOL_OVERVIEW = `# A2UI (Agent-to-UI) Protocol v0.9
|
|
534
|
+
|
|
535
|
+
A2UI is a JSON-based, streaming UI protocol from Google
|
|
536
|
+
(https://github.com/google/A2UI). It lets an LLM agent describe a user interface
|
|
537
|
+
by emitting a sequence of declarative JSON messages that a renderer turns into
|
|
538
|
+
native widgets. There is NO arbitrary code: the renderer only knows the
|
|
539
|
+
components in the agreed-upon catalog.
|
|
540
|
+
|
|
541
|
+
## Official v0.9 design principles
|
|
542
|
+
- Prompt-first: v0.9 is meant to be embedded directly in the model prompt, so
|
|
543
|
+
emit JSON that follows the in-context schema and examples exactly.
|
|
544
|
+
- Safe like data, expressive like code: describe UI intent using trusted catalog
|
|
545
|
+
components only. Never emit JavaScript, HTML, CSS, event handlers or scripts.
|
|
546
|
+
- Structure/data separation: component messages define the flat UI structure;
|
|
547
|
+
data model messages populate or change values used by dynamic bindings.
|
|
548
|
+
- Progressive rendering: clients may render after each valid message. Prefer a
|
|
549
|
+
useful minimal UI first, then add data and refinements in later messages.
|
|
550
|
+
- Transport-agnostic: A2UI messages can travel over SSE, REST, WebSocket, A2A,
|
|
551
|
+
AG UI or MCP. This service wraps the streamed messages in one JSON array for
|
|
552
|
+
validation and transport convenience.
|
|
553
|
+
|
|
554
|
+
## Server-to-client message types
|
|
555
|
+
Every message MUST be a top-level JSON object with the field "version": "v0.9"
|
|
556
|
+
and exactly ONE of the following keys:
|
|
557
|
+
|
|
558
|
+
1. "createSurface" – initialise a new UI surface.
|
|
559
|
+
2. "updateComponents" – send/replace the list of components on a surface.
|
|
560
|
+
3. "updateDataModel" – set values inside the surface's data model.
|
|
561
|
+
4. "deleteSurface" – tear down a surface.
|
|
562
|
+
|
|
563
|
+
## Required ordering for a fresh response
|
|
564
|
+
1. createSurface (with surfaceId + catalogId)
|
|
565
|
+
2. updateComponents (the FIRST one MUST contain a component whose id is "root")
|
|
566
|
+
3. zero or more updateDataModel (populate dynamic data referenced by paths)
|
|
567
|
+
4. (optional) further updateComponents / updateDataModel for incremental UI
|
|
568
|
+
|
|
569
|
+
## Envelope semantics
|
|
570
|
+
- createSurface creates a surface. Once created, its surfaceId and catalogId are
|
|
571
|
+
fixed. To change catalog/theme, delete and recreate the surface.
|
|
572
|
+
- updateComponents adds or replaces component definitions for that surface. It
|
|
573
|
+
may reference data paths that will be populated by updateDataModel.
|
|
574
|
+
- updateDataModel replaces the whole data model when "path" is omitted or "/".
|
|
575
|
+
With a specific "path", it replaces only the value at that JSON Pointer.
|
|
576
|
+
- deleteSurface removes a surface when the UI is no longer needed.
|
|
577
|
+
|
|
578
|
+
## Component model
|
|
579
|
+
- Components are kept FLAT (adjacency-list style). Children are referenced by
|
|
580
|
+
string ids, never inlined.
|
|
581
|
+
- Every component object has shape:
|
|
582
|
+
{
|
|
583
|
+
"id": "string", // unique within the surface
|
|
584
|
+
"component": "Text|Card|...", // discriminator from the catalog
|
|
585
|
+
...component specific props
|
|
586
|
+
}
|
|
587
|
+
- The component with id "root" is the entry point of the tree.
|
|
588
|
+
- Layout containers (Row / Column / List) take "children": ["id1", "id2", ...].
|
|
589
|
+
To repeat from the data model, use "children":
|
|
590
|
+
{ "path": "/items", "componentId": "itemRow" }
|
|
591
|
+
- Card uses "child": "id". Modal uses "trigger" + "content". Tabs uses an
|
|
592
|
+
array of {title, child}.
|
|
593
|
+
|
|
594
|
+
## Data binding
|
|
595
|
+
- Static text: "text": "Hello"
|
|
596
|
+
- Bound text: "text": { "path": "/user/name" }
|
|
597
|
+
- Bound list children:
|
|
598
|
+
"children": { "path": "/items", "componentId": "itemRow" }
|
|
599
|
+
- Use updateDataModel messages to populate values at those paths.
|
|
600
|
+
- DynamicString/DynamicNumber/DynamicBoolean props accept either a literal value
|
|
601
|
+
or { "path": "/json/pointer" }. If you bind a prop to a path, create the
|
|
602
|
+
matching value in updateDataModel.
|
|
603
|
+
|
|
604
|
+
## Client-to-server events
|
|
605
|
+
- Interactive components carry an "action":
|
|
606
|
+
{ "event": { "name": "submit_booking",
|
|
607
|
+
"context": { "restaurantId": { "path": "/selected/id" } } } }
|
|
608
|
+
- Button has NO "label" prop. Its visible label is a child Text component
|
|
609
|
+
(use "child": "<text-id>" and add a separate Text component with that id).
|
|
610
|
+
- The renderer will POST that action back to /a2ui/action with the same
|
|
611
|
+
surfaceId and current client-held conversation. Your next turn may receive a
|
|
612
|
+
user message whose content starts with "A2UI_USER_ACTION:" followed by JSON
|
|
613
|
+
describing the action; handle it by emitting additional updateComponents /
|
|
614
|
+
updateDataModel messages to update the same surface.
|
|
615
|
+
`;
|
|
616
|
+
function buildHardRules(catalogId) {
|
|
617
|
+
return `## Hard rules
|
|
618
|
+
1. Output MUST be a JSON ARRAY of A2UI messages. No prose, no Markdown, no
|
|
619
|
+
code fences, no XML. First character '[' – last character ']'.
|
|
620
|
+
2. Each element MUST include "version": "v0.9".
|
|
621
|
+
3. For a fresh non-action response, the first message MUST be createSurface with
|
|
622
|
+
catalogId = "${catalogId}". Use surfaceId "main" unless the user specifies
|
|
623
|
+
otherwise.
|
|
624
|
+
4. For a fresh non-action response, the second message MUST be
|
|
625
|
+
updateComponents; its components list MUST contain exactly one component
|
|
626
|
+
with id "root".
|
|
627
|
+
5. Use property-based component discriminators: "component": "Text", not
|
|
628
|
+
wrapper objects such as { "Text": {...} }.
|
|
629
|
+
6. Children are referenced by id only. NEVER inline a child component.
|
|
630
|
+
7. Container references MUST point to components present in the same response.
|
|
631
|
+
8. Card.child is exactly one id; wrap multiple elements in Row/Column/List.
|
|
632
|
+
9. Buttons MUST include a non-empty "action.event.name". Button has NO "label"
|
|
633
|
+
prop – provide the label via a child Text component ("child": "<text-id>").
|
|
634
|
+
10. Any "{path:...}" reference MUST be populated by some updateDataModel in the
|
|
635
|
+
same response.
|
|
636
|
+
11. Ids are kebab-case, unique per surface ("root", "title-text", "submit-btn").
|
|
637
|
+
12. Do not invent components outside the catalog.
|
|
638
|
+
13. No comments, trailing commas or unknown fields.
|
|
639
|
+
14. If the user asks for impossible, unsafe, or unsupported UI, render a concise
|
|
640
|
+
explanatory A2UI surface using supported components rather than prose.
|
|
641
|
+
15. If the latest user message starts with "A2UI_USER_ACTION:", this is an
|
|
642
|
+
action response for an existing surface. Return a non-empty JSON array with
|
|
643
|
+
updateDataModel and/or updateComponents for that same surfaceId. Do NOT
|
|
644
|
+
return [] and do NOT create a new surface unless the action explicitly asks
|
|
645
|
+
to replace the whole UI.
|
|
646
|
+
16. For UI that should change after a button tap, keep the initial response in
|
|
647
|
+
the pre-action state. Put confirmation, success, or result details in the
|
|
648
|
+
action response instead of showing them before the action happens.
|
|
649
|
+
17. For Image.url, provide a short English image search query such as
|
|
650
|
+
"fresh pasta on a table" or "city skyline at night". Do NOT invent photo
|
|
651
|
+
CDN URLs. The server resolves Image.url values through its image provider.
|
|
652
|
+
`;
|
|
653
|
+
}
|
|
654
|
+
function renderCatalogExamples(catalog) {
|
|
655
|
+
if (!catalog.examples || 0 === catalog.examples.length) return '';
|
|
656
|
+
const lines = [
|
|
657
|
+
'## Validated examples'
|
|
658
|
+
];
|
|
659
|
+
for (const example of catalog.examples){
|
|
660
|
+
lines.push('');
|
|
661
|
+
lines.push(`### ${example.name}`);
|
|
662
|
+
lines.push(`User: ${JSON.stringify(example.user)}`);
|
|
663
|
+
lines.push('Assistant (raw JSON array, no fences):');
|
|
664
|
+
lines.push(JSON.stringify(example.messages, null, 2));
|
|
665
|
+
}
|
|
666
|
+
return lines.join('\n');
|
|
667
|
+
}
|
|
668
|
+
function buildA2UISystemPrompt(opts = {}) {
|
|
669
|
+
const catalog = opts.catalog ?? BASIC_CATALOG;
|
|
670
|
+
const parts = [
|
|
671
|
+
'You are an A2UI (Agent-to-UI) generation agent. Translate the user\'s',
|
|
672
|
+
'natural-language request into a stream of A2UI v0.9 JSON messages that a',
|
|
673
|
+
'client renderer can consume. A downstream validator will reject malformed',
|
|
674
|
+
'output – if you violate the protocol the user sees nothing.',
|
|
675
|
+
'',
|
|
676
|
+
PROTOCOL_OVERVIEW,
|
|
677
|
+
'',
|
|
678
|
+
renderCatalogReference(catalog),
|
|
679
|
+
'',
|
|
680
|
+
buildHardRules(catalog.id),
|
|
681
|
+
'',
|
|
682
|
+
renderCatalogExamples(catalog)
|
|
683
|
+
];
|
|
684
|
+
if (opts.appendix) parts.push('', opts.appendix);
|
|
685
|
+
return parts.join('\n');
|
|
686
|
+
}
|
|
687
|
+
const A2UI_SYSTEM_PROMPT = buildA2UISystemPrompt();
|
|
688
|
+
function readA2UICatalogFromDirectory(options) {
|
|
689
|
+
const cwd = options.cwd ? resolve(options.cwd) : process.cwd();
|
|
690
|
+
const catalogDir = resolve(cwd, options.catalogDir);
|
|
691
|
+
if (!existsSync(catalogDir)) throw new Error(`[a2ui-prompt] Catalog directory does not exist: ${options.catalogDir}`);
|
|
692
|
+
if (!statSync(catalogDir).isDirectory()) throw new Error(`[a2ui-prompt] Catalog path is not a directory: ${options.catalogDir}`);
|
|
693
|
+
const componentManifests = [];
|
|
694
|
+
for (const entry of readdirSync(catalogDir, {
|
|
695
|
+
withFileTypes: true
|
|
696
|
+
})){
|
|
697
|
+
if (!entry.isDirectory() || 'functions' === entry.name) continue;
|
|
698
|
+
const catalogJsonPath = join(catalogDir, entry.name, 'catalog.json');
|
|
699
|
+
if (existsSync(catalogJsonPath)) componentManifests.push(readCatalogManifest(catalogJsonPath));
|
|
700
|
+
}
|
|
701
|
+
if (0 === componentManifests.length) throw new Error(`[a2ui-prompt] No component catalog files found in ${options.catalogDir}. Expected files like <Component>/catalog.json. Run "genui a2ui generate catalog" first or pass --catalog-dir to the generated catalog directory.`);
|
|
702
|
+
return createA2UICatalogFromManifests({
|
|
703
|
+
catalogId: options.catalogId,
|
|
704
|
+
componentManifests,
|
|
705
|
+
functions: readFunctionDefinitions(catalogDir),
|
|
706
|
+
...options.label ? {
|
|
707
|
+
label: options.label
|
|
708
|
+
} : {},
|
|
709
|
+
...options.version ? {
|
|
710
|
+
version: options.version
|
|
711
|
+
} : {}
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
function readFunctionDefinitions(catalogDir) {
|
|
715
|
+
const functionsDir = join(catalogDir, 'functions');
|
|
716
|
+
if (!existsSync(functionsDir)) return [];
|
|
717
|
+
if (!statSync(functionsDir).isDirectory()) throw new Error(`[a2ui-prompt] Expected functions directory at ${functionsDir}.`);
|
|
718
|
+
const functions = [];
|
|
719
|
+
for (const entry of readdirSync(functionsDir, {
|
|
720
|
+
withFileTypes: true
|
|
721
|
+
})){
|
|
722
|
+
if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
|
|
723
|
+
const functionRecord = readJsonObject(join(functionsDir, entry.name));
|
|
724
|
+
for (const [name, value] of Object.entries(functionRecord)){
|
|
725
|
+
if (!src_isRecord(value)) continue;
|
|
726
|
+
const description = value["description"];
|
|
727
|
+
const parameters = value['parameters'];
|
|
728
|
+
const returnType = value['returnType'];
|
|
729
|
+
functions.push({
|
|
730
|
+
name,
|
|
731
|
+
...'string' == typeof description ? {
|
|
732
|
+
description
|
|
733
|
+
} : {},
|
|
734
|
+
parameters: src_isRecord(parameters) ? parameters : {
|
|
735
|
+
type: 'object',
|
|
736
|
+
properties: {},
|
|
737
|
+
additionalProperties: false
|
|
738
|
+
},
|
|
739
|
+
returnType: isReturnType(returnType) ? returnType : 'any'
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
return functions.sort((left, right)=>left.name.localeCompare(right.name));
|
|
744
|
+
}
|
|
745
|
+
function readJsonObject(filePath) {
|
|
746
|
+
const value = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
747
|
+
if (!src_isRecord(value)) throw new Error(`[a2ui-prompt] Expected JSON object in ${filePath}.`);
|
|
748
|
+
return value;
|
|
749
|
+
}
|
|
750
|
+
function readCatalogManifest(filePath) {
|
|
751
|
+
const manifest = readJsonObject(filePath);
|
|
752
|
+
const keys = Object.keys(manifest);
|
|
753
|
+
if (1 !== keys.length) throw new Error(`[a2ui-prompt] Expected exactly one component manifest in ${filePath}, found ${keys.length}.`);
|
|
754
|
+
const componentName = keys[0];
|
|
755
|
+
const schema = manifest[componentName];
|
|
756
|
+
if (!src_isRecord(schema)) throw new Error(`[a2ui-prompt] Expected JSON schema object for ${componentName} in ${filePath}.`);
|
|
757
|
+
return {
|
|
758
|
+
[componentName]: schema
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
function src_isRecord(value) {
|
|
762
|
+
return null !== value && 'object' == typeof value && !Array.isArray(value);
|
|
763
|
+
}
|
|
764
|
+
function isReturnType(value) {
|
|
765
|
+
return 'string' === value || 'number' === value || 'boolean' === value || 'array' === value || 'object' === value || 'any' === value || 'void' === value;
|
|
766
|
+
}
|
|
767
|
+
export { A2UI_PROTOCOL_VERSION, A2UI_SYSTEM_PROMPT, BASIC_CATALOG, BASIC_CATALOG_EXAMPLES, a2ui_catalog_BASIC_CATALOG_ID as BASIC_CATALOG_ID, buildA2UISystemPrompt, createA2UICatalogFromManifests, readA2UICatalogFromDirectory, renderCatalogReference };
|