@tai42/studio-sdk 16.0.0 → 16.0.1

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 (317) hide show
  1. package/dist/components/explorer-body.d.ts +49 -0
  2. package/dist/components/explorer-body.d.ts.map +1 -0
  3. package/dist/components/explorer-body.js +67 -0
  4. package/dist/components/explorer-body.js.map +1 -0
  5. package/dist/components/explorer-page-size.d.ts +4 -0
  6. package/dist/components/explorer-page-size.d.ts.map +1 -0
  7. package/dist/components/explorer-page-size.js +36 -0
  8. package/dist/components/explorer-page-size.js.map +1 -0
  9. package/dist/components/explorer-tag-filter.d.ts +17 -0
  10. package/dist/components/explorer-tag-filter.d.ts.map +1 -0
  11. package/dist/components/explorer-tag-filter.js +29 -0
  12. package/dist/components/explorer-tag-filter.js.map +1 -0
  13. package/dist/components/explorer-tags.d.ts +22 -0
  14. package/dist/components/explorer-tags.d.ts.map +1 -0
  15. package/dist/components/explorer-tags.js +51 -0
  16. package/dist/components/explorer-tags.js.map +1 -0
  17. package/dist/components/explorer-view.d.ts +3 -17
  18. package/dist/components/explorer-view.d.ts.map +1 -1
  19. package/dist/components/explorer-view.js +53 -170
  20. package/dist/components/explorer-view.js.map +1 -1
  21. package/dist/components/extension-combo-builder.d.ts +0 -5
  22. package/dist/components/extension-combo-builder.d.ts.map +1 -1
  23. package/dist/components/extension-combo-builder.js +4 -42
  24. package/dist/components/extension-combo-builder.js.map +1 -1
  25. package/dist/components/extension-combo-parts.d.ts +44 -0
  26. package/dist/components/extension-combo-parts.d.ts.map +1 -0
  27. package/dist/components/extension-combo-parts.js +57 -0
  28. package/dist/components/extension-combo-parts.js.map +1 -0
  29. package/dist/components/fleet-report.d.ts.map +1 -1
  30. package/dist/components/fleet-report.js +51 -37
  31. package/dist/components/fleet-report.js.map +1 -1
  32. package/dist/components/icons/chrome-marks.d.ts +50 -0
  33. package/dist/components/icons/chrome-marks.d.ts.map +1 -0
  34. package/dist/components/icons/chrome-marks.js +51 -0
  35. package/dist/components/icons/chrome-marks.js.map +1 -0
  36. package/dist/components/icons/icon-frame.d.ts +29 -0
  37. package/dist/components/icons/icon-frame.d.ts.map +1 -0
  38. package/dist/components/icons/icon-frame.js +19 -0
  39. package/dist/components/icons/icon-frame.js.map +1 -0
  40. package/dist/components/icons/index.d.ts +40 -0
  41. package/dist/components/icons/index.d.ts.map +1 -0
  42. package/dist/components/icons/index.js +6 -0
  43. package/dist/components/icons/index.js.map +1 -0
  44. package/dist/components/icons/nav-map.d.ts +14 -0
  45. package/dist/components/icons/nav-map.d.ts.map +1 -0
  46. package/dist/components/icons/nav-map.js +28 -0
  47. package/dist/components/icons/nav-map.js.map +1 -0
  48. package/dist/components/icons/nav-marks.d.ts +44 -0
  49. package/dist/components/icons/nav-marks.d.ts.map +1 -0
  50. package/dist/components/icons/nav-marks.js +45 -0
  51. package/dist/components/icons/nav-marks.js.map +1 -0
  52. package/dist/components/icons/status-marks.d.ts +21 -0
  53. package/dist/components/icons/status-marks.d.ts.map +1 -0
  54. package/dist/components/icons/status-marks.js +22 -0
  55. package/dist/components/icons/status-marks.js.map +1 -0
  56. package/dist/components/icons/theme-marks.d.ts +9 -0
  57. package/dist/components/icons/theme-marks.d.ts.map +1 -0
  58. package/dist/components/icons/theme-marks.js +10 -0
  59. package/dist/components/icons/theme-marks.js.map +1 -0
  60. package/dist/components/json-diff.d.ts.map +1 -1
  61. package/dist/components/json-diff.js +1 -3
  62. package/dist/components/json-diff.js.map +1 -1
  63. package/dist/components/json-tree-context.d.ts +13 -0
  64. package/dist/components/json-tree-context.d.ts.map +1 -0
  65. package/dist/components/json-tree-context.js +15 -0
  66. package/dist/components/json-tree-context.js.map +1 -0
  67. package/dist/components/json-tree-model.d.ts +56 -0
  68. package/dist/components/json-tree-model.d.ts.map +1 -0
  69. package/dist/components/json-tree-model.js +125 -0
  70. package/dist/components/json-tree-model.js.map +1 -0
  71. package/dist/components/json-tree-nodes.d.ts +30 -0
  72. package/dist/components/json-tree-nodes.d.ts.map +1 -0
  73. package/dist/components/json-tree-nodes.js +118 -0
  74. package/dist/components/json-tree-nodes.js.map +1 -0
  75. package/dist/components/json-tree.d.ts.map +1 -1
  76. package/dist/components/json-tree.js +7 -295
  77. package/dist/components/json-tree.js.map +1 -1
  78. package/dist/components/json-value-format.d.ts +20 -0
  79. package/dist/components/json-value-format.d.ts.map +1 -0
  80. package/dist/components/json-value-format.js +63 -0
  81. package/dist/components/json-value-format.js.map +1 -0
  82. package/dist/components/markdown-blocks.d.ts +38 -0
  83. package/dist/components/markdown-blocks.d.ts.map +1 -0
  84. package/dist/components/markdown-blocks.js +168 -0
  85. package/dist/components/markdown-blocks.js.map +1 -0
  86. package/dist/components/markdown-inline.d.ts +24 -0
  87. package/dist/components/markdown-inline.d.ts.map +1 -0
  88. package/dist/components/markdown-inline.js +257 -0
  89. package/dist/components/markdown-inline.js.map +1 -0
  90. package/dist/components/markdown.d.ts +3 -52
  91. package/dist/components/markdown.d.ts.map +1 -1
  92. package/dist/components/markdown.js +4 -376
  93. package/dist/components/markdown.js.map +1 -1
  94. package/dist/components/observer-registry.d.ts +38 -0
  95. package/dist/components/observer-registry.d.ts.map +1 -0
  96. package/dist/components/observer-registry.js +131 -0
  97. package/dist/components/observer-registry.js.map +1 -0
  98. package/dist/components/overflow-measure.d.ts +38 -0
  99. package/dist/components/overflow-measure.d.ts.map +1 -0
  100. package/dist/components/overflow-measure.js +54 -0
  101. package/dist/components/overflow-measure.js.map +1 -0
  102. package/dist/components/prose-regions.d.ts +81 -0
  103. package/dist/components/prose-regions.d.ts.map +1 -0
  104. package/dist/components/prose-regions.js +128 -0
  105. package/dist/components/prose-regions.js.map +1 -0
  106. package/dist/components/scroll-region.d.ts.map +1 -1
  107. package/dist/components/scroll-region.js +3 -298
  108. package/dist/components/scroll-region.js.map +1 -1
  109. package/dist/components/tab-stop-parse.d.ts +53 -0
  110. package/dist/components/tab-stop-parse.d.ts.map +1 -0
  111. package/dist/components/tab-stop-parse.js +202 -0
  112. package/dist/components/tab-stop-parse.js.map +1 -0
  113. package/dist/components/templated-text-field.d.ts.map +1 -1
  114. package/dist/components/templated-text-field.js +10 -148
  115. package/dist/components/templated-text-field.js.map +1 -1
  116. package/dist/components/templated-text-kwargs-editor.d.ts +24 -0
  117. package/dist/components/templated-text-kwargs-editor.d.ts.map +1 -0
  118. package/dist/components/templated-text-kwargs-editor.js +48 -0
  119. package/dist/components/templated-text-kwargs-editor.js.map +1 -0
  120. package/dist/components/templated-text-kwargs.d.ts +37 -0
  121. package/dist/components/templated-text-kwargs.d.ts.map +1 -0
  122. package/dist/components/templated-text-kwargs.js +66 -0
  123. package/dist/components/templated-text-kwargs.js.map +1 -0
  124. package/dist/components/templated-text-source.d.ts +33 -0
  125. package/dist/components/templated-text-source.d.ts.map +1 -0
  126. package/dist/components/templated-text-source.js +53 -0
  127. package/dist/components/templated-text-source.js.map +1 -0
  128. package/dist/components/tool-picker-parts.d.ts +39 -0
  129. package/dist/components/tool-picker-parts.d.ts.map +1 -0
  130. package/dist/components/tool-picker-parts.js +87 -0
  131. package/dist/components/tool-picker-parts.js.map +1 -0
  132. package/dist/components/tool-picker.d.ts +1 -2
  133. package/dist/components/tool-picker.d.ts.map +1 -1
  134. package/dist/components/tool-picker.js +4 -66
  135. package/dist/components/tool-picker.js.map +1 -1
  136. package/dist/components/version-history-panel.d.ts.map +1 -1
  137. package/dist/components/version-history-panel.js +7 -34
  138. package/dist/components/version-history-panel.js.map +1 -1
  139. package/dist/components/version-history-rows.d.ts +48 -0
  140. package/dist/components/version-history-rows.d.ts.map +1 -0
  141. package/dist/components/version-history-rows.js +51 -0
  142. package/dist/components/version-history-rows.js.map +1 -0
  143. package/dist/guards.d.ts +6 -0
  144. package/dist/guards.d.ts.map +1 -0
  145. package/dist/guards.js +8 -0
  146. package/dist/guards.js.map +1 -0
  147. package/dist/hooks/interactions-connection.d.ts +43 -0
  148. package/dist/hooks/interactions-connection.d.ts.map +1 -0
  149. package/dist/hooks/interactions-connection.js +171 -0
  150. package/dist/hooks/interactions-connection.js.map +1 -0
  151. package/dist/hooks/interactions-stream.d.ts +80 -0
  152. package/dist/hooks/interactions-stream.d.ts.map +1 -0
  153. package/dist/hooks/interactions-stream.js +233 -0
  154. package/dist/hooks/interactions-stream.js.map +1 -0
  155. package/dist/hooks/useApi.d.ts +2192 -2192
  156. package/dist/hooks/useApi.d.ts.map +1 -1
  157. package/dist/hooks/useSse.d.ts +3 -6
  158. package/dist/hooks/useSse.d.ts.map +1 -1
  159. package/dist/hooks/useSse.js +38 -451
  160. package/dist/hooks/useSse.js.map +1 -1
  161. package/dist/navigation/app-link.d.ts +25 -0
  162. package/dist/navigation/app-link.d.ts.map +1 -0
  163. package/dist/navigation/app-link.js +33 -0
  164. package/dist/navigation/app-link.js.map +1 -0
  165. package/dist/navigation/context.d.ts +3 -20
  166. package/dist/navigation/context.d.ts.map +1 -1
  167. package/dist/navigation/context.js +7 -267
  168. package/dist/navigation/context.js.map +1 -1
  169. package/dist/navigation/entry-state.d.ts +23 -0
  170. package/dist/navigation/entry-state.d.ts.map +1 -0
  171. package/dist/navigation/entry-state.js +39 -0
  172. package/dist/navigation/entry-state.js.map +1 -0
  173. package/dist/navigation/guard-registry.d.ts +30 -0
  174. package/dist/navigation/guard-registry.d.ts.map +1 -0
  175. package/dist/navigation/guard-registry.js +46 -0
  176. package/dist/navigation/guard-registry.js.map +1 -0
  177. package/dist/navigation/guarded-navigation.d.ts +24 -0
  178. package/dist/navigation/guarded-navigation.d.ts.map +1 -0
  179. package/dist/navigation/guarded-navigation.js +102 -0
  180. package/dist/navigation/guarded-navigation.js.map +1 -0
  181. package/dist/navigation/index.d.ts +3 -2
  182. package/dist/navigation/index.d.ts.map +1 -1
  183. package/dist/navigation/index.js +2 -1
  184. package/dist/navigation/index.js.map +1 -1
  185. package/dist/navigation/types.d.ts +8 -11
  186. package/dist/navigation/types.d.ts.map +1 -1
  187. package/dist/navigation/use-back-forward-guard.d.ts +5 -0
  188. package/dist/navigation/use-back-forward-guard.d.ts.map +1 -0
  189. package/dist/navigation/use-back-forward-guard.js +82 -0
  190. package/dist/navigation/use-back-forward-guard.js.map +1 -0
  191. package/dist/plugin/registry.d.ts +6 -15
  192. package/dist/plugin/registry.d.ts.map +1 -1
  193. package/dist/plugin/registry.js +83 -75
  194. package/dist/plugin/registry.js.map +1 -1
  195. package/dist/schema-form/SecretRefField.d.ts +2 -3
  196. package/dist/schema-form/SecretRefField.d.ts.map +1 -1
  197. package/dist/schema-form/SecretRefField.js +23 -12
  198. package/dist/schema-form/SecretRefField.js.map +1 -1
  199. package/dist/schema-form/classify.d.ts +1 -122
  200. package/dist/schema-form/classify.d.ts.map +1 -1
  201. package/dist/schema-form/classify.js +174 -237
  202. package/dist/schema-form/classify.js.map +1 -1
  203. package/dist/schema-form/default-value.d.ts.map +1 -1
  204. package/dist/schema-form/default-value.js +42 -19
  205. package/dist/schema-form/default-value.js.map +1 -1
  206. package/dist/schema-form/enum-field.d.ts +1 -1
  207. package/dist/schema-form/enum-field.d.ts.map +1 -1
  208. package/dist/schema-form/field-model.d.ts +121 -0
  209. package/dist/schema-form/field-model.d.ts.map +1 -0
  210. package/dist/schema-form/field-model.js +2 -0
  211. package/dist/schema-form/field-model.js.map +1 -0
  212. package/dist/schema-form/field-node.d.ts.map +1 -1
  213. package/dist/schema-form/field-node.js +25 -14
  214. package/dist/schema-form/field-node.js.map +1 -1
  215. package/dist/schema-form/media-field.d.ts +1 -1
  216. package/dist/schema-form/media-field.d.ts.map +1 -1
  217. package/dist/schema-form/media-field.js +47 -28
  218. package/dist/schema-form/media-field.js.map +1 -1
  219. package/dist/schema-form/string-annotations.d.ts +20 -0
  220. package/dist/schema-form/string-annotations.d.ts.map +1 -0
  221. package/dist/schema-form/string-annotations.js +105 -0
  222. package/dist/schema-form/string-annotations.js.map +1 -0
  223. package/dist/schema-form/string-field.d.ts +1 -1
  224. package/dist/schema-form/string-field.d.ts.map +1 -1
  225. package/dist/schema-form/union-field.d.ts +1 -1
  226. package/dist/schema-form/union-field.d.ts.map +1 -1
  227. package/dist/schema-form/union-field.js.map +1 -1
  228. package/dist/schema-form/union.d.ts +1 -1
  229. package/dist/schema-form/union.d.ts.map +1 -1
  230. package/dist/schema-form/validate.d.ts.map +1 -1
  231. package/dist/schema-form/validate.js +126 -105
  232. package/dist/schema-form/validate.js.map +1 -1
  233. package/dist/state-binding/AdapterMapping.d.ts.map +1 -1
  234. package/dist/state-binding/AdapterMapping.js +0 -0
  235. package/dist/state-binding/AdapterMapping.js.map +1 -1
  236. package/dist/state-binding/StateAttachRow.d.ts.map +1 -1
  237. package/dist/state-binding/StateAttachRow.js +52 -36
  238. package/dist/state-binding/StateAttachRow.js.map +1 -1
  239. package/dist/state-binding/adapter-mapping-row.d.ts +17 -0
  240. package/dist/state-binding/adapter-mapping-row.d.ts.map +1 -0
  241. package/dist/state-binding/adapter-mapping-row.js +87 -0
  242. package/dist/state-binding/adapter-mapping-row.js.map +1 -0
  243. package/dist/structured-output/StructuredOutput.d.ts.map +1 -1
  244. package/dist/structured-output/StructuredOutput.js +1 -3
  245. package/dist/structured-output/StructuredOutput.js.map +1 -1
  246. package/package.json +2 -2
  247. package/src/components/explorer-body.tsx +239 -0
  248. package/src/components/explorer-page-size.ts +39 -0
  249. package/src/components/explorer-tag-filter.tsx +80 -0
  250. package/src/components/explorer-tags.ts +64 -0
  251. package/src/components/explorer-view.tsx +114 -366
  252. package/src/components/extension-combo-builder.tsx +35 -152
  253. package/src/components/extension-combo-parts.tsx +237 -0
  254. package/src/components/fleet-report.tsx +97 -54
  255. package/src/components/icons/chrome-marks.tsx +195 -0
  256. package/src/components/icons/icon-frame.tsx +50 -0
  257. package/src/components/icons/index.ts +84 -0
  258. package/src/components/icons/nav-map.ts +55 -0
  259. package/src/components/icons/nav-marks.tsx +205 -0
  260. package/src/components/icons/status-marks.tsx +46 -0
  261. package/src/components/icons/theme-marks.tsx +33 -0
  262. package/src/components/json-diff.tsx +1 -4
  263. package/src/components/json-tree-context.ts +26 -0
  264. package/src/components/json-tree-model.ts +139 -0
  265. package/src/components/json-tree-nodes.tsx +224 -0
  266. package/src/components/json-tree.tsx +15 -434
  267. package/src/components/json-value-format.ts +64 -0
  268. package/src/components/markdown-blocks.ts +198 -0
  269. package/src/components/markdown-inline.tsx +365 -0
  270. package/src/components/markdown.tsx +5 -471
  271. package/src/components/observer-registry.ts +139 -0
  272. package/src/components/overflow-measure.ts +69 -0
  273. package/src/components/prose-regions.ts +153 -0
  274. package/src/components/scroll-region.tsx +20 -360
  275. package/src/components/tab-stop-parse.ts +217 -0
  276. package/src/components/templated-text-field.tsx +38 -330
  277. package/src/components/templated-text-kwargs-editor.tsx +147 -0
  278. package/src/components/templated-text-kwargs.ts +92 -0
  279. package/src/components/templated-text-source.tsx +226 -0
  280. package/src/components/tool-picker-parts.tsx +184 -0
  281. package/src/components/tool-picker.tsx +22 -114
  282. package/src/components/version-history-panel.tsx +38 -142
  283. package/src/components/version-history-rows.tsx +220 -0
  284. package/src/guards.ts +7 -0
  285. package/src/hooks/interactions-connection.ts +227 -0
  286. package/src/hooks/interactions-stream.ts +274 -0
  287. package/src/hooks/useSse.ts +50 -477
  288. package/src/navigation/app-link.tsx +70 -0
  289. package/src/navigation/context.tsx +11 -330
  290. package/src/navigation/entry-state.ts +42 -0
  291. package/src/navigation/guard-registry.ts +65 -0
  292. package/src/navigation/guarded-navigation.ts +147 -0
  293. package/src/navigation/index.ts +2 -2
  294. package/src/navigation/types.ts +8 -11
  295. package/src/navigation/use-back-forward-guard.ts +90 -0
  296. package/src/plugin/registry.ts +111 -90
  297. package/src/schema-form/SecretRefField.tsx +121 -71
  298. package/src/schema-form/classify.ts +244 -395
  299. package/src/schema-form/default-value.ts +48 -17
  300. package/src/schema-form/enum-field.tsx +1 -1
  301. package/src/schema-form/field-model.ts +133 -0
  302. package/src/schema-form/field-node.tsx +94 -38
  303. package/src/schema-form/media-field.tsx +134 -65
  304. package/src/schema-form/string-annotations.ts +111 -0
  305. package/src/schema-form/string-field.tsx +1 -1
  306. package/src/schema-form/union-field.tsx +2 -1
  307. package/src/schema-form/union.ts +1 -1
  308. package/src/schema-form/validate.ts +184 -103
  309. package/src/state-binding/AdapterMapping.tsx +0 -0
  310. package/src/state-binding/StateAttachRow.tsx +222 -147
  311. package/src/state-binding/adapter-mapping-row.tsx +232 -0
  312. package/src/structured-output/StructuredOutput.tsx +1 -4
  313. package/dist/components/icons.d.ts +0 -160
  314. package/dist/components/icons.d.ts.map +0 -1
  315. package/dist/components/icons.js +0 -160
  316. package/dist/components/icons.js.map +0 -1
  317. package/src/components/icons.tsx +0 -586
@@ -33,15 +33,10 @@
33
33
  */
34
34
  import { useState } from 'react';
35
35
 
36
- import { toolDisplayLabel } from '../hooks/useToolDisplayNames';
37
- import { Badge } from './badge';
38
36
  import { Field } from './field';
39
- import { Select, type SelectGroup } from './select';
40
- import { Tooltip } from './tooltip';
37
+ import { ALL_TAGS, SelectedToolBadges, TagFilterField, ToolSelect } from './tool-picker-parts';
41
38
 
42
- /** The shared informational note every badge surface carries: declared, never enforced. */
43
- export const BADGES_NOTE =
44
- 'Declared capability labels — informational only. They describe what the tool touches; the server never enforces them.';
39
+ export { BADGES_NOTE } from './tool-picker-parts';
45
40
 
46
41
  export interface ToolPickerProps {
47
42
  readonly toolNames: readonly string[];
@@ -79,61 +74,6 @@ export interface ToolPickerProps {
79
74
  readonly badgesByTool?: Readonly<Record<string, readonly string[]>>;
80
75
  }
81
76
 
82
- const NO_DISPLAY_NAMES: Readonly<Record<string, string>> = {};
83
- const UNTAGGED = 'Untagged';
84
- // Radix `Select.Item` forbids an empty-string value, so the "no filter" choice
85
- // uses a sentinel rather than ''. A tool literally tagged this string would just
86
- // never be filtered out — harmless for a UI convenience whose authority is the server.
87
- const ALL_TAGS = '__all_tags__';
88
-
89
- /** The group a tool sorts into: its alphabetically-first tag, else "Untagged". */
90
- function groupFor(name: string, tagsByTool: Readonly<Record<string, readonly string[]>>): string {
91
- const tags = [...(tagsByTool[name] ?? [])].sort((a, b) => a.localeCompare(b));
92
- return tags[0] ?? UNTAGGED;
93
- }
94
-
95
- /**
96
- * The option label: `Display Name (raw)` when a distinct non-empty display name maps
97
- * the name, else the bare name; either way suffixed " (agent)" for an agent run tool.
98
- */
99
- function optionLabel(
100
- name: string,
101
- displayNames: Readonly<Record<string, string>> | undefined,
102
- agentToolNames: ReadonlySet<string> | undefined,
103
- ): string {
104
- const base = toolDisplayLabel(displayNames ?? NO_DISPLAY_NAMES, name);
105
- return agentToolNames?.has(name) === true ? `${base} (agent)` : base;
106
- }
107
-
108
- /** Build the grouped option clusters (sorted group labels, "Untagged" last). */
109
- function buildGroups(
110
- names: readonly string[],
111
- tagsByTool: Readonly<Record<string, readonly string[]>>,
112
- displayNames: Readonly<Record<string, string>> | undefined,
113
- agentToolNames: ReadonlySet<string> | undefined,
114
- ): SelectGroup[] {
115
- const byGroup = new Map<string, string[]>();
116
- for (const name of names) {
117
- const group = groupFor(name, tagsByTool);
118
- const bucket = byGroup.get(group) ?? [];
119
- bucket.push(name);
120
- byGroup.set(group, bucket);
121
- }
122
- return [...byGroup.entries()]
123
- .sort(([a], [b]) => {
124
- if (a === UNTAGGED) return 1;
125
- if (b === UNTAGGED) return -1;
126
- return a.localeCompare(b);
127
- })
128
- .map(([label, groupNames]) => ({
129
- label,
130
- options: groupNames.map((name) => ({
131
- value: name,
132
- label: optionLabel(name, displayNames, agentToolNames),
133
- })),
134
- }));
135
- }
136
-
137
77
  export function ToolPicker({
138
78
  toolNames,
139
79
  value,
@@ -167,64 +107,32 @@ export function ToolPicker({
167
107
  ? available.filter((name) => (tagsByTool[name] ?? []).includes(tagFilter))
168
108
  : available;
169
109
 
170
- const toolSelect =
171
- tagsByTool !== undefined ? (
172
- <Select
173
- groups={buildGroups(filtered, tagsByTool, displayNames, agentToolNames)}
174
- value={value ?? ''}
175
- onValueChange={onChange}
176
- placeholder={placeholder}
177
- aria-label={ariaLabel}
178
- disabled={disabled}
179
- />
180
- ) : (
181
- <Select
182
- // Keep Radix controlled even when the caller's value is null: `''` matches no
183
- // item (placeholder still shows) and avoids the uncontrolled→controlled warning
184
- // a subsequent selection would otherwise trigger.
185
- options={filtered.map((name) => ({
186
- value: name,
187
- label: optionLabel(name, displayNames, agentToolNames),
188
- }))}
189
- value={value ?? ''}
190
- onValueChange={onChange}
191
- placeholder={placeholder}
192
- aria-label={ariaLabel}
193
- disabled={disabled}
194
- />
195
- );
110
+ const toolSelect = (
111
+ <ToolSelect
112
+ tagsByTool={tagsByTool}
113
+ filtered={filtered}
114
+ value={value}
115
+ onChange={onChange}
116
+ placeholder={placeholder}
117
+ ariaLabel={ariaLabel}
118
+ disabled={disabled}
119
+ displayNames={displayNames}
120
+ agentToolNames={agentToolNames}
121
+ />
122
+ );
196
123
 
197
124
  return (
198
125
  <div data-testid={idPrefix} className="tai-stack tai-stack-2">
199
126
  {tagsByTool !== undefined && allTags.length > 0 ? (
200
- <Field label="Filter by tag">
201
- <Select
202
- options={[
203
- { value: ALL_TAGS, label: 'All tags' },
204
- ...allTags.map((tag) => ({ value: tag, label: tag })),
205
- ]}
206
- value={tagFilter}
207
- onValueChange={setTagFilter}
208
- disabled={disabled}
209
- />
210
- </Field>
127
+ <TagFilterField
128
+ allTags={allTags}
129
+ tagFilter={tagFilter}
130
+ onTagFilterChange={setTagFilter}
131
+ disabled={disabled}
132
+ />
211
133
  ) : null}
212
134
  {label !== undefined ? <Field label={label}>{toolSelect}</Field> : toolSelect}
213
- {value !== null && badgesByTool !== undefined && (badgesByTool[value]?.length ?? 0) > 0 ? (
214
- <Tooltip content={BADGES_NOTE}>
215
- <span
216
- className="tai-row"
217
- data-testid={`${idPrefix}-badges`}
218
- style={{ gap: 'var(--tai-space-1)' }}
219
- >
220
- {(badgesByTool[value] ?? []).map((badge) => (
221
- <Badge key={badge} variant="neutral">
222
- {badge}
223
- </Badge>
224
- ))}
225
- </span>
226
- </Tooltip>
227
- ) : null}
135
+ <SelectedToolBadges value={value} badgesByTool={badgesByTool} idPrefix={idPrefix} />
228
136
  </div>
229
137
  );
230
138
  }
@@ -30,13 +30,16 @@ import { useEffect, useRef, useState, type ReactNode } from 'react';
30
30
  import { errorMessage } from '../errors';
31
31
  import { Dialog } from './dialog';
32
32
  import { Field } from './field';
33
- import { CheckCircleIcon, PendingIcon } from './icons';
34
- import { JsonDiff } from './json-diff';
35
- import { JsonTree } from './json-tree';
36
33
  import { Button, Card, ErrorState, Spinner } from './primitives';
37
34
  import { ScrollRegion } from './scroll-region';
38
- import { Table, TBody, TD, TH, THead, TR } from './table';
39
- import { TagChips, TagsInput } from './tags';
35
+ import { Table, TBody, TH, THead, TR } from './table';
36
+ import { TagsInput } from './tags';
37
+ import {
38
+ CompareDialog,
39
+ RollbackConfirmDialog,
40
+ SelectedVersionBody,
41
+ VersionRow,
42
+ } from './version-history-rows';
40
43
 
41
44
  /** One version row — the kind-agnostic shape every consumer projects into. */
42
45
  export interface VersionHistoryEntry {
@@ -235,85 +238,19 @@ export function VersionHistoryPanel({
235
238
  </THead>
236
239
  <TBody>
237
240
  {versions.map((entry) => (
238
- <TR key={entry.version} data-testid={`version-row-${String(entry.version)}`}>
239
- <TD className="tai-table-id" numeric>
240
- {entry.version}
241
- </TD>
242
- <TD className="tai-mono">{entry.created_at}</TD>
243
- <TD>
244
- {entry.is_current ? (
245
- <span className="tai-status tai-status-ok">
246
- <CheckCircleIcon />
247
- Current
248
- </span>
249
- ) : (
250
- <span className="tai-status tai-status-pending">
251
- <PendingIcon />
252
- Historical
253
- </span>
254
- )}
255
- </TD>
256
- {showTags ? (
257
- <TD>
258
- {(entry.tags ?? []).length > 0 ? (
259
- <TagChips tags={entry.tags ?? []} />
260
- ) : (
261
- <span className="tai-muted">—</span>
262
- )}
263
- </TD>
264
- ) : null}
265
- <TD>
266
- <div className="tai-row">
267
- <Button
268
- aria-label={`View version ${String(entry.version)}`}
269
- onClick={() => {
270
- view(entry.version);
271
- }}
272
- >
273
- View
274
- </Button>
275
- {canCompare ? (
276
- <Button
277
- // The name starts with the word the button is SHOWING,
278
- // which is what WCAG 2.5.3 (Label in Name) asks: a constant
279
- // "Compare …" would leave a button reading "Comparing…"
280
- // named "Compare", and a voice-control user naming a control
281
- // they cannot see. The button stays ENABLED while armed —
282
- // unlike an Edit action, a second click on the armed row is
283
- // the disarm, so there is nothing to take away.
284
- aria-label={`${compareFrom === entry.version ? 'Comparing' : 'Compare'} version ${String(entry.version)}`}
285
- onClick={() => {
286
- compare(entry.version);
287
- }}
288
- >
289
- {compareFrom === entry.version ? 'Comparing…' : 'Compare'}
290
- </Button>
291
- ) : null}
292
- {onEditTags !== undefined && !readOnly ? (
293
- <Button
294
- aria-label={`Edit tags for version ${String(entry.version)}`}
295
- onClick={() => {
296
- setEditingTags(entry.version);
297
- }}
298
- >
299
- Edit tags
300
- </Button>
301
- ) : null}
302
- {readOnly ? null : (
303
- <Button
304
- variant="secondary"
305
- aria-label={`Roll back to version ${String(entry.version)}`}
306
- disabled={entry.is_current}
307
- onClick={() => {
308
- setConfirming(entry.version);
309
- }}
310
- >
311
- Roll back
312
- </Button>
313
- )}
314
- </div>
315
- </TD>
316
- </TR>
241
+ <VersionRow
242
+ key={entry.version}
243
+ entry={entry}
244
+ showTags={showTags}
245
+ canCompare={canCompare}
246
+ compareArmed={compareFrom === entry.version}
247
+ readOnly={readOnly}
248
+ canEditTags={onEditTags !== undefined}
249
+ onView={view}
250
+ onCompare={compare}
251
+ onEditTags={setEditingTags}
252
+ onRollback={setConfirming}
253
+ />
317
254
  ))}
318
255
  </TBody>
319
256
  </Table>
@@ -326,35 +263,16 @@ export function VersionHistoryPanel({
326
263
  </p>
327
264
  ) : null}
328
265
 
329
- {selectedEntry !== undefined ? (
330
- <section className="tai-stack tai-stack-2">
331
- <h3 className="tai-section-title">Version {selectedEntry.version} body</h3>
332
- <Card>
333
- <JsonTree
334
- data={selectedEntry.body}
335
- label={`Version ${String(selectedEntry.version)} body`}
336
- />
337
- </Card>
338
- </section>
339
- ) : null}
266
+ {selectedEntry !== undefined ? <SelectedVersionBody entry={selectedEntry} /> : null}
340
267
 
341
268
  {comparePair !== null ? (
342
- <Dialog
343
- title={`v${String(comparePair.from)} compared with v${String(comparePair.to)}`}
344
- description="A structural diff of the two version bodies. Arrays compare whole."
345
- open
346
- onOpenChange={(next) => {
347
- if (!next) closeCompare();
348
- }}
349
- >
350
- {/* A deep diff row can run wider than the dialog; it scrolls in place. */}
351
- <ScrollRegion label="Version diff">
352
- <JsonDiff before={fromBody} after={toBody} />
353
- </ScrollRegion>
354
- <div className="tai-dialog-actions">
355
- <Button onClick={closeCompare}>Close</Button>
356
- </div>
357
- </Dialog>
269
+ <CompareDialog
270
+ from={comparePair.from}
271
+ to={comparePair.to}
272
+ fromBody={fromBody}
273
+ toBody={toBody}
274
+ onClose={closeCompare}
275
+ />
358
276
  ) : null}
359
277
 
360
278
  {editingEntry !== undefined && onEditTags !== undefined ? (
@@ -369,38 +287,16 @@ export function VersionHistoryPanel({
369
287
  ) : null}
370
288
 
371
289
  {confirming !== null ? (
372
- <Dialog
373
- title="Roll back version"
374
- description={
375
- `Make version ${String(confirming)} the active version? The version history is preserved.` +
376
- (rollbackConfirmDescription !== undefined ? ` ${rollbackConfirmDescription}` : '')
377
- }
378
- open
379
- onOpenChange={(next) => {
380
- if (!next) setConfirming(null);
290
+ <RollbackConfirmDialog
291
+ version={confirming}
292
+ rollbackPending={rollbackPending}
293
+ rollbackError={rollbackError}
294
+ extraDescription={rollbackConfirmDescription}
295
+ onCancel={() => {
296
+ setConfirming(null);
381
297
  }}
382
- >
383
- {rollbackError !== undefined ? <ErrorState message={rollbackError} /> : null}
384
- <div className="tai-dialog-actions">
385
- <Button
386
- onClick={() => {
387
- setConfirming(null);
388
- }}
389
- >
390
- Cancel
391
- </Button>
392
- <Button
393
- variant="primary"
394
- disabled={rollbackPending}
395
- onClick={() => {
396
- onRollback(confirming);
397
- }}
398
- >
399
- {rollbackPending ? <Spinner label="Rolling back" /> : null}
400
- Roll back
401
- </Button>
402
- </div>
403
- </Dialog>
298
+ onConfirm={onRollback}
299
+ />
404
300
  ) : null}
405
301
  </div>
406
302
  );
@@ -0,0 +1,220 @@
1
+ /**
2
+ * The row and dialog pieces of `VersionHistoryPanel`: one version's table row and
3
+ * action cluster (`VersionRow`), the selected version's body (`SelectedVersionBody`),
4
+ * the two-version compare dialog (`CompareDialog`), and the rollback confirm
5
+ * dialog (`RollbackConfirmDialog`). All generic and kind-agnostic.
6
+ */
7
+ import type { ReactNode } from 'react';
8
+
9
+ import { Dialog } from './dialog';
10
+ import { CheckCircleIcon, PendingIcon } from './icons';
11
+ import { JsonDiff } from './json-diff';
12
+ import { JsonTree } from './json-tree';
13
+ import { Button, Card, ErrorState, Spinner } from './primitives';
14
+ import { ScrollRegion } from './scroll-region';
15
+ import { TD, TR } from './table';
16
+ import { TagChips } from './tags';
17
+ import type { VersionHistoryEntry } from './version-history-panel';
18
+
19
+ interface VersionRowProps {
20
+ readonly entry: VersionHistoryEntry;
21
+ readonly showTags: boolean;
22
+ readonly canCompare: boolean;
23
+ /** This row is the armed Compare source. */
24
+ readonly compareArmed: boolean;
25
+ readonly readOnly: boolean;
26
+ /** An edit-tags affordance is available (the consumer supplied `onEditTags`). */
27
+ readonly canEditTags: boolean;
28
+ readonly onView: (version: number) => void;
29
+ readonly onCompare: (version: number) => void;
30
+ readonly onEditTags: (version: number) => void;
31
+ readonly onRollback: (version: number) => void;
32
+ }
33
+
34
+ /** One version's row: its number, timestamp, status mark, optional tags, and the
35
+ * View / Compare / Edit tags / Roll back action cluster. */
36
+ export function VersionRow({
37
+ entry,
38
+ showTags,
39
+ canCompare,
40
+ compareArmed,
41
+ readOnly,
42
+ canEditTags,
43
+ onView,
44
+ onCompare,
45
+ onEditTags,
46
+ onRollback,
47
+ }: VersionRowProps): ReactNode {
48
+ return (
49
+ <TR data-testid={`version-row-${String(entry.version)}`}>
50
+ <TD className="tai-table-id" numeric>
51
+ {entry.version}
52
+ </TD>
53
+ <TD className="tai-mono">{entry.created_at}</TD>
54
+ <TD>
55
+ {entry.is_current ? (
56
+ <span className="tai-status tai-status-ok">
57
+ <CheckCircleIcon />
58
+ Current
59
+ </span>
60
+ ) : (
61
+ <span className="tai-status tai-status-pending">
62
+ <PendingIcon />
63
+ Historical
64
+ </span>
65
+ )}
66
+ </TD>
67
+ {showTags ? (
68
+ <TD>
69
+ {(entry.tags ?? []).length > 0 ? (
70
+ <TagChips tags={entry.tags ?? []} />
71
+ ) : (
72
+ <span className="tai-muted">—</span>
73
+ )}
74
+ </TD>
75
+ ) : null}
76
+ <TD>
77
+ <div className="tai-row">
78
+ <Button
79
+ aria-label={`View version ${String(entry.version)}`}
80
+ onClick={() => {
81
+ onView(entry.version);
82
+ }}
83
+ >
84
+ View
85
+ </Button>
86
+ {canCompare ? (
87
+ <Button
88
+ // The name starts with the word the button is SHOWING, which is what
89
+ // WCAG 2.5.3 (Label in Name) asks: a constant "Compare …" would leave a
90
+ // button reading "Comparing…" named "Compare", and a voice-control user
91
+ // naming a control they cannot see. The button stays ENABLED while armed
92
+ // — unlike an Edit action, a second click on the armed row is the disarm,
93
+ // so there is nothing to take away.
94
+ aria-label={`${compareArmed ? 'Comparing' : 'Compare'} version ${String(entry.version)}`}
95
+ onClick={() => {
96
+ onCompare(entry.version);
97
+ }}
98
+ >
99
+ {compareArmed ? 'Comparing…' : 'Compare'}
100
+ </Button>
101
+ ) : null}
102
+ {canEditTags && !readOnly ? (
103
+ <Button
104
+ aria-label={`Edit tags for version ${String(entry.version)}`}
105
+ onClick={() => {
106
+ onEditTags(entry.version);
107
+ }}
108
+ >
109
+ Edit tags
110
+ </Button>
111
+ ) : null}
112
+ {readOnly ? null : (
113
+ <Button
114
+ variant="secondary"
115
+ aria-label={`Roll back to version ${String(entry.version)}`}
116
+ disabled={entry.is_current}
117
+ onClick={() => {
118
+ onRollback(entry.version);
119
+ }}
120
+ >
121
+ Roll back
122
+ </Button>
123
+ )}
124
+ </div>
125
+ </TD>
126
+ </TR>
127
+ );
128
+ }
129
+
130
+ /** The selected version's opaque body, rendered through `JsonTree`. */
131
+ export function SelectedVersionBody({ entry }: { readonly entry: VersionHistoryEntry }): ReactNode {
132
+ return (
133
+ <section className="tai-stack tai-stack-2">
134
+ <h3 className="tai-section-title">Version {entry.version} body</h3>
135
+ <Card>
136
+ <JsonTree data={entry.body} label={`Version ${String(entry.version)} body`} />
137
+ </Card>
138
+ </section>
139
+ );
140
+ }
141
+
142
+ /** A structural diff of two version bodies, in a dialog. */
143
+ export function CompareDialog({
144
+ from,
145
+ to,
146
+ fromBody,
147
+ toBody,
148
+ onClose,
149
+ }: {
150
+ readonly from: number;
151
+ readonly to: number;
152
+ readonly fromBody: unknown;
153
+ readonly toBody: unknown;
154
+ readonly onClose: () => void;
155
+ }): ReactNode {
156
+ return (
157
+ <Dialog
158
+ title={`v${String(from)} compared with v${String(to)}`}
159
+ description="A structural diff of the two version bodies. Arrays compare whole."
160
+ open
161
+ onOpenChange={(next) => {
162
+ if (!next) onClose();
163
+ }}
164
+ >
165
+ {/* A deep diff row can run wider than the dialog; it scrolls in place. */}
166
+ <ScrollRegion label="Version diff">
167
+ <JsonDiff before={fromBody} after={toBody} />
168
+ </ScrollRegion>
169
+ <div className="tai-dialog-actions">
170
+ <Button onClick={onClose}>Close</Button>
171
+ </div>
172
+ </Dialog>
173
+ );
174
+ }
175
+
176
+ /** The rollback confirmation, surfacing any rollback failure verbatim. */
177
+ export function RollbackConfirmDialog({
178
+ version,
179
+ rollbackPending,
180
+ rollbackError,
181
+ extraDescription,
182
+ onCancel,
183
+ onConfirm,
184
+ }: {
185
+ readonly version: number;
186
+ readonly rollbackPending: boolean | undefined;
187
+ readonly rollbackError: string | undefined;
188
+ readonly extraDescription: string | undefined;
189
+ readonly onCancel: () => void;
190
+ readonly onConfirm: (version: number) => void;
191
+ }): ReactNode {
192
+ return (
193
+ <Dialog
194
+ title="Roll back version"
195
+ description={
196
+ `Make version ${String(version)} the active version? The version history is preserved.` +
197
+ (extraDescription !== undefined ? ` ${extraDescription}` : '')
198
+ }
199
+ open
200
+ onOpenChange={(next) => {
201
+ if (!next) onCancel();
202
+ }}
203
+ >
204
+ {rollbackError !== undefined ? <ErrorState message={rollbackError} /> : null}
205
+ <div className="tai-dialog-actions">
206
+ <Button onClick={onCancel}>Cancel</Button>
207
+ <Button
208
+ variant="primary"
209
+ disabled={rollbackPending}
210
+ onClick={() => {
211
+ onConfirm(version);
212
+ }}
213
+ >
214
+ {rollbackPending ? <Spinner label="Rolling back" /> : null}
215
+ Roll back
216
+ </Button>
217
+ </div>
218
+ </Dialog>
219
+ );
220
+ }
package/src/guards.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shared runtime type guards. Internal to studio-sdk — never re-exported from
3
+ * `index.ts` or any subpath, so it stays off the public plugin surface.
4
+ */
5
+ export function isRecord(value: unknown): value is Record<string, unknown> {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ }