@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,392 @@
1
+ /*
2
+ * The design-system token contract: colors, spacing, typography, radii, motion,
3
+ * elevation, and the z-scale, all as CSS variables. This ships as PRECOMPILED CSS
4
+ * with preflight disabled (the SHELL owns preflight). Plugins style via these
5
+ * tokens + SDK components ONLY.
6
+ *
7
+ * CASCADE ORDER. This file is the canonical layer-order emitter and loads FIRST.
8
+ * The host's reset/`base` layer is ordered BEFORE the design-system layers so a
9
+ * preflight that zeroes borders and heading typography cannot erase a DS
10
+ * component class; the host's own utility layers are declared afterwards and so
11
+ * sort AFTER `tai-utilities`, keeping host utilities the final word over DS
12
+ * component styles.
13
+ *
14
+ * THEMING. The light value is the token; the dark value sits beside it under a
15
+ * `--tai-dark-` name. Each colour is therefore still written exactly once, and
16
+ * the two blocks that apply the dark half carry no values of their own — they
17
+ * only re-point each token at its `--tai-dark-*` neighbour, so a light and dark
18
+ * pair cannot drift apart. A `data-theme` pin on the root decides the theme in
19
+ * BOTH directions — the media query stands down for a root pinned to light, and
20
+ * the pinned-dark block applies the same set outright — so `prefers-color-scheme`
21
+ * is the DEFAULT only for a root that carries no pin. In `apps/studio` that
22
+ * window is the first paint: `ThemeProvider` (`useTheme`) stamps `data-theme` on
23
+ * mount, after which the media query no longer decides and an OS theme flip
24
+ * while the app is open changes nothing. Following the OS live — a `matchMedia`
25
+ * listener and a `system` preference — belongs to the host's theme provider, not
26
+ * to this sheet. A host that mounts no provider keeps the media query as its
27
+ * theme source. `color-scheme` follows the same three states, so native scrollbars,
28
+ * selects and form autofill match the tokens.
29
+ *
30
+ * BROWSER FLOOR. Deliberately NOT `light-dark()`. That function would fold each
31
+ * pair onto one line, but it is invalid at computed-value time below Chrome 123 /
32
+ * Firefox 120 / Safari 17.5 — and because every themed token would use it, the
33
+ * whole palette drops out at once below that line, leaving text on text rather
34
+ * than an unstyled page. These sheets ship as authored (the SDK build is `tsc`
35
+ * plus a copy, so nothing lowers them), so what they ask for IS the floor: the
36
+ * highest thing here is `@layer` and `:focus-visible`, at Chrome/Edge 99,
37
+ * Firefox 97, and Safari / iOS Safari 15.4. That floor is declared in this
38
+ * package's `browserslist` rather than left implicit, and the token scanner test
39
+ * bans `light-dark()` so it cannot be raised back by accident. (The SHELL app
40
+ * sits higher — it compiles Tailwind v4, whose own output needs `@property` and
41
+ * `color-mix()` — and declares that floor in the repo root's `browserslist`.)
42
+ *
43
+ * UNITS. Type and spacing tokens are authored in rem so a viewer's font-size
44
+ * preference scales them; radius, elevation, and z tokens stay px/unitless. The
45
+ * comment beside each type/space token is its rendered px at a 16 px root.
46
+ * Line-height and weight are NOT baked into these tokens — the component classes
47
+ * own them, so a `font:` shorthand never resets them.
48
+ */
49
+ @layer theme, base, tai-tokens, tai-components, tai-utilities;
50
+
51
+ @keyframes tai-spin {
52
+ to {
53
+ transform: rotate(360deg);
54
+ }
55
+ }
56
+
57
+ /* Loading skeleton: a raised band sweeps across the LINE-tone ground the block
58
+ rests at, which is what makes a placeholder read as pending rather than as
59
+ empty content. The band is an OVERLAY (`.tai-skeleton::after` in
60
+ `components.css`) travelling on `transform`, so the block itself never moves
61
+ and the browser can carry the sweep on the compositor instead of restyling
62
+ and repainting every skeleton on the page each frame. */
63
+ @keyframes tai-shimmer {
64
+ from {
65
+ transform: translateX(-100%);
66
+ }
67
+ to {
68
+ transform: translateX(100%);
69
+ }
70
+ }
71
+
72
+ /* Indeterminate progress: a short fill sweeps across the track when a running
73
+ tool reports progress but no total is known. */
74
+ @keyframes tai-progress-indeterminate {
75
+ 0% {
76
+ transform: translateX(-100%);
77
+ }
78
+ 100% {
79
+ transform: translateX(320%);
80
+ }
81
+ }
82
+
83
+ @layer tai-tokens {
84
+ :root {
85
+ /* The OS preference is the default theme source; the data-theme blocks
86
+ below pin it. */
87
+ color-scheme: light dark;
88
+
89
+ /* Every colour below carries the LIGHT value; the `--tai-dark-` line beside
90
+ it carries the dark one. Nothing else in the repo writes either — the two
91
+ blocks further down only re-point a token at its dark neighbour. */
92
+
93
+ /* -- Grounds ---------------------------------------------------------- */
94
+ --tai-color-bg: #ffffff;
95
+ --tai-dark-color-bg: #0c0e12;
96
+ --tai-color-surface: #f9fafb;
97
+ --tai-dark-color-surface: #12151b;
98
+ --tai-color-surface-raised: #ffffff;
99
+ --tai-dark-color-surface-raised: #171c24;
100
+ --tai-color-surface-disabled: var(--tai-color-surface);
101
+ --tai-color-code-bg: #f3f4f6;
102
+ --tai-dark-color-code-bg: #10131a;
103
+
104
+ /* -- Lines ------------------------------------------------------------
105
+ `border` and `border-strong` are decorative: they are below 3:1 and may
106
+ never be a control's only boundary. `control-border` is the contrast-safe
107
+ boundary for inputs, checkboxes, and secondary buttons. */
108
+ --tai-color-border: #e5e7eb;
109
+ --tai-dark-color-border: #262c36;
110
+ --tai-color-border-strong: #d1d5db;
111
+ --tai-dark-color-border-strong: #39414e;
112
+ --tai-color-control-border: #767c85;
113
+ --tai-dark-color-control-border: #646c79;
114
+ --tai-color-border-disabled: var(--tai-color-border);
115
+
116
+ /* -- Ink ---------------------------------------------------------------
117
+ `decor` is the NON-TEXT tier (dividers, watermarks, decorative SVG fill
118
+ or stroke). It never appears on `color:` and never identifies a component
119
+ or a state. */
120
+ --tai-color-text: #111827;
121
+ --tai-dark-color-text: #e6e8ec;
122
+ --tai-color-heading: #000000;
123
+ --tai-dark-color-heading: #ffffff;
124
+ --tai-color-text-muted: rgba(17, 24, 39, 0.62);
125
+ --tai-dark-color-text-muted: rgba(230, 232, 236, 0.64);
126
+ --tai-color-text-disabled: rgba(17, 24, 39, 0.38);
127
+ --tai-dark-color-text-disabled: rgba(230, 232, 236, 0.36);
128
+ --tai-color-placeholder: var(--tai-color-text-muted);
129
+ --tai-color-decor: rgba(17, 24, 39, 0.44);
130
+ --tai-dark-color-decor: rgba(230, 232, 236, 0.42);
131
+
132
+ /* -- Accent (crimson) --------------------------------------------------
133
+ The accent is brand, never status. `accent-hover` darkens in light and
134
+ LIGHTENS in dark. `accent-on-tint` is accent TEXT sitting on accent-tint;
135
+ `on-accent` is the label on an accent FILL. */
136
+ --tai-color-accent: #dc143c;
137
+ --tai-dark-color-accent: #ed4c67;
138
+ --tai-color-accent-hover: #800020;
139
+ --tai-dark-color-accent-hover: #f4718a;
140
+ --tai-color-accent-on-tint: #be123c;
141
+ --tai-dark-color-accent-on-tint: #f4718a;
142
+ --tai-color-on-accent: #ffffff;
143
+ --tai-dark-color-on-accent: #0c0e12;
144
+ --tai-color-accent-tint: rgba(220, 20, 60, 0.08);
145
+ --tai-dark-color-accent-tint: rgba(237, 76, 103, 0.12);
146
+
147
+ /* -- Semantic ----------------------------------------------------------
148
+ The `-text` members are the ONLY ones legible as text, dots, or icons;
149
+ the `-fill` members are for fills and charts and carry `on-fill` ink
150
+ labels in both themes. */
151
+ --tai-color-ok-text: #047857;
152
+ --tai-dark-color-ok-text: #34d399;
153
+ --tai-color-err-text: #b91c1c;
154
+ --tai-dark-color-err-text: #f87171;
155
+ --tai-color-warn-text: #92400e;
156
+ --tai-dark-color-warn-text: #fbbf24;
157
+ --tai-color-ok-fill: #10b981;
158
+ --tai-dark-color-ok-fill: #34d399;
159
+ --tai-color-err-fill: #ef4444;
160
+ --tai-dark-color-err-fill: #f87171;
161
+ --tai-color-warn-fill: #d97706;
162
+ --tai-dark-color-warn-fill: #fbbf24;
163
+ --tai-color-ok-tint: rgba(16, 185, 129, 0.1);
164
+ --tai-dark-color-ok-tint: rgba(52, 211, 153, 0.12);
165
+ --tai-color-err-tint: rgba(239, 68, 68, 0.1);
166
+ --tai-dark-color-err-tint: rgba(248, 113, 113, 0.12);
167
+ --tai-color-warn-tint: rgba(217, 119, 6, 0.1);
168
+ --tai-dark-color-warn-tint: rgba(251, 191, 36, 0.12);
169
+ /* The one ink deliberately identical in both themes: every semantic fill is
170
+ light enough that only the dark label clears the contrast floor on it. */
171
+ --tai-color-on-fill: #0c0e12;
172
+
173
+ /* -- Focus + overlay ----------------------------------------------------
174
+ The ring is an ALIAS, not a restatement: it IS the accent, so referencing
175
+ it keeps two values that must move together from drifting apart, and it
176
+ needs no dark half of its own. */
177
+ --tai-color-focus-ring: var(--tai-color-accent);
178
+ --tai-color-scrim: rgba(0, 0, 0, 0.45);
179
+ --tai-dark-color-scrim: rgba(0, 0, 0, 0.6);
180
+
181
+ /* -- Prose --------------------------------------------------------------
182
+ A prose link is always underlined, so colour is a reinforcement rather
183
+ than the only cue. Dark rest is the LIGHTER crimson: the light-theme
184
+ accent is too dim against a dark ground, and the hover value is the
185
+ legible one there (6.17 on raised). Hover then moves to the other member
186
+ in both themes. Spelled out rather than aliased — the two halves of each
187
+ pair come from different members of the accent ramp. */
188
+ --tai-color-prose-link: #dc143c;
189
+ --tai-dark-color-prose-link: #f4718a;
190
+ --tai-color-prose-link-hover: #800020;
191
+ --tai-dark-color-prose-link-hover: #ed4c67;
192
+
193
+ /* -- Syntax (JSON/code on code-bg) -------------------------------------- */
194
+ --tai-color-syntax-key: var(--tai-color-text-muted);
195
+ --tai-color-syntax-string: var(--tai-color-ok-text);
196
+ --tai-color-syntax-number: var(--tai-color-accent);
197
+ --tai-color-syntax-bool: var(--tai-color-warn-text);
198
+
199
+ /* -- Legacy token slots -------------------------------------------------
200
+ The names a plugin already styles against, re-pointed at the members of
201
+ the contract above that keep them legible. `danger-text` is the label on
202
+ the danger FILL, so it is white in light and ink in dark. */
203
+ --tai-color-primary: var(--tai-color-accent);
204
+ --tai-color-primary-text: var(--tai-color-on-accent);
205
+ --tai-color-danger: var(--tai-color-err-text);
206
+ --tai-color-danger-text: #ffffff;
207
+ --tai-dark-color-danger-text: #0c0e12;
208
+ --tai-color-danger-hover: #7f1d1d;
209
+ --tai-dark-color-danger-hover: #fca5a5;
210
+ --tai-color-danger-surface: var(--tai-color-err-tint);
211
+ --tai-color-success: var(--tai-color-ok-text);
212
+ --tai-color-warning: var(--tai-color-warn-text);
213
+
214
+ /* -- Spacing (n x 4; there is deliberately no space-7) ------------------ */
215
+ --tai-space-1: 0.25rem; /* 4px */
216
+ --tai-space-2: 0.5rem; /* 8px */
217
+ --tai-space-3: 0.75rem; /* 12px */
218
+ --tai-space-4: 1rem; /* 16px */
219
+ --tai-space-5: 1.25rem; /* 20px */
220
+ --tai-space-6: 1.5rem; /* 24px */
221
+ --tai-space-8: 2rem; /* 32px */
222
+
223
+ /* -- Radius ------------------------------------------------------------- */
224
+ --tai-radius-sm: 4px;
225
+ --tai-radius-code: 6px;
226
+ --tai-radius-md: 8px;
227
+ --tai-radius-tile: 10px;
228
+ --tai-radius-lg: 12px;
229
+ --tai-radius-overlay: 14px;
230
+ --tai-radius-full: 999px;
231
+
232
+ /* -- Type --------------------------------------------------------------- */
233
+ --tai-font-sans:
234
+ 'Inter Variable', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
235
+ --tai-font-mono:
236
+ 'Geist Mono Variable', ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, monospace;
237
+ --tai-text-display: 1.625rem; /* 26px — KPI numbers */
238
+ --tai-text-xl: 1.4375rem; /* 23px — page title */
239
+ --tai-text-section: 1.0625rem; /* 17px — section heading */
240
+ --tai-text-lg: 0.9375rem; /* 15px — card title */
241
+ --tai-text-md: 0.84375rem; /* 13.5px — body */
242
+ --tai-text-sm: 0.78125rem; /* 12.5px — secondary */
243
+ --tai-text-code: 0.75rem; /* 12px — mono blocks */
244
+ --tai-text-xs: 0.6875rem; /* 11px — labels, badges, axes (the floor) */
245
+
246
+ /* -- Control geometry ---------------------------------------------------
247
+ One height for every button, input, and select trigger; the coarse value
248
+ is the pointer-target minimum a touch device gets instead. */
249
+ --tai-control-height: 36px;
250
+ --tai-control-height-coarse: 44px;
251
+
252
+ /* -- Motion -------------------------------------------------------------- */
253
+ --tai-motion-fast: 150ms;
254
+ --tai-motion-base: 250ms;
255
+
256
+ /* -- Elevation ------------------------------------------------------------
257
+ Only the shadow COLOUR varies by theme; the geometry is written once, so a
258
+ dark override cannot silently move an offset or a blur. */
259
+ --tai-shadow-lift-color: rgb(0 0 0 / 0.08);
260
+ --tai-dark-shadow-lift-color: rgb(0 0 0 / 0.45);
261
+ --tai-shadow-overlay-color: rgb(0 0 0 / 0.16);
262
+ --tai-dark-shadow-overlay-color: rgb(0 0 0 / 0.6);
263
+ --tai-shadow-lift: 0 12px 32px var(--tai-shadow-lift-color);
264
+ --tai-shadow-overlay: 0 24px 48px var(--tai-shadow-overlay-color);
265
+ /* Legacy slots, re-pointed at the new elevation pair. Both names are kept
266
+ and both changed MEANING: they were resting shadows (`0 1px 2px` /
267
+ `0 4px 12px`) and now alias the lift and overlay geometry. */
268
+ --tai-shadow-sm: var(--tai-shadow-lift);
269
+ --tai-shadow-md: var(--tai-shadow-overlay);
270
+
271
+ /* -- Stacking ------------------------------------------------------------
272
+ `popover` and `tooltip` sit ABOVE the modal layers on purpose. Both are
273
+ portalled to the document body as siblings of a dialog, and both can only
274
+ be opened from whatever layer is on top — so a listbox or a tip raised
275
+ from inside a dialog has to clear that dialog's scrim to be usable.
276
+ `dropdown` is the rung reserved for an in-flow menu surface — one that
277
+ stacks against the page rather than being portalled. Nothing in this repo
278
+ is on it; it is part of the published scale so a plugin's own menu has a
279
+ rung to sit on instead of inventing a number. */
280
+ --tai-z-sticky: 10;
281
+ --tai-z-dropdown: 20;
282
+ --tai-z-overlay: 30;
283
+ --tai-z-dialog: 40;
284
+ --tai-z-popover: 45;
285
+ --tai-z-tooltip: 50;
286
+ }
287
+
288
+ /*
289
+ * THE DARK HALF, APPLIED. The next two blocks are the only places the dark
290
+ * values are put into service, and neither carries a value of its own: every
291
+ * line re-points a token at its `--tai-dark-*` neighbour above. The media query
292
+ * stands down for a root explicitly pinned to light, so the viewer's choice
293
+ * always wins over the operating system's. The token scanner test holds the two
294
+ * blocks to the same token set, so a token can never take the dark half under
295
+ * one of them and keep the light half under the other.
296
+ */
297
+ @media (prefers-color-scheme: dark) {
298
+ :root:not([data-theme='light']) {
299
+ --tai-color-bg: var(--tai-dark-color-bg);
300
+ --tai-color-surface: var(--tai-dark-color-surface);
301
+ --tai-color-surface-raised: var(--tai-dark-color-surface-raised);
302
+ --tai-color-code-bg: var(--tai-dark-color-code-bg);
303
+ --tai-color-border: var(--tai-dark-color-border);
304
+ --tai-color-border-strong: var(--tai-dark-color-border-strong);
305
+ --tai-color-control-border: var(--tai-dark-color-control-border);
306
+ --tai-color-text: var(--tai-dark-color-text);
307
+ --tai-color-heading: var(--tai-dark-color-heading);
308
+ --tai-color-text-muted: var(--tai-dark-color-text-muted);
309
+ --tai-color-text-disabled: var(--tai-dark-color-text-disabled);
310
+ --tai-color-decor: var(--tai-dark-color-decor);
311
+ --tai-color-accent: var(--tai-dark-color-accent);
312
+ --tai-color-accent-hover: var(--tai-dark-color-accent-hover);
313
+ --tai-color-accent-on-tint: var(--tai-dark-color-accent-on-tint);
314
+ --tai-color-on-accent: var(--tai-dark-color-on-accent);
315
+ --tai-color-accent-tint: var(--tai-dark-color-accent-tint);
316
+ --tai-color-ok-text: var(--tai-dark-color-ok-text);
317
+ --tai-color-err-text: var(--tai-dark-color-err-text);
318
+ --tai-color-warn-text: var(--tai-dark-color-warn-text);
319
+ --tai-color-ok-fill: var(--tai-dark-color-ok-fill);
320
+ --tai-color-err-fill: var(--tai-dark-color-err-fill);
321
+ --tai-color-warn-fill: var(--tai-dark-color-warn-fill);
322
+ --tai-color-ok-tint: var(--tai-dark-color-ok-tint);
323
+ --tai-color-err-tint: var(--tai-dark-color-err-tint);
324
+ --tai-color-warn-tint: var(--tai-dark-color-warn-tint);
325
+ --tai-color-scrim: var(--tai-dark-color-scrim);
326
+ --tai-color-prose-link: var(--tai-dark-color-prose-link);
327
+ --tai-color-prose-link-hover: var(--tai-dark-color-prose-link-hover);
328
+ --tai-color-danger-text: var(--tai-dark-color-danger-text);
329
+ --tai-color-danger-hover: var(--tai-dark-color-danger-hover);
330
+ --tai-shadow-lift-color: var(--tai-dark-shadow-lift-color);
331
+ --tai-shadow-overlay-color: var(--tai-dark-shadow-overlay-color);
332
+ }
333
+ }
334
+
335
+ /* The viewer's explicit toggle wins over the system preference, in BOTH
336
+ directions (data-theme is stamped on the root by useTheme). Pinning
337
+ `color-scheme` is what keeps native UI — scrollbars, selects, autofill — on
338
+ the same theme as the tokens. */
339
+ :root[data-theme='light'] {
340
+ color-scheme: light;
341
+ }
342
+
343
+ :root[data-theme='dark'] {
344
+ color-scheme: dark;
345
+ --tai-color-bg: var(--tai-dark-color-bg);
346
+ --tai-color-surface: var(--tai-dark-color-surface);
347
+ --tai-color-surface-raised: var(--tai-dark-color-surface-raised);
348
+ --tai-color-code-bg: var(--tai-dark-color-code-bg);
349
+ --tai-color-border: var(--tai-dark-color-border);
350
+ --tai-color-border-strong: var(--tai-dark-color-border-strong);
351
+ --tai-color-control-border: var(--tai-dark-color-control-border);
352
+ --tai-color-text: var(--tai-dark-color-text);
353
+ --tai-color-heading: var(--tai-dark-color-heading);
354
+ --tai-color-text-muted: var(--tai-dark-color-text-muted);
355
+ --tai-color-text-disabled: var(--tai-dark-color-text-disabled);
356
+ --tai-color-decor: var(--tai-dark-color-decor);
357
+ --tai-color-accent: var(--tai-dark-color-accent);
358
+ --tai-color-accent-hover: var(--tai-dark-color-accent-hover);
359
+ --tai-color-accent-on-tint: var(--tai-dark-color-accent-on-tint);
360
+ --tai-color-on-accent: var(--tai-dark-color-on-accent);
361
+ --tai-color-accent-tint: var(--tai-dark-color-accent-tint);
362
+ --tai-color-ok-text: var(--tai-dark-color-ok-text);
363
+ --tai-color-err-text: var(--tai-dark-color-err-text);
364
+ --tai-color-warn-text: var(--tai-dark-color-warn-text);
365
+ --tai-color-ok-fill: var(--tai-dark-color-ok-fill);
366
+ --tai-color-err-fill: var(--tai-dark-color-err-fill);
367
+ --tai-color-warn-fill: var(--tai-dark-color-warn-fill);
368
+ --tai-color-ok-tint: var(--tai-dark-color-ok-tint);
369
+ --tai-color-err-tint: var(--tai-dark-color-err-tint);
370
+ --tai-color-warn-tint: var(--tai-dark-color-warn-tint);
371
+ --tai-color-scrim: var(--tai-dark-color-scrim);
372
+ --tai-color-prose-link: var(--tai-dark-color-prose-link);
373
+ --tai-color-prose-link-hover: var(--tai-dark-color-prose-link-hover);
374
+ --tai-color-danger-text: var(--tai-dark-color-danger-text);
375
+ --tai-color-danger-hover: var(--tai-dark-color-danger-hover);
376
+ --tai-shadow-lift-color: var(--tai-dark-shadow-lift-color);
377
+ --tai-shadow-overlay-color: var(--tai-dark-shadow-overlay-color);
378
+ }
379
+
380
+ /* The motion durations are published API: a plugin writes
381
+ `transition: … var(--tai-motion-fast)` against them and has no other way to
382
+ learn the viewer's preference. Zeroing them here honours that preference for
383
+ every consumer at once — this repo's classes, and code the design system
384
+ never sees. (The component layer additionally pins `transition: none` on its
385
+ own controls, which covers any that do not spell their duration as a token.) */
386
+ @media (prefers-reduced-motion: reduce) {
387
+ :root {
388
+ --tai-motion-fast: 0ms;
389
+ --tai-motion-base: 0ms;
390
+ }
391
+ }
392
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The documented CSS-variable token names — the Studio-plugin styling API. A
3
+ * snapshot test pins this list so the contract can't drift silently. The
4
+ * SDK compatibility version bumps if any of these are renamed/removed; the list
5
+ * grows additively.
6
+ *
7
+ * The `--tai-dark-*` half of each themed pair is deliberately NOT here. Those
8
+ * names are the theming mechanism's own storage — the two blocks in `tokens.css`
9
+ * that apply the dark theme are the only readers, and the scanner test holds
10
+ * them to that. A plugin styles against the name WITHOUT the `dark` segment and
11
+ * gets whichever half the viewer's theme resolves to.
12
+ */
13
+ export const TOKEN_NAMES = [
14
+ '--tai-color-bg',
15
+ '--tai-color-surface',
16
+ '--tai-color-surface-raised',
17
+ '--tai-color-surface-disabled',
18
+ '--tai-color-code-bg',
19
+ '--tai-color-border',
20
+ '--tai-color-border-strong',
21
+ '--tai-color-control-border',
22
+ '--tai-color-border-disabled',
23
+ '--tai-color-text',
24
+ '--tai-color-heading',
25
+ '--tai-color-text-muted',
26
+ '--tai-color-text-disabled',
27
+ '--tai-color-placeholder',
28
+ '--tai-color-decor',
29
+ '--tai-color-accent',
30
+ '--tai-color-accent-hover',
31
+ '--tai-color-accent-on-tint',
32
+ '--tai-color-on-accent',
33
+ '--tai-color-accent-tint',
34
+ '--tai-color-ok-text',
35
+ '--tai-color-err-text',
36
+ '--tai-color-warn-text',
37
+ '--tai-color-ok-fill',
38
+ '--tai-color-err-fill',
39
+ '--tai-color-warn-fill',
40
+ '--tai-color-ok-tint',
41
+ '--tai-color-err-tint',
42
+ '--tai-color-warn-tint',
43
+ '--tai-color-on-fill',
44
+ '--tai-color-focus-ring',
45
+ '--tai-color-scrim',
46
+ '--tai-color-prose-link',
47
+ '--tai-color-prose-link-hover',
48
+ '--tai-color-syntax-key',
49
+ '--tai-color-syntax-string',
50
+ '--tai-color-syntax-number',
51
+ '--tai-color-syntax-bool',
52
+ '--tai-color-primary',
53
+ '--tai-color-primary-text',
54
+ '--tai-color-danger',
55
+ '--tai-color-danger-text',
56
+ '--tai-color-danger-hover',
57
+ '--tai-color-danger-surface',
58
+ '--tai-color-success',
59
+ '--tai-color-warning',
60
+ '--tai-space-1',
61
+ '--tai-space-2',
62
+ '--tai-space-3',
63
+ '--tai-space-4',
64
+ '--tai-space-5',
65
+ '--tai-space-6',
66
+ '--tai-space-8',
67
+ '--tai-radius-sm',
68
+ '--tai-radius-code',
69
+ '--tai-radius-md',
70
+ '--tai-radius-tile',
71
+ '--tai-radius-lg',
72
+ '--tai-radius-overlay',
73
+ '--tai-radius-full',
74
+ '--tai-font-sans',
75
+ '--tai-font-mono',
76
+ '--tai-text-display',
77
+ '--tai-text-xl',
78
+ '--tai-text-section',
79
+ '--tai-text-lg',
80
+ '--tai-text-md',
81
+ '--tai-text-sm',
82
+ '--tai-text-code',
83
+ '--tai-text-xs',
84
+ '--tai-control-height',
85
+ '--tai-control-height-coarse',
86
+ '--tai-motion-fast',
87
+ '--tai-motion-base',
88
+ '--tai-shadow-lift-color',
89
+ '--tai-shadow-overlay-color',
90
+ '--tai-shadow-lift',
91
+ '--tai-shadow-overlay',
92
+ '--tai-shadow-sm',
93
+ '--tai-shadow-md',
94
+ '--tai-z-sticky',
95
+ '--tai-z-dropdown',
96
+ '--tai-z-overlay',
97
+ '--tai-z-dialog',
98
+ '--tai-z-popover',
99
+ '--tai-z-tooltip',
100
+ ] as const;
101
+
102
+ export type TokenName = (typeof TOKEN_NAMES)[number];
@@ -0,0 +1,175 @@
1
+ /**
2
+ * `ToolPicker` — a controlled single-select over the available tool names, built
3
+ * on the DS `Select` so features share one picker instead of each re-implementing
4
+ * a listbox (the same reason `SchemaForm` lives in the SDK). It is
5
+ * PRESENTATIONAL: the caller fetches the tool names (GET /api/tools) and owns the
6
+ * selected value; this component fetches nothing.
7
+ *
8
+ * Three optional extensions, each inert unless supplied; the preset create flow
9
+ * passes all three and the multi-tool picker the first two:
10
+ * - `excludeNames` removes names from the options (e.g. a preset cannot be
11
+ * another preset's base, so the preset create flow excludes existing preset
12
+ * names). The server stays the authority — a slipped-through name is rejected
13
+ * loudly there — this is only a UI convenience.
14
+ * - `tagsByTool` (from GET /api/tools/tags) turns on tag GROUPING + a tag FILTER:
15
+ * the options are grouped under their tag and a filter control narrows them to
16
+ * a chosen tag.
17
+ * - `agentToolNames` suffixes the LABEL of any option whose name is an agent run
18
+ * tool with " (agent)" (in grouped and flat renderings alike) — a purely visual
19
+ * honesty cue; the value stays the bare name and no option is excluded, since an
20
+ * agent run tool is a legal preset base for an authored agent.
21
+ *
22
+ * SAFETY: a tool name AND a tag are server-supplied, so every name/tag renders as
23
+ * TEXT through the DS `Select` (React escapes it) — never an HTML sink. Pinned by
24
+ * a test.
25
+ */
26
+ import { useState } from 'react';
27
+
28
+ import { Field } from './field';
29
+ import { Select, type SelectGroup } from './select';
30
+
31
+ export interface ToolPickerProps {
32
+ readonly toolNames: readonly string[];
33
+ readonly value: string | null;
34
+ readonly onChange: (toolName: string) => void;
35
+ readonly disabled?: boolean;
36
+ readonly placeholder?: string;
37
+ readonly idPrefix?: string;
38
+ readonly label?: string;
39
+ /**
40
+ * Accessible name for the tool listbox when `label` renders none and no
41
+ * enclosing `Field` names it. `label` wins where both are given: it names the
42
+ * control from visible text, and the `Select` drops a caller name it would
43
+ * otherwise override.
44
+ */
45
+ readonly 'aria-label'?: string;
46
+ /** Names removed from the options (the server remains the authority). */
47
+ readonly excludeNames?: readonly string[];
48
+ /** Per-tool native tags; when present, options are grouped and a tag filter shows. */
49
+ readonly tagsByTool?: Readonly<Record<string, readonly string[]>>;
50
+ /** Tool names that are agent run tools; their option labels are suffixed " (agent)". */
51
+ readonly agentToolNames?: ReadonlySet<string>;
52
+ }
53
+
54
+ const UNTAGGED = 'Untagged';
55
+ // Radix `Select.Item` forbids an empty-string value, so the "no filter" choice
56
+ // uses a sentinel rather than ''. A tool literally tagged this string would just
57
+ // never be filtered out — harmless for a UI convenience whose authority is the server.
58
+ const ALL_TAGS = '__all_tags__';
59
+
60
+ /** The group a tool sorts into: its alphabetically-first tag, else "Untagged". */
61
+ function groupFor(name: string, tagsByTool: Readonly<Record<string, readonly string[]>>): string {
62
+ const tags = [...(tagsByTool[name] ?? [])].sort((a, b) => a.localeCompare(b));
63
+ return tags[0] ?? UNTAGGED;
64
+ }
65
+
66
+ /** The option label: the bare name, suffixed " (agent)" for an agent run tool. */
67
+ function optionLabel(name: string, agentToolNames: ReadonlySet<string> | undefined): string {
68
+ return agentToolNames?.has(name) === true ? `${name} (agent)` : name;
69
+ }
70
+
71
+ /** Build the grouped option clusters (sorted group labels, "Untagged" last). */
72
+ function buildGroups(
73
+ names: readonly string[],
74
+ tagsByTool: Readonly<Record<string, readonly string[]>>,
75
+ agentToolNames: ReadonlySet<string> | undefined,
76
+ ): SelectGroup[] {
77
+ const byGroup = new Map<string, string[]>();
78
+ for (const name of names) {
79
+ const group = groupFor(name, tagsByTool);
80
+ const bucket = byGroup.get(group) ?? [];
81
+ bucket.push(name);
82
+ byGroup.set(group, bucket);
83
+ }
84
+ return [...byGroup.entries()]
85
+ .sort(([a], [b]) => {
86
+ if (a === UNTAGGED) return 1;
87
+ if (b === UNTAGGED) return -1;
88
+ return a.localeCompare(b);
89
+ })
90
+ .map(([label, groupNames]) => ({
91
+ label,
92
+ options: groupNames.map((name) => ({
93
+ value: name,
94
+ label: optionLabel(name, agentToolNames),
95
+ })),
96
+ }));
97
+ }
98
+
99
+ export function ToolPicker({
100
+ toolNames,
101
+ value,
102
+ onChange,
103
+ disabled,
104
+ placeholder = 'Select a tool…',
105
+ idPrefix = 'tool-picker',
106
+ label,
107
+ 'aria-label': ariaLabel,
108
+ excludeNames,
109
+ tagsByTool,
110
+ agentToolNames,
111
+ }: ToolPickerProps) {
112
+ const [tagFilter, setTagFilter] = useState<string>(ALL_TAGS);
113
+
114
+ const excluded = new Set(excludeNames ?? []);
115
+ const available = toolNames.filter((name) => !excluded.has(name));
116
+
117
+ // Distinct tags across the available tools, sorted, feeding the filter control.
118
+ const allTags =
119
+ tagsByTool === undefined
120
+ ? []
121
+ : [...new Set(available.flatMap((name) => tagsByTool[name] ?? []))].sort((a, b) =>
122
+ a.localeCompare(b),
123
+ );
124
+
125
+ const filtered =
126
+ tagsByTool !== undefined && tagFilter !== ALL_TAGS
127
+ ? available.filter((name) => (tagsByTool[name] ?? []).includes(tagFilter))
128
+ : available;
129
+
130
+ const toolSelect =
131
+ tagsByTool !== undefined ? (
132
+ <Select
133
+ groups={buildGroups(filtered, tagsByTool, agentToolNames)}
134
+ value={value ?? ''}
135
+ onValueChange={onChange}
136
+ placeholder={placeholder}
137
+ aria-label={ariaLabel}
138
+ disabled={disabled}
139
+ />
140
+ ) : (
141
+ <Select
142
+ // Keep Radix controlled even when the caller's value is null: `''` matches no
143
+ // item (placeholder still shows) and avoids the uncontrolled→controlled warning
144
+ // a subsequent selection would otherwise trigger.
145
+ options={filtered.map((name) => ({
146
+ value: name,
147
+ label: optionLabel(name, agentToolNames),
148
+ }))}
149
+ value={value ?? ''}
150
+ onValueChange={onChange}
151
+ placeholder={placeholder}
152
+ aria-label={ariaLabel}
153
+ disabled={disabled}
154
+ />
155
+ );
156
+
157
+ return (
158
+ <div data-testid={idPrefix} className="tai-stack tai-stack-2">
159
+ {tagsByTool !== undefined && allTags.length > 0 ? (
160
+ <Field label="Filter by tag">
161
+ <Select
162
+ options={[
163
+ { value: ALL_TAGS, label: 'All tags' },
164
+ ...allTags.map((tag) => ({ value: tag, label: tag })),
165
+ ]}
166
+ value={tagFilter}
167
+ onValueChange={setTagFilter}
168
+ disabled={disabled}
169
+ />
170
+ </Field>
171
+ ) : null}
172
+ {label !== undefined ? <Field label={label}>{toolSelect}</Field> : toolSelect}
173
+ </div>
174
+ );
175
+ }