@kitn.ai/ui 0.16.0 → 0.18.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 (493) hide show
  1. package/README.md +55 -17
  2. package/dist/components/artifact.d.ts +39 -1
  3. package/dist/components/attachment-types.d.ts +12 -0
  4. package/dist/components/attachments.d.ts +2 -10
  5. package/dist/components/card.d.ts +27 -0
  6. package/dist/components/chain-of-thought.d.ts +78 -2
  7. package/dist/components/chat-scope-picker.d.ts +7 -0
  8. package/dist/components/chat-thread.d.ts +39 -16
  9. package/dist/components/choice-card.d.ts +27 -0
  10. package/dist/components/coachmark.d.ts +49 -0
  11. package/dist/components/composer-dom.d.ts +35 -0
  12. package/dist/components/composer-highlight.d.ts +48 -0
  13. package/dist/components/composer-history.d.ts +34 -0
  14. package/dist/components/composer.d.ts +86 -0
  15. package/dist/components/confirm-card.d.ts +17 -0
  16. package/dist/components/conversation-item.d.ts +9 -0
  17. package/dist/components/conversation-list.d.ts +36 -1
  18. package/dist/components/dismissed-stub.d.ts +22 -0
  19. package/dist/components/file-tree.d.ts +15 -0
  20. package/dist/components/form.d.ts +34 -0
  21. package/dist/components/markdown.d.ts +3 -0
  22. package/dist/components/message.d.ts +71 -2
  23. package/dist/components/model-switcher.d.ts +7 -0
  24. package/dist/components/prompt-input.d.ts +13 -4
  25. package/dist/components/prompt-suggestion.d.ts +9 -1
  26. package/dist/components/reasoning.d.ts +14 -1
  27. package/dist/components/response-compare-types.d.ts +66 -0
  28. package/dist/components/response-compare.d.ts +65 -0
  29. package/dist/components/screen.d.ts +46 -0
  30. package/dist/components/tasks-card.d.ts +50 -1
  31. package/dist/components/toast.d.ts +58 -0
  32. package/dist/components/tool-types.d.ts +10 -0
  33. package/dist/components/tool.d.ts +8 -8
  34. package/dist/components/use-card-resolution.d.ts +10 -6
  35. package/dist/components/voice-input.d.ts +27 -0
  36. package/dist/components/voice-output.d.ts +33 -0
  37. package/dist/core-C8fzo39E.js +12 -0
  38. package/dist/custom-elements.json +8472 -1691
  39. package/dist/elements/agent-card.d.ts +1 -0
  40. package/dist/elements/agent-card.js +1 -0
  41. package/dist/elements/artifact.js +1 -0
  42. package/dist/elements/attachments.js +1 -0
  43. package/dist/elements/autoloader.d.ts +2 -0
  44. package/dist/elements/autoloader.js +1 -0
  45. package/dist/elements/avatar.d.ts +1 -0
  46. package/dist/elements/avatar.js +1 -0
  47. package/dist/elements/badge.d.ts +1 -0
  48. package/dist/elements/badge.js +1 -0
  49. package/dist/elements/button.d.ts +1 -0
  50. package/dist/elements/button.js +1 -0
  51. package/dist/elements/card.js +1 -0
  52. package/dist/elements/cards.d.ts +1 -0
  53. package/dist/elements/cards.js +1 -0
  54. package/dist/elements/chain-of-thought.d.ts +0 -12
  55. package/dist/elements/chain-of-thought.js +1 -0
  56. package/dist/elements/chat-scope-picker.js +1 -0
  57. package/dist/elements/chat-types.d.ts +8 -2
  58. package/dist/elements/chat-workspace.js +1 -0
  59. package/dist/elements/chat.js +1 -0
  60. package/dist/elements/checkpoint.js +1 -0
  61. package/dist/elements/choice.js +1 -0
  62. package/dist/elements/chunks/Icon-DLLBeLG5.js +1 -0
  63. package/dist/elements/chunks/action-icons-UB4V5FNq.js +1 -0
  64. package/dist/elements/chunks/arrow-left-C-gwlUmx.js +1 -0
  65. package/dist/elements/chunks/attachments-CgbErf1r.js +1 -0
  66. package/dist/elements/chunks/badge-DTQpgB6N.js +1 -0
  67. package/dist/elements/chunks/bash-InADTalH.js +1 -0
  68. package/dist/elements/chunks/button-XDUM6lXs.js +1 -0
  69. package/dist/elements/chunks/card-CBlFZ-7I.js +1 -0
  70. package/dist/elements/chunks/card-routing-C986KuUA.js +1 -0
  71. package/dist/elements/chunks/chat-thread-BIhmqwNL.js +1 -0
  72. package/dist/elements/chunks/check-hGUoVIKb.js +1 -0
  73. package/dist/elements/chunks/chevron-down-DDoaSUtO.js +1 -0
  74. package/dist/elements/chunks/chevron-right-CdGEUakB.js +1 -0
  75. package/dist/elements/chunks/circle-BKAZdmzb.js +1 -0
  76. package/dist/elements/chunks/circle-check-CmYG-y3I.js +1 -0
  77. package/dist/elements/chunks/circle-x-DewgAEym.js +1 -0
  78. package/dist/elements/chunks/cn-CU7UAWzk.js +1 -0
  79. package/dist/elements/chunks/code-block-DedwxwFI.js +1 -0
  80. package/dist/elements/chunks/collapsible-FfgmTHtM.js +1 -0
  81. package/dist/elements/chunks/composer-Cfbw5HKT.js +65 -0
  82. package/dist/elements/chunks/context-DwJmxlRE.js +1 -0
  83. package/dist/elements/chunks/conversation-list-DsCQMfPq.js +1 -0
  84. package/dist/elements/chunks/core-AYMC6_lb.js +12 -0
  85. package/dist/elements/chunks/css-M7EaDHN_.js +1 -0
  86. package/dist/elements/chunks/default-input-BuqsoENC.js +1 -0
  87. package/dist/elements/chunks/define-rGsaWsLL.js +1 -0
  88. package/dist/elements/chunks/disclosure-CwoAkpMH.js +1 -0
  89. package/dist/elements/chunks/download-DJ06G3Bz.js +1 -0
  90. package/dist/elements/chunks/dropdown-BFg8ri_R.js +1 -0
  91. package/dist/elements/chunks/ellipsis-dlN6YvGw.js +1 -0
  92. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +141 -0
  93. package/dist/elements/chunks/external-link-DBX2lc_R.js +1 -0
  94. package/dist/elements/chunks/file-text-HfwUx4F9.js +1 -0
  95. package/dist/elements/chunks/file-tree-C6Toj71u.js +1 -0
  96. package/dist/elements/chunks/folder-MYuqa9Xp.js +1 -0
  97. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +1 -0
  98. package/dist/elements/chunks/github-light-JYsPkUQd.js +1 -0
  99. package/dist/elements/chunks/hover-card-gE7iyXdW.js +1 -0
  100. package/dist/elements/chunks/html-CPZ3oZQ7.js +1 -0
  101. package/dist/elements/chunks/icon-Vqz4ivrY.js +1 -0
  102. package/dist/elements/chunks/index-BCRhi84a.js +1 -0
  103. package/dist/elements/chunks/info-B_toFS0l.js +1 -0
  104. package/dist/elements/chunks/input-gzbHYZvQ.js +1 -0
  105. package/dist/elements/chunks/javascript-C25yR2R2.js +1 -0
  106. package/dist/elements/chunks/json-DxJze_jm.js +1 -0
  107. package/dist/elements/chunks/kbd-n49c4KZm.js +1 -0
  108. package/dist/elements/chunks/link-CKPs9yM0.js +1 -0
  109. package/dist/elements/chunks/loader-q6MamGus.js +1 -0
  110. package/dist/elements/chunks/markdown-DBrcqq4S.js +64 -0
  111. package/dist/elements/chunks/message-CTqaYI0s.js +1 -0
  112. package/dist/elements/chunks/message-feedback-Cvzm03w0.js +1 -0
  113. package/dist/elements/chunks/message-square-CUg9eRCi.js +1 -0
  114. package/dist/elements/chunks/minimize-2-l2klxP6K.js +1 -0
  115. package/dist/elements/chunks/model-switcher-CmyjiHO1.js +1 -0
  116. package/dist/elements/chunks/overlay-C8sOYqjT.js +1 -0
  117. package/dist/elements/chunks/paperclip-42Ia7xHD.js +1 -0
  118. package/dist/elements/chunks/progress-bar-tzKMWAwO.js +1 -0
  119. package/dist/elements/chunks/prompt-suggestion-D3F6vP_A.js +1 -0
  120. package/dist/elements/chunks/reasoning-BJNmmyov.js +1 -0
  121. package/dist/elements/chunks/resizable-BRsk19qI.js +1 -0
  122. package/dist/elements/chunks/rotate-cw-BgcggLHC.js +1 -0
  123. package/dist/elements/chunks/scroll-area-CgGbFFbT.js +1 -0
  124. package/dist/elements/chunks/separator-8hM4GdHE.js +1 -0
  125. package/dist/elements/chunks/settings-CaUIYOg-.js +1 -0
  126. package/dist/elements/chunks/settings-group-CYjOVIZQ.js +1 -0
  127. package/dist/elements/chunks/share-BTYM3Ddt.js +1 -0
  128. package/dist/elements/chunks/skeleton-DsTYrPUe.js +1 -0
  129. package/dist/elements/chunks/slots-DkZ-Cmha.js +1 -0
  130. package/dist/elements/chunks/star-9R3LBQ9n.js +1 -0
  131. package/dist/elements/chunks/store-CYl5Y0_u.js +1 -0
  132. package/dist/elements/chunks/svelte--5p79yCD.js +1 -0
  133. package/dist/elements/chunks/text-shimmer-CaYGv8Se.js +1 -0
  134. package/dist/elements/chunks/textarea-NEr2SLcD.js +1 -0
  135. package/dist/elements/chunks/thumbs-up-DZYZkFfc.js +1 -0
  136. package/dist/elements/chunks/toast-store-ClxVZRcc.js +1 -0
  137. package/dist/elements/chunks/tool-CHZOmoe9.js +1 -0
  138. package/dist/elements/chunks/tooltip-WpHElELC.js +1 -0
  139. package/dist/elements/chunks/triangle-alert-BlS80PUg.js +1 -0
  140. package/dist/elements/chunks/tsx-B8rCNbgL.js +1 -0
  141. package/dist/elements/chunks/typescript-RycA9KXf.js +1 -0
  142. package/dist/elements/chunks/use-card-resolution-VSVoxDeJ.js +1 -0
  143. package/dist/elements/chunks/video-DxTPZqT8.js +1 -0
  144. package/dist/elements/chunks/vue-BmIZj4XD.js +1 -0
  145. package/dist/elements/chunks/x-C1qtndxn.js +1 -0
  146. package/dist/elements/coachmark.d.ts +1 -0
  147. package/dist/elements/coachmark.js +1 -0
  148. package/dist/elements/code-block.js +1 -0
  149. package/dist/elements/command.d.ts +1 -0
  150. package/dist/elements/command.js +1 -0
  151. package/dist/elements/compare.d.ts +1 -0
  152. package/dist/elements/compare.js +1 -0
  153. package/dist/elements/composer.d.ts +1 -0
  154. package/dist/elements/composer.js +1 -0
  155. package/dist/elements/confirm-card.js +1 -0
  156. package/dist/elements/context-meter.js +1 -0
  157. package/dist/elements/conversation-list.d.ts +1 -11
  158. package/dist/elements/conversation-list.js +1 -0
  159. package/dist/elements/default-input.d.ts +20 -9
  160. package/dist/elements/define.d.ts +11 -0
  161. package/dist/elements/dialog.d.ts +1 -0
  162. package/dist/elements/dialog.js +1 -0
  163. package/dist/elements/disclosure.d.ts +37 -0
  164. package/dist/elements/editable-label.d.ts +1 -0
  165. package/dist/elements/editable-label.js +1 -0
  166. package/dist/elements/embed.js +1 -0
  167. package/dist/elements/empty.js +1 -0
  168. package/dist/elements/feedback-bar.js +1 -0
  169. package/dist/elements/file-tree.js +1 -0
  170. package/dist/elements/file-upload.js +1 -0
  171. package/dist/elements/form.js +1 -0
  172. package/dist/elements/hover-card.d.ts +1 -0
  173. package/dist/elements/hover-card.js +1 -0
  174. package/dist/elements/icon.d.ts +1 -0
  175. package/dist/elements/icon.js +1 -0
  176. package/dist/elements/image.js +1 -0
  177. package/dist/elements/input.d.ts +1 -0
  178. package/dist/elements/input.js +1 -0
  179. package/dist/elements/kbd.d.ts +1 -0
  180. package/dist/elements/kbd.js +1 -0
  181. package/dist/elements/link-preview.js +1 -0
  182. package/dist/elements/loader.js +1 -0
  183. package/dist/elements/markdown.js +1 -0
  184. package/dist/elements/menu.d.ts +1 -0
  185. package/dist/elements/menu.js +1 -0
  186. package/dist/elements/message-skills.d.ts +0 -16
  187. package/dist/elements/message-skills.js +1 -0
  188. package/dist/elements/message.js +1 -0
  189. package/dist/elements/model-switcher.d.ts +1 -11
  190. package/dist/elements/model-switcher.js +1 -0
  191. package/dist/elements/nav.d.ts +1 -0
  192. package/dist/elements/nav.js +1 -0
  193. package/dist/elements/notice.d.ts +1 -0
  194. package/dist/elements/notice.js +1 -0
  195. package/dist/elements/pane-group.d.ts +1 -0
  196. package/dist/elements/pane-group.js +1 -0
  197. package/dist/elements/pane.d.ts +1 -0
  198. package/dist/elements/pane.js +1 -0
  199. package/dist/elements/popover.js +1 -0
  200. package/dist/elements/progress-bar.d.ts +1 -0
  201. package/dist/elements/progress-bar.js +1 -0
  202. package/dist/elements/prompt-dock.d.ts +1 -0
  203. package/dist/elements/prompt-dock.js +1 -0
  204. package/dist/elements/prompt-input.d.ts +1 -9
  205. package/dist/elements/prompt-input.js +1 -0
  206. package/dist/elements/prompt-suggestions.d.ts +0 -6
  207. package/dist/elements/prompt-suggestions.js +1 -0
  208. package/dist/elements/reasoning.js +1 -0
  209. package/dist/elements/register.d.ts +10 -0
  210. package/dist/elements/remote.js +1 -0
  211. package/dist/elements/resizable.d.ts +1 -17
  212. package/dist/elements/resizable.js +1 -0
  213. package/dist/elements/response-stream.js +14 -0
  214. package/dist/elements/screen.d.ts +1 -0
  215. package/dist/elements/screen.js +1 -0
  216. package/dist/elements/scroll-area.d.ts +1 -0
  217. package/dist/elements/scroll-area.js +1 -0
  218. package/dist/elements/scroll-button.js +1 -0
  219. package/dist/elements/search.d.ts +1 -0
  220. package/dist/elements/search.js +1 -0
  221. package/dist/elements/segmented.d.ts +1 -0
  222. package/dist/elements/segmented.js +1 -0
  223. package/dist/elements/separator.d.ts +1 -0
  224. package/dist/elements/separator.js +1 -0
  225. package/dist/elements/setting-item.d.ts +1 -0
  226. package/dist/elements/setting-item.js +1 -0
  227. package/dist/elements/settings-group.d.ts +1 -0
  228. package/dist/elements/settings-group.js +1 -0
  229. package/dist/elements/skeleton.d.ts +1 -0
  230. package/dist/elements/skeleton.js +1 -0
  231. package/dist/elements/slots.d.ts +176 -0
  232. package/dist/elements/source.d.ts +0 -17
  233. package/dist/elements/source.js +1 -0
  234. package/dist/elements/status.d.ts +1 -0
  235. package/dist/elements/status.js +1 -0
  236. package/dist/elements/switch.js +1 -0
  237. package/dist/elements/tabs.d.ts +1 -0
  238. package/dist/elements/tabs.js +1 -0
  239. package/dist/elements/tasks.js +1 -0
  240. package/dist/elements/text-shimmer.js +1 -0
  241. package/dist/elements/thinking-bar.js +1 -0
  242. package/dist/elements/toast.d.ts +1 -0
  243. package/dist/elements/toast.js +1 -0
  244. package/dist/elements/tool.js +1 -0
  245. package/dist/elements/tooltip.d.ts +1 -0
  246. package/dist/elements/tooltip.js +1 -0
  247. package/dist/elements/voice-input.js +1 -0
  248. package/dist/elements/voice-output.d.ts +1 -0
  249. package/dist/elements/voice-output.js +1 -0
  250. package/dist/elements.d.ts +772 -61
  251. package/dist/engine-javascript-C1x7zo1_.js +141 -0
  252. package/dist/index.d.ts +15 -2
  253. package/dist/index.js +14405 -9810
  254. package/dist/kai.es.js +1 -0
  255. package/dist/llms/llms-full.txt +1234 -107
  256. package/dist/llms/llms.txt +4 -4
  257. package/dist/mcp.es.js +217 -6
  258. package/dist/primitives/card-contract.d.ts +20 -4
  259. package/dist/primitives/card-recovery.d.ts +55 -0
  260. package/dist/primitives/composer-model.d.ts +22 -0
  261. package/dist/primitives/composer-triggers.d.ts +19 -0
  262. package/dist/primitives/create-kai-chat.d.ts +38 -0
  263. package/dist/primitives/message-feedback.d.ts +35 -0
  264. package/dist/primitives/toast-store.d.ts +118 -0
  265. package/dist/primitives/use-speech-recognition.d.ts +26 -0
  266. package/dist/react/index.d.ts +1166 -98
  267. package/dist/react/runtime.d.ts +9 -2
  268. package/dist/react/use-kai-chat.d.ts +41 -0
  269. package/dist/react.js +575 -186
  270. package/dist/register-impl-CYWGkgbI.js +141 -0
  271. package/dist/schemas/choice.schema.json +5 -0
  272. package/dist/schemas/tasks.schema.json +7 -2
  273. package/dist/state/index.d.ts +4 -0
  274. package/dist/state/messages.d.ts +11 -0
  275. package/dist/state/stream.d.ts +22 -0
  276. package/dist/state/suggestions.d.ts +4 -0
  277. package/dist/state.js +117 -0
  278. package/dist/theme.tokens.css +87 -0
  279. package/dist/types.d.ts +4 -0
  280. package/dist/ui/agent-card.d.ts +61 -0
  281. package/dist/ui/button.d.ts +5 -2
  282. package/dist/ui/card.d.ts +61 -0
  283. package/dist/ui/collapsible.d.ts +15 -1
  284. package/dist/ui/command.d.ts +33 -0
  285. package/dist/ui/dialog.d.ts +42 -0
  286. package/dist/ui/dropdown.d.ts +95 -1
  287. package/dist/ui/editable-label.d.ts +26 -0
  288. package/dist/ui/hover-card.d.ts +13 -1
  289. package/dist/ui/icon.d.ts +18 -0
  290. package/dist/ui/input.d.ts +33 -0
  291. package/dist/ui/kbd.d.ts +21 -0
  292. package/dist/ui/nav.d.ts +62 -0
  293. package/dist/ui/notice.d.ts +31 -0
  294. package/dist/ui/overlay.d.ts +12 -0
  295. package/dist/ui/pane-grid.d.ts +58 -0
  296. package/dist/ui/pane-group.d.ts +75 -0
  297. package/dist/ui/pane.d.ts +78 -0
  298. package/dist/ui/popover.d.ts +11 -1
  299. package/dist/ui/progress-bar.d.ts +23 -0
  300. package/dist/ui/prompt-dock.d.ts +96 -0
  301. package/dist/ui/resizable.d.ts +12 -0
  302. package/dist/ui/scroll-area.d.ts +4 -0
  303. package/dist/ui/segmented.d.ts +28 -0
  304. package/dist/ui/settings-group.d.ts +32 -0
  305. package/dist/ui/skeleton.d.ts +12 -0
  306. package/dist/ui/stat.d.ts +16 -0
  307. package/dist/ui/status.d.ts +13 -0
  308. package/dist/ui/switch.d.ts +8 -1
  309. package/dist/ui/tabs.d.ts +35 -0
  310. package/dist/ui/tooltip.d.ts +20 -1
  311. package/frameworks/react/index.tsx +1100 -163
  312. package/frameworks/react/runtime.tsx +30 -1
  313. package/frameworks/react/use-kai-chat.tsx +87 -0
  314. package/llms-full.txt +1234 -107
  315. package/llms.txt +4 -4
  316. package/package.json +36 -12
  317. package/src/agent-tooling/mcp/manifest.ts +14 -0
  318. package/src/agent-tooling/mcp/tools/debug.ts +194 -6
  319. package/src/agent-tooling/mcp/tools/reference.ts +32 -0
  320. package/src/agent-tooling/mcp/tools/scaffold.ts +162 -1
  321. package/src/components/artifact.tsx +98 -13
  322. package/src/components/attachment-types.ts +13 -0
  323. package/src/components/attachments.tsx +4 -14
  324. package/src/components/card.tsx +109 -15
  325. package/src/components/chain-of-thought.tsx +210 -1
  326. package/src/components/chat-scope-picker.tsx +13 -3
  327. package/src/components/chat-thread.tsx +238 -137
  328. package/src/components/choice-card.tsx +136 -15
  329. package/src/components/coachmark.tsx +245 -0
  330. package/src/components/component-meta.json +921 -108
  331. package/src/components/composer-dom.ts +159 -0
  332. package/src/components/composer-highlight.ts +242 -0
  333. package/src/components/composer-history.ts +82 -0
  334. package/src/components/composer.tsx +979 -0
  335. package/src/components/confirm-card.tsx +69 -2
  336. package/src/components/conversation-item.tsx +69 -7
  337. package/src/components/conversation-list.tsx +134 -37
  338. package/src/components/dismissed-stub.tsx +80 -0
  339. package/src/components/file-tree.tsx +197 -47
  340. package/src/components/form-widgets.tsx +9 -7
  341. package/src/components/form.tsx +161 -20
  342. package/src/components/image.tsx +3 -5
  343. package/src/components/link-preview.tsx +5 -4
  344. package/src/components/markdown.tsx +5 -2
  345. package/src/components/message.tsx +174 -14
  346. package/src/components/model-switcher.tsx +19 -4
  347. package/src/components/prompt-input.tsx +46 -75
  348. package/src/components/prompt-suggestion.tsx +48 -16
  349. package/src/components/reasoning.tsx +29 -6
  350. package/src/components/response-compare-types.ts +127 -0
  351. package/src/components/response-compare.tsx +460 -0
  352. package/src/components/screen.tsx +176 -0
  353. package/src/components/tasks-card.tsx +374 -23
  354. package/src/components/toast.tsx +486 -0
  355. package/src/components/tool-types.ts +10 -0
  356. package/src/components/tool.tsx +22 -16
  357. package/src/components/use-card-resolution.ts +24 -8
  358. package/src/components/voice-input.tsx +102 -19
  359. package/src/components/voice-output.tsx +202 -0
  360. package/src/elements/agent-card.tsx +67 -0
  361. package/src/elements/artifact.tsx +53 -4
  362. package/src/elements/attachments.tsx +1 -0
  363. package/src/elements/autoloader.ts +89 -0
  364. package/src/elements/avatar.tsx +33 -0
  365. package/src/elements/badge.tsx +32 -0
  366. package/src/elements/button.tsx +126 -0
  367. package/src/elements/card-media.jpg +0 -0
  368. package/src/elements/card.tsx +124 -31
  369. package/src/elements/cards.tsx +81 -7
  370. package/src/elements/chain-of-thought.tsx +77 -32
  371. package/src/elements/chat-scope-picker.tsx +35 -8
  372. package/src/elements/chat-types.ts +9 -2
  373. package/src/elements/chat-workspace.tsx +157 -43
  374. package/src/elements/chat.tsx +49 -25
  375. package/src/elements/choice.tsx +71 -19
  376. package/src/elements/coachmark.tsx +90 -0
  377. package/src/elements/command.tsx +209 -0
  378. package/src/elements/compare.tsx +110 -0
  379. package/src/elements/compiled.css +1 -1
  380. package/src/elements/composer.tsx +151 -0
  381. package/src/elements/confirm-card.tsx +36 -11
  382. package/src/elements/conversation-list.tsx +80 -11
  383. package/src/elements/default-input.tsx +75 -53
  384. package/src/elements/define.tsx +47 -6
  385. package/src/elements/dialog.tsx +98 -0
  386. package/src/elements/disclosure.ts +73 -0
  387. package/src/elements/editable-label.tsx +125 -0
  388. package/src/elements/element-manifest.json +310 -0
  389. package/src/elements/element-meta.json +5954 -1605
  390. package/src/elements/element-types.d.ts +771 -60
  391. package/src/elements/file-tree.tsx +12 -2
  392. package/src/elements/file-upload.tsx +1 -1
  393. package/src/elements/form.tsx +54 -11
  394. package/src/elements/hover-card.tsx +80 -0
  395. package/src/elements/icon-names.json +48 -0
  396. package/src/elements/icon.tsx +41 -0
  397. package/src/elements/input.tsx +203 -0
  398. package/src/elements/kbd.tsx +45 -0
  399. package/src/elements/menu.tsx +217 -0
  400. package/src/elements/message.tsx +84 -55
  401. package/src/elements/model-switcher.tsx +27 -1
  402. package/src/elements/nav.tsx +87 -0
  403. package/src/elements/notice.tsx +53 -0
  404. package/src/elements/pane-group.tsx +119 -0
  405. package/src/elements/pane.tsx +129 -0
  406. package/src/elements/popover.tsx +39 -17
  407. package/src/elements/progress-bar.tsx +39 -0
  408. package/src/elements/prompt-dock.tsx +81 -0
  409. package/src/elements/prompt-input.tsx +112 -63
  410. package/src/elements/prompt-suggestions.tsx +25 -9
  411. package/src/elements/reasoning.tsx +30 -9
  412. package/src/elements/register-impl.ts +41 -3
  413. package/src/elements/register.ts +20 -3
  414. package/src/elements/remote.tsx +4 -0
  415. package/src/elements/resizable.tsx +48 -11
  416. package/src/elements/screen.tsx +90 -0
  417. package/src/elements/scroll-area.tsx +33 -0
  418. package/src/elements/search.tsx +180 -0
  419. package/src/elements/segmented.tsx +135 -0
  420. package/src/elements/separator.tsx +36 -0
  421. package/src/elements/setting-item.tsx +50 -0
  422. package/src/elements/settings-group.tsx +37 -0
  423. package/src/elements/skeleton.tsx +45 -0
  424. package/src/elements/slots.ts +607 -0
  425. package/src/elements/source.tsx +13 -6
  426. package/src/elements/status.tsx +42 -0
  427. package/src/elements/styles.css +19 -0
  428. package/src/elements/switch.tsx +89 -14
  429. package/src/elements/tabs.tsx +95 -0
  430. package/src/elements/tasks.tsx +70 -14
  431. package/src/elements/toast.tsx +89 -0
  432. package/src/elements/tool.tsx +43 -9
  433. package/src/elements/tooltip.tsx +85 -0
  434. package/src/elements/video-poster.jpg +0 -0
  435. package/src/elements/voice-input.tsx +66 -16
  436. package/src/elements/voice-output.tsx +69 -0
  437. package/src/index.ts +35 -2
  438. package/src/primitives/card-contract.ts +14 -5
  439. package/src/primitives/card-recovery.ts +149 -0
  440. package/src/primitives/card-routing.ts +3 -0
  441. package/src/primitives/card-schemas/choice.schema.json +5 -0
  442. package/src/primitives/card-schemas/tasks.schema.json +7 -2
  443. package/src/primitives/composer-model.ts +49 -0
  444. package/src/primitives/composer-triggers.ts +35 -0
  445. package/src/primitives/create-kai-chat.ts +66 -0
  446. package/src/primitives/message-feedback.ts +123 -0
  447. package/src/primitives/toast-store.ts +276 -0
  448. package/src/primitives/use-speech-recognition.ts +135 -0
  449. package/src/state/index.ts +6 -0
  450. package/src/state/messages.ts +36 -0
  451. package/src/state/stream.ts +92 -0
  452. package/src/state/suggestions.ts +9 -0
  453. package/src/types.ts +4 -0
  454. package/src/ui/agent-card.tsx +212 -0
  455. package/src/ui/button.tsx +16 -5
  456. package/src/ui/card.tsx +245 -0
  457. package/src/ui/collapsible.tsx +30 -4
  458. package/src/ui/command.tsx +99 -0
  459. package/src/ui/dialog.tsx +217 -0
  460. package/src/ui/dropdown.tsx +375 -13
  461. package/src/ui/editable-label.tsx +112 -0
  462. package/src/ui/hover-card.tsx +28 -5
  463. package/src/ui/icon.tsx +110 -0
  464. package/src/ui/input.tsx +132 -0
  465. package/src/ui/kbd.tsx +118 -0
  466. package/src/ui/nav.tsx +282 -0
  467. package/src/ui/notice.tsx +84 -0
  468. package/src/ui/overlay.tsx +108 -2
  469. package/src/ui/pane-grid.tsx +116 -0
  470. package/src/ui/pane-group.tsx +280 -0
  471. package/src/ui/pane.tsx +193 -0
  472. package/src/ui/popover.tsx +16 -2
  473. package/src/ui/progress-bar.tsx +83 -0
  474. package/src/ui/prompt-dock.tsx +145 -0
  475. package/src/ui/resizable.tsx +69 -1
  476. package/src/ui/scroll-area.tsx +14 -3
  477. package/src/ui/segmented.tsx +109 -0
  478. package/src/ui/settings-group.tsx +67 -0
  479. package/src/ui/skeleton.tsx +62 -5
  480. package/src/ui/stat.tsx +41 -0
  481. package/src/ui/status.tsx +48 -0
  482. package/src/ui/switch.tsx +49 -23
  483. package/src/ui/tabs.tsx +168 -0
  484. package/src/ui/tooltip.tsx +57 -7
  485. package/theme.css +113 -0
  486. package/dist/components/slash-command.d.ts +0 -15
  487. package/dist/kitn-chat.es.js +0 -1
  488. package/dist/register-impl-wCsFPoh9.js +0 -77
  489. package/dist/register-ujhr1nBS.js +0 -1
  490. package/src/components/slash-command.tsx +0 -232
  491. package/src/elements/framework-usage.json +0 -563
  492. /package/dist/{kitn-chat-provider.d.ts → kai-provider.d.ts} +0 -0
  493. /package/dist/{kitn-chat-provider.es.js → kai-provider.es.js} +0 -0
@@ -1,43 +1,126 @@
1
- import { splitProps, Show, For, createSignal } from 'solid-js';
1
+ import { splitProps, Show, For, createSignal, createEffect, on } from 'solid-js';
2
2
  import { cn } from '../utils/cn';
3
3
  import { Button } from '../ui/button';
4
4
  import { Tooltip } from '../ui/tooltip';
5
5
  import { useVoiceRecorder } from '../primitives/use-voice-recorder';
6
+ import { useSpeechRecognition } from '../primitives/use-speech-recognition';
7
+
8
+ /** Imperative handle exposed via `controllerRef` — surfaces the recorder's latent
9
+ * start/stop so the `<kai-voice-input>` facade can forward them as instance
10
+ * methods (push-to-talk). Both run the SAME getUserMedia → blob → transcription
11
+ * path as clicking the mic, so manual + programmatic emit identically. */
12
+ export interface VoiceInputController {
13
+ /** Begin recording programmatically (same path as clicking the mic). */
14
+ start(): void;
15
+ /** Stop the in-progress recording (produces the blob → onTranscribe). */
16
+ stop(): void;
17
+ }
6
18
 
7
19
  export interface VoiceInputProps {
8
20
  onTranscribe: (audio: Blob) => Promise<string>;
9
21
  onTranscription: (text: string) => void;
10
22
  disabled?: boolean;
11
23
  class?: string;
24
+ /** Host supplied a `transcribe` callback. When true the MediaRecorder →
25
+ * onTranscribe path runs. When false the component prefers native
26
+ * SpeechRecognition (capable browsers), falling back to record-only. */
27
+ hasTranscribe?: boolean;
28
+ /** BCP-47 language tag for native recognition (e.g. `en-US`). */
29
+ lang?: string;
30
+ /** Emit live partial transcripts via `onInterim` (native path only). */
31
+ interim?: boolean;
32
+ /** Live partial transcript from native recognition (when `interim`). */
33
+ onInterim?: (text: string) => void;
34
+ /** Fires whenever recording starts or stops. Guarded against the spurious
35
+ * initial `false` — only true transitions emit (the facade maps this to
36
+ * kai-recording-change). */
37
+ onRecordingChange?: (recording: boolean) => void;
38
+ /** Receive the imperative controller once mounted. The `<kai-voice-input>`
39
+ * facade forwards these as element methods (start/stop). */
40
+ controllerRef?: (controller: VoiceInputController) => void;
12
41
  }
13
42
 
14
43
  export function VoiceInput(props: VoiceInputProps) {
15
44
  const [local] = splitProps(props, ['onTranscribe', 'onTranscription', 'disabled', 'class']);
16
45
  const { isRecording, start, stop } = useVoiceRecorder();
46
+ const speech = useSpeechRecognition({ interim: props.interim });
17
47
  const [isProcessing, setIsProcessing] = createSignal(false);
18
48
 
49
+ // Path selection (§6): a `transcribe` callback always wins (MediaRecorder →
50
+ // host transcriber). Otherwise prefer native SpeechRecognition when the browser
51
+ // supports it. The unsupported-no-callback case falls through to recording the
52
+ // blob (→ onTranscribe → kai-audio-captured) with no text.
53
+ const useNative = () => !props.hasTranscribe && speech.isSupported;
54
+
19
55
  const label = () =>
20
- isProcessing() ? 'Transcribing...' : isRecording() ? 'Stop recording' : 'Voice input';
56
+ isProcessing() ? 'Transcribing...' : isActive() ? 'Stop recording' : 'Voice input';
21
57
 
22
- async function handleClick() {
23
- if (isRecording()) {
24
- stop();
25
- } else {
58
+ const isActive = () => (useNative() ? speech.isListening() : isRecording());
59
+
60
+ // Native path: SpeechRecognition resolves with the final transcript when it
61
+ // ends (click again or controller.stop()). Final text → onTranscription.
62
+ async function beginRecognition() {
63
+ try {
64
+ const text = await speech.start({
65
+ lang: props.lang,
66
+ onInterim: props.interim ? (t) => props.onInterim?.(t) : undefined,
67
+ });
68
+ if (text.trim()) local.onTranscription(text.trim());
69
+ } catch {
70
+ /* surfaced via speech.error; the mic returns to idle */
71
+ }
72
+ }
73
+
74
+ // Begin recording, then run transcription once the blob resolves. Shared by the
75
+ // mic click and the imperative start() so both flows emit identically. start()
76
+ // resolves its Blob when the recorder stops (click again or controller.stop()).
77
+ async function beginRecording() {
78
+ try {
79
+ const blob = await start();
80
+ setIsProcessing(true);
26
81
  try {
27
- const blob = await start();
28
- setIsProcessing(true);
29
- try {
30
- const text = await local.onTranscribe(blob);
31
- if (text.trim()) local.onTranscription(text.trim());
32
- } finally {
33
- setIsProcessing(false);
34
- }
35
- } catch {
82
+ const text = await local.onTranscribe(blob);
83
+ if (text.trim()) local.onTranscription(text.trim());
84
+ } finally {
36
85
  setIsProcessing(false);
37
86
  }
87
+ } catch {
88
+ setIsProcessing(false);
38
89
  }
39
90
  }
40
91
 
92
+ // Drive whichever path is active (§6: start/stop must follow the live path).
93
+ function begin() {
94
+ if (useNative()) void beginRecognition();
95
+ else void beginRecording();
96
+ }
97
+ function end() {
98
+ if (useNative()) speech.stop();
99
+ else stop();
100
+ }
101
+
102
+ async function handleClick() {
103
+ if (isActive()) end();
104
+ else begin();
105
+ }
106
+
107
+ // Emit recording transitions to the host. `on(..., { defer: true })` skips the
108
+ // initial mount value (a spurious {recording:false}) — only real start/stop
109
+ // transitions fire. Drives BOTH manual (click) and programmatic (start/stop)
110
+ // since they all flip the active path's signal (MediaRecorder or recognition).
111
+ createEffect(on(isActive, (recording) => {
112
+ props.onRecordingChange?.(recording);
113
+ }, { defer: true }));
114
+
115
+ // Imperative controller (Pattern C): hand the facade a start/stop handle.
116
+ // start() runs the active path; stop() ends the in-progress session (resolving
117
+ // the active path's promise). Guard start() so a double-start while already
118
+ // active is a no-op (matches the click toggle).
119
+ props.controllerRef?.({
120
+ start: () => { if (!isActive()) begin(); },
121
+ stop: () => end(),
122
+ });
123
+
41
124
  // Mic icon (not recording)
42
125
  const MicIcon = () => (
43
126
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -65,7 +148,7 @@ export function VoiceInput(props: VoiceInputProps) {
65
148
  return (
66
149
  <div class={cn('relative inline-flex items-center justify-center', local.class)}>
67
150
  {/* Animated pulse rings when recording */}
68
- <Show when={isRecording()}>
151
+ <Show when={isActive()}>
69
152
  <For each={[0, 1, 2]}>
70
153
  {(index) => (
71
154
  <div
@@ -88,16 +171,16 @@ export function VoiceInput(props: VoiceInputProps) {
88
171
  disabled={local.disabled || isProcessing()}
89
172
  class={cn(
90
173
  'relative z-10 rounded-full transition-all duration-300',
91
- isRecording() && 'bg-destructive text-white hover:bg-destructive/80',
174
+ isActive() && 'bg-destructive text-white hover:bg-destructive/80',
92
175
  )}
93
176
  >
94
177
  <Show when={isProcessing()}>
95
178
  <Spinner />
96
179
  </Show>
97
- <Show when={!isProcessing() && isRecording()}>
180
+ <Show when={!isProcessing() && isActive()}>
98
181
  <StopIcon />
99
182
  </Show>
100
- <Show when={!isProcessing() && !isRecording()}>
183
+ <Show when={!isProcessing() && !isActive()}>
101
184
  <MicIcon />
102
185
  </Show>
103
186
  </Button>
@@ -0,0 +1,202 @@
1
+ import { splitProps, Show, createSignal, createEffect, on, onCleanup } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+ import { Button } from '../ui/button';
4
+ import { Tooltip } from '../ui/tooltip';
5
+
6
+ /** Imperative handle exposed via `controllerRef` — surfaces the playback controls
7
+ * so the `<kai-voice-output>` facade can forward them as instance methods. Both
8
+ * the native (speechSynthesis) and model (synthesize → Audio) paths run through
9
+ * these, so manual clicks and programmatic calls behave identically. */
10
+ export interface VoiceOutputController {
11
+ /** Speak the current `text` (native by default, model if `synthesize` is set). */
12
+ speak(): void;
13
+ /** Pause playback (resumable). */
14
+ pause(): void;
15
+ /** Resume paused playback. */
16
+ resume(): void;
17
+ /** Stop playback and reset. */
18
+ stop(): void;
19
+ }
20
+
21
+ export interface VoiceOutputProps {
22
+ /** The utterance to read aloud. */
23
+ text: string;
24
+ /** Speak automatically when `text` is set/changed. */
25
+ autoplay?: boolean;
26
+ /** TTS model seam: given text, return an audio Blob to play. When set, the
27
+ * native speechSynthesis path is bypassed. Mirrors VoiceInput's `onTranscribe`. */
28
+ onSynthesize?: (text: string) => Promise<Blob>;
29
+ /** Fires whenever playback starts or stops. */
30
+ onSpeakingChange?: (speaking: boolean) => void;
31
+ /** Fires once the model path resolves audio (model path only). */
32
+ onSynthesized?: (blob: Blob) => void;
33
+ disabled?: boolean;
34
+ class?: string;
35
+ /** Receive the imperative controller once mounted. The `<kai-voice-output>`
36
+ * facade forwards these as element methods (speak/pause/resume/stop). */
37
+ controllerRef?: (controller: VoiceOutputController) => void;
38
+ }
39
+
40
+ /** True when the browser exposes the Web Speech synthesis API. */
41
+ function hasSpeechSynthesis(): boolean {
42
+ return typeof window !== 'undefined' && 'speechSynthesis' in window;
43
+ }
44
+
45
+ export function VoiceOutput(props: VoiceOutputProps) {
46
+ const [local] = splitProps(props, ['text', 'disabled', 'class']);
47
+ const [isSpeaking, setIsSpeaking] = createSignal(false);
48
+ const [isLoading, setIsLoading] = createSignal(false);
49
+
50
+ // The model path plays through one reused <Audio>; the native path drives
51
+ // speechSynthesis. Only one is live at a time (branch on onSynthesize).
52
+ let audio: HTMLAudioElement | undefined;
53
+ let objectUrl: string | undefined;
54
+ let utterance: SpeechSynthesisUtterance | undefined;
55
+
56
+ // No way to speak at all → render disabled. A model seam OR native synthesis
57
+ // is enough; absent both, the control is inert (see the facade/spec).
58
+ const capable = () => !!props.onSynthesize || hasSpeechSynthesis();
59
+
60
+ const label = () =>
61
+ isLoading() ? 'Synthesizing...' : isSpeaking() ? 'Stop' : 'Read aloud';
62
+
63
+ function revokeUrl() {
64
+ if (objectUrl) {
65
+ URL.revokeObjectURL(objectUrl);
66
+ objectUrl = undefined;
67
+ }
68
+ }
69
+
70
+ // Native path: speechSynthesis.speak(new SpeechSynthesisUtterance(text)).
71
+ function speakNative() {
72
+ if (!hasSpeechSynthesis() || !local.text) return;
73
+ window.speechSynthesis.cancel();
74
+ utterance = new SpeechSynthesisUtterance(local.text);
75
+ utterance.onend = () => setIsSpeaking(false);
76
+ utterance.onerror = () => setIsSpeaking(false);
77
+ setIsSpeaking(true);
78
+ window.speechSynthesis.speak(utterance);
79
+ }
80
+
81
+ // Model path: await onSynthesize(text) → play the Blob via an <Audio>.
82
+ async function speakModel() {
83
+ if (!props.onSynthesize || !local.text) return;
84
+ setIsLoading(true);
85
+ try {
86
+ const blob = await props.onSynthesize(local.text);
87
+ props.onSynthesized?.(blob);
88
+ revokeUrl();
89
+ objectUrl = URL.createObjectURL(blob);
90
+ audio ??= new Audio();
91
+ audio.src = objectUrl;
92
+ audio.onended = () => setIsSpeaking(false);
93
+ audio.onerror = () => setIsSpeaking(false);
94
+ setIsSpeaking(true);
95
+ await audio.play();
96
+ } catch {
97
+ setIsSpeaking(false);
98
+ } finally {
99
+ setIsLoading(false);
100
+ }
101
+ }
102
+
103
+ function speak() {
104
+ if (props.onSynthesize) void speakModel();
105
+ else speakNative();
106
+ }
107
+
108
+ function stop() {
109
+ if (props.onSynthesize) {
110
+ if (audio) {
111
+ audio.pause();
112
+ audio.currentTime = 0;
113
+ }
114
+ } else if (hasSpeechSynthesis()) {
115
+ window.speechSynthesis.cancel();
116
+ }
117
+ setIsSpeaking(false);
118
+ }
119
+
120
+ function pause() {
121
+ if (props.onSynthesize) audio?.pause();
122
+ else if (hasSpeechSynthesis()) window.speechSynthesis.pause();
123
+ }
124
+
125
+ function resume() {
126
+ if (props.onSynthesize) void audio?.play();
127
+ else if (hasSpeechSynthesis()) window.speechSynthesis.resume();
128
+ }
129
+
130
+ function handleClick() {
131
+ if (isSpeaking()) stop();
132
+ else speak();
133
+ }
134
+
135
+ // Emit speaking transitions to the host. `defer: true` skips the spurious
136
+ // initial `false` — only real start/stop transitions fire.
137
+ createEffect(on(isSpeaking, (speaking) => {
138
+ props.onSpeakingChange?.(speaking);
139
+ }, { defer: true }));
140
+
141
+ // Autoplay: speak when text first arrives / changes (defer skips the mount
142
+ // value so an empty seed doesn't speak).
143
+ createEffect(on(() => local.text, (text) => {
144
+ if (props.autoplay && text) speak();
145
+ }, { defer: true }));
146
+
147
+ props.controllerRef?.({ speak, pause, resume, stop });
148
+
149
+ onCleanup(() => {
150
+ stop();
151
+ revokeUrl();
152
+ });
153
+
154
+ // Speaker icon (idle)
155
+ const SpeakerIcon = () => (
156
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
157
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
158
+ <path d="M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07" />
159
+ </svg>
160
+ );
161
+
162
+ // Square stop icon (speaking)
163
+ const StopIcon = () => (
164
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
165
+ <rect x="4" y="4" width="16" height="16" rx="2" />
166
+ </svg>
167
+ );
168
+
169
+ // Spinner (model synthesis in flight)
170
+ const Spinner = () => (
171
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="animate-spin">
172
+ <path d="M21 12a9 9 0 11-6.219-8.56" />
173
+ </svg>
174
+ );
175
+
176
+ return (
177
+ <div class={cn('relative inline-flex items-center justify-center', local.class)}>
178
+ <Tooltip content={label()}>
179
+ <Button
180
+ part="button"
181
+ variant="ghost"
182
+ size="icon-sm"
183
+ aria-label={label()}
184
+ aria-pressed={isSpeaking()}
185
+ onClick={handleClick}
186
+ disabled={local.disabled || !capable() || isLoading()}
187
+ class={cn('relative z-10 rounded-full transition-all duration-300')}
188
+ >
189
+ <Show when={isLoading()}>
190
+ <Spinner />
191
+ </Show>
192
+ <Show when={!isLoading() && isSpeaking()}>
193
+ <StopIcon />
194
+ </Show>
195
+ <Show when={!isLoading() && !isSpeaking()}>
196
+ <SpeakerIcon />
197
+ </Show>
198
+ </Button>
199
+ </Tooltip>
200
+ </div>
201
+ );
202
+ }
@@ -0,0 +1,67 @@
1
+ import { defineWebComponent } from './define';
2
+ import { AgentCard, type AgentStatus } from '../ui/agent-card';
3
+
4
+ interface Props extends Record<string, unknown> {
5
+ /** The agent's name — the primary label. Attribute: `name`. */
6
+ name?: string;
7
+ /** Selected / focused state: highlighted border + surface. Attribute: `active`. */
8
+ active?: boolean;
9
+ /** Raise a prominent "Needs you" pill plus a glowing amber edge — the
10
+ * attention-routing signal that pulls focus to this agent. Attribute:
11
+ * `needs-attention`. */
12
+ needsAttention?: boolean;
13
+ /** Run status — a JS PROPERTY (object), not an attribute. Shape:
14
+ * `{ tone, label?, pulse? }`, where `tone` is one of `working` | `idle` |
15
+ * `done` | `error` | `blocked` (maps to the kit's tool hues), `label` is an
16
+ * optional short string beside the dot, and `pulse` animates the dot. Set it
17
+ * with `el.status = { tone: 'working', label: 'Working', pulse: true }`. */
18
+ status?: AgentStatus;
19
+ }
20
+
21
+ /** Events fired by `<kai-agent-card>`. */
22
+ interface Events {
23
+ /** The card was activated — clicked, or Enter / Space while focused. Promote
24
+ * this agent back to focus. */
25
+ 'kai-activate': void;
26
+ /** The trailing "..." kebab was clicked. The consumer opens its own menu; the
27
+ * card only surfaces the affordance (the click does not also activate the card). */
28
+ 'kai-menu': void;
29
+ }
30
+
31
+ /**
32
+ * `<kai-agent-card>` — the compact glanceable card for one agent in a multi-agent
33
+ * workspace. The agent name with a leading tone-colored status dot, an optional
34
+ * "Needs you" attention treatment, an `active` (selected) state, and a trailing
35
+ * "..." overflow button.
36
+ *
37
+ * Set `status` as a JS property (it's an object); `name` is a string attribute and
38
+ * `active` / `needs-attention` are boolean attributes. Listen for `kai-activate`
39
+ * (promote to focus) and `kai-menu` (open your own per-agent menu).
40
+ *
41
+ * ```html
42
+ * <kai-agent-card name="Refactor bot" active></kai-agent-card>
43
+ * <script type="module">
44
+ * import '@kitn.ai/ui/elements';
45
+ * const card = document.querySelector('kai-agent-card');
46
+ * card.status = { tone: 'working', label: 'Working', pulse: true };
47
+ * card.addEventListener('kai-activate', () => focusAgent());
48
+ * card.addEventListener('kai-menu', () => openMenu());
49
+ * </script>
50
+ * ```
51
+ * Recolor the status dot via `::part(status)` and the kebab via `::part(menu)`.
52
+ */
53
+ defineWebComponent<Props, Events>('kai-agent-card', {
54
+ name: undefined,
55
+ active: undefined,
56
+ needsAttention: undefined,
57
+ status: undefined,
58
+ }, (props, { dispatch, flag }) => (
59
+ <AgentCard
60
+ name={(props.name as string) ?? ''}
61
+ active={flag('active')}
62
+ needsAttention={flag('needsAttention')}
63
+ status={(props.status as AgentStatus) ?? { tone: 'idle' }}
64
+ onActivate={() => dispatch('kai-activate')}
65
+ onMenu={() => dispatch('kai-menu')}
66
+ />
67
+ ));
@@ -1,14 +1,19 @@
1
1
  import { createSignal, onMount, onCleanup } from 'solid-js';
2
2
  import { defineWebComponent } from './define';
3
- import { Artifact, type ArtifactFile, type ArtifactTab } from '../components/artifact';
3
+ import { Artifact, type ArtifactController, type ArtifactFile, type ArtifactTab } from '../components/artifact';
4
4
 
5
5
  interface Props extends Record<string, unknown> {
6
6
  /** URL the preview iframe frames. Consumer-controlled. */
7
7
  src?: string;
8
8
  /** Files for the Code tab tree + each file's preview `url`. Set as a JS property (array). */
9
9
  files: ArtifactFile[];
10
- /** Active tab: `preview` (default) or `code`. */
10
+ /** Controlled active tab: `preview` or `code`. When set, the artifact follows it
11
+ * (re-asserted on change). Leave unset for an uncontrolled tab (see `defaultTab`). */
11
12
  tab?: ArtifactTab;
13
+ /** Uncontrolled INITIAL tab (used only when `tab` is unset). Default `preview`.
14
+ * Seeds the starting tab; the user can then switch freely without the consumer
15
+ * re-asserting a controlled `tab`. */
16
+ defaultTab?: ArtifactTab;
12
17
  /** Selected file path — syncs the tree highlight, Code source, and preview. */
13
18
  activeFile?: string;
14
19
  /** iframe `sandbox` override. Secure default `allow-scripts allow-forms` (NOT `allow-same-origin`). */
@@ -35,6 +40,11 @@ interface Props extends Record<string, unknown> {
35
40
  standalone?: boolean;
36
41
  /** Show the address but make it read-only (visible, nav-tracking, non-editable). */
37
42
  readonlyPath?: boolean;
43
+ /** Friendly address shown in the path field instead of the real current url
44
+ * (read-only, non-navigable). Use when the framed url is not consumer-facing
45
+ * (e.g. a `data:` blob) so a clean address shows instead of leaking it. Scalar
46
+ * string: set as the `display-url` attribute or the `displayUrl` property. */
47
+ displayUrl?: string;
38
48
  }
39
49
 
40
50
  interface Events extends Record<string, unknown> {
@@ -59,7 +69,8 @@ interface Events extends Record<string, unknown> {
59
69
  defineWebComponent<Props, Events>('kai-artifact', {
60
70
  src: undefined,
61
71
  files: [],
62
- tab: 'preview',
72
+ tab: undefined,
73
+ defaultTab: undefined,
63
74
  activeFile: undefined,
64
75
  sandbox: 'allow-scripts allow-forms',
65
76
  iframeTitle: undefined,
@@ -73,9 +84,44 @@ defineWebComponent<Props, Events>('kai-artifact', {
73
84
  noTabs: false,
74
85
  standalone: false,
75
86
  readonlyPath: false,
76
- }, (props, { element, dispatch, flag }) => {
87
+ displayUrl: undefined,
88
+ }, (props, { element, dispatch, flag, expose }) => {
77
89
  const [maximized, setMaximized] = createSignal(flag('maximized'));
78
90
 
91
+ // ── Imperative API (instance methods on the host) ──────────────────────────
92
+ // Pattern C: the Artifact component owns the history stack + tab/file/maximize
93
+ // state and hands up a controller; the facade captures it and exposes delegating
94
+ // methods. Every internal handler still fires its event (navigate → kai-navigate;
95
+ // selectFile → kai-file-select; maximize/restore → kai-maximize-change). Names are
96
+ // collision-checked against the props: openExternal (not openInTab — a prop),
97
+ // maximize/restore (not maximized — a prop), navigate/selectFile (not src/activeFile
98
+ // — props); NO tab-switching method (tab is a prop → switch via `el.tab`).
99
+ let controller: ArtifactController | undefined;
100
+ expose({
101
+ /** Go back in the artifact's own history stack (no-op when there's no prior entry). */
102
+ back: () => controller?.back(),
103
+ /** Go forward in the history stack (no-op when there's no forward entry). */
104
+ forward: () => controller?.forward(),
105
+ /** Force-reload the current preview url (also re-renders an inline PDF). */
106
+ reload: () => controller?.reload(),
107
+ /** Navigate to the `src` home url (no-op when there's no `src`). */
108
+ home: () => controller?.home(),
109
+ /** Push + load a url in the preview — the path-field submit path (fires kai-navigate). */
110
+ navigate: (url: string) => controller?.navigate(url),
111
+ /** Select a file by path: highlights the tree, shows its source, navigates the
112
+ * preview (fires kai-file-select + kai-navigate). Named selectFile to avoid the
113
+ * `activeFile` prop. */
114
+ selectFile: (path: string) => controller?.selectFile(path),
115
+ /** Open the current url in a new browser tab (no-op when there's no concrete url).
116
+ * Named openExternal, NOT openInTab — that's a prop (toolbar button visibility). */
117
+ openExternal: () => controller?.openExternal(),
118
+ /** Enter the maximized view-state (fires kai-maximize-change{maximized:true}).
119
+ * Named maximize, NOT maximized — that's a prop. */
120
+ maximize: () => controller?.maximize(),
121
+ /** Exit the maximized view-state (fires kai-maximize-change{maximized:false}). */
122
+ restore: () => controller?.restore(),
123
+ });
124
+
79
125
  const onMaximizeChange = (next: boolean) => {
80
126
  setMaximized(next);
81
127
  // 1) The PROTOCOL intent — raw, bubbling + composed (NOT via dispatch()).
@@ -113,6 +159,7 @@ defineWebComponent<Props, Events>('kai-artifact', {
113
159
  src={props.src}
114
160
  files={props.files}
115
161
  tab={props.tab}
162
+ defaultTab={props.defaultTab}
116
163
  activeFile={props.activeFile}
117
164
  sandbox={props.sandbox}
118
165
  iframeTitle={props.iframeTitle}
@@ -126,10 +173,12 @@ defineWebComponent<Props, Events>('kai-artifact', {
126
173
  showTabs={!flag('noTabs')}
127
174
  standalone={flag('standalone')}
128
175
  readonlyPath={flag('readonlyPath')}
176
+ displayUrl={props.displayUrl}
129
177
  onMaximizeChange={onMaximizeChange}
130
178
  onNavigate={(url) => dispatch('kai-navigate', { url })}
131
179
  onTabChange={(tab) => dispatch('kai-tab-change', { tab })}
132
180
  onFileSelect={(path) => dispatch('kai-file-select', { path })}
181
+ controllerRef={(c) => (controller = c)}
133
182
  />
134
183
  </div>
135
184
  </>
@@ -118,6 +118,7 @@ defineWebComponent<Props, Events>('kai-attachments', {
118
118
  }
119
119
  >
120
120
  <img
121
+ part="preview"
121
122
  src={item.url}
122
123
  alt={getAttachmentLabel(item)}
123
124
  class="block max-h-64 max-w-xs rounded object-contain"
@@ -0,0 +1,89 @@
1
+ // The kai-* autoloader (opt-in, additive — the Web Awesome model).
2
+ //
3
+ // Include this ONE module (via a CDN/static `<script type="module">` tag) and it
4
+ // watches the DOM for undefined kai-* elements and dynamically imports each
5
+ // element's module on demand — so a page that only uses <kai-chat> never downloads
6
+ // the others. The register-all bundle (@kitn.ai/ui/elements) stays the default;
7
+ // this is the delivery for NO-BUILD / CDN / static-served pages.
8
+ //
9
+ // IMPORTANT — this is a CDN / static-file pattern (it resolves sibling element
10
+ // modules relative to its own URL). It is NOT importable through a bundler: Vite /
11
+ // webpack relocate `import.meta.url` away from the element files and cannot analyze
12
+ // the dynamic import, so it 404s. In a BUNDLED app use per-element imports
13
+ // (`import '@kitn.ai/ui/elements/<el>'`) or the register-all bundle. (Advanced: host
14
+ // dist/elements/ yourself and call setAutoloaderBasePath('<url>/') before use.)
15
+ //
16
+ // Tag → module is resolved through the generated manifest (filenames don't always
17
+ // equal the tag, and some modules register more than one tag), and modules are
18
+ // fetched relative to THIS module's URL.
19
+ import manifest from './element-manifest.json';
20
+
21
+ const tagToModule: Record<string, string> = manifest.tags;
22
+ const inFlight = new Set<string>();
23
+
24
+ // Base = the directory of this module. Computed via string ops on import.meta.url
25
+ // (NOT `new URL('./x', import.meta.url)`, which Vite rewrites into a static-asset
26
+ // glob that resolves to undefined at runtime).
27
+ const BASE = import.meta.url.slice(0, import.meta.url.lastIndexOf('/') + 1);
28
+
29
+ /** Override where element modules are fetched from (e.g. a CDN). Call before use. */
30
+ let baseOverride: string | null = null;
31
+ export function setAutoloaderBasePath(path: string): void {
32
+ baseOverride = path.endsWith('/') ? path : path + '/';
33
+ }
34
+
35
+ async function register(tag: string): Promise<void> {
36
+ if (customElements.get(tag) || inFlight.has(tag)) return;
37
+ const file = tagToModule[tag];
38
+ if (!file) return; // not one of ours
39
+ inFlight.add(tag);
40
+ try {
41
+ await import(/* @vite-ignore */ `${baseOverride ?? BASE}${file}.js`);
42
+ } catch (err) {
43
+ inFlight.delete(tag);
44
+ warnOnce(tag, err);
45
+ }
46
+ }
47
+
48
+ // One actionable warning per session (the failure is almost always "imported
49
+ // through a bundler" — see the header note), instead of a silent per-tag error.
50
+ let warned = false;
51
+ function warnOnce(tag: string, err: unknown): void {
52
+ if (warned) return;
53
+ warned = true;
54
+ const msg = (err as { message?: string })?.message ?? String(err);
55
+ // eslint-disable-next-line no-console
56
+ console.warn(
57
+ `[kai-autoloader] could not load "${tag}" (${msg}). The autoloader is a CDN / ` +
58
+ `static-file tool — load it from a <script type="module" src=".../@kitn.ai/ui/dist/elements/autoloader.js">. ` +
59
+ `It is NOT importable through a bundler. In a bundled app, register elements with per-element imports ` +
60
+ `(import '@kitn.ai/ui/elements/<el>') or the register-all bundle (import '@kitn.ai/ui/elements'). To drive ` +
61
+ `the autoloader from a bundler, host dist/elements/ and call setAutoloaderBasePath('<url>/') before use.`,
62
+ );
63
+ }
64
+
65
+ function discover(root: ParentNode | Element): void {
66
+ const tags = new Set<string>();
67
+ const self = (root as Element).tagName?.toLowerCase?.();
68
+ if (self && self in tagToModule && !customElements.get(self)) tags.add(self);
69
+ root.querySelectorAll?.(':not(:defined)').forEach((el) => {
70
+ const t = el.tagName.toLowerCase();
71
+ if (t in tagToModule) tags.add(t);
72
+ });
73
+ tags.forEach(register);
74
+ }
75
+
76
+ /** Start watching `root` (default: the whole document) for undefined kai-* elements. */
77
+ export function startAutoloader(root: ParentNode = document): void {
78
+ discover(root as Element);
79
+ new MutationObserver((mutations) => {
80
+ for (const m of mutations)
81
+ for (const node of m.addedNodes)
82
+ if (node.nodeType === 1) discover(node as Element);
83
+ }).observe(document.documentElement, { childList: true, subtree: true });
84
+ }
85
+
86
+ // Self-start on import (the <script type="module"> use case). Deferred to a
87
+ // microtask so a consumer that calls setAutoloaderBasePath() right after importing
88
+ // (the advanced bundler-from-CDN path) takes effect before the first discovery pass.
89
+ if (typeof document !== 'undefined') queueMicrotask(() => startAutoloader());