@tai42/studio-sdk 0.2.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 (422) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +15 -0
  3. package/README.md +318 -0
  4. package/SECURITY.md +51 -0
  5. package/dist/components/badge.d.ts +13 -0
  6. package/dist/components/badge.d.ts.map +1 -0
  7. package/dist/components/badge.js +14 -0
  8. package/dist/components/badge.js.map +1 -0
  9. package/dist/components/checkbox.d.ts +24 -0
  10. package/dist/components/checkbox.d.ts.map +1 -0
  11. package/dist/components/checkbox.js +31 -0
  12. package/dist/components/checkbox.js.map +1 -0
  13. package/dist/components/code-block.d.ts +7 -0
  14. package/dist/components/code-block.d.ts.map +1 -0
  15. package/dist/components/code-block.js +23 -0
  16. package/dist/components/code-block.js.map +1 -0
  17. package/dist/components/completion-input.d.ts +31 -0
  18. package/dist/components/completion-input.d.ts.map +1 -0
  19. package/dist/components/completion-input.js +61 -0
  20. package/dist/components/completion-input.js.map +1 -0
  21. package/dist/components/components.css +2212 -0
  22. package/dist/components/confirm-dialog.d.ts +40 -0
  23. package/dist/components/confirm-dialog.d.ts.map +1 -0
  24. package/dist/components/confirm-dialog.js +11 -0
  25. package/dist/components/confirm-dialog.js.map +1 -0
  26. package/dist/components/control-styles.d.ts +12 -0
  27. package/dist/components/control-styles.d.ts.map +1 -0
  28. package/dist/components/control-styles.js +14 -0
  29. package/dist/components/control-styles.js.map +1 -0
  30. package/dist/components/copy-field.d.ts +8 -0
  31. package/dist/components/copy-field.d.ts.map +1 -0
  32. package/dist/components/copy-field.js +106 -0
  33. package/dist/components/copy-field.js.map +1 -0
  34. package/dist/components/dialog.d.ts +21 -0
  35. package/dist/components/dialog.d.ts.map +1 -0
  36. package/dist/components/dialog.js +25 -0
  37. package/dist/components/dialog.js.map +1 -0
  38. package/dist/components/drawer.d.ts +22 -0
  39. package/dist/components/drawer.d.ts.map +1 -0
  40. package/dist/components/drawer.js +32 -0
  41. package/dist/components/drawer.js.map +1 -0
  42. package/dist/components/error-boundary.d.ts +33 -0
  43. package/dist/components/error-boundary.d.ts.map +1 -0
  44. package/dist/components/error-boundary.js +36 -0
  45. package/dist/components/error-boundary.js.map +1 -0
  46. package/dist/components/extension-combo-builder.d.ts +70 -0
  47. package/dist/components/extension-combo-builder.d.ts.map +1 -0
  48. package/dist/components/extension-combo-builder.js +154 -0
  49. package/dist/components/extension-combo-builder.js.map +1 -0
  50. package/dist/components/extension-grouping.d.ts +65 -0
  51. package/dist/components/extension-grouping.d.ts.map +1 -0
  52. package/dist/components/extension-grouping.js +78 -0
  53. package/dist/components/extension-grouping.js.map +1 -0
  54. package/dist/components/extension-picker.d.ts +35 -0
  55. package/dist/components/extension-picker.d.ts.map +1 -0
  56. package/dist/components/extension-picker.js +31 -0
  57. package/dist/components/extension-picker.js.map +1 -0
  58. package/dist/components/external-link.d.ts +19 -0
  59. package/dist/components/external-link.d.ts.map +1 -0
  60. package/dist/components/external-link.js +12 -0
  61. package/dist/components/external-link.js.map +1 -0
  62. package/dist/components/field.d.ts +64 -0
  63. package/dist/components/field.d.ts.map +1 -0
  64. package/dist/components/field.js +56 -0
  65. package/dist/components/field.js.map +1 -0
  66. package/dist/components/fleet-report.d.ts +39 -0
  67. package/dist/components/fleet-report.d.ts.map +1 -0
  68. package/dist/components/fleet-report.js +50 -0
  69. package/dist/components/fleet-report.js.map +1 -0
  70. package/dist/components/fonts.css +22 -0
  71. package/dist/components/icons.d.ts +142 -0
  72. package/dist/components/icons.d.ts.map +1 -0
  73. package/dist/components/icons.js +139 -0
  74. package/dist/components/icons.js.map +1 -0
  75. package/dist/components/inputs.d.ts +52 -0
  76. package/dist/components/inputs.d.ts.map +1 -0
  77. package/dist/components/inputs.js +35 -0
  78. package/dist/components/inputs.js.map +1 -0
  79. package/dist/components/json-diff.d.ts +40 -0
  80. package/dist/components/json-diff.d.ts.map +1 -0
  81. package/dist/components/json-diff.js +119 -0
  82. package/dist/components/json-diff.js.map +1 -0
  83. package/dist/components/json-tree.d.ts +8 -0
  84. package/dist/components/json-tree.d.ts.map +1 -0
  85. package/dist/components/json-tree.js +70 -0
  86. package/dist/components/json-tree.js.map +1 -0
  87. package/dist/components/modal-focus.d.ts +15 -0
  88. package/dist/components/modal-focus.d.ts.map +1 -0
  89. package/dist/components/modal-focus.js +136 -0
  90. package/dist/components/modal-focus.js.map +1 -0
  91. package/dist/components/page-header.d.ts +56 -0
  92. package/dist/components/page-header.d.ts.map +1 -0
  93. package/dist/components/page-header.js +24 -0
  94. package/dist/components/page-header.js.map +1 -0
  95. package/dist/components/primitives.d.ts +186 -0
  96. package/dist/components/primitives.d.ts.map +1 -0
  97. package/dist/components/primitives.js +204 -0
  98. package/dist/components/primitives.js.map +1 -0
  99. package/dist/components/progress-bar.d.ts +29 -0
  100. package/dist/components/progress-bar.d.ts.map +1 -0
  101. package/dist/components/progress-bar.js +48 -0
  102. package/dist/components/progress-bar.js.map +1 -0
  103. package/dist/components/radio-group.d.ts +37 -0
  104. package/dist/components/radio-group.d.ts.map +1 -0
  105. package/dist/components/radio-group.js +56 -0
  106. package/dist/components/radio-group.js.map +1 -0
  107. package/dist/components/reveal-input.d.ts +13 -0
  108. package/dist/components/reveal-input.d.ts.map +1 -0
  109. package/dist/components/reveal-input.js +31 -0
  110. package/dist/components/reveal-input.js.map +1 -0
  111. package/dist/components/scroll-region.d.ts +85 -0
  112. package/dist/components/scroll-region.d.ts.map +1 -0
  113. package/dist/components/scroll-region.js +574 -0
  114. package/dist/components/scroll-region.js.map +1 -0
  115. package/dist/components/select.d.ts +53 -0
  116. package/dist/components/select.d.ts.map +1 -0
  117. package/dist/components/select.js +33 -0
  118. package/dist/components/select.js.map +1 -0
  119. package/dist/components/table.d.ts +59 -0
  120. package/dist/components/table.d.ts.map +1 -0
  121. package/dist/components/table.js +20 -0
  122. package/dist/components/table.js.map +1 -0
  123. package/dist/components/tabs.d.ts +15 -0
  124. package/dist/components/tabs.d.ts.map +1 -0
  125. package/dist/components/tabs.js +13 -0
  126. package/dist/components/tabs.js.map +1 -0
  127. package/dist/components/tags.d.ts +46 -0
  128. package/dist/components/tags.d.ts.map +1 -0
  129. package/dist/components/tags.js +68 -0
  130. package/dist/components/tags.js.map +1 -0
  131. package/dist/components/tokens.css +392 -0
  132. package/dist/components/tokens.d.ts +15 -0
  133. package/dist/components/tokens.d.ts.map +1 -0
  134. package/dist/components/tokens.js +101 -0
  135. package/dist/components/tokens.js.map +1 -0
  136. package/dist/components/tool-picker.d.ts +24 -0
  137. package/dist/components/tool-picker.d.ts.map +1 -0
  138. package/dist/components/tool-picker.js +97 -0
  139. package/dist/components/tool-picker.js.map +1 -0
  140. package/dist/components/tooltip.d.ts +17 -0
  141. package/dist/components/tooltip.d.ts.map +1 -0
  142. package/dist/components/tooltip.js +18 -0
  143. package/dist/components/tooltip.js.map +1 -0
  144. package/dist/components/version-history-panel.d.ts +71 -0
  145. package/dist/components/version-history-panel.d.ts.map +1 -0
  146. package/dist/components/version-history-panel.js +148 -0
  147. package/dist/components/version-history-panel.js.map +1 -0
  148. package/dist/download.d.ts +7 -0
  149. package/dist/download.d.ts.map +1 -0
  150. package/dist/download.js +18 -0
  151. package/dist/download.js.map +1 -0
  152. package/dist/element-slot.d.ts +4 -0
  153. package/dist/element-slot.d.ts.map +1 -0
  154. package/dist/element-slot.js +20 -0
  155. package/dist/element-slot.js.map +1 -0
  156. package/dist/elicitation/ElicitationForm.d.ts +34 -0
  157. package/dist/elicitation/ElicitationForm.d.ts.map +1 -0
  158. package/dist/elicitation/ElicitationForm.js +24 -0
  159. package/dist/elicitation/ElicitationForm.js.map +1 -0
  160. package/dist/errors.d.ts +9 -0
  161. package/dist/errors.d.ts.map +1 -0
  162. package/dist/errors.js +15 -0
  163. package/dist/errors.js.map +1 -0
  164. package/dist/extension-combos.d.ts +13 -0
  165. package/dist/extension-combos.d.ts.map +1 -0
  166. package/dist/extension-combos.js +9 -0
  167. package/dist/extension-combos.js.map +1 -0
  168. package/dist/hooks/useApi.d.ts +1222 -0
  169. package/dist/hooks/useApi.d.ts.map +1 -0
  170. package/dist/hooks/useApi.js +22 -0
  171. package/dist/hooks/useApi.js.map +1 -0
  172. package/dist/hooks/useAuth.d.ts +13 -0
  173. package/dist/hooks/useAuth.d.ts.map +1 -0
  174. package/dist/hooks/useAuth.js +53 -0
  175. package/dist/hooks/useAuth.js.map +1 -0
  176. package/dist/hooks/useBreakpoint.d.ts +10 -0
  177. package/dist/hooks/useBreakpoint.d.ts.map +1 -0
  178. package/dist/hooks/useBreakpoint.js +66 -0
  179. package/dist/hooks/useBreakpoint.js.map +1 -0
  180. package/dist/hooks/useCapabilities.d.ts +88 -0
  181. package/dist/hooks/useCapabilities.d.ts.map +1 -0
  182. package/dist/hooks/useCapabilities.js +203 -0
  183. package/dist/hooks/useCapabilities.js.map +1 -0
  184. package/dist/hooks/useSse.d.ts +13 -0
  185. package/dist/hooks/useSse.d.ts.map +1 -0
  186. package/dist/hooks/useSse.js +298 -0
  187. package/dist/hooks/useSse.js.map +1 -0
  188. package/dist/hooks/useTheme.d.ts +20 -0
  189. package/dist/hooks/useTheme.d.ts.map +1 -0
  190. package/dist/hooks/useTheme.js +97 -0
  191. package/dist/hooks/useTheme.js.map +1 -0
  192. package/dist/hooks/useUnauthorized.d.ts +3 -0
  193. package/dist/hooks/useUnauthorized.d.ts.map +1 -0
  194. package/dist/hooks/useUnauthorized.js +18 -0
  195. package/dist/hooks/useUnauthorized.js.map +1 -0
  196. package/dist/host.d.ts +11 -0
  197. package/dist/host.d.ts.map +1 -0
  198. package/dist/host.js +10 -0
  199. package/dist/host.js.map +1 -0
  200. package/dist/index.d.ts +118 -0
  201. package/dist/index.d.ts.map +1 -0
  202. package/dist/index.js +91 -0
  203. package/dist/index.js.map +1 -0
  204. package/dist/navigation/context.d.ts +25 -0
  205. package/dist/navigation/context.d.ts.map +1 -0
  206. package/dist/navigation/context.js +45 -0
  207. package/dist/navigation/context.js.map +1 -0
  208. package/dist/navigation/index.d.ts +4 -0
  209. package/dist/navigation/index.d.ts.map +1 -0
  210. package/dist/navigation/index.js +2 -0
  211. package/dist/navigation/index.js.map +1 -0
  212. package/dist/navigation/types.d.ts +97 -0
  213. package/dist/navigation/types.d.ts.map +1 -0
  214. package/dist/navigation/types.js +16 -0
  215. package/dist/navigation/types.js.map +1 -0
  216. package/dist/plugin/host-state.d.ts +45 -0
  217. package/dist/plugin/host-state.d.ts.map +1 -0
  218. package/dist/plugin/host-state.js +62 -0
  219. package/dist/plugin/host-state.js.map +1 -0
  220. package/dist/plugin/registry.d.ts +35 -0
  221. package/dist/plugin/registry.d.ts.map +1 -0
  222. package/dist/plugin/registry.js +129 -0
  223. package/dist/plugin/registry.js.map +1 -0
  224. package/dist/plugin/types.d.ts +165 -0
  225. package/dist/plugin/types.d.ts.map +1 -0
  226. package/dist/plugin/types.js +2 -0
  227. package/dist/plugin/types.js.map +1 -0
  228. package/dist/plugin/version.d.ts +34 -0
  229. package/dist/plugin/version.d.ts.map +1 -0
  230. package/dist/plugin/version.js +37 -0
  231. package/dist/plugin/version.js.map +1 -0
  232. package/dist/query-keys.d.ts +31 -0
  233. package/dist/query-keys.d.ts.map +1 -0
  234. package/dist/query-keys.js +31 -0
  235. package/dist/query-keys.js.map +1 -0
  236. package/dist/schema-editor/SchemaEditor.d.ts +43 -0
  237. package/dist/schema-editor/SchemaEditor.d.ts.map +1 -0
  238. package/dist/schema-editor/SchemaEditor.js +106 -0
  239. package/dist/schema-editor/SchemaEditor.js.map +1 -0
  240. package/dist/schema-editor/index.d.ts +10 -0
  241. package/dist/schema-editor/index.d.ts.map +1 -0
  242. package/dist/schema-editor/index.js +8 -0
  243. package/dist/schema-editor/index.js.map +1 -0
  244. package/dist/schema-editor/lint.d.ts +37 -0
  245. package/dist/schema-editor/lint.d.ts.map +1 -0
  246. package/dist/schema-editor/lint.js +49 -0
  247. package/dist/schema-editor/lint.js.map +1 -0
  248. package/dist/schema-form/SchemaForm.d.ts +70 -0
  249. package/dist/schema-form/SchemaForm.d.ts.map +1 -0
  250. package/dist/schema-form/SchemaForm.js +13 -0
  251. package/dist/schema-form/SchemaForm.js.map +1 -0
  252. package/dist/schema-form/array-field.d.ts +20 -0
  253. package/dist/schema-form/array-field.d.ts.map +1 -0
  254. package/dist/schema-form/array-field.js +39 -0
  255. package/dist/schema-form/array-field.js.map +1 -0
  256. package/dist/schema-form/classify.d.ts +87 -0
  257. package/dist/schema-form/classify.d.ts.map +1 -0
  258. package/dist/schema-form/classify.js +199 -0
  259. package/dist/schema-form/classify.js.map +1 -0
  260. package/dist/schema-form/context.d.ts +4 -0
  261. package/dist/schema-form/context.d.ts.map +1 -0
  262. package/dist/schema-form/context.js +14 -0
  263. package/dist/schema-form/context.js.map +1 -0
  264. package/dist/schema-form/default-value.d.ts +8 -0
  265. package/dist/schema-form/default-value.d.ts.map +1 -0
  266. package/dist/schema-form/default-value.js +64 -0
  267. package/dist/schema-form/default-value.js.map +1 -0
  268. package/dist/schema-form/enum-field.d.ts +19 -0
  269. package/dist/schema-form/enum-field.d.ts.map +1 -0
  270. package/dist/schema-form/enum-field.js +26 -0
  271. package/dist/schema-form/enum-field.js.map +1 -0
  272. package/dist/schema-form/field-group.d.ts +9 -0
  273. package/dist/schema-form/field-group.d.ts.map +1 -0
  274. package/dist/schema-form/field-group.js +32 -0
  275. package/dist/schema-form/field-group.js.map +1 -0
  276. package/dist/schema-form/field-node.d.ts +24 -0
  277. package/dist/schema-form/field-node.d.ts.map +1 -0
  278. package/dist/schema-form/field-node.js +51 -0
  279. package/dist/schema-form/field-node.js.map +1 -0
  280. package/dist/schema-form/index.d.ts +13 -0
  281. package/dist/schema-form/index.d.ts.map +1 -0
  282. package/dist/schema-form/index.js +11 -0
  283. package/dist/schema-form/index.js.map +1 -0
  284. package/dist/schema-form/media-field.d.ts +24 -0
  285. package/dist/schema-form/media-field.d.ts.map +1 -0
  286. package/dist/schema-form/media-field.js +169 -0
  287. package/dist/schema-form/media-field.js.map +1 -0
  288. package/dist/schema-form/media.d.ts +40 -0
  289. package/dist/schema-form/media.d.ts.map +1 -0
  290. package/dist/schema-form/media.js +65 -0
  291. package/dist/schema-form/media.js.map +1 -0
  292. package/dist/schema-form/object-fields.d.ts +20 -0
  293. package/dist/schema-form/object-fields.d.ts.map +1 -0
  294. package/dist/schema-form/object-fields.js +24 -0
  295. package/dist/schema-form/object-fields.js.map +1 -0
  296. package/dist/schema-form/resolve.d.ts +41 -0
  297. package/dist/schema-form/resolve.d.ts.map +1 -0
  298. package/dist/schema-form/resolve.js +102 -0
  299. package/dist/schema-form/resolve.js.map +1 -0
  300. package/dist/schema-form/string-field.d.ts +20 -0
  301. package/dist/schema-form/string-field.d.ts.map +1 -0
  302. package/dist/schema-form/string-field.js +68 -0
  303. package/dist/schema-form/string-field.js.map +1 -0
  304. package/dist/schema-form/styles.d.ts +18 -0
  305. package/dist/schema-form/styles.d.ts.map +1 -0
  306. package/dist/schema-form/styles.js +18 -0
  307. package/dist/schema-form/styles.js.map +1 -0
  308. package/dist/schema-form/types.d.ts +67 -0
  309. package/dist/schema-form/types.d.ts.map +1 -0
  310. package/dist/schema-form/types.js +10 -0
  311. package/dist/schema-form/types.js.map +1 -0
  312. package/dist/schema-form/union-field.d.ts +24 -0
  313. package/dist/schema-form/union-field.d.ts.map +1 -0
  314. package/dist/schema-form/union-field.js +55 -0
  315. package/dist/schema-form/union-field.js.map +1 -0
  316. package/dist/schema-form/union.d.ts +4 -0
  317. package/dist/schema-form/union.d.ts.map +1 -0
  318. package/dist/schema-form/union.js +68 -0
  319. package/dist/schema-form/union.js.map +1 -0
  320. package/dist/schema-form/unsupported-notice.d.ts +18 -0
  321. package/dist/schema-form/unsupported-notice.d.ts.map +1 -0
  322. package/dist/schema-form/unsupported-notice.js +8 -0
  323. package/dist/schema-form/unsupported-notice.js.map +1 -0
  324. package/dist/schema-form/validate.d.ts +18 -0
  325. package/dist/schema-form/validate.d.ts.map +1 -0
  326. package/dist/schema-form/validate.js +162 -0
  327. package/dist/schema-form/validate.js.map +1 -0
  328. package/dist/structured-output/StructuredOutput.d.ts +27 -0
  329. package/dist/structured-output/StructuredOutput.d.ts.map +1 -0
  330. package/dist/structured-output/StructuredOutput.js +41 -0
  331. package/dist/structured-output/StructuredOutput.js.map +1 -0
  332. package/dist/testing.d.ts +80 -0
  333. package/dist/testing.d.ts.map +1 -0
  334. package/dist/testing.js +207 -0
  335. package/dist/testing.js.map +1 -0
  336. package/package.json +101 -0
  337. package/src/components/badge.tsx +31 -0
  338. package/src/components/checkbox.tsx +93 -0
  339. package/src/components/code-block.tsx +37 -0
  340. package/src/components/completion-input.tsx +137 -0
  341. package/src/components/components.css +2212 -0
  342. package/src/components/confirm-dialog.tsx +77 -0
  343. package/src/components/control-styles.ts +14 -0
  344. package/src/components/copy-field.tsx +178 -0
  345. package/src/components/dialog.tsx +76 -0
  346. package/src/components/drawer.tsx +83 -0
  347. package/src/components/error-boundary.tsx +56 -0
  348. package/src/components/extension-combo-builder.tsx +305 -0
  349. package/src/components/extension-grouping.ts +116 -0
  350. package/src/components/extension-picker.tsx +97 -0
  351. package/src/components/external-link.tsx +40 -0
  352. package/src/components/field.tsx +169 -0
  353. package/src/components/fleet-report.tsx +136 -0
  354. package/src/components/fonts.css +22 -0
  355. package/src/components/icons.tsx +504 -0
  356. package/src/components/inputs.tsx +125 -0
  357. package/src/components/json-diff.tsx +192 -0
  358. package/src/components/json-tree.tsx +129 -0
  359. package/src/components/modal-focus.ts +150 -0
  360. package/src/components/page-header.tsx +107 -0
  361. package/src/components/primitives.tsx +489 -0
  362. package/src/components/progress-bar.tsx +100 -0
  363. package/src/components/radio-group.tsx +160 -0
  364. package/src/components/reveal-input.tsx +87 -0
  365. package/src/components/scroll-region.tsx +703 -0
  366. package/src/components/select.tsx +149 -0
  367. package/src/components/table.tsx +94 -0
  368. package/src/components/tabs.tsx +51 -0
  369. package/src/components/tags.tsx +137 -0
  370. package/src/components/tokens.css +392 -0
  371. package/src/components/tokens.ts +102 -0
  372. package/src/components/tool-picker.tsx +175 -0
  373. package/src/components/tooltip.tsx +46 -0
  374. package/src/components/version-history-panel.tsx +407 -0
  375. package/src/css.d.ts +2 -0
  376. package/src/download.ts +17 -0
  377. package/src/element-slot.ts +22 -0
  378. package/src/elicitation/ElicitationForm.tsx +87 -0
  379. package/src/errors.ts +12 -0
  380. package/src/extension-combos.ts +18 -0
  381. package/src/hooks/useApi.ts +27 -0
  382. package/src/hooks/useAuth.ts +67 -0
  383. package/src/hooks/useBreakpoint.ts +78 -0
  384. package/src/hooks/useCapabilities.ts +242 -0
  385. package/src/hooks/useSse.ts +314 -0
  386. package/src/hooks/useTheme.ts +133 -0
  387. package/src/hooks/useUnauthorized.ts +20 -0
  388. package/src/host.ts +15 -0
  389. package/src/index.ts +267 -0
  390. package/src/navigation/context.tsx +90 -0
  391. package/src/navigation/index.ts +9 -0
  392. package/src/navigation/types.ts +90 -0
  393. package/src/plugin/host-state.ts +93 -0
  394. package/src/plugin/registry.ts +174 -0
  395. package/src/plugin/types.ts +178 -0
  396. package/src/plugin/version.ts +44 -0
  397. package/src/query-keys.ts +34 -0
  398. package/src/schema-editor/SchemaEditor.tsx +186 -0
  399. package/src/schema-editor/index.ts +9 -0
  400. package/src/schema-editor/lint.ts +70 -0
  401. package/src/schema-form/SchemaForm.tsx +107 -0
  402. package/src/schema-form/array-field.tsx +110 -0
  403. package/src/schema-form/classify.ts +302 -0
  404. package/src/schema-form/context.ts +17 -0
  405. package/src/schema-form/default-value.ts +65 -0
  406. package/src/schema-form/enum-field.tsx +65 -0
  407. package/src/schema-form/field-group.tsx +71 -0
  408. package/src/schema-form/field-node.tsx +161 -0
  409. package/src/schema-form/index.ts +12 -0
  410. package/src/schema-form/media-field.tsx +281 -0
  411. package/src/schema-form/media.ts +69 -0
  412. package/src/schema-form/object-fields.tsx +74 -0
  413. package/src/schema-form/resolve.ts +114 -0
  414. package/src/schema-form/string-field.tsx +161 -0
  415. package/src/schema-form/styles.ts +20 -0
  416. package/src/schema-form/types.ts +71 -0
  417. package/src/schema-form/union-field.tsx +122 -0
  418. package/src/schema-form/union.ts +78 -0
  419. package/src/schema-form/unsupported-notice.tsx +39 -0
  420. package/src/schema-form/validate.ts +183 -0
  421. package/src/structured-output/StructuredOutput.tsx +81 -0
  422. package/src/testing.ts +235 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Renders the property fields of an object schema, one {@link FieldNode} per
3
+ * property. Each child's edits are folded back into an immutable copy of the
4
+ * object value; an optional `skip` omits a property (used for a union's
5
+ * discriminator tag, which the variant picker owns).
6
+ */
7
+ import type { ReactNode } from 'react';
8
+
9
+ import { FieldNode } from './field-node';
10
+ import type { JsonSchema, SchemaFormErrors } from './types';
11
+
12
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
13
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ }
15
+
16
+ export function ObjectFields({
17
+ properties,
18
+ requiredKeys,
19
+ root,
20
+ value,
21
+ onChange,
22
+ path,
23
+ errors,
24
+ idPrefix,
25
+ skip,
26
+ }: {
27
+ properties: readonly (readonly [string, JsonSchema])[];
28
+ requiredKeys: ReadonlySet<string>;
29
+ root: JsonSchema;
30
+ value: unknown;
31
+ onChange: (value: unknown) => void;
32
+ path: string;
33
+ errors: SchemaFormErrors | undefined;
34
+ idPrefix: string;
35
+ skip: string | undefined;
36
+ }): ReactNode {
37
+ const obj = isPlainObject(value) ? value : {};
38
+ return (
39
+ <>
40
+ {properties.map(([name, propSchema]) => {
41
+ if (skip !== undefined && name === skip) return null;
42
+ const childPath = path === '' ? name : `${path}.${name}`;
43
+ return (
44
+ <FieldNode
45
+ key={name}
46
+ schema={propSchema}
47
+ root={root}
48
+ value={obj[name]}
49
+ onChange={(next) => {
50
+ onChange(setKey(obj, name, next));
51
+ }}
52
+ path={childPath}
53
+ label={name}
54
+ required={requiredKeys.has(name)}
55
+ errors={errors}
56
+ idPrefix={idPrefix}
57
+ />
58
+ );
59
+ })}
60
+ </>
61
+ );
62
+ }
63
+
64
+ function setKey(
65
+ obj: Record<string, unknown>,
66
+ name: string,
67
+ next: unknown,
68
+ ): Record<string, unknown> {
69
+ if (next === undefined) {
70
+ const { [name]: _removed, ...rest } = obj;
71
+ return rest;
72
+ }
73
+ return { ...obj, [name]: next };
74
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Pure helpers over the JSON-Schema subset: `$ref`/`$defs` resolution and the
3
+ * small predicates the renderer, defaulting, and validation all share. Kept
4
+ * side-effect-free so they can be unit-tested and reused across the module.
5
+ */
6
+ import type { JsonSchema, JsonSchemaType } from './types';
7
+
8
+ /** Un-escape one JSON-Pointer reference token (`~1`→`/`, `~0`→`~`). */
9
+ function unescapePointerToken(token: string): string {
10
+ return token.replace(/~1/g, '/').replace(/~0/g, '~');
11
+ }
12
+
13
+ /**
14
+ * Resolve an internal `$ref` against the document root, following chained refs
15
+ * (a `$ref` whose target is itself a `$ref`). Only same-document pointers are
16
+ * supported (`#/$defs/Name`, `#/definitions/Name`, `#`); an external or
17
+ * unresolvable ref throws LOUDLY rather than yielding a silent empty schema.
18
+ * A schema with no `$ref` is returned unchanged. A pointer cycle throws.
19
+ */
20
+ export function resolveRef(schema: JsonSchema, root: JsonSchema): JsonSchema {
21
+ let current = schema;
22
+ const seen = new Set<string>();
23
+ while (typeof current.$ref === 'string') {
24
+ const ref = current.$ref;
25
+ if (seen.has(ref)) {
26
+ throw new Error(`SchemaForm: circular $ref chain at "${ref}"`);
27
+ }
28
+ seen.add(ref);
29
+ if (!ref.startsWith('#')) {
30
+ throw new Error(`SchemaForm: only same-document $ref is supported, got "${ref}"`);
31
+ }
32
+ const segments = ref.split('/').slice(1).map(unescapePointerToken);
33
+ let node: unknown = root;
34
+ for (const segment of segments) {
35
+ if (node === null || typeof node !== 'object') {
36
+ throw new Error(`SchemaForm: unresolvable $ref "${ref}"`);
37
+ }
38
+ node = (node as Record<string, unknown>)[segment];
39
+ }
40
+ if (node === null || typeof node !== 'object') {
41
+ throw new Error(`SchemaForm: unresolvable $ref "${ref}"`);
42
+ }
43
+ current = node as JsonSchema;
44
+ }
45
+ return current;
46
+ }
47
+
48
+ /** Does this schema describe the JSON `null` type (and nothing else)? */
49
+ export function isNullSchema(schema: JsonSchema): boolean {
50
+ return schema.type === 'null';
51
+ }
52
+
53
+ /** The list of type tokens a schema declares, normalized to an array. */
54
+ export function typeList(schema: JsonSchema): readonly JsonSchemaType[] {
55
+ const type = schema.type;
56
+ if (type === undefined) return [];
57
+ if (typeof type === 'string') return [type];
58
+ return type;
59
+ }
60
+
61
+ /**
62
+ * The union member list from a schema, if it is a union. Pydantic emits
63
+ * discriminated unions under `oneOf` and plain/null unions under `anyOf`; both
64
+ * are treated as a union here. `allOf` is NOT a union (it is an intersection)
65
+ * and is not returned.
66
+ */
67
+ export function unionMembers(schema: JsonSchema): readonly JsonSchema[] | undefined {
68
+ if (schema.anyOf !== undefined) return schema.anyOf;
69
+ if (schema.oneOf !== undefined) return schema.oneOf;
70
+ return undefined;
71
+ }
72
+
73
+ /** An option row derived from an `enum` or a discriminated variant tag. */
74
+ export interface EnumOption {
75
+ readonly value: unknown;
76
+ readonly label: string;
77
+ }
78
+
79
+ /** Render-facing label for an arbitrary JSON scalar used as an enum/const value. */
80
+ export function scalarLabel(value: unknown): string {
81
+ if (typeof value === 'string') return value;
82
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
83
+ if (value === null) return 'null';
84
+ return JSON.stringify(value);
85
+ }
86
+
87
+ /**
88
+ * The single tag value a discriminated-union variant pins on the discriminator
89
+ * property (Pydantic emits it as `const`, or a single-entry `enum`), or
90
+ * `undefined` if the variant does not constrain that property to one value.
91
+ */
92
+ export function variantTag(variant: JsonSchema, propertyName: string, root: JsonSchema): unknown {
93
+ const resolved = resolveRef(variant, root);
94
+ const prop = resolved.properties?.[propertyName];
95
+ if (prop === undefined) return undefined;
96
+ const propSchema = resolveRef(prop, root);
97
+ if ('const' in propSchema) return propSchema.const;
98
+ const enumValues = propSchema.enum;
99
+ if (enumValues?.length === 1) return enumValues[0];
100
+ return undefined;
101
+ }
102
+
103
+ /** A human label for a union variant: its title, its tag, or a positional fallback. */
104
+ export function variantLabel(
105
+ variant: JsonSchema,
106
+ index: number,
107
+ discriminatorTag: unknown,
108
+ root: JsonSchema,
109
+ ): string {
110
+ if (discriminatorTag !== undefined) return scalarLabel(discriminatorTag);
111
+ const resolved = resolveRef(variant, root);
112
+ if (typeof resolved.title === 'string' && resolved.title.length > 0) return resolved.title;
113
+ return `Option ${String(index + 1)}`;
114
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * The string field renderer. A plain string renders as a text input (its
3
+ * `type` derived from any JSON-Schema `format`); a media-annotated schema
4
+ * renders the upload control instead; and when the form supplies a completion
5
+ * provider, the input is backed by argument autocomplete.
6
+ */
7
+ import type { ReactNode } from 'react';
8
+ import { useCallback, useContext, useState } from 'react';
9
+
10
+ import { CompletionInput } from '../components/completion-input';
11
+ import { Field } from '../components/field';
12
+ import { AlertTriangleIcon } from '../components/icons';
13
+ import { TextInput } from '../components/inputs';
14
+ import { errorMessage } from '../errors';
15
+ import type { MediaUpload } from './classify';
16
+ import { CompletionProviderContext } from './context';
17
+ import { MediaField } from './media-field';
18
+ import type { CompletionProvider } from './SchemaForm';
19
+
20
+ export function StringField({
21
+ heading,
22
+ description,
23
+ error,
24
+ format,
25
+ media,
26
+ argName,
27
+ value,
28
+ required,
29
+ onChange,
30
+ }: {
31
+ heading: string;
32
+ description: string | undefined;
33
+ error: string | undefined;
34
+ format: string | undefined;
35
+ media: MediaUpload | undefined;
36
+ argName: string;
37
+ value: unknown;
38
+ required: boolean;
39
+ onChange: (value: unknown) => void;
40
+ }): ReactNode {
41
+ const completionProvider = useContext(CompletionProviderContext);
42
+ const current = typeof value === 'string' ? value : '';
43
+ // An empty optional field emits `undefined` (drops the key); a required one
44
+ // keeps the empty string so validation can flag it.
45
+ const emit = (next: string): void => {
46
+ onChange(next === '' && !required ? undefined : next);
47
+ };
48
+
49
+ // A media-annotated field renders the upload control instead of a text box.
50
+ if (media !== undefined) {
51
+ return (
52
+ <MediaField
53
+ heading={heading}
54
+ description={description}
55
+ error={error}
56
+ media={media}
57
+ value={current}
58
+ onChange={emit}
59
+ />
60
+ );
61
+ }
62
+
63
+ return (
64
+ <Field label={heading} description={description} error={error}>
65
+ {completionProvider !== undefined ? (
66
+ <CompletionField
67
+ argName={argName}
68
+ value={current}
69
+ onChange={emit}
70
+ provider={completionProvider}
71
+ />
72
+ ) : (
73
+ <TextInput
74
+ type={stringInputType(format)}
75
+ value={current}
76
+ onChange={(event) => {
77
+ emit(event.target.value);
78
+ }}
79
+ />
80
+ )}
81
+ </Field>
82
+ );
83
+ }
84
+
85
+ /**
86
+ * A string field backed by argument completions. Split out so its memoised
87
+ * hooks stay unconditional (they cannot live behind the provider check in
88
+ * {@link StringField}) and so the completion `provider` is a non-optional prop.
89
+ */
90
+ function CompletionField({
91
+ argName,
92
+ value,
93
+ onChange,
94
+ provider,
95
+ }: {
96
+ argName: string;
97
+ value: string;
98
+ onChange: (value: string) => void;
99
+ provider: CompletionProvider;
100
+ }): ReactNode {
101
+ // Memoised so its identity is stable across renders: CompletionInput lists
102
+ // `fetchCompletions` in its effect deps, so a fresh function each render would
103
+ // re-fetch on every keystroke in every string field.
104
+ const fetchCompletions = useCallback(
105
+ (partial: string) => provider(argName, partial),
106
+ [provider, argName],
107
+ );
108
+ // A completion fetch is a best-effort enhancement, not a fatal error — but it
109
+ // must never be swallowed. The failure goes to the human as a hint under the
110
+ // input AND to the console with the field's path, because the two audiences
111
+ // need different things: the reason the suggestions are gone, and which field
112
+ // and which throw produced it.
113
+ const [failure, setFailure] = useState<string | null>(null);
114
+ // Memoised for the same effect-deps reason as `fetchCompletions`.
115
+ const onError = useCallback(
116
+ (error: unknown) => {
117
+ console.error(`SchemaForm: completion fetch failed for field "${argName}"`, error);
118
+ setFailure(errorMessage(error));
119
+ },
120
+ [argName],
121
+ );
122
+
123
+ return (
124
+ <>
125
+ <CompletionInput
126
+ value={value}
127
+ // Every edit starts a fresh fetch, so the previous one's failure stops
128
+ // being true here; a new one re-reports through `onError`.
129
+ onChange={(next) => {
130
+ setFailure(null);
131
+ onChange(next);
132
+ }}
133
+ fetchCompletions={fetchCompletions}
134
+ onError={onError}
135
+ />
136
+ {/* A degradation, not a rejected value: the field still accepts what is
137
+ typed, only the suggestions are gone. So it wears the warning mark and
138
+ the hint style, never the error one. */}
139
+ {failure === null ? null : (
140
+ <p role="status" className="tai-field-hint" style={{ margin: 0 }}>
141
+ <AlertTriangleIcon />
142
+ {`Suggestions are unavailable: ${failure}`}
143
+ </p>
144
+ )}
145
+ </>
146
+ );
147
+ }
148
+
149
+ const FORMAT_INPUT_TYPES: Record<string, string> = {
150
+ email: 'email',
151
+ uri: 'url',
152
+ 'uri-reference': 'url',
153
+ date: 'date',
154
+ 'date-time': 'datetime-local',
155
+ time: 'time',
156
+ };
157
+
158
+ function stringInputType(format: string | undefined): string {
159
+ if (format === undefined) return 'text';
160
+ return FORMAT_INPUT_TYPES[format] ?? 'text';
161
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The design-system CLASS NAMES the schema-form field tree is built from. One
3
+ * module owns them so the form root, the group wrapper and the union editor
4
+ * cannot drift apart — and so no field component re-derives control geometry or
5
+ * color of its own: every name below is defined in `components.css`, which is
6
+ * the single source of truth for what they look like.
7
+ */
8
+
9
+ /** The vertical rhythm a sibling set of fields sits in. */
10
+ export const stackClass = 'tai-stack';
11
+
12
+ /** The label + description + error block that introduces a nested group. */
13
+ export const groupHeaderClass = 'tai-stack tai-stack-2';
14
+
15
+ /**
16
+ * The surface a nested object / array / union renders its children on. A card
17
+ * makes every level of nesting a visible container on its own ground, so depth
18
+ * reads without any hand-drawn indent rail.
19
+ */
20
+ export const groupClass = 'tai-card tai-stack tai-stack-3';
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Structural types for the JSON-Schema subset the auto-form supports. The shape
3
+ * is deliberately PERMISSIVE — every field is optional and an open index
4
+ * signature admits unknown keywords — because the schemas come from Pydantic v2
5
+ * and may carry constructs the renderer does not consume. Unknown keys never
6
+ * break typing; the renderer classifies a node and surfaces anything it cannot
7
+ * render as a LOUD "unsupported field" notice rather than dropping it.
8
+ */
9
+
10
+ /** The JSON-Schema `type` keyword values Pydantic emits. */
11
+ export type JsonSchemaType =
12
+ 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array' | 'null';
13
+
14
+ /** A Pydantic-style discriminated-union tag: which property selects the variant. */
15
+ export interface Discriminator {
16
+ readonly propertyName: string;
17
+ readonly mapping?: Readonly<Record<string, string>>;
18
+ }
19
+
20
+ /**
21
+ * A structural JSON Schema node. All keys optional; the index signature keeps
22
+ * the type permissive so an unrecognized keyword is never a type error — it is a
23
+ * runtime classification concern the renderer handles.
24
+ */
25
+ export interface JsonSchema {
26
+ readonly $ref?: string;
27
+ readonly $defs?: Readonly<Record<string, JsonSchema>>;
28
+ readonly definitions?: Readonly<Record<string, JsonSchema>>;
29
+ readonly type?: JsonSchemaType | readonly JsonSchemaType[];
30
+ readonly title?: string;
31
+ readonly description?: string;
32
+ readonly default?: unknown;
33
+ readonly const?: unknown;
34
+ readonly enum?: readonly unknown[];
35
+ readonly format?: string;
36
+ /**
37
+ * Media annotations (see the `SchemaForm` module). `contentEncoding: "base64"`
38
+ * together with `contentMediaType` (or `format: "data-url"`) opts a string
39
+ * field into the upload control; `contentMaxBytes` pins a per-field size cap.
40
+ *
41
+ * `contentMaxBytes` is a NONSTANDARD JSON-Schema extension keyword — a standard
42
+ * JSON-Schema validator ignores it. The client-side caps it drives (see the
43
+ * `media` module) are UX guards, NOT a security boundary: the SERVER MUST
44
+ * independently validate the decoded size of any uploaded content.
45
+ */
46
+ readonly contentEncoding?: string;
47
+ readonly contentMediaType?: string;
48
+ readonly contentMaxBytes?: number;
49
+ readonly properties?: Readonly<Record<string, JsonSchema>>;
50
+ readonly required?: readonly string[];
51
+ readonly items?: JsonSchema;
52
+ readonly anyOf?: readonly JsonSchema[];
53
+ readonly oneOf?: readonly JsonSchema[];
54
+ readonly allOf?: readonly JsonSchema[];
55
+ readonly discriminator?: Discriminator;
56
+ readonly additionalProperties?: boolean | JsonSchema;
57
+ readonly minimum?: number;
58
+ readonly maximum?: number;
59
+ readonly minLength?: number;
60
+ readonly maxLength?: number;
61
+ readonly minItems?: number;
62
+ readonly maxItems?: number;
63
+ readonly [key: string]: unknown;
64
+ }
65
+
66
+ /**
67
+ * A structured, per-path bag of validation problems. Keys are dotted/bracketed
68
+ * paths from the form root (`""` = the root value, `"user.name"`, `"tags[0]"`);
69
+ * each entry is a loud, human-readable message. Empty object = valid.
70
+ */
71
+ export type SchemaFormErrors = Readonly<Record<string, string>>;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * The union field renderer: a variant picker plus the active variant's fields.
3
+ * An object variant renders its properties inline (skipping the discriminator
4
+ * tag, which the picker owns); a scalar/enum variant renders as a single nested
5
+ * {@link FieldNode}. Selecting a variant seeds a concrete value for it.
6
+ */
7
+ import type { ReactNode } from 'react';
8
+
9
+ import { Field } from '../components/field';
10
+ import { Select } from '../components/select';
11
+ import { classifySchema, type FieldModel } from './classify';
12
+ import { defaultValueForSchema } from './default-value';
13
+ import { FieldGroup } from './field-group';
14
+ import { FieldNode } from './field-node';
15
+ import { ObjectFields } from './object-fields';
16
+ import { resolveRef } from './resolve';
17
+ import { groupClass } from './styles';
18
+ import { activeVariantIndex } from './union';
19
+ import type { JsonSchema, SchemaFormErrors } from './types';
20
+
21
+ /**
22
+ * A concrete seed for a chosen union variant. Object variants get their
23
+ * required-field skeleton; a scalar/enum variant that has no explicit default
24
+ * still needs a non-`undefined` value so the selection sticks (otherwise
25
+ * `activeVariantIndex` reads it back as "no variant" and the picker snaps empty).
26
+ */
27
+ function seedVariantValue(schema: JsonSchema, root: JsonSchema): unknown {
28
+ const seeded = defaultValueForSchema(schema, root);
29
+ if (seeded !== undefined) return seeded;
30
+ const resolved = resolveRef(schema, root);
31
+ if (resolved.const !== undefined) return resolved.const;
32
+ if (resolved.enum !== undefined && resolved.enum.length > 0) return resolved.enum[0];
33
+ switch (classifySchema(schema, root).model.kind) {
34
+ case 'string':
35
+ return '';
36
+ case 'number':
37
+ return 0;
38
+ case 'boolean':
39
+ return false;
40
+ default:
41
+ return undefined;
42
+ }
43
+ }
44
+
45
+ export function UnionField({
46
+ heading,
47
+ description,
48
+ error,
49
+ model,
50
+ root,
51
+ value,
52
+ onChange,
53
+ path,
54
+ errors,
55
+ idPrefix,
56
+ }: {
57
+ heading: string;
58
+ description: string | undefined;
59
+ error: string | undefined;
60
+ model: Extract<FieldModel, { kind: 'union' }>;
61
+ root: JsonSchema;
62
+ value: unknown;
63
+ onChange: (value: unknown) => void;
64
+ path: string;
65
+ errors: SchemaFormErrors | undefined;
66
+ idPrefix: string;
67
+ }): ReactNode {
68
+ const index = activeVariantIndex(value, model.variants, model.discriminator, root);
69
+ const options = model.variants.map((variant, position) => ({
70
+ value: String(position),
71
+ label: variant.label,
72
+ }));
73
+ const selectVariant = (key: string): void => {
74
+ const position = Number(key);
75
+ const variant = model.variants[position];
76
+ if (variant === undefined) return;
77
+ onChange(seedVariantValue(variant.schema, root));
78
+ };
79
+
80
+ const active = index === -1 ? undefined : model.variants[index];
81
+ const activeClassified = active === undefined ? undefined : classifySchema(active.schema, root);
82
+
83
+ return (
84
+ <FieldGroup heading={heading} description={description} error={error} atRoot={false}>
85
+ <Field label="Variant">
86
+ <Select
87
+ options={options}
88
+ value={index === -1 ? '' : String(index)}
89
+ placeholder="Select a variant…"
90
+ onValueChange={selectVariant}
91
+ />
92
+ </Field>
93
+ {activeClassified?.model.kind === 'object' ? (
94
+ <div className={groupClass}>
95
+ <ObjectFields
96
+ properties={activeClassified.model.properties}
97
+ requiredKeys={activeClassified.model.required}
98
+ root={root}
99
+ value={value}
100
+ onChange={onChange}
101
+ path={path}
102
+ errors={errors}
103
+ idPrefix={idPrefix}
104
+ skip={model.discriminator}
105
+ />
106
+ </div>
107
+ ) : active !== undefined ? (
108
+ <FieldNode
109
+ schema={active.schema}
110
+ root={root}
111
+ value={value}
112
+ onChange={onChange}
113
+ path={path}
114
+ label={active.label}
115
+ required
116
+ errors={errors}
117
+ idPrefix={idPrefix}
118
+ />
119
+ ) : null}
120
+ </FieldGroup>
121
+ );
122
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Which variant of a union a given value currently inhabits. Discriminated
3
+ * unions read the tag property directly; plain unions score each object variant
4
+ * by how many of its required properties the value carries (a best-effort match
5
+ * when Pydantic emitted no discriminator). Returns `-1` when no variant is
6
+ * active (an unset/`null` value, or nothing matches).
7
+ */
8
+ import { resolveRef } from './resolve';
9
+ import type { UnionVariant } from './classify';
10
+ import type { JsonSchema } from './types';
11
+
12
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
13
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ }
15
+
16
+ /** Whether a resolved scalar schema accepts the runtime type of `value`. */
17
+ function scalarSchemaAccepts(schema: JsonSchema, value: unknown): boolean {
18
+ if (schema.const !== undefined) return schema.const === value;
19
+ if (schema.enum !== undefined) return schema.enum.includes(value);
20
+ const types =
21
+ schema.type === undefined ? [] : Array.isArray(schema.type) ? schema.type : [schema.type];
22
+ return types.some((type) => {
23
+ switch (type) {
24
+ case 'string':
25
+ return typeof value === 'string';
26
+ case 'number':
27
+ return typeof value === 'number';
28
+ case 'integer':
29
+ return typeof value === 'number' && Number.isInteger(value);
30
+ case 'boolean':
31
+ return typeof value === 'boolean';
32
+ case 'null':
33
+ return value === null;
34
+ default:
35
+ return false;
36
+ }
37
+ });
38
+ }
39
+
40
+ export function activeVariantIndex(
41
+ value: unknown,
42
+ variants: readonly UnionVariant[],
43
+ discriminator: string | undefined,
44
+ root: JsonSchema,
45
+ ): number {
46
+ if (value === undefined || value === null) return -1;
47
+
48
+ if (discriminator !== undefined) {
49
+ if (!isPlainObject(value)) return -1;
50
+ const tag = value[discriminator];
51
+ const index = variants.findIndex((variant) => variant.tag === tag);
52
+ return index;
53
+ }
54
+
55
+ if (!isPlainObject(value)) {
56
+ // A scalar value: match the first variant whose resolved schema accepts the
57
+ // value's runtime type (a non-discriminated `Union[str, int]` and friends).
58
+ return variants.findIndex((variant) =>
59
+ scalarSchemaAccepts(resolveRef(variant.schema, root), value),
60
+ );
61
+ }
62
+
63
+ let bestIndex = -1;
64
+ let bestScore = -1;
65
+ variants.forEach((variant, index) => {
66
+ const resolved = resolveRef(variant.schema, root);
67
+ const required = resolved.required ?? [];
68
+ let score = 0;
69
+ for (const key of required) {
70
+ if (key in value) score += 1;
71
+ }
72
+ if (score > bestScore) {
73
+ bestScore = score;
74
+ bestIndex = index;
75
+ }
76
+ });
77
+ return bestIndex;
78
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The notice rendered for a schema node the form cannot turn into an input: a
3
+ * warning badge — a mark beside its label, so the meaning never rests on the
4
+ * tint alone — followed by the reason, shown inside a labelled field.
5
+ *
6
+ * The three parts each say ONE thing once. The `Field` prints the heading, the
7
+ * `Badge` prints the state, and the hint prints the REASON alone — repeating
8
+ * either of the first two there would name the field twice and the state twice,
9
+ * in a developer-log register on a surface a user reads.
10
+ */
11
+ import type { ReactNode } from 'react';
12
+
13
+ import { Badge } from '../components/badge';
14
+ import { Field } from '../components/field';
15
+ import { AlertTriangleIcon } from '../components/icons';
16
+
17
+ export function UnsupportedNotice({
18
+ heading,
19
+ reason,
20
+ description,
21
+ error,
22
+ }: {
23
+ heading: string;
24
+ reason: string;
25
+ description: string | undefined;
26
+ error: string | undefined;
27
+ }): ReactNode {
28
+ return (
29
+ <Field label={heading} description={description} error={error} group>
30
+ <div role="alert" className="tai-row">
31
+ <Badge variant="warning">
32
+ <AlertTriangleIcon />
33
+ Unsupported
34
+ </Badge>
35
+ <span className="tai-field-hint">{reason}</span>
36
+ </div>
37
+ </Field>
38
+ );
39
+ }