@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
@@ -0,0 +1,123 @@
1
+ // Message action-row feedback state — copy + like/dislike — owned ABOVE the
2
+ // per-message `<For>` so a streaming re-render (a fresh `messages` array ref per
3
+ // chunk) never wipes the user's optimistic vote or the transient "copied" check.
4
+ //
5
+ // Both facades (`ChatThread` and the `<kai-message>` element) create one of
6
+ // these and route their action-bar clicks through `handleAction`. Keeping the
7
+ // logic here (not inside the un-unit-testable `defineWebComponent` body) makes
8
+ // the toggle/toast/clipboard behavior directly testable.
9
+
10
+ import { createSignal, onCleanup } from 'solid-js';
11
+ import type { ChatMessage, FeedbackVote } from '../elements/chat-types';
12
+ import { toast } from './toast-store';
13
+
14
+ /** Detail shape emitted by the action row. `state` is present only for the
15
+ * toggleable feedback votes: `'on'` when a vote is set, `'off'` when cleared.
16
+ * Copy / regenerate / edit / custom actions omit it. */
17
+ export interface MessageActionDetail {
18
+ messageId: string;
19
+ action: string;
20
+ state?: 'on' | 'off';
21
+ }
22
+
23
+ export interface MessageFeedbackOptions {
24
+ /** Emit the `kai-message-action` detail (facade maps this to `dispatch` /
25
+ * `onMessageAction`). */
26
+ emit: (detail: MessageActionDetail) => void;
27
+ /** How long the copy check stays before auto-clearing. Defaults to 2000ms. */
28
+ copiedDuration?: number;
29
+ /** Container the copy/feedback toasts should be scoped to (the chat, by
30
+ * default) so they appear in-chat rather than at the page top. */
31
+ target?: () => HTMLElement | undefined;
32
+ }
33
+
34
+ export interface MessageFeedback {
35
+ /** Resolve the active vote for a message: a controlled `m.feedback` wins,
36
+ * else the facade's own optimistic map. */
37
+ resolveFeedback: (m: Pick<ChatMessage, 'id' | 'feedback'>) => FeedbackVote | undefined;
38
+ /** Whether a message's copy button should currently show its check. */
39
+ isCopied: (id: string) => boolean;
40
+ /** Route a clicked action through copy / vote / passthrough handling. */
41
+ handleAction: (m: Pick<ChatMessage, 'id' | 'content' | 'feedback'>, action: string) => void;
42
+ }
43
+
44
+ const DEFAULT_COPIED_DURATION = 2000;
45
+
46
+ /**
47
+ * Create the action-row feedback controller for one message list (or one
48
+ * standalone message). Owns the optimistic `feedbackMap` + transient
49
+ * `copiedIds`, resolves the controlled-wins vote, and turns clicks into
50
+ * clipboard writes, toasts, and `kai-message-action` emissions.
51
+ */
52
+ export function createMessageFeedback(opts: MessageFeedbackOptions): MessageFeedback {
53
+ const [feedbackMap, setFeedbackMap] = createSignal<Record<string, FeedbackVote>>({});
54
+ const [copiedIds, setCopiedIds] = createSignal<Set<string>>(new Set());
55
+ const copiedDuration = opts.copiedDuration ?? DEFAULT_COPIED_DURATION;
56
+ const timers = new Map<string, ReturnType<typeof setTimeout>>();
57
+
58
+ onCleanup(() => {
59
+ timers.forEach((t) => clearTimeout(t));
60
+ timers.clear();
61
+ });
62
+
63
+ const resolveFeedback: MessageFeedback['resolveFeedback'] = (m) =>
64
+ m.feedback ?? feedbackMap()[m.id];
65
+
66
+ const isCopied: MessageFeedback['isCopied'] = (id) => copiedIds().has(id);
67
+
68
+ const markCopied = (id: string) => {
69
+ setCopiedIds((prev) => {
70
+ const next = new Set(prev);
71
+ next.add(id);
72
+ return next;
73
+ });
74
+ const existing = timers.get(id);
75
+ if (existing) clearTimeout(existing);
76
+ timers.set(id, setTimeout(() => {
77
+ timers.delete(id);
78
+ setCopiedIds((prev) => {
79
+ if (!prev.has(id)) return prev;
80
+ const next = new Set(prev);
81
+ next.delete(id);
82
+ return next;
83
+ });
84
+ }, copiedDuration));
85
+ };
86
+
87
+ const setVote = (id: string, vote: FeedbackVote | undefined) => {
88
+ setFeedbackMap((prev) => {
89
+ const next = { ...prev };
90
+ if (vote === undefined) delete next[id];
91
+ else next[id] = vote;
92
+ return next;
93
+ });
94
+ };
95
+
96
+ const handleAction: MessageFeedback['handleAction'] = (m, action) => {
97
+ if (action === 'copy') {
98
+ // Clipboard may be unavailable (insecure context / jsdom); fail soft.
99
+ try { navigator.clipboard?.writeText(m.content); } catch { /* ignore */ }
100
+ markCopied(m.id);
101
+ toast('Copied to clipboard', { target: opts.target?.() });
102
+ opts.emit({ messageId: m.id, action });
103
+ return;
104
+ }
105
+ if (action === 'like' || action === 'dislike') {
106
+ const current = resolveFeedback(m);
107
+ if (current === action) {
108
+ // Re-tap clears the vote — no toast on un-vote.
109
+ setVote(m.id, undefined);
110
+ opts.emit({ messageId: m.id, action, state: 'off' });
111
+ } else {
112
+ setVote(m.id, action);
113
+ toast('Thanks for your feedback', { target: opts.target?.() });
114
+ opts.emit({ messageId: m.id, action, state: 'on' });
115
+ }
116
+ return;
117
+ }
118
+ // regenerate / edit / custom — passthrough, no state.
119
+ opts.emit({ messageId: m.id, action });
120
+ };
121
+
122
+ return { resolveFeedback, isCopied, handleAction };
123
+ }
@@ -0,0 +1,276 @@
1
+ // Module-level toast store + the imperative `toast()` API.
2
+ //
3
+ // This is the PRIMARY way consumers raise a toast: `toast('Saved')`,
4
+ // `toast.success('Copied')`, `toast.dismiss(id)`. The store is a single
5
+ // reactive list held in a long-lived `createRoot` (so its reactivity survives
6
+ // outside any component), and `ensureMounted()` lazily creates exactly ONE
7
+ // `<kai-toast-region>` on `document.body` the first time a toast is raised,
8
+ // binding the list to its `toasts` property. The region is a real `kai-*`
9
+ // element, so it carries its own shadow root + the shared kit stylesheet — it's
10
+ // viewport-positioned AND fully kit-styled, never a raw div.
11
+ //
12
+ // SSR-safe: every DOM touch is guarded by `typeof document`. On the server,
13
+ // raising a toast is an inert no-op (the store updates, nothing mounts).
14
+
15
+ import { createRoot, createSignal } from 'solid-js';
16
+ import { createStore, produce } from 'solid-js/store';
17
+ import type { ToastPosition } from '../components/toast';
18
+
19
+ export type ToastVariant = 'neutral' | 'success' | 'warning' | 'error' | 'info';
20
+ /** Visual treatment: `'pill'` (default, compact single-line) or `'card'` (richer
21
+ * rounded card with an optional description line). */
22
+ export type ToastAppearance = 'pill' | 'card';
23
+
24
+ export interface ToastConfig {
25
+ stack?: 'expanded' | 'collapsed';
26
+ position?: ToastPosition;
27
+ max?: number;
28
+ /** Default appearance for imperatively-raised toasts. Defaults to `'pill'`. */
29
+ appearance?: ToastAppearance;
30
+ /** Default high-contrast inverse treatment for imperatively-raised toasts.
31
+ * Defaults to `false`. */
32
+ inverse?: boolean;
33
+ }
34
+
35
+ // Module-level config the imperative singleton regions inherit. Defaults match
36
+ // the element's own defaults, so an un-configured app behaves exactly as before.
37
+ let toastConfig: ToastConfig = {};
38
+
39
+ /** Apply the current config to a region element (attributes for scalars). */
40
+ function applyConfig(el: HTMLElement): void {
41
+ if (toastConfig.stack) el.setAttribute('stack', toastConfig.stack);
42
+ if (toastConfig.position) el.setAttribute('position', toastConfig.position);
43
+ if (toastConfig.max !== undefined) el.setAttribute('max', String(toastConfig.max));
44
+ if (toastConfig.appearance) el.setAttribute('appearance', toastConfig.appearance);
45
+ if (toastConfig.inverse !== undefined) {
46
+ if (toastConfig.inverse) el.setAttribute('inverse', '');
47
+ else el.removeAttribute('inverse');
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Configure the imperative `toast()` singleton — call once at app start.
53
+ * `toast.success('…')` has no element to set a prop on, so this is how you opt
54
+ * the auto-mounted region into collapsed stacking / a position / a max. Updates
55
+ * any already-mounted regions too, so call order doesn't matter.
56
+ */
57
+ export function configureToasts(config: ToastConfig): void {
58
+ toastConfig = { ...toastConfig, ...config };
59
+ if (typeof document === 'undefined') return;
60
+ document.querySelectorAll('kai-toast-region').forEach((el) => applyConfig(el as HTMLElement));
61
+ }
62
+
63
+ /** An action button rendered inside the toast. Returning `false` from
64
+ * `onAction` keeps the toast open (e.g. to show a follow-up); any other
65
+ * return value dismisses it. */
66
+ export interface ToastAction {
67
+ label: string;
68
+ onAction: () => void | false;
69
+ }
70
+
71
+ export interface ToastItem {
72
+ id: string;
73
+ message: string;
74
+ variant?: ToastVariant;
75
+ /** Visual treatment: `'pill'` (default, compact single-line) or `'card'` (richer
76
+ * rounded card with an optional description line). */
77
+ appearance?: ToastAppearance;
78
+ /** High-contrast inverse surface — works on either appearance, popping in light
79
+ * AND dark. Defaults to `false`. */
80
+ inverse?: boolean;
81
+ /** Secondary line shown below the message in the `'card'` appearance. The
82
+ * `'pill'` appearance ignores it. */
83
+ description?: string;
84
+ action?: ToastAction;
85
+ /** Auto-dismiss delay in ms. `0` = sticky (never auto-dismisses). When an
86
+ * `action` is present the effective floor is 4000ms so it stays long enough
87
+ * to act on. Defaults to 2000ms. */
88
+ duration?: number;
89
+ /** Whether the × close affordance is shown. Defaults to `true`. */
90
+ dismissible?: boolean;
91
+ /** Container to scope this toast WITHIN — it floats anchored to that element's
92
+ * bounds instead of the viewport. Omit for a global, viewport-anchored toast.
93
+ * The chat targets itself by default so its copy/feedback toasts stay in-chat. */
94
+ target?: HTMLElement;
95
+ }
96
+
97
+ /** Options accepted by `toast()` — everything but the message. Pass `id` to
98
+ * update an existing toast in place. */
99
+ export interface ToastOptions {
100
+ id?: string;
101
+ variant?: ToastVariant;
102
+ /** Visual treatment: `'pill'` (default) or `'card'`. Falls back to the value set
103
+ * via `configureToasts`, then `'pill'`. */
104
+ appearance?: ToastAppearance;
105
+ /** High-contrast inverse surface. Falls back to `configureToasts`, then `false`. */
106
+ inverse?: boolean;
107
+ /** Secondary line shown below the message in the `'card'` appearance. */
108
+ description?: string;
109
+ action?: ToastAction;
110
+ duration?: number;
111
+ dismissible?: boolean;
112
+ /** Scope this toast to a container's bounds (e.g. a chat) instead of the
113
+ * viewport. Omit for a global, viewport-anchored toast. */
114
+ target?: HTMLElement;
115
+ }
116
+
117
+ /** Handle returned from `toast()` for imperative control. */
118
+ export interface ToastHandle {
119
+ id: string;
120
+ dismiss: () => void;
121
+ update: (patch: Partial<Omit<ToastItem, 'id'>>) => void;
122
+ }
123
+
124
+ /** Default auto-dismiss delay. Long enough to read + reach before it leaves. */
125
+ export const DEFAULT_TOAST_DURATION = 5000;
126
+ /** Minimum auto-dismiss delay when the toast carries an action (e.g. Undo) — it
127
+ * has to stay up long enough to actually act on. */
128
+ export const ACTION_TOAST_FLOOR = 7000;
129
+
130
+ // ── The reactive store ──────────────────────────────────────────────────────
131
+ // Held in a never-disposed root so the signal/store graph stays live for the
132
+ // lifetime of the page, independent of any component.
133
+ const { toasts, setToasts, mounted, setMounted } = createRoot(() => {
134
+ const [list, setList] = createStore<ToastItem[]>([]);
135
+ const [isMounted, setIsMounted] = createSignal(false);
136
+ return { toasts: list, setToasts: setList, mounted: isMounted, setMounted: setIsMounted };
137
+ });
138
+
139
+ /** The live toast list (reactive). The region facade binds to this. */
140
+ export function getToasts(): ToastItem[] {
141
+ return toasts;
142
+ }
143
+
144
+ let counter = 0;
145
+ function nextId(): string {
146
+ counter += 1;
147
+ return `toast-${counter}`;
148
+ }
149
+
150
+ /** Resolve the effective duration, applying the action floor. */
151
+ function resolveDuration(item: Pick<ToastItem, 'duration' | 'action'>): number {
152
+ const base = item.duration ?? DEFAULT_TOAST_DURATION;
153
+ if (base === 0) return 0; // explicit sticky wins
154
+ if (item.action) return Math.max(base, ACTION_TOAST_FLOOR);
155
+ return base;
156
+ }
157
+
158
+ /**
159
+ * Lazily create the single `<kai-toast-region>` host on `document.body` and
160
+ * bind the store to its `toasts` property. Idempotent + SSR-safe. The element
161
+ * upgrade (its `defineWebComponent` registration) happens when the elements
162
+ * bundle loads; setting the property before/after upgrade both work because
163
+ * `customElement` reflects late-set properties.
164
+ */
165
+ // One region per distinct target (the `null` key = the global / viewport region).
166
+ // Every region binds the SAME store proxy and filters to its own target, so a
167
+ // toast routes to the region for its `target`.
168
+ const regions = new Map<HTMLElement | null, HTMLElement>();
169
+
170
+ export function ensureMounted(target: HTMLElement | null = null): HTMLElement | undefined {
171
+ if (typeof document === 'undefined') return undefined;
172
+ const cached = regions.get(target);
173
+ if (cached && cached.isConnected) return cached;
174
+ const el = document.createElement('kai-toast-region') as HTMLElement;
175
+ // Append FIRST so the element upgrades, THEN bind. Setting a property before
176
+ // upgrade loses it: component-register resets every prop in the constructor on
177
+ // upgrade (the same pre-upgrade clobber that bites <kai-chat>), so a pre-append
178
+ // `el.toasts = …` is wiped and the first toast never renders.
179
+ document.body.appendChild(el);
180
+ (el as unknown as { toasts: ToastItem[] }).toasts = toasts as ToastItem[];
181
+ if (target) (el as unknown as { target: HTMLElement }).target = target;
182
+ applyConfig(el); // inherit stack/position/max from configureToasts()
183
+ regions.set(target, el);
184
+ setMounted(true);
185
+ return el;
186
+ }
187
+
188
+ /** Test-only: whether `ensureMounted` has run. */
189
+ export function isToastRegionMounted(): boolean {
190
+ return mounted();
191
+ }
192
+
193
+ function upsert(item: ToastItem): void {
194
+ const idx = toasts.findIndex((t) => t.id === item.id);
195
+ if (idx >= 0) {
196
+ setToasts(idx, item);
197
+ } else {
198
+ setToasts(produce((list) => { list.push(item); }));
199
+ }
200
+ }
201
+
202
+ function dismiss(id: string): void {
203
+ setToasts((list) => list.filter((t) => t.id !== id));
204
+ }
205
+
206
+ function makeHandle(id: string): ToastHandle {
207
+ return {
208
+ id,
209
+ dismiss: () => dismiss(id),
210
+ update: (patch) => {
211
+ const idx = toasts.findIndex((t) => t.id === id);
212
+ if (idx >= 0) setToasts(idx, (prev) => ({ ...prev, ...patch }));
213
+ },
214
+ };
215
+ }
216
+
217
+ /** Internal: raise/refresh a toast of a given variant. */
218
+ function raise(message: string, opts: ToastOptions | undefined, variant: ToastVariant): ToastHandle {
219
+ ensureMounted(opts?.target ?? null);
220
+ const id = opts?.id ?? nextId();
221
+ const item: ToastItem = {
222
+ id,
223
+ message,
224
+ variant: opts?.variant ?? variant,
225
+ appearance: opts?.appearance ?? toastConfig.appearance ?? 'pill',
226
+ inverse: opts?.inverse ?? toastConfig.inverse ?? false,
227
+ description: opts?.description,
228
+ action: opts?.action,
229
+ duration: opts?.duration,
230
+ dismissible: opts?.dismissible,
231
+ target: opts?.target,
232
+ };
233
+ upsert(item);
234
+ return makeHandle(id);
235
+ }
236
+
237
+ export interface ToastFn {
238
+ (message: string, opts?: ToastOptions): ToastHandle;
239
+ /** Raise a success (green check) toast. */
240
+ success: (message: string, opts?: ToastOptions) => ToastHandle;
241
+ /** Raise a warning (amber) toast — e.g. an agent needs your input. */
242
+ warning: (message: string, opts?: ToastOptions) => ToastHandle;
243
+ /** Raise an error (destructive/red) toast — e.g. an agent failed. */
244
+ error: (message: string, opts?: ToastOptions) => ToastHandle;
245
+ /** Raise an info (blue) toast. */
246
+ info: (message: string, opts?: ToastOptions) => ToastHandle;
247
+ /** Dismiss a toast by id. */
248
+ dismiss: (id: string) => void;
249
+ /** Dismiss every active toast. */
250
+ clear: () => void;
251
+ }
252
+
253
+ /**
254
+ * Raise a transient toast. Returns a `{ id, dismiss, update }` handle.
255
+ *
256
+ * ```ts
257
+ * toast('Copied to clipboard');
258
+ * toast.success('Saved');
259
+ * const t = toast('Working…', { duration: 0 });
260
+ * t.update({ message: 'Done', variant: 'success', duration: 2000 });
261
+ * ```
262
+ */
263
+ export const toast: ToastFn = Object.assign(
264
+ (message: string, opts?: ToastOptions) => raise(message, opts, 'neutral'),
265
+ {
266
+ success: (message: string, opts?: ToastOptions) => raise(message, opts, 'success'),
267
+ warning: (message: string, opts?: ToastOptions) => raise(message, opts, 'warning'),
268
+ error: (message: string, opts?: ToastOptions) => raise(message, opts, 'error'),
269
+ info: (message: string, opts?: ToastOptions) => raise(message, opts, 'info'),
270
+ dismiss,
271
+ clear: () => setToasts([]),
272
+ },
273
+ );
274
+
275
+ // Re-export for the region facade so the effective-duration rule lives in one place.
276
+ export { resolveDuration };
@@ -0,0 +1,135 @@
1
+ import { createSignal, onCleanup } from 'solid-js';
2
+
3
+ // Minimal Web Speech API surface. The DOM lib doesn't ship these types reliably
4
+ // across TS versions, so we declare only what we touch. `webkitSpeechRecognition`
5
+ // is the Chrome/Safari-prefixed constructor; the unprefixed one is rare.
6
+ interface SpeechRecognitionAlternative {
7
+ transcript: string;
8
+ }
9
+ interface SpeechRecognitionResult {
10
+ readonly isFinal: boolean;
11
+ readonly length: number;
12
+ [index: number]: SpeechRecognitionAlternative;
13
+ }
14
+ interface SpeechRecognitionResultList {
15
+ readonly length: number;
16
+ [index: number]: SpeechRecognitionResult;
17
+ }
18
+ interface SpeechRecognitionEventLike {
19
+ readonly resultIndex: number;
20
+ readonly results: SpeechRecognitionResultList;
21
+ }
22
+ interface SpeechRecognitionLike {
23
+ lang: string;
24
+ continuous: boolean;
25
+ interimResults: boolean;
26
+ start(): void;
27
+ stop(): void;
28
+ abort(): void;
29
+ onresult: ((event: SpeechRecognitionEventLike) => void) | null;
30
+ onerror: ((event: { error?: string }) => void) | null;
31
+ onend: (() => void) | null;
32
+ }
33
+ type SpeechRecognitionCtor = new () => SpeechRecognitionLike;
34
+
35
+ function getCtor(): SpeechRecognitionCtor | undefined {
36
+ if (typeof window === 'undefined') return undefined;
37
+ const w = window as unknown as {
38
+ SpeechRecognition?: SpeechRecognitionCtor;
39
+ webkitSpeechRecognition?: SpeechRecognitionCtor;
40
+ };
41
+ return w.SpeechRecognition ?? w.webkitSpeechRecognition;
42
+ }
43
+
44
+ export interface UseSpeechRecognitionOptions {
45
+ /** Emit live, non-final partials via the `onInterim` callback passed to start(). */
46
+ interim?: boolean;
47
+ }
48
+
49
+ export interface SpeechRecognitionStartOptions {
50
+ /** BCP-47 language tag, e.g. `en-US`. */
51
+ lang?: string;
52
+ /** Called with each interim (non-final) transcript when `interim` is enabled. */
53
+ onInterim?: (text: string) => void;
54
+ }
55
+
56
+ /**
57
+ * Wrap the browser's Web Speech `SpeechRecognition` (Chrome/Safari; no Firefox).
58
+ * Mirrors `useVoiceRecorder`'s shape: an `isListening` signal, an `error` signal,
59
+ * and `start`/`stop` controls. `start()` resolves with the final transcript when
60
+ * recognition ends; `stop()` ends the in-progress session (resolving start()).
61
+ *
62
+ * Caveat (documented at the element): in Chrome this is cloud-based — audio is
63
+ * sent to Google — so it is "native to the browser," not on-device/private.
64
+ */
65
+ export function useSpeechRecognition(options: UseSpeechRecognitionOptions = {}) {
66
+ const [isListening, setIsListening] = createSignal(false);
67
+ const [error, setError] = createSignal<string | null>(null);
68
+
69
+ const isSupported = getCtor() !== undefined;
70
+
71
+ let recognition: SpeechRecognitionLike | undefined;
72
+ let resolveText: ((text: string) => void) | undefined;
73
+ let finalText = '';
74
+
75
+ function start(opts: SpeechRecognitionStartOptions = {}): Promise<string> {
76
+ setError(null);
77
+ finalText = '';
78
+ const Ctor = getCtor();
79
+ if (!Ctor) {
80
+ const message = 'SpeechRecognition is not supported in this browser';
81
+ setError(message);
82
+ return Promise.reject(new Error(message));
83
+ }
84
+ try {
85
+ recognition = new Ctor();
86
+ if (opts.lang) recognition.lang = opts.lang;
87
+ recognition.interimResults = options.interim === true;
88
+ recognition.continuous = false;
89
+
90
+ recognition.onresult = (event) => {
91
+ let interim = '';
92
+ for (let i = event.resultIndex; i < event.results.length; i++) {
93
+ const result = event.results[i];
94
+ const transcript = result[0]?.transcript ?? '';
95
+ if (result.isFinal) {
96
+ finalText += transcript;
97
+ } else {
98
+ interim += transcript;
99
+ }
100
+ }
101
+ if (interim && opts.onInterim) opts.onInterim(interim);
102
+ };
103
+
104
+ recognition.onerror = (event) => {
105
+ setError(event.error ?? 'Speech recognition error');
106
+ };
107
+
108
+ recognition.onend = () => {
109
+ setIsListening(false);
110
+ resolveText?.(finalText);
111
+ recognition = undefined;
112
+ };
113
+
114
+ recognition.start();
115
+ setIsListening(true);
116
+ return new Promise<string>((resolve) => { resolveText = resolve; });
117
+ } catch (err) {
118
+ setError(err instanceof Error ? err.message : 'Speech recognition failed to start');
119
+ setIsListening(false);
120
+ throw err;
121
+ }
122
+ }
123
+
124
+ function stop() {
125
+ if (recognition && isListening()) {
126
+ recognition.stop();
127
+ }
128
+ }
129
+
130
+ onCleanup(() => {
131
+ if (recognition) recognition.abort();
132
+ });
133
+
134
+ return { isSupported, isListening, error, start, stop };
135
+ }
@@ -0,0 +1,6 @@
1
+ // Framework-neutral state core for @kitn.ai/ui. Pure functions over ChatMessage[]
2
+ // + a typed streaming handle. No React/Solid runtime — drives a consumer setter.
3
+ export { appendMessage, upsertMessage, updateMessage, removeMessage, appendContent } from './messages';
4
+ export { addSuggestion, removeSuggestion } from './suggestions';
5
+ export { createAssistantStream, onStreamSettled } from './stream';
6
+ export type { SetMessages, AssistantStream } from './stream';
@@ -0,0 +1,36 @@
1
+ import type { ChatMessage } from '../elements/chat-types';
2
+
3
+ /** Append a message; returns a new array. */
4
+ export function appendMessage(messages: ChatMessage[], msg: ChatMessage): ChatMessage[] {
5
+ return [...messages, msg];
6
+ }
7
+
8
+ /** Replace a same-id message, or append when absent. */
9
+ export function upsertMessage(messages: ChatMessage[], msg: ChatMessage): ChatMessage[] {
10
+ const i = messages.findIndex((x) => x.id === msg.id);
11
+ if (i === -1) return [...messages, msg];
12
+ const next = messages.slice();
13
+ next[i] = msg;
14
+ return next;
15
+ }
16
+
17
+ /** Patch the matched message (object patch or updater). Untouched items keep their reference. */
18
+ export function updateMessage(
19
+ messages: ChatMessage[],
20
+ id: string,
21
+ patch: Partial<ChatMessage> | ((m: ChatMessage) => ChatMessage),
22
+ ): ChatMessage[] {
23
+ return messages.map((x) =>
24
+ x.id === id ? (typeof patch === 'function' ? patch(x) : { ...x, ...patch }) : x,
25
+ );
26
+ }
27
+
28
+ /** Remove the matched message. */
29
+ export function removeMessage(messages: ChatMessage[], id: string): ChatMessage[] {
30
+ return messages.filter((x) => x.id !== id);
31
+ }
32
+
33
+ /** Streaming primitive: concatenate `delta` onto the matched message's content. */
34
+ export function appendContent(messages: ChatMessage[], id: string, delta: string): ChatMessage[] {
35
+ return messages.map((x) => (x.id === id ? { ...x, content: x.content + delta } : x));
36
+ }
@@ -0,0 +1,92 @@
1
+ // src/state/stream.ts
2
+ import type { ChatMessage } from '../elements/chat-types';
3
+ import type { ToolPart } from '../components/tool-types';
4
+
5
+ /** The one universal contract: a functional-updater setter (React setState shape). */
6
+ export type SetMessages = (updater: (prev: ChatMessage[]) => ChatMessage[]) => void;
7
+
8
+ /** A fluent builder for one in-flight assistant message. Owns no state. */
9
+ export interface AssistantStream {
10
+ readonly id: string;
11
+ appendText(delta: string): AssistantStream;
12
+ setText(content: string): AssistantStream;
13
+ appendReasoning(delta: string, label?: string): AssistantStream;
14
+ setReasoning(text: string, label?: string): AssistantStream;
15
+ upsertTool(tool: ToolPart): AssistantStream;
16
+ updateTool(toolCallId: string, patch: Partial<ToolPart>): AssistantStream;
17
+ patch(patch: Partial<ChatMessage>): AssistantStream;
18
+ done(final?: Partial<ChatMessage>): void;
19
+ abort(reason?: string): void;
20
+ }
21
+
22
+ function newId(): string {
23
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID();
24
+ return 'kai-' + Math.random().toString(36).slice(2);
25
+ }
26
+
27
+ /** Start an assistant message and drive it through `set`. New refs on every mutation. */
28
+ export function createAssistantStream(set: SetMessages, init: Partial<ChatMessage> = {}): AssistantStream {
29
+ const id = init.id ?? newId();
30
+ const seed: ChatMessage = { id, role: 'assistant', content: '', ...init };
31
+ set((prev) => [...prev, seed]);
32
+
33
+ const mutate = (fn: (m: ChatMessage) => ChatMessage) =>
34
+ set((prev) => prev.map((m) => (m.id === id ? fn(m) : m)));
35
+
36
+ const stream: AssistantStream = {
37
+ id,
38
+ appendText(delta) { mutate((m) => ({ ...m, content: m.content + delta })); return stream; },
39
+ setText(content) { mutate((m) => ({ ...m, content })); return stream; },
40
+ appendReasoning(delta, label) {
41
+ mutate((m) => ({ ...m, reasoning: { text: (m.reasoning?.text ?? '') + delta, label: label ?? m.reasoning?.label } }));
42
+ return stream;
43
+ },
44
+ setReasoning(text, label) {
45
+ mutate((m) => ({ ...m, reasoning: { text, label: label ?? m.reasoning?.label } }));
46
+ return stream;
47
+ },
48
+ upsertTool(tool) {
49
+ mutate((m) => {
50
+ const tools = m.tools ? m.tools.slice() : [];
51
+ const i = tool.toolCallId != null
52
+ ? tools.findIndex((t) => t.toolCallId === tool.toolCallId)
53
+ : tools.findIndex((t) => t.type === tool.type);
54
+ if (i === -1) tools.push(tool); else tools[i] = tool;
55
+ return { ...m, tools };
56
+ });
57
+ return stream;
58
+ },
59
+ updateTool(toolCallId, patch) {
60
+ mutate((m) => ({ ...m, tools: (m.tools ?? []).map((t) => (t.toolCallId === toolCallId ? { ...t, ...patch } : t)) }));
61
+ return stream;
62
+ },
63
+ patch(patch) { mutate((m) => ({ ...m, ...patch })); return stream; },
64
+ done(final) { if (final) mutate((m) => ({ ...m, ...final })); },
65
+ abort(reason) {
66
+ if (reason) {
67
+ mutate((m) => ({ ...m, tools: (m.tools ?? []).map((t) => ({ ...t, state: 'output-error' as const, errorText: reason })) }));
68
+ } else {
69
+ set((prev) => prev.filter((m) => m.id !== id));
70
+ }
71
+ },
72
+ };
73
+ return stream;
74
+ }
75
+
76
+ /** Wrap a stream so `onSettle` fires on done/abort (used to toggle a `loading` flag).
77
+ * Preserves the fluent chain by returning the wrapper from every mutator. */
78
+ export function onStreamSettled(inner: AssistantStream, onSettle: () => void): AssistantStream {
79
+ const wrapper: AssistantStream = {
80
+ id: inner.id,
81
+ appendText(d) { inner.appendText(d); return wrapper; },
82
+ setText(c) { inner.setText(c); return wrapper; },
83
+ appendReasoning(d, l) { inner.appendReasoning(d, l); return wrapper; },
84
+ setReasoning(t, l) { inner.setReasoning(t, l); return wrapper; },
85
+ upsertTool(t) { inner.upsertTool(t); return wrapper; },
86
+ updateTool(tcId, p) { inner.updateTool(tcId, p); return wrapper; },
87
+ patch(p) { inner.patch(p); return wrapper; },
88
+ done(final) { inner.done(final); onSettle(); },
89
+ abort(reason) { inner.abort(reason); onSettle(); },
90
+ };
91
+ return wrapper;
92
+ }
@@ -0,0 +1,9 @@
1
+ /** Append a suggestion; no-op when already present. Returns a new array. */
2
+ export function addSuggestion(suggestions: string[], s: string): string[] {
3
+ return suggestions.includes(s) ? suggestions.slice() : [...suggestions, s];
4
+ }
5
+
6
+ /** Remove a suggestion by value. */
7
+ export function removeSuggestion(suggestions: string[], s: string): string[] {
8
+ return suggestions.filter((x) => x !== s);
9
+ }