@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,5 +1,5 @@
1
1
  import { defineWebComponent } from './define';
2
- import { VoiceInput } from '../components/voice-input';
2
+ import { VoiceInput, type VoiceInputController } from '../components/voice-input';
3
3
 
4
4
  interface Props extends Record<string, unknown> {
5
5
  /**
@@ -10,32 +10,82 @@ interface Props extends Record<string, unknown> {
10
10
  transcribe?: (audio: Blob) => Promise<string>;
11
11
  /** Disable the mic button (non-interactive). */
12
12
  disabled?: boolean;
13
+ /**
14
+ * BCP-47 language tag for the native `SpeechRecognition` path (e.g. `en-US`).
15
+ * Attribute: `recognition-lang` (the plain `lang` attribute is reserved by
16
+ * `HTMLElement` and can't be a custom-element property). No effect when
17
+ * `transcribe` is set or the browser lacks SpeechRecognition.
18
+ */
19
+ recognitionLang?: string;
20
+ /** Emit live partial transcripts (`kai-transcript-interim`) during native
21
+ * recognition. Attribute: `interim`. No-op on the transcribe/fallback paths. */
22
+ interim?: boolean;
13
23
  }
14
24
 
15
25
  /** Events fired by `<kai-voice-input>`. */
16
26
  interface Events {
17
27
  /** Raw audio captured (before transcription) — for hosts that prefer to
18
- * handle transcription themselves instead of via the `transcribe` property. */
28
+ * handle transcription themselves instead of via the `transcribe` property.
29
+ * Also the unsupported-fallback signal: no `transcribe`, no SpeechRecognition,
30
+ * so only the blob is produced (no text). */
19
31
  'kai-audio-captured': { blob: Blob };
20
- /** Transcription completed (the `transcribe` property resolved). */
32
+ /** Final transcript the `transcribe` property resolved, OR native
33
+ * `SpeechRecognition` produced final text (no `transcribe` set). */
21
34
  'kai-transcription': { text: string };
35
+ /** Live partial transcript during native recognition (only when `interim` is
36
+ * set). Fires repeatedly before the final `kai-transcription`. */
37
+ 'kai-transcript-interim': { text: string };
38
+ /** Recording started or stopped — lets the host drive its own UI (waveform,
39
+ * push-to-talk indicator) in sync with the mic. Fires on real transitions
40
+ * only (manual click and programmatic start()/stop()), never on mount. */
41
+ 'kai-recording-change': { recording: boolean };
22
42
  }
23
43
 
24
44
  /**
25
- * `<kai-voice-input>` — a mic button that records and transcribes. The
26
- * canonical **function-property** element: set `el.transcribe` to your async
27
- * transcriber. Also emits `kai-audio-captured` (raw blob) and `kai-transcription` (text).
45
+ * `<kai-voice-input>` — a mic button that records and transcribes. Works
46
+ * natively by default: with no `transcribe` callback it uses the browser's
47
+ * `SpeechRecognition` (Chrome/Safari; no Firefox — and cloud-based in Chrome).
48
+ * Set `el.transcribe` to route audio through your own async transcriber instead.
49
+ * Where neither is available it records the blob and emits `kai-audio-captured`
50
+ * with no text. Also emits `kai-transcription` (final text) and, with `interim`,
51
+ * `kai-transcript-interim` (live partials).
28
52
  */
29
53
  defineWebComponent<Props, Events>('kai-voice-input', {
30
54
  transcribe: undefined,
31
55
  disabled: false,
32
- }, (props, { dispatch, flag }) => (
33
- <VoiceInput
34
- disabled={flag('disabled')}
35
- onTranscribe={async (blob) => {
36
- dispatch('kai-audio-captured', { blob });
37
- return props.transcribe ? props.transcribe(blob) : '';
38
- }}
39
- onTranscription={(text) => dispatch('kai-transcription', { text })}
40
- />
41
- ));
56
+ recognitionLang: undefined,
57
+ interim: false,
58
+ }, (props, { dispatch, flag, expose }) => {
59
+ // Pattern C: the VoiceInput component owns useVoiceRecorder; it hands up a
60
+ // start/stop controller and an onRecordingChange callback. The facade captures
61
+ // the controller and exposes delegating methods (start/stop both run the same
62
+ // record→transcribe path as the mic click, so manual + programmatic emit the
63
+ // same kai-audio-captured/kai-transcription/kai-recording-change events).
64
+ let controller: VoiceInputController | undefined;
65
+ expose({
66
+ /** Begin recording programmatically (e.g. push-to-talk bound to a global
67
+ * key). Runs the same getUserMedia path as clicking the mic; no-ops if
68
+ * already recording. */
69
+ start: () => controller?.start(),
70
+ /** Stop the in-progress recording, producing the blob (→ kai-audio-captured)
71
+ * and running transcription. Pairs with start() for push-to-talk. */
72
+ stop: () => controller?.stop(),
73
+ });
74
+
75
+ return (
76
+ <VoiceInput
77
+ disabled={flag('disabled')}
78
+ hasTranscribe={typeof props.transcribe === 'function'}
79
+ lang={props.recognitionLang}
80
+ interim={flag('interim')}
81
+ onTranscribe={async (blob) => {
82
+ dispatch('kai-audio-captured', { blob });
83
+ return props.transcribe ? props.transcribe(blob) : '';
84
+ }}
85
+ onTranscription={(text) => dispatch('kai-transcription', { text })}
86
+ onInterim={(text) => dispatch('kai-transcript-interim', { text })}
87
+ onRecordingChange={(recording) => dispatch('kai-recording-change', { recording })}
88
+ controllerRef={(c) => (controller = c)}
89
+ />
90
+ );
91
+ });
@@ -0,0 +1,69 @@
1
+ import { defineWebComponent } from './define';
2
+ import { VoiceOutput, type VoiceOutputController } from '../components/voice-output';
3
+
4
+ interface Props extends Record<string, unknown> {
5
+ /** The utterance to read aloud. */
6
+ text?: string;
7
+ /** Speak automatically when `text` is set/changed. */
8
+ autoplay?: boolean;
9
+ /**
10
+ * TTS model seam the host supplies — given text, returns an audio `Blob` to
11
+ * play. This is a **function-valued property** (`el.synthesize = async text =>
12
+ * blob`); when set, the native `speechSynthesis` path is bypassed. Mirrors
13
+ * `<kai-voice-input>`'s `transcribe`. A value-returning callback can't be
14
+ * modelled as a fire-and-forget event, hence a property.
15
+ */
16
+ synthesize?: (text: string) => Promise<Blob>;
17
+ /** Disable the button (non-interactive). */
18
+ disabled?: boolean;
19
+ }
20
+
21
+ /** Events fired by `<kai-voice-output>`. */
22
+ interface Events {
23
+ /** Playback started or stopped — drive your own UI in sync. Fires on real
24
+ * transitions only (manual click and programmatic speak()/stop()), never on mount. */
25
+ 'kai-speaking-change': { speaking: boolean };
26
+ /** The model path (`synthesize`) resolved audio — the raw `Blob` before playback. */
27
+ 'kai-synthesized': { blob: Blob };
28
+ }
29
+
30
+ /**
31
+ * `<kai-voice-output>` — a speaker button that reads `text` aloud. Native by
32
+ * default (`speechSynthesis`); set `el.synthesize` to route through your TTS
33
+ * model instead. The output sibling of `<kai-voice-input>`. Emits
34
+ * `kai-speaking-change` and (model path) `kai-synthesized`.
35
+ */
36
+ defineWebComponent<Props, Events>('kai-voice-output', {
37
+ text: '',
38
+ autoplay: false,
39
+ synthesize: undefined,
40
+ disabled: false,
41
+ }, (props, { dispatch, flag, expose }) => {
42
+ // Pattern C: the VoiceOutput component owns playback; it hands up a
43
+ // speak/pause/resume/stop controller. The facade captures it and exposes
44
+ // delegating methods (manual + programmatic run the same path, so both emit
45
+ // the same kai-speaking-change / kai-synthesized events).
46
+ let controller: VoiceOutputController | undefined;
47
+ expose({
48
+ /** Speak the current `text` (native, or via `synthesize` if set). */
49
+ speak: () => controller?.speak(),
50
+ /** Pause playback (resumable). */
51
+ pause: () => controller?.pause(),
52
+ /** Resume paused playback. */
53
+ resume: () => controller?.resume(),
54
+ /** Stop playback and reset. */
55
+ stop: () => controller?.stop(),
56
+ });
57
+
58
+ return (
59
+ <VoiceOutput
60
+ text={(props.text as string | undefined) ?? ''}
61
+ autoplay={flag('autoplay')}
62
+ disabled={flag('disabled')}
63
+ onSynthesize={props.synthesize ? (text) => props.synthesize!(text) : undefined}
64
+ onSpeakingChange={(speaking) => dispatch('kai-speaking-change', { speaking })}
65
+ onSynthesized={(blob) => dispatch('kai-synthesized', { blob })}
66
+ controllerRef={(c) => (controller = c)}
67
+ />
68
+ );
69
+ });
package/src/index.ts CHANGED
@@ -5,6 +5,8 @@ export type { ModelOption, SearchFilters, ConversationScope, ConversationSummary
5
5
  export { cn } from './utils/cn';
6
6
 
7
7
  // Layer 1: Headless Primitives
8
+ export { createKaiChat } from './primitives/create-kai-chat';
9
+ export type { CreateKaiChatOptions, KaiChatStore } from './primitives/create-kai-chat';
8
10
  export { useAutoResize } from './primitives/use-auto-resize';
9
11
  export { useStickToBottom } from './primitives/use-stick-to-bottom';
10
12
  export { useTextStream } from './primitives/use-text-stream';
@@ -18,6 +20,16 @@ export type { CodeHighlightingOptions } from './primitives/highlighter';
18
20
  export { configurePdfPreview, isPdfPreviewEnabled } from './primitives/pdf-preview';
19
21
  export type { PdfPreviewOptions } from './primitives/pdf-preview';
20
22
 
23
+ // Toasts: imperative `toast()` API + the reactive store behind <kai-toast-region>
24
+ export { toast, configureToasts, ensureMounted as ensureToastRegion, getToasts } from './primitives/toast-store';
25
+ export type {
26
+ ToastItem, ToastVariant, ToastAction, ToastOptions, ToastHandle, ToastFn, ToastConfig,
27
+ } from './primitives/toast-store';
28
+ export { Toast, ToastRegion } from './components/toast';
29
+ export type {
30
+ ToastRegionProps, ToastDismissReason, ToastPosition,
31
+ } from './components/toast';
32
+
21
33
  // Card Contract (generative-UI foundation)
22
34
  export { CARD_CONTRACT_VERSION } from './primitives/card-contract';
23
35
  export type {
@@ -27,6 +39,10 @@ export { applyResolution, resolutionFromEvent } from './primitives/card-resoluti
27
39
  export { CardProvider, useCardHost } from './primitives/card-host';
28
40
  export type { CardProviderProps } from './primitives/card-host';
29
41
  export { CARD_EVENT_NAME, emitCardEvent, routeCardEvent, listenForCardEvents } from './primitives/card-routing';
42
+ export { dismissRecovery, defaultIsReopenable } from './primitives/card-recovery';
43
+ export type {
44
+ RecoveryToast, ReopenEnv, DismissRecoveryOptions,
45
+ } from './primitives/card-recovery';
30
46
  export { validateAgainstSchema } from './primitives/card-validate';
31
47
  export type { JsonSchema, ValidationResult } from './primitives/card-validate';
32
48
 
@@ -47,6 +63,8 @@ export type { CardComponent, CardComponentMap, CardTagMap } from './primitives/c
47
63
  // Card: kai-card (base shell) + kai-form (JSON-Schema form renderer)
48
64
  export { Card } from './components/card';
49
65
  export type { CardProps } from './components/card';
66
+ export { DismissedStub, stubIntent } from './components/dismissed-stub';
67
+ export type { DismissedStubProps, DismissedCardType } from './components/dismissed-stub';
50
68
  export { Form, validateForm, buildResult, widgetFor, orderedKeys, coerceValue } from './components/form';
51
69
  export type {
52
70
  FormProps,
@@ -168,14 +186,26 @@ export {
168
186
  } from './components/chat-container';
169
187
  export { Message, MessageAvatar, MessageContent, MessageActions, MessageAction, MessageCopyButton } from './components/message';
170
188
  export type { MessageCopyButtonProps } from './components/message';
189
+ export { ResponseCompare, useResolved } from './components/response-compare';
190
+ export type { ResponseCompareProps, CompareLayout, ResolvedController } from './components/response-compare';
191
+ export {
192
+ normalizeCandidates,
193
+ buildSelection,
194
+ isAnyStreaming,
195
+ } from './components/response-compare';
196
+ export type {
197
+ CompareCandidate,
198
+ ComparePair,
199
+ CompareCollapse,
200
+ ResponseCompareData,
201
+ CompareSelection,
202
+ } from './components/response-compare';
171
203
  export { MessageSkills } from './components/message-skills';
172
204
  export type { Skill as MessageSkill } from './components/message-skills';
173
205
  export {
174
206
  PromptInput, PromptInputTextarea, PromptInputActions, PromptInputAction,
175
207
  usePromptInput,
176
208
  } from './components/prompt-input';
177
- export { SlashCommand } from './components/slash-command';
178
- export type { SlashCommandItem, SlashCommandProps } from './components/slash-command';
179
209
  export { ResponseStream } from './components/response-stream';
180
210
  export { Markdown } from './components/markdown';
181
211
  export { CodeBlock, CodeBlockCode, CodeBlockGroup } from './components/code-block';
@@ -251,3 +281,6 @@ export type {
251
281
  AttachmentInfoProps, AttachmentRemoveProps, AttachmentEmptyProps,
252
282
  AttachmentHoverCardProps, AttachmentHoverCardTriggerProps, AttachmentHoverCardContentProps,
253
283
  } from './components/attachments';
284
+
285
+ // Chat message types — public API for consumers who need to type their own message arrays.
286
+ export type { ChatMessage, ChatMessageAction, CustomAction, AvatarData, FeedbackVote } from './elements/chat-types';
@@ -38,18 +38,24 @@ export type CardEvent =
38
38
  | { kind: 'resize'; cardId: string; height: number }
39
39
  | { kind: 'state'; cardId: string; patch: unknown }
40
40
  | { kind: 'dismiss'; cardId: string }
41
+ | { kind: 'reopen'; cardId: string }
41
42
  | { kind: 'error'; cardId: string; message: string };
42
43
 
43
44
  export type CardEventKind = CardEvent['kind'];
44
45
 
45
46
  /** How a card was resolved by the user — the re-hydration channel for the chromed
46
- * read-only state. Mirrors the two terminal CardEvents (minus `cardId`): the
47
- * resolution is just the event that resolved the card. `at` is optional ISO-8601
48
- * provenance (data only; never rendered). Additivedoes not bump the contract
49
- * version. */
47
+ * read-only state. The terminal kinds (`action`/`submit`) mirror the two terminal
48
+ * CardEvents (minus `cardId`): the resolution is just the event that resolved the
49
+ * card. `dismissed` is a DEFERRED (non-terminal) statethe user set the card
50
+ * aside; it can be re-opened. `expired` is terminal — a dismissed card the host
51
+ * decided can no longer be re-opened (e.g. the agent proceeded, or it went stale).
52
+ * `at` is optional ISO-8601 provenance (data only; never rendered). Additive —
53
+ * does not bump the contract version. */
50
54
  export type CardResolution =
51
55
  | { kind: 'action'; action: string; payload?: unknown; at?: string }
52
- | { kind: 'submit'; data: unknown; at?: string };
56
+ | { kind: 'submit'; data: unknown; at?: string }
57
+ | { kind: 'dismissed'; at?: string }
58
+ | { kind: 'expired'; reason?: string; at?: string };
53
59
 
54
60
  /** What every card is handed (via native context or the iframe bridge). */
55
61
  export interface CardHost {
@@ -65,6 +71,9 @@ export interface CardPolicy {
65
71
  onOpen?: (url: string, target: 'tab' | 'artifact') => void;
66
72
  onState?: (cardId: string, patch: unknown) => void;
67
73
  onDismiss?: (cardId: string) => void;
74
+ /** A dismissed card asked to be re-opened. The host decides whether it can come
75
+ * back (clear the resolution → live again) or has expired (stamp `expired`). */
76
+ onReopen?: (cardId: string) => void;
68
77
  onError?: (cardId: string, message: string) => void;
69
78
  /** Cap on send-prompt: 'compose' (default) forbids silent sends. 'send' to allow. */
70
79
  maxSendPromptMode?: 'compose' | 'send';
@@ -0,0 +1,149 @@
1
+ // src/primitives/card-recovery.ts
2
+ // Recovery glue for the card dismiss/reopen flow. `dismissRecovery` builds the
3
+ // `{ onDismiss, onReopen }` half of a CardPolicy for a host that keeps its cards in
4
+ // some store: it writes a `dismissed` resolution onto the matching envelope (a NEW
5
+ // array reference, never an in-place mutation, so a Solid/React host re-renders),
6
+ // optionally shows an injected "Dismissed · Undo" toast (Undo restores the prior
7
+ // resolution), and on reopen decides — via `isReopenable` — whether the card comes
8
+ // back live (resolution cleared) or `expired`.
9
+ //
10
+ // The toast is an INJECTED ADAPTER, never an import — cards (and this helper) stay
11
+ // decoupled from the toast module. The re-appear rule is the host's: "the agent
12
+ // already proceeded" is a host fact, surfaced through `isReopenable`.
13
+ import type { CardEnvelope, CardPolicy, CardResolution } from './card-contract';
14
+
15
+ /** The minimal toast surface this helper needs. Inject a real adapter (e.g. one
16
+ * backed by `kai-toast-region`) — this module never imports the toast itself. */
17
+ export interface RecoveryToast {
18
+ show(opts: {
19
+ message: string;
20
+ action?: { label: string; onClick: () => void };
21
+ durationMs?: number;
22
+ }): { dismiss(): void };
23
+ }
24
+
25
+ /** Context handed to a custom `isReopenable` predicate. */
26
+ export interface ReopenEnv {
27
+ cardId: string;
28
+ /** The card's current resolution at reopen time (should be `dismissed`). */
29
+ resolution: CardResolution | undefined;
30
+ /** `Date.now()` at reopen time (injected for testability). */
31
+ now: number;
32
+ }
33
+
34
+ export interface DismissRecoveryOptions {
35
+ /** Read the current cards array. */
36
+ get: () => CardEnvelope[];
37
+ /** Write the next cards array (a NEW reference). */
38
+ set: (next: CardEnvelope[]) => void;
39
+ /** Optional injected toast adapter for the "Dismissed · Undo" affordance. */
40
+ toast?: RecoveryToast;
41
+ /** Override the default re-openable rule. Return false to expire instead. */
42
+ isReopenable?: (env: ReopenEnv) => boolean;
43
+ /** A dismissed card older than this (ms since `dismissed.at`) is no longer
44
+ * re-openable (→ expired). Omit/Infinity = never stale. */
45
+ staleAfterMs?: number;
46
+ /** Auto-dismiss the Undo toast after this long. Default 6000ms. */
47
+ undoMs?: number;
48
+ /** Injected clock (defaults to Date.now); keeps the helper testable. */
49
+ now?: () => number;
50
+ }
51
+
52
+ /** The terminal resolution kinds that block a reopen (a finished card can't come back). */
53
+ const TERMINAL_KINDS = new Set<CardResolution['kind']>(['action', 'submit', 'expired']);
54
+
55
+ /** Default re-openable rule: a card can come back unless it carries a terminal
56
+ * resolution OR it has gone stale (`staleAfterMs` elapsed since `dismissed.at`).
57
+ * A `dismissed` card with no/invalid `at` is always re-openable. */
58
+ export function defaultIsReopenable(env: ReopenEnv, staleAfterMs?: number): boolean {
59
+ const r = env.resolution;
60
+ if (r && TERMINAL_KINDS.has(r.kind)) return false;
61
+ if (
62
+ staleAfterMs !== undefined &&
63
+ Number.isFinite(staleAfterMs) &&
64
+ r?.kind === 'dismissed' &&
65
+ typeof r.at === 'string'
66
+ ) {
67
+ const at = Date.parse(r.at);
68
+ if (!Number.isNaN(at) && env.now - at >= staleAfterMs) return false;
69
+ }
70
+ return true;
71
+ }
72
+
73
+ /** Return a new cards array with `cardId`'s envelope stamped with `resolution`
74
+ * (or cleared when `resolution` is undefined). Same reference if nothing changed. */
75
+ function withResolution(
76
+ cards: CardEnvelope[],
77
+ cardId: string,
78
+ resolution: CardResolution | undefined,
79
+ ): CardEnvelope[] {
80
+ let changed = false;
81
+ const next = cards.map((c) => {
82
+ if (c.id !== cardId) return c;
83
+ changed = true;
84
+ if (resolution === undefined) {
85
+ if (c.resolution === undefined) return c;
86
+ const { resolution: _drop, ...rest } = c;
87
+ return rest;
88
+ }
89
+ return { ...c, resolution };
90
+ });
91
+ return changed ? next : cards;
92
+ }
93
+
94
+ /** The current resolution on `cardId`, if any. */
95
+ function resolutionOf(cards: CardEnvelope[], cardId: string): CardResolution | undefined {
96
+ return cards.find((c) => c.id === cardId)?.resolution;
97
+ }
98
+
99
+ /**
100
+ * Build the `{ onDismiss, onReopen }` policy handlers for the card dismiss/recovery
101
+ * flow over a host store (`get`/`set`).
102
+ *
103
+ * - `onDismiss(cardId)` — stamps `{ kind:'dismissed', at }` immutably and, when a
104
+ * `toast` is injected, shows "Dismissed" with an Undo that restores the card's
105
+ * prior resolution (live again when there was none).
106
+ * - `onReopen(cardId)` — clears the resolution (live) when `isReopenable`, else
107
+ * stamps `{ kind:'expired', at }`.
108
+ */
109
+ export function dismissRecovery(
110
+ opts: DismissRecoveryOptions,
111
+ ): Pick<CardPolicy, 'onDismiss' | 'onReopen'> {
112
+ const now = opts.now ?? (() => Date.now());
113
+ const reopenable = opts.isReopenable ?? ((env: ReopenEnv) => defaultIsReopenable(env, opts.staleAfterMs));
114
+ const undoMs = opts.undoMs ?? 6000;
115
+
116
+ const onDismiss = (cardId: string): void => {
117
+ const before = opts.get();
118
+ const prior = resolutionOf(before, cardId);
119
+ const dismissed: CardResolution = { kind: 'dismissed', at: new Date(now()).toISOString() };
120
+ opts.set(withResolution(before, cardId, dismissed));
121
+
122
+ opts.toast?.show({
123
+ message: 'Dismissed',
124
+ durationMs: undoMs,
125
+ action: {
126
+ label: 'Undo',
127
+ onClick: () => {
128
+ // Restore the prior resolution (undefined → live again). Read fresh in
129
+ // case the store moved on; only touch this card.
130
+ opts.set(withResolution(opts.get(), cardId, prior));
131
+ },
132
+ },
133
+ });
134
+ };
135
+
136
+ const onReopen = (cardId: string): void => {
137
+ const before = opts.get();
138
+ const current = resolutionOf(before, cardId);
139
+ const env: ReopenEnv = { cardId, resolution: current, now: now() };
140
+ if (reopenable(env)) {
141
+ opts.set(withResolution(before, cardId, undefined));
142
+ } else {
143
+ const expired: CardResolution = { kind: 'expired', at: new Date(now()).toISOString() };
144
+ opts.set(withResolution(before, cardId, expired));
145
+ }
146
+ };
147
+
148
+ return { onDismiss, onReopen };
149
+ }
@@ -59,6 +59,9 @@ export function routeCardEvent(policy: CardPolicy | undefined, event: CardEvent)
59
59
  case 'dismiss':
60
60
  p.onDismiss ? p.onDismiss(event.cardId) : warnNoHandler('dismiss');
61
61
  break;
62
+ case 'reopen':
63
+ p.onReopen ? p.onReopen(event.cardId) : warnNoHandler('reopen');
64
+ break;
62
65
  case 'error':
63
66
  p.onError ? p.onError(event.cardId, event.message) : warnNoHandler('error');
64
67
  break;
@@ -65,6 +65,11 @@
65
65
  "allowOther": {
66
66
  "description": "Optional free-text escape ('none of these / specify'). `true` appends a final 'Other…' option; an object customizes its `label`/`placeholder`. Selecting it reveals a text input; the shared Submit button then emits `{ kind:'action', action:'__other__', payload:{ text } }`.",
67
67
  "x-kai-control": "allow-other"
68
+ },
69
+ "dismissible": {
70
+ "type": "boolean",
71
+ "default": false,
72
+ "description": "Show a close affordance that emits the `dismiss` verb. The card collapses to a re-openable 'dismissed' stub; the host decides via `onReopen` whether it comes back live or `expired`."
68
73
  }
69
74
  }
70
75
  }
@@ -8,9 +8,9 @@
8
8
  "properties": {
9
9
  "mode": {
10
10
  "type": "string",
11
- "enum": ["select"],
11
+ "enum": ["select", "progress"],
12
12
  "default": "select",
13
- "description": "v1 supports only 'select' (toggle + confirm). 'progress' (live AG-UI status) is a future enum value; restricting it now keeps the wire forward-compatible.",
13
+ "description": "'select' (default) = toggle + a confirm button that emits `submit`. 'progress' = an onboarding-checklist look (header `done / total` count, circular indicators, per-item title/description, no confirm button, where checking a row IS the action). Both share the selection model.",
14
14
  "x-kai-mode": true
15
15
  },
16
16
  "heading": {
@@ -73,6 +73,11 @@
73
73
  "minimum": 1,
74
74
  "description": "Optional maximum selectable; further toggles are blocked once reached.",
75
75
  "x-kai-select-max": true
76
+ },
77
+ "dismissible": {
78
+ "type": "boolean",
79
+ "default": false,
80
+ "description": "Show a close affordance that emits the `dismiss` verb. The card collapses to a re-openable 'dismissed' stub; the host decides via `onReopen` whether it comes back live or `expired`."
76
81
  }
77
82
  }
78
83
  }
@@ -0,0 +1,49 @@
1
+ export interface EntityRef {
2
+ kind: string;
3
+ id: string;
4
+ label: string;
5
+ icon?: string;
6
+ promptText?: string;
7
+ data?: Record<string, unknown>;
8
+ }
9
+ export type Segment =
10
+ | { type: 'text'; text: string }
11
+ | { type: 'entity'; entity: EntityRef };
12
+ export type ComposerDoc = Segment[];
13
+
14
+ export function normalizeValue(value: string | ComposerDoc | null | undefined): ComposerDoc {
15
+ if (value == null) return [];
16
+ if (typeof value === 'string') return value.length ? [{ type: 'text', text: value }] : [];
17
+ const out: ComposerDoc = [];
18
+ for (const seg of value) {
19
+ if (seg.type === 'text') {
20
+ if (!seg.text) continue;
21
+ const last = out[out.length - 1];
22
+ // Replace (don't mutate) the prior segment so every object in the returned
23
+ // doc is freshly built and never an aliased/held reference.
24
+ if (last && last.type === 'text') out[out.length - 1] = { type: 'text', text: last.text + seg.text };
25
+ else out.push({ type: 'text', text: seg.text });
26
+ } else {
27
+ out.push({ type: 'entity', entity: seg.entity });
28
+ }
29
+ }
30
+ return out;
31
+ }
32
+
33
+ export function serializeToText(
34
+ doc: ComposerDoc,
35
+ opts?: { entity?: (e: EntityRef) => string },
36
+ ): string {
37
+ let out = '';
38
+ for (const seg of doc) {
39
+ if (seg.type === 'text') out += seg.text;
40
+ else out += opts?.entity ? opts.entity(seg.entity) : (seg.entity.promptText ?? seg.entity.label);
41
+ }
42
+ return out;
43
+ }
44
+ export function entitiesOf(doc: ComposerDoc): EntityRef[] {
45
+ return doc.filter((s): s is Extract<Segment, { type: 'entity' }> => s.type === 'entity').map((s) => s.entity);
46
+ }
47
+ export function docIsEmpty(doc: ComposerDoc): boolean {
48
+ return doc.every((s) => s.type === 'text' && s.text.length === 0);
49
+ }
@@ -0,0 +1,35 @@
1
+ export interface ActiveTrigger { char: string; query: string; start: number; }
2
+
3
+ export function detectTrigger(text: string, caret: number, chars: string[]): ActiveTrigger | null {
4
+ // Caret is a position within `text` (0..text.length). Out-of-range = no trigger.
5
+ if (caret < 0 || caret > text.length) return null;
6
+ for (let i = caret - 1; i >= 0; i--) {
7
+ const c = text[i];
8
+ if (/\s/.test(c)) return null; // hit whitespace before any trigger char
9
+ if (chars.includes(c)) {
10
+ const boundaryOk = i === 0 || /\s/.test(text[i - 1]);
11
+ if (!boundaryOk) return null;
12
+ return { char: c, query: text.slice(i + 1, caret), start: i };
13
+ }
14
+ }
15
+ return null;
16
+ }
17
+
18
+ /**
19
+ * Wraps detectTrigger with a list of trigger definitions.
20
+ * Returns { def, query, start } when a trigger is active, else null.
21
+ * Accepts generic defs so it does not import from composer.tsx (no cycle).
22
+ */
23
+ export function activeTriggerFor<T extends { char: string; kind: string }>(
24
+ text: string,
25
+ caret: number,
26
+ defs: T[],
27
+ ): { def: T; query: string; start: number } | null {
28
+ if (!defs.length) return null;
29
+ const chars = defs.map((d) => d.char);
30
+ const hit = detectTrigger(text, caret, chars);
31
+ if (!hit) return null;
32
+ const def = defs.find((d) => d.char === hit.char);
33
+ if (!def) return null;
34
+ return { def, query: hit.query, start: hit.start };
35
+ }
@@ -0,0 +1,66 @@
1
+ // src/primitives/create-kai-chat.ts
2
+ import { createSignal } from 'solid-js';
3
+ import type { ChatMessage } from '../elements/chat-types';
4
+ import type { AttachmentData } from '../components/attachments';
5
+ import {
6
+ appendMessage, updateMessage, removeMessage, addSuggestion, removeSuggestion,
7
+ createAssistantStream, onStreamSettled, type AssistantStream, type SetMessages,
8
+ } from '../state';
9
+
10
+ export interface CreateKaiChatOptions {
11
+ /** Seed messages, read once at creation and copied. Later changes are ignored — drive updates through the returned ops. */
12
+ initialMessages?: ChatMessage[];
13
+ /** Seed suggestions, read once at creation and copied. Later changes are ignored. */
14
+ initialSuggestions?: string[];
15
+ onSubmit?: (detail: { value: string; attachments: AttachmentData[] }) => void | Promise<void>;
16
+ }
17
+
18
+ /** Solid store: consumer-owned chat state + the same ergonomic ops as `useKaiChat`. */
19
+ export interface KaiChatStore {
20
+ messages: () => ChatMessage[];
21
+ setMessages: SetMessages;
22
+ suggestions: () => string[];
23
+ loading: () => boolean;
24
+ append: (msg: ChatMessage) => void;
25
+ update: (id: string, patch: Partial<ChatMessage> | ((m: ChatMessage) => ChatMessage)) => void;
26
+ remove: (id: string) => void;
27
+ addSuggestion: (s: string) => void;
28
+ removeSuggestion: (s: string) => void;
29
+ clearSuggestions: () => void;
30
+ streamAssistant: (init?: Partial<ChatMessage>) => AssistantStream;
31
+ handleSubmit: (event: CustomEvent<{ value: string; attachments: AttachmentData[] }>) => void;
32
+ /** Spread onto `<kai-chat {...chat.bind} />` (reactive getters). Wire submit via `on:kai-submit={chat.handleSubmit}`. */
33
+ bind: { readonly messages: ChatMessage[]; readonly loading: boolean; readonly suggestions: string[] };
34
+ }
35
+
36
+ export function createKaiChat(options: CreateKaiChatOptions = {}): KaiChatStore {
37
+ // Copy the seed arrays so a caller mutating the array it passed in can't reach our state.
38
+ const [messages, setMessagesSignal] = createSignal<ChatMessage[]>([...(options.initialMessages ?? [])]);
39
+ const [suggestions, setSuggestions] = createSignal<string[]>([...(options.initialSuggestions ?? [])]);
40
+ const [loading, setLoading] = createSignal(false);
41
+
42
+ const setMessages: SetMessages = (updater) => setMessagesSignal((prev) => updater(prev));
43
+
44
+ return {
45
+ messages,
46
+ setMessages,
47
+ suggestions,
48
+ loading,
49
+ append: (msg) => setMessages((prev) => appendMessage(prev, msg)),
50
+ update: (id, patch) => setMessages((prev) => updateMessage(prev, id, patch)),
51
+ remove: (id) => setMessages((prev) => removeMessage(prev, id)),
52
+ addSuggestion: (s) => setSuggestions((prev) => addSuggestion(prev, s)),
53
+ removeSuggestion: (s) => setSuggestions((prev) => removeSuggestion(prev, s)),
54
+ clearSuggestions: () => setSuggestions([]),
55
+ streamAssistant: (init) => {
56
+ setLoading(true);
57
+ return onStreamSettled(createAssistantStream(setMessages, init), () => setLoading(false));
58
+ },
59
+ handleSubmit: (event) => { void options.onSubmit?.(event.detail); },
60
+ bind: {
61
+ get messages() { return messages(); },
62
+ get loading() { return loading(); },
63
+ get suggestions() { return suggestions(); },
64
+ },
65
+ };
66
+ }