@kitn.ai/ui 0.28.0 → 0.29.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 (376) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-tooling/construct/public.d.ts +15 -0
  3. package/dist/agent-tooling/construct/schema.d.ts +111 -0
  4. package/dist/construct-cli.es.js +3 -3
  5. package/dist/construct.d.ts +5 -0
  6. package/dist/construct.js +327 -0
  7. package/dist/core-C8fzo39E.js +1 -12
  8. package/dist/engine-javascript-C1x7zo1_.js +1 -141
  9. package/dist/mcp.es.js +1 -1
  10. package/package.json +16 -16
  11. package/bin/route.test.js +0 -58
  12. package/src/agent-tooling/README.md +0 -192
  13. package/src/agent-tooling/archetypes.ts +0 -205
  14. package/src/agent-tooling/catalog/README.md +0 -676
  15. package/src/agent-tooling/catalog/catalog-types.ts +0 -231
  16. package/src/agent-tooling/catalog/fabrications.ts +0 -96
  17. package/src/agent-tooling/catalog/invariants.ts +0 -284
  18. package/src/agent-tooling/catalog/labs-titles.ts +0 -114
  19. package/src/agent-tooling/catalog/scenarios.ts +0 -87
  20. package/src/agent-tooling/catalog/surfaces.ts +0 -347
  21. package/src/agent-tooling/construct/cli-entry.ts +0 -8
  22. package/src/agent-tooling/construct/cli.ts +0 -147
  23. package/src/agent-tooling/construct/codegen.ts +0 -1635
  24. package/src/agent-tooling/construct/construct.v1.schema.json +0 -319
  25. package/src/agent-tooling/construct/dev.ts +0 -148
  26. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +0 -7
  27. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +0 -44
  28. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +0 -32
  29. package/src/agent-tooling/construct/schema.ts +0 -452
  30. package/src/agent-tooling/integrations/anthropic.ts +0 -442
  31. package/src/agent-tooling/integrations/cloudflare.ts +0 -218
  32. package/src/agent-tooling/integrations/langgraph.ts +0 -133
  33. package/src/agent-tooling/integrations/mastra.ts +0 -182
  34. package/src/agent-tooling/integrations/mock.ts +0 -148
  35. package/src/agent-tooling/integrations/ollama.ts +0 -97
  36. package/src/agent-tooling/integrations/openai.ts +0 -95
  37. package/src/agent-tooling/integrations/openrouter.ts +0 -82
  38. package/src/agent-tooling/integrations/pi.ts +0 -176
  39. package/src/agent-tooling/integrations/pydantic-ai.ts +0 -108
  40. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +0 -398
  41. package/src/agent-tooling/mcp/css-raw.d.ts +0 -12
  42. package/src/agent-tooling/mcp/manifest.ts +0 -422
  43. package/src/agent-tooling/mcp/server.ts +0 -116
  44. package/src/agent-tooling/mcp/stdio.ts +0 -18
  45. package/src/agent-tooling/mcp/tools/construct.ts +0 -130
  46. package/src/agent-tooling/mcp/tools/debug.ts +0 -621
  47. package/src/agent-tooling/mcp/tools/reference.ts +0 -980
  48. package/src/agent-tooling/mcp/tools/scaffold.ts +0 -6708
  49. package/src/agent-tooling/mcp/tools/theme.ts +0 -456
  50. package/src/agent-tooling/mcp/tools/types.ts +0 -14
  51. package/src/agent-tooling/mcp/validate-args.ts +0 -141
  52. package/src/agent-tooling/recipes/composed-thread.ts +0 -714
  53. package/src/agent-tooling/recipes/index.ts +0 -22
  54. package/src/agent-tooling/recipes/types.ts +0 -32
  55. package/src/agent-tooling/registry.ts +0 -132
  56. package/src/agent-tooling/route-emit.ts +0 -305
  57. package/src/agent-tooling/types.ts +0 -390
  58. package/src/components/artifact-card.tsx +0 -120
  59. package/src/components/artifact.tsx +0 -914
  60. package/src/components/attachment-types.ts +0 -66
  61. package/src/components/attachments.tsx +0 -536
  62. package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +0 -257
  63. package/src/components/audio-visualizer/aurora.glsl.ts +0 -330
  64. package/src/components/audio-visualizer/fit-scale.ts +0 -112
  65. package/src/components/audio-visualizer/index.tsx +0 -528
  66. package/src/components/audio-visualizer/labs/lab-choreography.ts +0 -187
  67. package/src/components/audio-visualizer/labs/lab-shaders.ts +0 -363
  68. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +0 -102
  69. package/src/components/audio-visualizer/shader-canvas.tsx +0 -876
  70. package/src/components/audio-visualizer/sizes.ts +0 -54
  71. package/src/components/audio-visualizer/variant-aurora.tsx +0 -305
  72. package/src/components/audio-visualizer/variant-bar.tsx +0 -175
  73. package/src/components/audio-visualizer/variant-custom.tsx +0 -344
  74. package/src/components/audio-visualizer/variant-grid.tsx +0 -175
  75. package/src/components/audio-visualizer/variant-radial.tsx +0 -163
  76. package/src/components/audio-visualizer/variant-wave.tsx +0 -130
  77. package/src/components/audio-visualizer/wave.glsl.ts +0 -130
  78. package/src/components/card-fallback.tsx +0 -60
  79. package/src/components/card-renderer.tsx +0 -220
  80. package/src/components/card.tsx +0 -212
  81. package/src/components/chain-of-thought.tsx +0 -348
  82. package/src/components/chat-container.tsx +0 -89
  83. package/src/components/chat-scope-picker.tsx +0 -57
  84. package/src/components/chat-thread.tsx +0 -1014
  85. package/src/components/checkpoint.tsx +0 -88
  86. package/src/components/choice-card.tsx +0 -743
  87. package/src/components/coachmark.tsx +0 -249
  88. package/src/components/code-block.tsx +0 -181
  89. package/src/components/composer-dom.ts +0 -159
  90. package/src/components/composer-highlight.ts +0 -242
  91. package/src/components/composer-history.ts +0 -82
  92. package/src/components/composer.tsx +0 -1075
  93. package/src/components/confirm-card.tsx +0 -402
  94. package/src/components/context.tsx +0 -377
  95. package/src/components/conversation-item.tsx +0 -241
  96. package/src/components/conversation-list.tsx +0 -429
  97. package/src/components/conversation-panel.tsx +0 -118
  98. package/src/components/dismissed-stub.tsx +0 -80
  99. package/src/components/embed.tsx +0 -200
  100. package/src/components/empty.tsx +0 -177
  101. package/src/components/feedback-bar.tsx +0 -166
  102. package/src/components/file-tree.tsx +0 -530
  103. package/src/components/file-upload.tsx +0 -167
  104. package/src/components/form-widgets.tsx +0 -525
  105. package/src/components/form.tsx +0 -1296
  106. package/src/components/home-panel.tsx +0 -159
  107. package/src/components/image.tsx +0 -65
  108. package/src/components/link-preview.tsx +0 -195
  109. package/src/components/loader.tsx +0 -367
  110. package/src/components/markdown.tsx +0 -162
  111. package/src/components/message-skills.tsx +0 -40
  112. package/src/components/message.tsx +0 -765
  113. package/src/components/model-switcher.tsx +0 -132
  114. package/src/components/prompt-input.tsx +0 -188
  115. package/src/components/prompt-suggestion.tsx +0 -150
  116. package/src/components/reasoning.tsx +0 -293
  117. package/src/components/response-compare-types.ts +0 -152
  118. package/src/components/response-compare.tsx +0 -455
  119. package/src/components/response-stream.tsx +0 -109
  120. package/src/components/screen.tsx +0 -181
  121. package/src/components/scroll-button.tsx +0 -93
  122. package/src/components/source.tsx +0 -192
  123. package/src/components/tasks-card.tsx +0 -801
  124. package/src/components/text-shimmer.tsx +0 -37
  125. package/src/components/thinking-bar.tsx +0 -50
  126. package/src/components/thread.tsx +0 -228
  127. package/src/components/toast.tsx +0 -503
  128. package/src/components/tool-classify.ts +0 -26
  129. package/src/components/tool-types.ts +0 -49
  130. package/src/components/tool.tsx +0 -190
  131. package/src/components/use-card-resolution.ts +0 -44
  132. package/src/components/voice-input.tsx +0 -222
  133. package/src/components/voice-output.tsx +0 -242
  134. package/src/components/widget-tab-bar.tsx +0 -73
  135. package/src/components/workspace-shell.tsx +0 -285
  136. package/src/diagnostics/hook.ts +0 -352
  137. package/src/diagnostics/index.ts +0 -82
  138. package/src/diagnostics/report-request.ts +0 -296
  139. package/src/elements/agent-card.tsx +0 -67
  140. package/src/elements/artifact.tsx +0 -194
  141. package/src/elements/attachments.tsx +0 -140
  142. package/src/elements/audio-visualizer.tsx +0 -227
  143. package/src/elements/autoloader.ts +0 -89
  144. package/src/elements/avatar.tsx +0 -33
  145. package/src/elements/badge.tsx +0 -32
  146. package/src/elements/button.tsx +0 -149
  147. package/src/elements/card.tsx +0 -133
  148. package/src/elements/cards.tsx +0 -266
  149. package/src/elements/chain-of-thought.tsx +0 -141
  150. package/src/elements/chat-scope-picker.tsx +0 -65
  151. package/src/elements/chat-types.ts +0 -102
  152. package/src/elements/chat-workspace.tsx +0 -188
  153. package/src/elements/chat.tsx +0 -304
  154. package/src/elements/checkbox-group.tsx +0 -164
  155. package/src/elements/checkbox.tsx +0 -134
  156. package/src/elements/checkpoint.tsx +0 -47
  157. package/src/elements/choice.tsx +0 -100
  158. package/src/elements/coachmark.tsx +0 -90
  159. package/src/elements/code-block.tsx +0 -60
  160. package/src/elements/command.tsx +0 -197
  161. package/src/elements/compare.tsx +0 -111
  162. package/src/elements/compiled.css +0 -2
  163. package/src/elements/composer.tsx +0 -162
  164. package/src/elements/confirm-card.tsx +0 -76
  165. package/src/elements/context-meter.tsx +0 -107
  166. package/src/elements/conversation-item.tsx +0 -120
  167. package/src/elements/conversation-list.tsx +0 -203
  168. package/src/elements/css.ts +0 -5
  169. package/src/elements/default-input.tsx +0 -392
  170. package/src/elements/define-entry.ts +0 -12
  171. package/src/elements/define.tsx +0 -563
  172. package/src/elements/diagnostic-events.ts +0 -114
  173. package/src/elements/dialog.tsx +0 -145
  174. package/src/elements/disclosure.ts +0 -95
  175. package/src/elements/dock.tsx +0 -215
  176. package/src/elements/dropdown.tsx +0 -123
  177. package/src/elements/editable-label.tsx +0 -140
  178. package/src/elements/element-data-types.ts +0 -152
  179. package/src/elements/element-diagnostics.ts +0 -392
  180. package/src/elements/element-manifest.json +0 -354
  181. package/src/elements/element-nonscalar.json +0 -187
  182. package/src/elements/element-types.d.ts +0 -4353
  183. package/src/elements/embed.tsx +0 -35
  184. package/src/elements/empty.tsx +0 -29
  185. package/src/elements/feedback-bar.tsx +0 -66
  186. package/src/elements/file-tree.tsx +0 -62
  187. package/src/elements/file-upload.tsx +0 -44
  188. package/src/elements/form.tsx +0 -94
  189. package/src/elements/hover-card.tsx +0 -80
  190. package/src/elements/icon.tsx +0 -41
  191. package/src/elements/image.tsx +0 -32
  192. package/src/elements/input.tsx +0 -492
  193. package/src/elements/kbd.tsx +0 -45
  194. package/src/elements/link-preview.tsx +0 -34
  195. package/src/elements/loader.tsx +0 -25
  196. package/src/elements/markdown.tsx +0 -38
  197. package/src/elements/menu.tsx +0 -241
  198. package/src/elements/message-skills.tsx +0 -83
  199. package/src/elements/message.tsx +0 -361
  200. package/src/elements/model-switcher.tsx +0 -121
  201. package/src/elements/nav.tsx +0 -87
  202. package/src/elements/notice.tsx +0 -53
  203. package/src/elements/pane-grid.tsx +0 -111
  204. package/src/elements/pane-group.tsx +0 -119
  205. package/src/elements/pane.tsx +0 -129
  206. package/src/elements/popover.tsx +0 -81
  207. package/src/elements/progress-bar.tsx +0 -39
  208. package/src/elements/prompt-dock.tsx +0 -82
  209. package/src/elements/prompt-input.tsx +0 -240
  210. package/src/elements/prompt-suggestions.tsx +0 -113
  211. package/src/elements/radio-group.tsx +0 -129
  212. package/src/elements/reasoning.tsx +0 -71
  213. package/src/elements/register-impl.ts +0 -148
  214. package/src/elements/register.ts +0 -76
  215. package/src/elements/remote.tsx +0 -225
  216. package/src/elements/resizable.globals.d.ts +0 -36
  217. package/src/elements/resizable.tsx +0 -704
  218. package/src/elements/response-stream.tsx +0 -40
  219. package/src/elements/screen.tsx +0 -90
  220. package/src/elements/scroll-area.tsx +0 -33
  221. package/src/elements/scroll-button.tsx +0 -179
  222. package/src/elements/search.tsx +0 -185
  223. package/src/elements/segmented.tsx +0 -129
  224. package/src/elements/select.tsx +0 -165
  225. package/src/elements/separator.tsx +0 -36
  226. package/src/elements/setting-item.tsx +0 -50
  227. package/src/elements/settings-group.tsx +0 -37
  228. package/src/elements/skeleton.tsx +0 -45
  229. package/src/elements/slider.tsx +0 -171
  230. package/src/elements/slot-text.ts +0 -73
  231. package/src/elements/slots.ts +0 -787
  232. package/src/elements/source.tsx +0 -126
  233. package/src/elements/status.tsx +0 -42
  234. package/src/elements/styles.css +0 -328
  235. package/src/elements/switch.tsx +0 -118
  236. package/src/elements/tabs.tsx +0 -95
  237. package/src/elements/tasks.tsx +0 -103
  238. package/src/elements/text-shimmer.tsx +0 -28
  239. package/src/elements/thinking-bar.tsx +0 -43
  240. package/src/elements/thread.tsx +0 -139
  241. package/src/elements/toast.tsx +0 -106
  242. package/src/elements/tool.tsx +0 -78
  243. package/src/elements/tooltip.tsx +0 -85
  244. package/src/elements/validate-messages.ts +0 -76
  245. package/src/elements/voice-input.tsx +0 -100
  246. package/src/elements/voice-output.tsx +0 -82
  247. package/src/index.ts +0 -430
  248. package/src/primitives/audio-bands.ts +0 -174
  249. package/src/primitives/card-component-types.ts +0 -63
  250. package/src/primitives/card-contract.ts +0 -80
  251. package/src/primitives/card-data-types.ts +0 -361
  252. package/src/primitives/card-host.tsx +0 -37
  253. package/src/primitives/card-recovery.ts +0 -160
  254. package/src/primitives/card-registry.tsx +0 -78
  255. package/src/primitives/card-resolution.ts +0 -36
  256. package/src/primitives/card-routing.ts +0 -85
  257. package/src/primitives/card-schemas/artifact.schema.json +0 -92
  258. package/src/primitives/card-schemas/card-envelope.schema.json +0 -14
  259. package/src/primitives/card-schemas/card-event.schema.json +0 -12
  260. package/src/primitives/card-schemas/choice.schema.json +0 -75
  261. package/src/primitives/card-schemas/confirm.schema.json +0 -65
  262. package/src/primitives/card-schemas/embed.schema.json +0 -63
  263. package/src/primitives/card-schemas/form.result.schema.json +0 -7
  264. package/src/primitives/card-schemas/form.schema.json +0 -53
  265. package/src/primitives/card-schemas/link.schema.json +0 -54
  266. package/src/primitives/card-schemas/tasks.result.schema.json +0 -16
  267. package/src/primitives/card-schemas/tasks.schema.json +0 -83
  268. package/src/primitives/card-tags.ts +0 -61
  269. package/src/primitives/card-validate-cards.ts +0 -255
  270. package/src/primitives/card-validate-schemas.ts +0 -58
  271. package/src/primitives/card-validate.ts +0 -131
  272. package/src/primitives/chat-config.tsx +0 -103
  273. package/src/primitives/composer-model.ts +0 -49
  274. package/src/primitives/composer-triggers.ts +0 -35
  275. package/src/primitives/controllable.ts +0 -21
  276. package/src/primitives/conversation-store.ts +0 -248
  277. package/src/primitives/create-kai-chat.ts +0 -66
  278. package/src/primitives/create-tween.ts +0 -266
  279. package/src/primitives/embed-providers.ts +0 -254
  280. package/src/primitives/field-mask.ts +0 -256
  281. package/src/primitives/field-semantics.ts +0 -115
  282. package/src/primitives/highlighter.ts +0 -157
  283. package/src/primitives/input-mask.ts +0 -853
  284. package/src/primitives/link-preview.ts +0 -87
  285. package/src/primitives/message-feedback.ts +0 -124
  286. package/src/primitives/pdf-preview.ts +0 -121
  287. package/src/primitives/toast-store.ts +0 -338
  288. package/src/primitives/url-scheme-policy.ts +0 -70
  289. package/src/primitives/use-audio-analysis.ts +0 -340
  290. package/src/primitives/use-auto-resize.ts +0 -31
  291. package/src/primitives/use-resize-observer.ts +0 -45
  292. package/src/primitives/use-sequencer.ts +0 -72
  293. package/src/primitives/use-speech-recognition.ts +0 -146
  294. package/src/primitives/use-stick-to-bottom.ts +0 -75
  295. package/src/primitives/use-text-stream.ts +0 -143
  296. package/src/primitives/use-voice-recorder.ts +0 -62
  297. package/src/primitives/visualizer-sequences.ts +0 -350
  298. package/src/remote/host-embed.ts +0 -345
  299. package/src/remote/index.ts +0 -2
  300. package/src/remote/origin.ts +0 -30
  301. package/src/remote/provider-runtime.ts +0 -262
  302. package/src/remote/provider.ts +0 -2
  303. package/src/remote/validate.ts +0 -22
  304. package/src/remote/version.ts +0 -12
  305. package/src/remote/wire.ts +0 -48
  306. package/src/schemas/from-tool-call.ts +0 -151
  307. package/src/schemas/index.ts +0 -341
  308. package/src/schemas/provider-subsets.ts +0 -538
  309. package/src/schemas/registry.ts +0 -355
  310. package/src/schemas/tool-defs.ts +0 -793
  311. package/src/solid.ts +0 -173
  312. package/src/state/index.ts +0 -44
  313. package/src/state/messages.ts +0 -53
  314. package/src/state/mock.ts +0 -297
  315. package/src/state/parts.ts +0 -295
  316. package/src/state/persistence.ts +0 -180
  317. package/src/state/stream.ts +0 -246
  318. package/src/state/suggestions.ts +0 -9
  319. package/src/state/threads.ts +0 -142
  320. package/src/test-utils/fake-clock.ts +0 -88
  321. package/src/types.ts +0 -95
  322. package/src/ui/action-icons.ts +0 -53
  323. package/src/ui/agent-card.tsx +0 -218
  324. package/src/ui/avatar.tsx +0 -23
  325. package/src/ui/badge.tsx +0 -21
  326. package/src/ui/button.tsx +0 -49
  327. package/src/ui/card.tsx +0 -245
  328. package/src/ui/checkbox-group.tsx +0 -153
  329. package/src/ui/checkbox.tsx +0 -63
  330. package/src/ui/collapsible.tsx +0 -163
  331. package/src/ui/command.tsx +0 -103
  332. package/src/ui/dialog.tsx +0 -223
  333. package/src/ui/dock.tsx +0 -630
  334. package/src/ui/dropdown.tsx +0 -636
  335. package/src/ui/editable-label.tsx +0 -112
  336. package/src/ui/hover-card.tsx +0 -345
  337. package/src/ui/icon.tsx +0 -167
  338. package/src/ui/input.tsx +0 -391
  339. package/src/ui/kbd.tsx +0 -118
  340. package/src/ui/nav.tsx +0 -282
  341. package/src/ui/notice.tsx +0 -84
  342. package/src/ui/overlay.tsx +0 -277
  343. package/src/ui/pane-grid.tsx +0 -117
  344. package/src/ui/pane-group.tsx +0 -291
  345. package/src/ui/pane.tsx +0 -193
  346. package/src/ui/popover.tsx +0 -106
  347. package/src/ui/progress-bar.tsx +0 -83
  348. package/src/ui/prompt-dock.tsx +0 -145
  349. package/src/ui/radio.tsx +0 -150
  350. package/src/ui/resizable.tsx +0 -693
  351. package/src/ui/scroll-area.tsx +0 -26
  352. package/src/ui/segmented.tsx +0 -109
  353. package/src/ui/select.tsx +0 -168
  354. package/src/ui/separator.tsx +0 -10
  355. package/src/ui/settings-group.tsx +0 -67
  356. package/src/ui/skeleton.tsx +0 -73
  357. package/src/ui/slider.tsx +0 -178
  358. package/src/ui/status.tsx +0 -48
  359. package/src/ui/switch.tsx +0 -119
  360. package/src/ui/tabs.tsx +0 -168
  361. package/src/ui/textarea.tsx +0 -21
  362. package/src/ui/tooltip.tsx +0 -118
  363. package/src/utils/cn.ts +0 -30
  364. package/src/wire/chunk.ts +0 -248
  365. package/src/wire/consume.ts +0 -599
  366. package/src/wire/diagnostics.ts +0 -727
  367. package/src/wire/encode-probe.ts +0 -214
  368. package/src/wire/encode.ts +0 -947
  369. package/src/wire/files.ts +0 -342
  370. package/src/wire/formats/anthropic.ts +0 -274
  371. package/src/wire/formats/openai.ts +0 -228
  372. package/src/wire/index.ts +0 -95
  373. package/src/wire/media-types.ts +0 -344
  374. package/src/wire/read.ts +0 -396
  375. package/src/wire/sink-helpers.ts +0 -50
  376. package/src/wire/sse.ts +0 -153
@@ -1,390 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- export const Category = z.enum(['provider', 'gateway', 'framework', 'harness', 'mock']);
4
- export const Language = z.enum(['ts', 'python']);
5
- export const StreamFormat = z.enum(['openai-sse', 'ai-sdk', 'native']);
6
- /**
7
- * The scaffold's target.
8
- *
9
- * Two of these are NOT web-component consumers, and the difference is load-bearing:
10
- * - `angular` consumes `kai-*` like vue/svelte/html, but needs
11
- * `CUSTOM_ELEMENTS_SCHEMA` and binds arrays/objects with `[prop]="…"`.
12
- * - `solid` consumes the SolidJS components DIRECTLY from the `@kitn.ai/ui`
13
- * root entry. The kit is authored in Solid, so routing a Solid consumer
14
- * through the custom-element facade would ship the Solid runtime twice and
15
- * cross a reactive-context boundary for nothing.
16
- */
17
- export const Framework = z.enum(['html', 'react', 'next', 'vue', 'svelte', 'angular', 'solid', 'fastapi', 'express', 'worker', 'tanstack-start']);
18
- export const Placement = z.enum(['side', 'full-page', 'docked-widget', 'inline']);
19
-
20
- /**
21
- * Whether this integration's SECRET may sit in the browser bundle.
22
- *
23
- * Read `'needs-proxy'` as "a server hop is mandatory" and `'frontend-safe'` as
24
- * "the browser may call the upstream itself". This is the flag `create-kai`
25
- * consults before it decides where a key goes, so getting it wrong in the
26
- * permissive direction writes an API key into client code. There is deliberately
27
- * no default and no boolean: the refinement below REJECTS an integration that
28
- * declares neither, because "absent" must never read as "safe".
29
- *
30
- * `'needs-proxy'` covers two cases, and the second is easy to miss:
31
- * 1. a secret. The route holds a key, so a static bundle cannot.
32
- * 2. a server-only CAPABILITY. `pi` spawns a local process; no key is involved
33
- * and a browser still cannot do it.
34
- *
35
- * `'frontend-safe'` is the narrow claim: nothing secret AND no server-only
36
- * capability. Two integrations qualify, and both were read off their routes —
37
- * `ollama` fetches `localhost:11434` with no auth header at all, and `mock`
38
- * ships no route to reach.
39
- *
40
- * Do not infer this from the provider's name. A provider with a CORS-friendly
41
- * public endpoint and one that requires a proxy look identical from outside; only
42
- * the route says which it is. That is the same lesson `forwardsFromClient` and
43
- * `clientToolFormat` were each added to record.
44
- */
45
- export const KeyExposure = z.enum(['frontend-safe', 'needs-proxy']);
46
-
47
- /**
48
- * What this integration needs the developer to supply OUT OF BAND — beyond an
49
- * install and a key.
50
- *
51
- * `create-kai` groups its gateway prompt three ways: **No backend**, **Bring a
52
- * key**, **Bring a server or runtime**. The third group is defined by exactly
53
- * this question, and until now the catalog could not answer it. Every derivation
54
- * that reproduced the spec's split did so by accident and broke on inspection:
55
- * · `category` does not separate them at all — `provider` holds `openai` and
56
- * `ollama`, `framework` holds `vercel-ai-sdk` and `langgraph`.
57
- * · a non-empty `routeTemplates` picks out `cloudflare`/`pi`/`pydantic-ai`,
58
- * which is a fact about how a route is EXPRESSED, not about what it needs.
59
- * · "declares a secret env var" puts `langgraph` and `pydantic-ai` in the key
60
- * group, which is right for one and wrong for the other.
61
- * So it is declared, for the reason `keyExposure`, `forwardsFromClient` and
62
- * `clientToolFormat` are each declared: only the route knows.
63
- *
64
- * The four values, and what makes each its own case for the CLI rather than a
65
- * boolean it could have been:
66
- *
67
- * · `'none'` ......... `npm install` plus (maybe) a key is the whole story.
68
- * The upstream is a remote HTTPS endpoint, or the agent runs IN the route.
69
- * `langgraph` is the one worth reading twice: it is in the spec's "server or
70
- * runtime" group and does not belong there — its emitted route builds the
71
- * graph in process (`createReactAgent` + `new ChatOpenAI(...)`), so there is
72
- * no LangGraph server anywhere and its runNote asks only for a key.
73
- * · `'local-server'` . a separate process must already be LISTENING before the
74
- * app works. The CLI cannot create it and must say so: `ollama serve` on
75
- * 11434, a `mastra dev` server behind `MASTRA_URL`.
76
- * · `'local-binary'` . an executable must be on PATH; the route spawns it on
77
- * demand rather than talking to a port. Nothing is listening beforehand, so
78
- * "start the server" is the wrong instruction — `pi` is the case.
79
- * · `'language-runtime'` the emitted backend runs on a runtime the JS toolchain
80
- * does not provide, and the user starts it themselves. `pydantic-ai` is a
81
- * FastAPI service: `deps.pip`, `uvicorn main:app`, a second port.
82
- *
83
- * A boolean would collapse the last three, and they are three different sentences
84
- * to print next to a menu item. The refinement below REFUSES an integration that
85
- * declares nothing, for the same reason `keyExposure` does: a missing value read
86
- * as `'none'` is a scaffold that says "npm run dev" to someone who needs to start
87
- * Ollama first, and then reports as a broken kit.
88
- */
89
- export const OutOfBand = z.enum(['none', 'local-server', 'local-binary', 'language-runtime']);
90
-
91
- /**
92
- * The packages an app must install BEYOND `@kitn.ai/ui` to run this
93
- * integration's route.
94
- *
95
- * Derived once, here, from the route's own import statements, so the CLI never
96
- * has to re-parse route source to find them. Today this fact only exists as prose
97
- * in `runNote`, which no installer can read.
98
- *
99
- * The rule, enforced by `registry.test.ts`: `npm` is exactly the set of
100
- * bare-module specifiers imported by the TS route sources, normalised to package
101
- * names (`@langchain/core/tools` counts as `@langchain/core`), minus `node:`
102
- * builtins and minus `@kitn.ai/ui` itself, which every scaffold already depends
103
- * on. `pip` is a superset of the python route's imports, because a Python app
104
- * also needs its ASGI server (`uvicorn`), which nothing imports.
105
- *
106
- * Type-only packages (`@types/*`) are not listed: nothing imports them, so the
107
- * rule cannot see them, and the frameworks that need them already carry them.
108
- */
109
- export const DepsSchema = z.object({
110
- npm: z.array(z.string()).default([]),
111
- pip: z.array(z.string()).default([]),
112
- });
113
-
114
- /**
115
- * An env var name that holds a secret rather than an address.
116
- *
117
- * Anchored at the END, which is the whole precision of it: `CF_API_TOKEN` and
118
- * `AI_GATEWAY_API_KEY` match, while `CF_ACCOUNT_ID` and `MASTRA_URL` do not.
119
- * `MASTRA_URL` is the case that stops this being a proxy for "declares any env
120
- * var at all" — a base URL is not a credential.
121
- *
122
- * This is a SAFETY NET over the declaration, never its source. Three integrations
123
- * (`langgraph`, `vercel-ai-sdk`, `pydantic-ai`) hold a key their route source
124
- * never names, because the SDK reads the env var itself; their `envVars` is what
125
- * catches them here, and nothing but the author reading the route would have.
126
- *
127
- * Exported because the scaffolder names the secrets back at the developer in its
128
- * run note ("OPENAI_API_KEY stays on the server"). That sentence and the
129
- * refinement below have to agree about which env vars are secret, so there is one
130
- * pattern rather than two free to drift apart.
131
- */
132
- export const SECRET_ENV_VAR = /(?:KEY|TOKEN|SECRET|PASSWORD)$/;
133
-
134
- /**
135
- * A credential written into a request from route source. The second alternative
136
- * is Anthropic's spelling, which is not an `Authorization` header at all.
137
- */
138
- const AUTH_HEADER = /Authorization\s*:|['"]x-api-key['"]\s*:/i;
139
-
140
- /**
141
- * A route that FETCHES a loopback address — so something local has to be
142
- * listening. `ollama`'s `fetch('http://localhost:11434/v1/chat/completions')` is
143
- * the case this catches.
144
- *
145
- * Anchored at the fetch/URL call, not matched anywhere in the source, and that
146
- * precision is the whole point. Two other routes mention loopback in prose:
147
- * mastra's `throw new Error('MASTRA_URL is not set… e.g. http://localhost:4111')`
148
- * and pi's `console.log('chat api: http://localhost:3001/api/chat')`. A bare
149
- * /localhost/ would flag both — mastra for the right answer by the wrong
150
- * evidence, pi for a line about its OWN listening port, which says nothing about
151
- * what it depends on. A net that is right by accident is the thing this field
152
- * exists to replace, so it is narrowed until it only fires on a real dependency.
153
- */
154
- const LOOPBACK_FETCH = /\b(?:fetch|URL)\s*\(\s*['"`]https?:\/\/(?:localhost|127\.0\.0\.1)/;
155
-
156
- /** A route that shells out. The program it names must exist on the host. */
157
- const SPAWNS_PROCESS = /\bfrom\s+['"]node:child_process['"]|\bspawn\s*\(/;
158
-
159
- export const IntegrationSchema = z.object({
160
- id: z.string(),
161
- title: z.string(),
162
- category: Category,
163
- language: Language,
164
- streamFormat: StreamFormat,
165
- envVars: z.array(z.string()).default([]),
166
- routeTemplates: z.record(z.string(), z.string()), // keyed by Framework value → code string
167
- /**
168
- * The backend as ONE web-standard handler, `(Request) => Response`, written as
169
- * a complete `async function chatHandler(request: Request)` declaration.
170
- *
171
- * WHY THIS EXISTS. `routeTemplates` is keyed by framework, and in practice
172
- * every TS integration only ever filled in `next`. Everything else fell
173
- * through to it, so a svelte scaffold emitted a Next.js `export async function
174
- * POST(req)` — which TYPECHECKS under SvelteKit and then throws
175
- * `req.json is not a function` on the first submit, because SvelteKit calls
176
- * `POST(event)`. vue and html got the same snippet with nowhere to put it.
177
- *
178
- * The handler BODY is portable; only the few lines that declare the route
179
- * differ per framework. So integrations write the body once here and the
180
- * scaffolder wraps it (see `WEB_ROUTE_ADAPTERS` in mcp/tools/scaffold.ts).
181
- * `routeTemplates` stays for routes that genuinely cannot be expressed this
182
- * way: a Worker using an `env` binding, an Express `(req, res)` bridge, a
183
- * FastAPI service.
184
- *
185
- * An exact `routeTemplates[framework]` still wins over this.
186
- */
187
- webRoute: z.string().optional(),
188
- streamMapping: z.string(), // prose: how the stream maps to messages
189
- runNote: z.string(),
190
- docsSlug: z.string(),
191
- /**
192
- * The request-body fields this integration's ROUTE reads off the client and
193
- * forwards upstream. Anything not listed is owned by the route or by the agent
194
- * behind it.
195
- *
196
- * The scaffolder emits an editable const for a field only when it appears
197
- * here, and that is the whole point: `model` used to be emitted whenever a
198
- * route template contained the substring 'model', which is true of any
199
- * template that so much as writes `model: 'llama3.2'`, so four integrations
200
- * shipped an editable const their route threw away. The mirror-image bug is a
201
- * `tools` array that never gets sent, which leaves kai-tool a panel no code
202
- * path can populate.
203
- *
204
- * Empty by default, so a new integration emits nothing until its route really
205
- * does forward the field.
206
- */
207
- forwardsFromClient: z.array(z.enum(['model', 'tools'])).default([]),
208
- /**
209
- * The tool-definition envelope this integration's ROUTE expects to find in the
210
- * `tools` array the client POSTs. Required exactly when `forwardsFromClient`
211
- * includes `'tools'`, and meaningless otherwise.
212
- *
213
- * WHY THIS IS DECLARED AND NOT DERIVED. It used to be looked up from
214
- * `streamFormat`, and that is a category error: `streamFormat` describes the
215
- * shape of the RESPONSE stream, while the tool envelope is a REQUEST concern.
216
- * The two only appeared to agree because every integration that forwarded
217
- * tools happened to be `openai-sse`. `anthropic` is where the conflation
218
- * becomes visible — it is `streamFormat: 'native'`, which says nothing about
219
- * request shape, and the old table mapped `native` to null.
220
- *
221
- * And keying on the integration's PROVIDER would be wrong too, which is the
222
- * part worth reading the route before assuming: `anthropic`'s handler converts
223
- * the array itself (`toAnthropicTools` reads `raw.function.name` /
224
- * `.function.parameters`), so it wants `'openai'` here. Handing it Anthropic's
225
- * own `{ name, input_schema }` shape would yield tools with a blank name. Only
226
- * the route knows what it accepts, so only the integration can state it — the
227
- * same reason `forwardsFromClient` lives here rather than being sniffed out of
228
- * the route source.
229
- *
230
- * Optional in the type, mandatory in practice: the refinement below rejects an
231
- * integration that forwards tools without one. There is deliberately no
232
- * default — a default is how a tools array with no card in it ships silently.
233
- */
234
- clientToolFormat: z.enum(['openai', 'anthropic', 'jsonschema']).optional(),
235
- /** See {@link DepsSchema}. Empty is a real answer: a fetch-only route needs nothing. */
236
- deps: DepsSchema.default({ npm: [], pip: [] }),
237
- /**
238
- * See {@link KeyExposure}. Optional in the TYPE only so the refinement below
239
- * owns the error message — Zod skips refinements when the base object already
240
- * failed, so a plain required field would report "Invalid input" and this is
241
- * the one field where the reader needs to be told what is at stake.
242
- */
243
- keyExposure: KeyExposure.optional(),
244
- /**
245
- * See {@link OutOfBand}. Optional in the TYPE only so the refinement below owns
246
- * the error message — Zod skips refinements when the base object already
247
- * failed, so a plain required field would report "Invalid input" and this is a
248
- * field whose reader needs to be told what the four values mean.
249
- */
250
- outOfBand: OutOfBand.optional(),
251
- }).superRefine((integration, ctx) => {
252
- // Enforced against the real catalog by `registry.test.ts`, which parses every
253
- // integration through this schema. A new host that forwards tools therefore
254
- // cannot reach the scaffolder undeclared: it fails at the catalog boundary,
255
- // before any emit path has to decide what to do about it.
256
- if (integration.forwardsFromClient.includes('tools') && integration.clientToolFormat === undefined) {
257
- ctx.addIssue({
258
- code: z.ZodIssueCode.custom,
259
- path: ['clientToolFormat'],
260
- message:
261
- `integration '${integration.id}' forwards a 'tools' array but declares no clientToolFormat. ` +
262
- `State the envelope its route expects: 'openai' ({ type: 'function', function: { parameters } }), ` +
263
- `'anthropic' ({ name, input_schema }), or 'jsonschema' ({ name, description, schema }). ` +
264
- `Read the route's own handler to decide — a route that CONVERTS the array server-side wants the ` +
265
- `shape it converts FROM, not its own provider's.`,
266
- });
267
- }
268
-
269
- // ---- keyExposure ----
270
- //
271
- // Two checks, and the split matters. The first refuses SILENCE. The second
272
- // refuses a specific LIE, and only ever in the dangerous direction: it can
273
- // turn a 'frontend-safe' claim into an error, never the reverse. Declaring
274
- // 'needs-proxy' where a proxy was not strictly required costs a server hop;
275
- // declaring 'frontend-safe' where it was required costs the key.
276
-
277
- if (integration.keyExposure === undefined) {
278
- ctx.addIssue({
279
- code: z.ZodIssueCode.custom,
280
- path: ['keyExposure'],
281
- message:
282
- `integration '${integration.id}' declares no keyExposure. State whether its SECRET may reach the ` +
283
- `browser bundle: 'needs-proxy' (a key, or a server-only capability such as spawning a process, so ` +
284
- `the CLI must keep it behind a server) or 'frontend-safe' (nothing secret and nothing server-only, ` +
285
- `so the browser may call the upstream directly). Read the route to decide, not the provider's name: ` +
286
- `a public CORS-friendly endpoint and one that requires a proxy are indistinguishable from outside. ` +
287
- `There is no default on purpose — a missing flag read as "safe" is how an API key ships in client code.`,
288
- });
289
- }
290
-
291
- if (integration.keyExposure === 'frontend-safe') {
292
- const secrets = integration.envVars.filter((name) => SECRET_ENV_VAR.test(name));
293
- if (secrets.length > 0) {
294
- ctx.addIssue({
295
- code: z.ZodIssueCode.custom,
296
- path: ['keyExposure'],
297
- message:
298
- `integration '${integration.id}' claims keyExposure 'frontend-safe' but declares the secret env ` +
299
- `var(s) ${secrets.join(', ')}. A frontend bundle is public, so that combination puts a key in ` +
300
- `client code. Use 'needs-proxy'.`,
301
- });
302
- }
303
-
304
- const routeSources = [
305
- ...Object.values(integration.routeTemplates),
306
- ...(integration.webRoute ? [integration.webRoute] : []),
307
- ];
308
- if (routeSources.some((code) => AUTH_HEADER.test(code))) {
309
- ctx.addIssue({
310
- code: z.ZodIssueCode.custom,
311
- path: ['keyExposure'],
312
- message:
313
- `integration '${integration.id}' claims keyExposure 'frontend-safe' but its route sends an ` +
314
- `authorization header, so it holds a credential a browser bundle must not. Use 'needs-proxy'.`,
315
- });
316
- }
317
- }
318
-
319
- // ---- outOfBand ----
320
- //
321
- // Same two-check shape as keyExposure, and the same asymmetry. The first
322
- // refuses SILENCE. The second refuses a specific LIE, and only in the direction
323
- // that costs the developer something: it can turn a `'none'` claim into an
324
- // error, never the reverse. Over-declaring prints a prerequisite that was not
325
- // strictly needed; under-declaring ships a scaffold that cannot possibly run
326
- // and blames the kit.
327
-
328
- if (integration.outOfBand === undefined) {
329
- ctx.addIssue({
330
- code: z.ZodIssueCode.custom,
331
- path: ['outOfBand'],
332
- message:
333
- `integration '${integration.id}' declares no outOfBand. State what the developer must supply ` +
334
- `BEYOND an install and a key, because that is the group create-kai's gateway prompt puts it in: ` +
335
- `'none' (a remote endpoint, or the agent runs inside the route), 'local-server' (a process must ` +
336
- `already be listening, e.g. 'ollama serve'), 'local-binary' (an executable on PATH that the route ` +
337
- `spawns), or 'language-runtime' (the emitted backend needs a runtime the JS toolchain does not ` +
338
- `provide, e.g. python + uvicorn). Read the route and the runNote to decide, not the category: ` +
339
- `'provider' holds both openai and ollama. There is no default on purpose — a missing value read ` +
340
- `as 'none' is a scaffold that says "npm run dev" to someone who has nothing to connect to.`,
341
- });
342
- }
343
-
344
- if (integration.outOfBand === 'none') {
345
- const routeSources = [
346
- ...Object.values(integration.routeTemplates),
347
- ...(integration.webRoute ? [integration.webRoute] : []),
348
- ];
349
- if (integration.language === 'python') {
350
- ctx.addIssue({
351
- code: z.ZodIssueCode.custom,
352
- path: ['outOfBand'],
353
- message:
354
- `integration '${integration.id}' claims outOfBand 'none' but its route is python, so a consumer ` +
355
- `whose whole toolchain is node cannot run it without an interpreter and an ASGI server they ` +
356
- `start themselves. Use 'language-runtime'.`,
357
- });
358
- }
359
- if (routeSources.some((code) => SPAWNS_PROCESS.test(code))) {
360
- ctx.addIssue({
361
- code: z.ZodIssueCode.custom,
362
- path: ['outOfBand'],
363
- message:
364
- `integration '${integration.id}' claims outOfBand 'none' but its route spawns a child process, ` +
365
- `so the program it spawns has to be installed on the host — nothing in package.json puts it ` +
366
- `there. Use 'local-binary'.`,
367
- });
368
- }
369
- if (routeSources.some((code) => LOOPBACK_FETCH.test(code))) {
370
- ctx.addIssue({
371
- code: z.ZodIssueCode.custom,
372
- path: ['outOfBand'],
373
- message:
374
- `integration '${integration.id}' claims outOfBand 'none' but its route fetches a loopback ` +
375
- `address, so something local has to be listening before the first message works. ` +
376
- `Use 'local-server'.`,
377
- });
378
- }
379
- }
380
- });
381
- export type Integration = z.infer<typeof IntegrationSchema>;
382
-
383
- export const ArchetypeSchema = z.object({
384
- id: z.string(),
385
- title: z.string(),
386
- components: z.array(z.string()), // kai-* tags, e.g. ['kai-chat', 'kai-sources']
387
- defaultPlacement: Placement,
388
- docsSlug: z.string(),
389
- });
390
- export type Archetype = z.infer<typeof ArchetypeSchema>;
@@ -1,120 +0,0 @@
1
- // src/components/artifact-card.tsx
2
- // The `artifact` built-in card: chrome + sizing around <Artifact>, plus the
3
- // bridge from its observation callbacks onto the frozen Card Contract.
4
- import { createUniqueId, untrack, Show, type JSX } from 'solid-js';
5
- import type { CardHost } from '../primitives/card-contract';
6
- import { Artifact } from './artifact';
7
-
8
- // `ArtifactCardData` is AUTHORED IN ../primitives/card-data-types.ts, along with
9
- // `ArtifactCardFile` — the same declaration `FileTree`/`Artifact` use, which is
10
- // why nothing here restates the file shape. See the note in confirm-card.tsx, or
11
- // that file's header, for why the card payload types left a `.tsx` and why they
12
- // are `type` aliases. Re-exported here unchanged.
13
-
14
- import type { ArtifactCardData } from '../primitives/card-data-types';
15
-
16
- export type {
17
- ArtifactCardData,
18
- ArtifactCardEnvelope,
19
- ArtifactCardFile,
20
- ArtifactCardTab,
21
- } from '../primitives/card-data-types';
22
-
23
- /** Height the artifact card falls back to. Tall enough that a framed page is
24
- * actually legible in a thread, short enough to leave the conversation visible. */
25
- export const DEFAULT_ARTIFACT_CARD_HEIGHT = '420px';
26
-
27
- function resolveHeight(height: number | string | undefined): string {
28
- if (typeof height === 'number') return `${height}px`;
29
- if (typeof height === 'string' && height.trim() !== '') return height;
30
- return DEFAULT_ARTIFACT_CARD_HEIGHT;
31
- }
32
-
33
- export interface ArtifactCardProps {
34
- data: ArtifactCardData;
35
- cardId: string;
36
- heading?: string;
37
- host?: CardHost;
38
- }
39
-
40
- /** Chrome + sizing around `<Artifact>` for the `artifact` card type.
41
- *
42
- * WHY A WRAPPER IS REQUIRED, not cosmetic. `<Artifact>`'s root is
43
- * `flex h-full w-full flex-col` — it is built to FILL a container that already
44
- * has a height (a resizable panel, a split view). A message thread gives its
45
- * parts no height at all, so `h-full` resolves against an auto-height parent,
46
- * the frame computes to ZERO, and the card renders as an invisible nothing.
47
- * The explicit `height` here is what makes the card visible; `data.height`
48
- * overrides it.
49
- *
50
- * `part`/`data-card-type` are stable handles. `CardRenderer` renders card
51
- * components bare, and in the web-component path `cardComponentsFromTags` maps a
52
- * non-overridden built-in straight to its Solid component — so there is no
53
- * `<kai-artifact>` tag in the DOM to point at. This wrapper is the only stable
54
- * thing to select, which is what lets a test count rendered artifacts inside
55
- * `<kai-thread>`'s shadow root.
56
- *
57
- * `part="card"` matches the other chromed cards, which get it from `<Card>`;
58
- * this one hand-rolls its chrome and so states it directly. The artifact-specific
59
- * handle is `data-card-type`, NOT a second part token: every `::part` name has to
60
- * be declared in `elements/slots.ts` (the drift guard in `slots.test.ts` enforces
61
- * it), so adding an `artifact` part is a deliberate public-API decision to make
62
- * there, not something to smuggle in from a component.
63
- *
64
- * `envelope.title` renders as a HEADING above the frame rather than being folded
65
- * into `displayUrl`: the address field states where the preview is pointing, and
66
- * overwriting it with a title would make it lie. A heading is also what the four
67
- * other chromed cards (form/confirm/tasks/choice) already do with `title`.
68
- *
69
- * `tab`/`activeFile` are SEEDS, never re-asserted — see their notes on
70
- * `ArtifactCardData`. A revised envelope must not undo what the user did.
71
- *
72
- * The three observation callbacks emit the contract's EXISTING `state` verb —
73
- * the contract is frozen, and a new kind would force a CARD_CONTRACT_VERSION
74
- * bump. Each patch key mirrors a `data` field name, so a host can merge the
75
- * patch straight back into `envelope.data` and re-send it; `addCard` upserts on
76
- * `envelope.id`, so that round-trip revises the card instead of duplicating it. */
77
- export function ArtifactCard(props: ArtifactCardProps): JSX.Element {
78
- const headingId = createUniqueId();
79
- // Read ONCE, non-reactively: these seed the view and must not be re-asserted
80
- // when a revision replaces the envelope. See ArtifactCardData.activeFile.
81
- const seedTab = untrack(() => props.data.tab);
82
- const seedActiveFile = untrack(() => props.data.activeFile);
83
- const patch = (p: Record<string, unknown>) =>
84
- props.host?.emit({ kind: 'state', cardId: props.cardId, patch: p });
85
-
86
- return (
87
- <div
88
- part="card"
89
- data-card-type="artifact"
90
- data-card-id={props.cardId}
91
- role="group"
92
- aria-labelledby={props.heading ? headingId : undefined}
93
- aria-label={props.heading ? undefined : 'Artifact'}
94
- style={{ height: resolveHeight(props.data.height) }}
95
- class="flex w-full flex-col overflow-hidden rounded-xl border border-border bg-card"
96
- >
97
- <Show when={props.heading}>
98
- <h3
99
- id={headingId}
100
- class="shrink-0 truncate border-b border-border px-3 py-2 text-sm font-medium text-foreground"
101
- >
102
- {props.heading}
103
- </h3>
104
- </Show>
105
- <div class="min-h-0 flex-1">
106
- <Artifact
107
- src={props.data.src}
108
- files={props.data.files}
109
- defaultTab={seedTab}
110
- activeFile={seedActiveFile}
111
- displayUrl={props.data.displayUrl}
112
- iframeTitle={props.heading ? `${props.heading} preview` : 'Artifact preview'}
113
- onNavigate={(url) => patch({ src: url })}
114
- onTabChange={(tab) => patch({ tab })}
115
- onFileSelect={(path) => patch({ activeFile: path })}
116
- />
117
- </div>
118
- </div>
119
- );
120
- }