@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.
Files changed (254) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +170 -0
  3. package/a2ui/README.md +140 -0
  4. package/a2ui/dist/catalog/Button/catalog.json +156 -0
  5. package/a2ui/dist/catalog/Button/index.d.ts +35 -0
  6. package/a2ui/dist/catalog/Button/index.js +35 -0
  7. package/a2ui/dist/catalog/Button/index.js.map +1 -0
  8. package/a2ui/dist/catalog/Card/catalog.json +24 -0
  9. package/a2ui/dist/catalog/Card/index.d.ts +11 -0
  10. package/a2ui/dist/catalog/Card/index.js +19 -0
  11. package/a2ui/dist/catalog/Card/index.js.map +1 -0
  12. package/a2ui/dist/catalog/CheckBox/catalog.json +170 -0
  13. package/a2ui/dist/catalog/CheckBox/index.d.ts +32 -0
  14. package/a2ui/dist/catalog/CheckBox/index.js +24 -0
  15. package/a2ui/dist/catalog/CheckBox/index.js.map +1 -0
  16. package/a2ui/dist/catalog/Column/catalog.json +57 -0
  17. package/a2ui/dist/catalog/Column/index.d.ts +15 -0
  18. package/a2ui/dist/catalog/Column/index.js +55 -0
  19. package/a2ui/dist/catalog/Column/index.js.map +1 -0
  20. package/a2ui/dist/catalog/Divider/catalog.json +14 -0
  21. package/a2ui/dist/catalog/Divider/index.d.ts +9 -0
  22. package/a2ui/dist/catalog/Divider/index.js +8 -0
  23. package/a2ui/dist/catalog/Divider/index.js.map +1 -0
  24. package/a2ui/dist/catalog/Icon/catalog.json +45 -0
  25. package/a2ui/dist/catalog/Icon/index.d.ts +14 -0
  26. package/a2ui/dist/catalog/Icon/index.js +11 -0
  27. package/a2ui/dist/catalog/Icon/index.js.map +1 -0
  28. package/a2ui/dist/catalog/Image/catalog.json +62 -0
  29. package/a2ui/dist/catalog/Image/index.d.ts +16 -0
  30. package/a2ui/dist/catalog/Image/index.js +30 -0
  31. package/a2ui/dist/catalog/Image/index.js.map +1 -0
  32. package/a2ui/dist/catalog/LineChart/catalog.json +98 -0
  33. package/a2ui/dist/catalog/LineChart/index.d.ts +31 -0
  34. package/a2ui/dist/catalog/LineChart/index.js +195 -0
  35. package/a2ui/dist/catalog/LineChart/index.js.map +1 -0
  36. package/a2ui/dist/catalog/List/catalog.json +52 -0
  37. package/a2ui/dist/catalog/List/index.d.ts +15 -0
  38. package/a2ui/dist/catalog/List/index.js +53 -0
  39. package/a2ui/dist/catalog/List/index.js.map +1 -0
  40. package/a2ui/dist/catalog/Modal/catalog.json +18 -0
  41. package/a2ui/dist/catalog/Modal/index.d.ts +12 -0
  42. package/a2ui/dist/catalog/Modal/index.js +33 -0
  43. package/a2ui/dist/catalog/Modal/index.js.map +1 -0
  44. package/a2ui/dist/catalog/PieChart/catalog.json +87 -0
  45. package/a2ui/dist/catalog/PieChart/index.d.ts +37 -0
  46. package/a2ui/dist/catalog/PieChart/index.js +131 -0
  47. package/a2ui/dist/catalog/PieChart/index.js.map +1 -0
  48. package/a2ui/dist/catalog/RadioGroup/catalog.json +184 -0
  49. package/a2ui/dist/catalog/RadioGroup/index.d.ts +36 -0
  50. package/a2ui/dist/catalog/RadioGroup/index.js +36 -0
  51. package/a2ui/dist/catalog/RadioGroup/index.js.map +1 -0
  52. package/a2ui/dist/catalog/Row/catalog.json +57 -0
  53. package/a2ui/dist/catalog/Row/index.d.ts +15 -0
  54. package/a2ui/dist/catalog/Row/index.js +28 -0
  55. package/a2ui/dist/catalog/Row/index.js.map +1 -0
  56. package/a2ui/dist/catalog/Slider/catalog.json +183 -0
  57. package/a2ui/dist/catalog/Slider/index.d.ts +41 -0
  58. package/a2ui/dist/catalog/Slider/index.js +39 -0
  59. package/a2ui/dist/catalog/Slider/index.js.map +1 -0
  60. package/a2ui/dist/catalog/Slider/utils.d.ts +11 -0
  61. package/a2ui/dist/catalog/Slider/utils.js +58 -0
  62. package/a2ui/dist/catalog/Slider/utils.js.map +1 -0
  63. package/a2ui/dist/catalog/Tabs/catalog.json +28 -0
  64. package/a2ui/dist/catalog/Tabs/index.d.ts +12 -0
  65. package/a2ui/dist/catalog/Tabs/index.js +32 -0
  66. package/a2ui/dist/catalog/Tabs/index.js.map +1 -0
  67. package/a2ui/dist/catalog/Text/catalog.json +74 -0
  68. package/a2ui/dist/catalog/Text/index.d.ts +18 -0
  69. package/a2ui/dist/catalog/Text/index.js +27 -0
  70. package/a2ui/dist/catalog/Text/index.js.map +1 -0
  71. package/a2ui/dist/catalog/TextField/catalog.json +147 -0
  72. package/a2ui/dist/catalog/TextField/index.d.ts +35 -0
  73. package/a2ui/dist/catalog/TextField/index.js +43 -0
  74. package/a2ui/dist/catalog/TextField/index.js.map +1 -0
  75. package/a2ui/dist/catalog/TextField/utils.d.ts +7 -0
  76. package/a2ui/dist/catalog/TextField/utils.js +51 -0
  77. package/a2ui/dist/catalog/TextField/utils.js.map +1 -0
  78. package/a2ui/dist/catalog/defineCatalog.d.ts +119 -0
  79. package/a2ui/dist/catalog/defineCatalog.js +196 -0
  80. package/a2ui/dist/catalog/defineCatalog.js.map +1 -0
  81. package/a2ui/dist/catalog/index.d.ts +20 -0
  82. package/a2ui/dist/catalog/index.js +26 -0
  83. package/a2ui/dist/catalog/index.js.map +1 -0
  84. package/a2ui/dist/catalog/utils/chart.d.ts +3 -0
  85. package/a2ui/dist/catalog/utils/chart.js +28 -0
  86. package/a2ui/dist/catalog/utils/chart.js.map +1 -0
  87. package/a2ui/dist/functions/index.d.ts +19 -0
  88. package/a2ui/dist/functions/index.js +87 -0
  89. package/a2ui/dist/functions/index.js.map +1 -0
  90. package/a2ui/dist/index.d.ts +11 -0
  91. package/a2ui/dist/index.js +35 -0
  92. package/a2ui/dist/index.js.map +1 -0
  93. package/a2ui/dist/react/A2UI.d.ts +77 -0
  94. package/a2ui/dist/react/A2UI.js +159 -0
  95. package/a2ui/dist/react/A2UI.js.map +1 -0
  96. package/a2ui/dist/react/A2UIProvider.d.ts +25 -0
  97. package/a2ui/dist/react/A2UIProvider.js +20 -0
  98. package/a2ui/dist/react/A2UIProvider.js.map +1 -0
  99. package/a2ui/dist/react/A2UIRenderer.d.ts +34 -0
  100. package/a2ui/dist/react/A2UIRenderer.js +161 -0
  101. package/a2ui/dist/react/A2UIRenderer.js.map +1 -0
  102. package/a2ui/dist/react/FormContext.d.ts +10 -0
  103. package/a2ui/dist/react/FormContext.js +12 -0
  104. package/a2ui/dist/react/FormContext.js.map +1 -0
  105. package/a2ui/dist/react/index.d.ts +8 -0
  106. package/a2ui/dist/react/index.js +23 -0
  107. package/a2ui/dist/react/index.js.map +1 -0
  108. package/a2ui/dist/react/useA2UIContext.d.ts +7 -0
  109. package/a2ui/dist/react/useA2UIContext.js +19 -0
  110. package/a2ui/dist/react/useA2UIContext.js.map +1 -0
  111. package/a2ui/dist/react/useAction.d.ts +9 -0
  112. package/a2ui/dist/react/useAction.js +38 -0
  113. package/a2ui/dist/react/useAction.js.map +1 -0
  114. package/a2ui/dist/react/useCatalog.d.ts +7 -0
  115. package/a2ui/dist/react/useCatalog.js +13 -0
  116. package/a2ui/dist/react/useCatalog.js.map +1 -0
  117. package/a2ui/dist/react/useChecks.d.ts +27 -0
  118. package/a2ui/dist/react/useChecks.js +76 -0
  119. package/a2ui/dist/react/useChecks.js.map +1 -0
  120. package/a2ui/dist/react/useDataBinding.d.ts +10 -0
  121. package/a2ui/dist/react/useDataBinding.js +175 -0
  122. package/a2ui/dist/react/useDataBinding.js.map +1 -0
  123. package/a2ui/dist/store/FormController.d.ts +23 -0
  124. package/a2ui/dist/store/FormController.js +40 -0
  125. package/a2ui/dist/store/FormController.js.map +1 -0
  126. package/a2ui/dist/store/FunctionRegistry.d.ts +47 -0
  127. package/a2ui/dist/store/FunctionRegistry.js +23 -0
  128. package/a2ui/dist/store/FunctionRegistry.js.map +1 -0
  129. package/a2ui/dist/store/MessageProcessor.d.ts +28 -0
  130. package/a2ui/dist/store/MessageProcessor.js +408 -0
  131. package/a2ui/dist/store/MessageProcessor.js.map +1 -0
  132. package/a2ui/dist/store/MessageStore.d.ts +38 -0
  133. package/a2ui/dist/store/MessageStore.js +37 -0
  134. package/a2ui/dist/store/MessageStore.js.map +1 -0
  135. package/a2ui/dist/store/Resource.d.ts +45 -0
  136. package/a2ui/dist/store/Resource.js +80 -0
  137. package/a2ui/dist/store/Resource.js.map +1 -0
  138. package/a2ui/dist/store/SignalStore.d.ts +10 -0
  139. package/a2ui/dist/store/SignalStore.js +29 -0
  140. package/a2ui/dist/store/SignalStore.js.map +1 -0
  141. package/a2ui/dist/store/index.d.ts +14 -0
  142. package/a2ui/dist/store/index.js +15 -0
  143. package/a2ui/dist/store/index.js.map +1 -0
  144. package/a2ui/dist/store/payloadNormalizer.d.ts +27 -0
  145. package/a2ui/dist/store/payloadNormalizer.js +179 -0
  146. package/a2ui/dist/store/payloadNormalizer.js.map +1 -0
  147. package/a2ui/dist/store/resolveFunctionCall.d.ts +18 -0
  148. package/a2ui/dist/store/resolveFunctionCall.js +131 -0
  149. package/a2ui/dist/store/resolveFunctionCall.js.map +1 -0
  150. package/a2ui/dist/store/types.d.ts +68 -0
  151. package/a2ui/dist/store/types.js +2 -0
  152. package/a2ui/dist/store/types.js.map +1 -0
  153. package/a2ui/dist/tsconfig.build.tsbuildinfo +1 -0
  154. package/a2ui/styles/catalog/Button.css +83 -0
  155. package/a2ui/styles/catalog/Card.css +49 -0
  156. package/a2ui/styles/catalog/CheckBox.css +46 -0
  157. package/a2ui/styles/catalog/Column.css +89 -0
  158. package/a2ui/styles/catalog/Divider.css +20 -0
  159. package/a2ui/styles/catalog/Icon.css +39 -0
  160. package/a2ui/styles/catalog/Image.css +54 -0
  161. package/a2ui/styles/catalog/LineChart.css +116 -0
  162. package/a2ui/styles/catalog/List.css +38 -0
  163. package/a2ui/styles/catalog/Modal.css +60 -0
  164. package/a2ui/styles/catalog/PieChart.css +109 -0
  165. package/a2ui/styles/catalog/RadioGroup.css +123 -0
  166. package/a2ui/styles/catalog/Row.css +83 -0
  167. package/a2ui/styles/catalog/Slider.css +96 -0
  168. package/a2ui/styles/catalog/Tabs.css +46 -0
  169. package/a2ui/styles/catalog/Text.css +121 -0
  170. package/a2ui/styles/catalog/TextField.css +48 -0
  171. package/a2ui/styles/theme.css +62 -0
  172. package/a2ui-catalog-extractor/README.md +605 -0
  173. package/a2ui-catalog-extractor/bin/a2ui-catalog-extractor.js +6 -0
  174. package/a2ui-catalog-extractor/dist/cli.d.ts +12 -0
  175. package/a2ui-catalog-extractor/dist/cli.js +171 -0
  176. package/a2ui-catalog-extractor/dist/cli.js.map +1 -0
  177. package/a2ui-catalog-extractor/dist/index.d.ts +140 -0
  178. package/a2ui-catalog-extractor/dist/index.js +755 -0
  179. package/a2ui-catalog-extractor/dist/index.js.map +1 -0
  180. package/a2ui-catalog-extractor/dist/tsconfig.build.tsbuildinfo +1 -0
  181. package/a2ui-catalog-extractor/skills/a2ui-catalog-extractor/SKILL.md +30 -0
  182. package/a2ui-prompt/README.md +65 -0
  183. package/a2ui-prompt/dist/index.d.ts +91 -0
  184. package/a2ui-prompt/dist/index.js +767 -0
  185. package/cli/README.md +88 -0
  186. package/cli/bin/cli.js +271 -0
  187. package/dist/index.d.ts +10 -0
  188. package/dist/index.js +10 -0
  189. package/dist/index.js.map +1 -0
  190. package/dist/tsconfig.build.tsbuildinfo +1 -0
  191. package/index.ts +114 -0
  192. package/openui/README.md +211 -0
  193. package/openui/dist/catalog/Action/index.d.ts +20 -0
  194. package/openui/dist/catalog/Action/index.js +23 -0
  195. package/openui/dist/catalog/Action/index.js.map +1 -0
  196. package/openui/dist/catalog/Button/index.d.ts +64 -0
  197. package/openui/dist/catalog/Button/index.js +75 -0
  198. package/openui/dist/catalog/Button/index.js.map +1 -0
  199. package/openui/dist/catalog/Card/index.d.ts +34 -0
  200. package/openui/dist/catalog/Card/index.js +69 -0
  201. package/openui/dist/catalog/Card/index.js.map +1 -0
  202. package/openui/dist/catalog/CardHeader/index.d.ts +5 -0
  203. package/openui/dist/catalog/CardHeader/index.js +18 -0
  204. package/openui/dist/catalog/CardHeader/index.js.map +1 -0
  205. package/openui/dist/catalog/Separator/index.d.ts +2 -0
  206. package/openui/dist/catalog/Separator/index.js +13 -0
  207. package/openui/dist/catalog/Separator/index.js.map +1 -0
  208. package/openui/dist/catalog/Stack/index.d.ts +29 -0
  209. package/openui/dist/catalog/Stack/index.js +61 -0
  210. package/openui/dist/catalog/Stack/index.js.map +1 -0
  211. package/openui/dist/catalog/Tag/index.d.ts +4 -0
  212. package/openui/dist/catalog/Tag/index.js +15 -0
  213. package/openui/dist/catalog/Tag/index.js.map +1 -0
  214. package/openui/dist/catalog/TextContent/index.d.ts +11 -0
  215. package/openui/dist/catalog/TextContent/index.js +33 -0
  216. package/openui/dist/catalog/TextContent/index.js.map +1 -0
  217. package/openui/dist/catalog/index.d.ts +7 -0
  218. package/openui/dist/catalog/index.js +11 -0
  219. package/openui/dist/catalog/index.js.map +1 -0
  220. package/openui/dist/catalog/utils.d.ts +2 -0
  221. package/openui/dist/catalog/utils.js +17 -0
  222. package/openui/dist/catalog/utils.js.map +1 -0
  223. package/openui/dist/core/context.d.ts +112 -0
  224. package/openui/dist/core/context.js +99 -0
  225. package/openui/dist/core/context.js.map +1 -0
  226. package/openui/dist/core/createLibrary.d.ts +10 -0
  227. package/openui/dist/core/createLibrary.js +36 -0
  228. package/openui/dist/core/createLibrary.js.map +1 -0
  229. package/openui/dist/core/hooks/index.d.ts +4 -0
  230. package/openui/dist/core/hooks/index.js +6 -0
  231. package/openui/dist/core/hooks/index.js.map +1 -0
  232. package/openui/dist/core/hooks/useFormValidation.d.ts +13 -0
  233. package/openui/dist/core/hooks/useFormValidation.js +76 -0
  234. package/openui/dist/core/hooks/useFormValidation.js.map +1 -0
  235. package/openui/dist/core/hooks/useOpenUIState.d.ts +33 -0
  236. package/openui/dist/core/hooks/useOpenUIState.js +413 -0
  237. package/openui/dist/core/hooks/useOpenUIState.js.map +1 -0
  238. package/openui/dist/core/hooks/useStateField.d.ts +2 -0
  239. package/openui/dist/core/hooks/useStateField.js +11 -0
  240. package/openui/dist/core/hooks/useStateField.js.map +1 -0
  241. package/openui/dist/core/index.d.ts +7 -0
  242. package/openui/dist/core/index.js +8 -0
  243. package/openui/dist/core/index.js.map +1 -0
  244. package/openui/dist/core/library.d.ts +20 -0
  245. package/openui/dist/core/library.js +13 -0
  246. package/openui/dist/core/library.js.map +1 -0
  247. package/openui/dist/core/renderer.css +271 -0
  248. package/openui/dist/core/renderer.d.ts +9 -0
  249. package/openui/dist/core/renderer.js +139 -0
  250. package/openui/dist/core/renderer.js.map +1 -0
  251. package/openui/dist/core/utils.d.ts +1 -0
  252. package/openui/dist/core/utils.js +76 -0
  253. package/openui/dist/core/utils.js.map +1 -0
  254. 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 };