@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
@@ -35,13 +35,43 @@ export type ArtifactTab = 'preview' | 'code';
35
35
  /** A file the artifact can preview + show source for. */
36
36
  export type ArtifactFile = FileTreeFile;
37
37
 
38
+ /** Imperative handle exposed via `controllerRef` — surfaces the artifact's latent
39
+ * toolbar capabilities (history back/forward/reload/home, programmatic navigate,
40
+ * file selection, open-in-new-tab, maximize/restore) so the `<kai-artifact>`
41
+ * facade can forward them as instance methods. Each delegates to the SAME internal
42
+ * handler the toolbar buttons use, so every existing event still fires. */
43
+ export interface ArtifactController {
44
+ /** Go back in the artifact's own history stack (no-op when there's no prior entry). */
45
+ back(): void;
46
+ /** Go forward in the history stack (no-op when there's no forward entry). */
47
+ forward(): void;
48
+ /** Force-reload the current preview url (also re-renders an inline PDF). */
49
+ reload(): void;
50
+ /** Navigate to the `src` home url (no-op when there's no `src`). */
51
+ home(): void;
52
+ /** Push + load a url in the preview (the path-field submit path). */
53
+ navigate(url: string): void;
54
+ /** Select a file by path: highlights the tree, shows its source, navigates the preview. */
55
+ selectFile(path: string): void;
56
+ /** Open the current url in a new browser tab (no-op when there's no concrete url). */
57
+ openExternal(): void;
58
+ /** Enter the maximized view-state (fires onMaximizeChange/kai-maximize-change). */
59
+ maximize(): void;
60
+ /** Exit the maximized view-state. */
61
+ restore(): void;
62
+ }
63
+
38
64
  export interface ArtifactProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onSelect'> {
39
65
  /** URL the preview iframe frames. */
40
66
  src?: string;
41
67
  /** Files for the Code tab's tree (+ each file's preview `url`). */
42
68
  files?: ArtifactFile[];
43
- /** Active tab. Default `preview`. */
69
+ /** Controlled active tab when set, the artifact follows it (re-asserted on
70
+ * every change). When undefined the tab is uncontrolled (see `defaultTab`). */
44
71
  tab?: ArtifactTab;
72
+ /** Uncontrolled INITIAL tab (used only when `tab` is undefined). The user can
73
+ * then freely switch tabs; defaults to `preview`. */
74
+ defaultTab?: ArtifactTab;
45
75
  /** Selected file path (syncs tree highlight + Code source + preview). */
46
76
  activeFile?: string;
47
77
  /** iframe `sandbox` override. Default `allow-scripts allow-forms`. */
@@ -80,6 +110,15 @@ export interface ArtifactProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>,
80
110
  standalone?: boolean;
81
111
  /** Make the path field read-only (visible, nav-tracking, non-editable). */
82
112
  readonlyPath?: boolean;
113
+ /** Friendly address shown in the path field INSTEAD of the real current url
114
+ * (read-only, non-navigable). Use when the framed url is not consumer-facing
115
+ * (e.g. a `data:` blob) so a clean address is shown instead of leaking it.
116
+ * Unset = show the real url (editable per `readonlyPath`). */
117
+ displayUrl?: string;
118
+ /** Receive the imperative controller once mounted. The `<kai-artifact>` facade
119
+ * forwards these as element methods (back/forward/reload/home/navigate/
120
+ * selectFile/openExternal/maximize/restore). */
121
+ controllerRef?: (controller: ArtifactController) => void;
83
122
  }
84
123
 
85
124
  const DEFAULT_SANDBOX = 'allow-scripts allow-forms';
@@ -116,7 +155,9 @@ export function Artifact(props: ArtifactProps): JSX.Element {
116
155
  const merged = mergeProps(
117
156
  {
118
157
  files: [] as ArtifactFile[],
119
- tab: 'preview' as ArtifactTab,
158
+ // No `tab` default: a present `tab` means controlled. The uncontrolled
159
+ // initial tab comes from `defaultTab` (seeded into the internal signal below).
160
+ defaultTab: 'preview' as ArtifactTab,
120
161
  sandbox: DEFAULT_SANDBOX,
121
162
  showNav: true,
122
163
  showReload: true,
@@ -135,6 +176,7 @@ export function Artifact(props: ArtifactProps): JSX.Element {
135
176
  'src',
136
177
  'files',
137
178
  'tab',
179
+ 'defaultTab',
138
180
  'activeFile',
139
181
  'sandbox',
140
182
  'iframeTitle',
@@ -152,6 +194,8 @@ export function Artifact(props: ArtifactProps): JSX.Element {
152
194
  'openInTab',
153
195
  'standalone',
154
196
  'readonlyPath',
197
+ 'displayUrl',
198
+ 'controllerRef',
155
199
  'class',
156
200
  ]);
157
201
 
@@ -169,7 +213,11 @@ export function Artifact(props: ArtifactProps): JSX.Element {
169
213
  const canBack = () => cursor() > 0;
170
214
  const canForward = () => cursor() < history().length - 1;
171
215
 
172
- const [tab, setTab] = createSignal<ArtifactTab>(local.tab);
216
+ // Seed the internal tab: a present `tab` (controlled) wins; otherwise the
217
+ // uncontrolled `defaultTab` (which merges to 'preview'). The user can then
218
+ // switch freely unless `tab` is set (the effect below re-asserts the controlled
219
+ // value on each change).
220
+ const [tab, setTab] = createSignal<ArtifactTab>(local.tab ?? local.defaultTab);
173
221
  const [activeFile, setActiveFile] = createSignal<string | undefined>(local.activeFile);
174
222
  const [reloadKey, setReloadKey] = createSignal(0);
175
223
 
@@ -208,8 +256,12 @@ export function Artifact(props: ArtifactProps): JSX.Element {
208
256
 
209
257
  let iframeEl: HTMLIFrameElement | undefined;
210
258
 
211
- // Controlled syncing: when the consumer changes the props, follow them.
212
- createEffect(() => setTab(local.tab));
259
+ // Controlled syncing: when the consumer changes the props, follow them. `tab` is
260
+ // followed ONLY when it's actually set — an undefined `tab` means uncontrolled,
261
+ // so the internal signal (seeded from defaultTab) is left for the user to drive.
262
+ createEffect(() => {
263
+ if (local.tab !== undefined) setTab(local.tab);
264
+ });
213
265
  createEffect(() => setActiveFile(local.activeFile));
214
266
  // `src` change → navigate. Use `on(local.src, …)` so the effect tracks ONLY
215
267
  // the `src` prop — NOT `currentUrl()` — otherwise navigating away (file click,
@@ -290,6 +342,38 @@ export function Artifact(props: ArtifactProps): JSX.Element {
290
342
  if (local.src) navigate(local.src);
291
343
  };
292
344
 
345
+ // Explicitly drive the maximize view-state to a target (the imperative twin of
346
+ // the toggle button). Fires onMaximizeChange only when the value actually flips,
347
+ // matching toggleMaximize's "report on change" contract.
348
+ const setMaximizeState = (next: boolean) => {
349
+ if (maximized() === next) return;
350
+ setMaximized(next);
351
+ local.onMaximizeChange?.(next);
352
+ };
353
+
354
+ // --- Imperative controller (Pattern C): hand the facade a handle over the
355
+ // artifact's latent toolbar capabilities. Every method delegates to the
356
+ // SAME internal handler the toolbar buttons use, so navigate/tab-change/
357
+ // file-select/maximize-change all still fire. ---
358
+ onMount(() => {
359
+ local.controllerRef?.({
360
+ back: () => goBack(),
361
+ forward: () => goForward(),
362
+ reload: () => reload(),
363
+ home: () => goHome(),
364
+ navigate: (url) => navigate(url),
365
+ // Resolve the file for the path so highlight + source + preview all sync;
366
+ // a path with no matching file entry is a no-op (nothing to show).
367
+ selectFile: (path) => {
368
+ const file = fileFor(path);
369
+ if (file) selectFile(path, file);
370
+ },
371
+ openExternal: () => openInNewTab(),
372
+ maximize: () => setMaximizeState(true),
373
+ restore: () => setMaximizeState(false),
374
+ });
375
+ });
376
+
293
377
  // Best-effort: if the consumer opted into `allow-same-origin`, keep the path
294
378
  // field truthful as the framed doc navigates itself. Cross-origin (the secure
295
379
  // default) throws and we keep our own stack.
@@ -311,9 +395,10 @@ export function Artifact(props: ArtifactProps): JSX.Element {
311
395
  const input = (e.currentTarget as HTMLFormElement).elements.namedItem(
312
396
  'kai-artifact-path',
313
397
  ) as HTMLInputElement | null;
314
- if (local.readonlyPath) {
315
- // Submit is a no-op while read-only; keep the field reflecting currentUrl.
316
- if (input) input.value = currentUrl();
398
+ if (local.readonlyPath || local.displayUrl != null) {
399
+ // Submit is a no-op while read-only or showing a friendly displayUrl; keep
400
+ // the field reflecting the shown value (never push the real, possibly data:, url).
401
+ if (input) input.value = local.displayUrl ?? currentUrl();
317
402
  return;
318
403
  }
319
404
  if (input && input.value) navigate(input.value);
@@ -330,7 +415,7 @@ export function Artifact(props: ArtifactProps): JSX.Element {
330
415
  >
331
416
  <Show when={showAnyToolbar()}>
332
417
  <ArtifactToolbar
333
- url={currentUrl}
418
+ url={() => local.displayUrl ?? currentUrl()}
334
419
  tab={tab}
335
420
  canBack={canBack}
336
421
  canForward={canForward}
@@ -352,7 +437,7 @@ export function Artifact(props: ArtifactProps): JSX.Element {
352
437
  onToggleMaximize={toggleMaximize}
353
438
  canOpenInTab={canOpenInTab}
354
439
  onOpenInTab={openInNewTab}
355
- readonlyPath={() => local.readonlyPath}
440
+ readonlyPath={() => local.readonlyPath || local.displayUrl != null}
356
441
  />
357
442
  </Show>
358
443
  <div class="relative min-h-0 flex-1">
@@ -420,7 +505,7 @@ function ArtifactToolbar(props: ToolbarProps): JSX.Element {
420
505
  return (
421
506
  <div
422
507
  data-artifact-toolbar
423
- class="flex shrink-0 items-center gap-1.5 border-b border-border bg-muted/40 px-2 py-1.5"
508
+ class="flex shrink-0 items-center gap-1.5 border-b border-border bg-surface px-2 py-1.5"
424
509
  >
425
510
  <Show when={props.showNav()}>
426
511
  <Button
@@ -595,7 +680,7 @@ interface CodeProps {
595
680
  function ArtifactCode(props: CodeProps): JSX.Element {
596
681
  return (
597
682
  <div class="absolute inset-0 flex">
598
- <div class="w-56 shrink-0 overflow-auto border-r border-border bg-muted/20 py-1.5 scrollbar-thin">
683
+ <div class="w-56 shrink-0 overflow-auto border-r border-border bg-surface-sunken py-1.5 scrollbar-thin">
599
684
  <FileTree
600
685
  files={props.files}
601
686
  activeFile={props.activeFile()}
@@ -728,7 +813,7 @@ function ArtifactPdfPreview(props: { url: string; reloadKey: number }): JSX.Elem
728
813
  aria-label="PDF preview"
729
814
  aria-busy={state() === 'loading'}
730
815
  tabindex="0"
731
- class="absolute inset-0 flex flex-col items-center gap-3 overflow-auto bg-muted/20 p-3 scrollbar-thin focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
816
+ class="absolute inset-0 flex flex-col items-center gap-3 overflow-auto bg-surface-sunken p-3 scrollbar-thin focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
732
817
  />
733
818
  <Show when={isPdfPreviewEnabled() && state() === 'loading'}>
734
819
  <div class="pointer-events-none absolute inset-0 flex items-center justify-center">
@@ -0,0 +1,13 @@
1
+ /** A message attachment descriptor. Pure type — kept JSX-free for the
2
+ * framework-neutral consumers (state core, React hook typecheck). */
3
+ export interface AttachmentData {
4
+ id: string;
5
+ type: 'file' | 'source-document';
6
+ filename?: string;
7
+ mediaType?: string;
8
+ url?: string;
9
+ title?: string;
10
+ }
11
+
12
+ export type AttachmentMediaCategory = 'image' | 'video' | 'audio' | 'document' | 'source' | 'unknown';
13
+ export type AttachmentVariant = 'grid' | 'inline' | 'list';
@@ -11,23 +11,13 @@ import {
11
11
  Video,
12
12
  X,
13
13
  } from 'lucide-solid';
14
+ import type { AttachmentData, AttachmentMediaCategory, AttachmentVariant } from './attachment-types';
15
+ export type { AttachmentData, AttachmentMediaCategory, AttachmentVariant } from './attachment-types';
14
16
 
15
17
  // ============================================================================
16
18
  // Types
17
19
  // ============================================================================
18
20
 
19
- export interface AttachmentData {
20
- id: string;
21
- type: 'file' | 'source-document';
22
- filename?: string;
23
- mediaType?: string;
24
- url?: string;
25
- title?: string;
26
- }
27
-
28
- export type AttachmentMediaCategory = 'image' | 'video' | 'audio' | 'document' | 'source' | 'unknown';
29
- export type AttachmentVariant = 'grid' | 'inline' | 'list';
30
-
31
21
  const mediaCategoryIcons: Record<AttachmentMediaCategory, typeof ImageIcon> = {
32
22
  audio: Music2,
33
23
  document: FileText,
@@ -159,12 +149,12 @@ function Attachment(props: AttachmentProps) {
159
149
  ctx.variant === 'grid' && 'size-24 overflow-hidden rounded-lg',
160
150
  ctx.variant === 'inline' && [
161
151
  'flex h-8 cursor-pointer select-none items-center gap-1.5',
162
- 'rounded-md bg-muted/50 px-1.5 text-foreground',
152
+ 'rounded-md bg-surface-strong px-1.5 text-foreground',
163
153
  'font-medium text-sm transition-all',
164
154
  'hover:bg-muted',
165
155
  ],
166
156
  ctx.variant === 'list' && [
167
- 'flex w-full items-center gap-3 rounded-lg bg-muted/30 p-3 text-foreground',
157
+ 'flex w-full items-center gap-3 rounded-lg bg-surface p-3 text-foreground',
168
158
  'hover:bg-muted/50',
169
159
  ],
170
160
  local.class,
@@ -1,6 +1,7 @@
1
- import { type JSX, Show, splitProps, mergeProps, createUniqueId } from 'solid-js';
1
+ import { type JSX, Show, splitProps, mergeProps, createSignal, createUniqueId } from 'solid-js';
2
+ import { Dynamic } from 'solid-js/web';
2
3
  import { cn } from '../utils/cn';
3
- import { AlertTriangle } from 'lucide-solid';
4
+ import { AlertTriangle, X } from 'lucide-solid';
4
5
 
5
6
  export interface CardProps extends JSX.HTMLAttributes<HTMLDivElement> {
6
7
  /** Heading rendered in the card chrome (the contract's CardEnvelope.title).
@@ -19,6 +20,25 @@ export interface CardProps extends JSX.HTMLAttributes<HTMLDivElement> {
19
20
  /** Stable id for the heading (so composing cards can `aria-labelledby` it).
20
21
  * Auto-generated when omitted. */
21
22
  headingId?: string;
23
+ /** Render a dismiss (×) button that hides the card and calls `onDismiss`.
24
+ * Opt-in, OFF by default — the contract cards never set it, so they are
25
+ * unaffected. */
26
+ dismissible?: boolean;
27
+ /** Called after the card is dismissed via its ×. */
28
+ onDismiss?: () => void;
29
+ /** Render the whole card as a link (`<a>`). Wins over `clickable`. */
30
+ href?: string;
31
+ /** `target` for the `href` anchor. */
32
+ target?: string;
33
+ /** `rel` for the `href` anchor (default `noopener noreferrer` when `target="_blank"`). */
34
+ rel?: string;
35
+ /** Make the whole card behave like a button: `role="button"`, a tabindex, and
36
+ * Enter/Space activation, firing `onCardClick`. Ignored when `href` is set.
37
+ * A clickable/href card must NOT also contain footer action buttons — that
38
+ * nests interactive elements. Use `actions` OR clickable, never both. */
39
+ clickable?: boolean;
40
+ /** Called when a `clickable` (or `href`) card is activated (click, or Enter/Space). */
41
+ onCardClick?: (event: MouseEvent | KeyboardEvent) => void;
22
42
  }
23
43
 
24
44
  /**
@@ -28,6 +48,14 @@ export interface CardProps extends JSX.HTMLAttributes<HTMLDivElement> {
28
48
  * "never a broken/partial card" rule). It is intentionally chrome-only: it reads
29
49
  * no `CardContext` and emits no `CardEvent` — the cards that compose it (e.g.
30
50
  * `kai-form`) own the contract interaction.
51
+ *
52
+ * The optional `dismissible` / `href` / `clickable` behaviors are all OFF by
53
+ * default, so the contract cards (which set none of them) keep their original
54
+ * presentational behavior unchanged.
55
+ *
56
+ * a11y: a `clickable` or `href` card MUST NOT also contain footer action buttons
57
+ * — that nests interactive controls inside a button/link. Use `actions` OR
58
+ * make the card clickable, never both.
31
59
  */
32
60
  export function Card(props: CardProps): JSX.Element {
33
61
  const merged = mergeProps({ dense: false }, props);
@@ -39,6 +67,13 @@ export function Card(props: CardProps): JSX.Element {
39
67
  'errorMessage',
40
68
  'dense',
41
69
  'headingId',
70
+ 'dismissible',
71
+ 'onDismiss',
72
+ 'href',
73
+ 'target',
74
+ 'rel',
75
+ 'clickable',
76
+ 'onCardClick',
42
77
  'class',
43
78
  'children',
44
79
  ]);
@@ -48,19 +83,36 @@ export function Card(props: CardProps): JSX.Element {
48
83
  const hasError = () => local.errorMessage !== undefined && local.errorMessage !== '';
49
84
  const hasHeader = () => Boolean(local.heading) || Boolean(local.description);
50
85
 
51
- return (
52
- <div
53
- class={cn(
54
- 'flex flex-col rounded-xl border border-border bg-card text-card-foreground kai-elevation-sm',
55
- local.dense ? 'gap-2.5 p-3.5' : 'gap-4 p-5',
56
- local.class,
57
- )}
58
- {...rest}
59
- >
60
- <Show when={local.media}>
61
- <div class="overflow-hidden rounded-lg">{local.media}</div>
62
- </Show>
86
+ const [open, setOpen] = createSignal(true);
87
+ const dismiss = (event: MouseEvent) => {
88
+ // Don't let the × activate a clickable/href card underneath it.
89
+ event.stopPropagation();
90
+ setOpen(false);
91
+ local.onDismiss?.();
92
+ };
93
+
94
+ const isLink = () => Boolean(local.href);
95
+ const isButton = () => !isLink() && Boolean(local.clickable);
96
+ const isInteractive = () => isLink() || isButton();
63
97
 
98
+ const activate = (event: MouseEvent | KeyboardEvent) => local.onCardClick?.(event);
99
+ const onKeyDown = (event: KeyboardEvent) => {
100
+ if (!isButton()) return;
101
+ if (event.key === 'Enter' || event.key === ' ' || event.key === 'Spacebar') {
102
+ event.preventDefault();
103
+ activate(event);
104
+ }
105
+ };
106
+
107
+ // The interactive elements (`<a>` / role="button") get keyboard + hover/focus
108
+ // affordances; the plain card is a `<div>`. Anchors carry a safe default `rel`
109
+ // when opening a new tab.
110
+ const rel = () =>
111
+ local.rel ?? (local.target === '_blank' ? 'noopener noreferrer' : undefined);
112
+
113
+ // The card's heading + body + actions column.
114
+ const inner = (
115
+ <>
64
116
  <Show when={hasHeader()}>
65
117
  <div
66
118
  class={cn(
@@ -105,6 +157,48 @@ export function Card(props: CardProps): JSX.Element {
105
157
  <span>{local.errorMessage}</span>
106
158
  </div>
107
159
  </Show>
108
- </div>
160
+ </>
161
+ );
162
+
163
+ return (
164
+ <Show when={open()}>
165
+ <Dynamic
166
+ component={isLink() ? 'a' : 'div'}
167
+ part="card"
168
+ href={isLink() ? local.href : undefined}
169
+ target={isLink() ? local.target : undefined}
170
+ rel={isLink() ? rel() : undefined}
171
+ role={isButton() ? 'button' : undefined}
172
+ tabindex={isButton() ? 0 : undefined}
173
+ onClick={isInteractive() ? activate : undefined}
174
+ onKeyDown={isButton() ? onKeyDown : undefined}
175
+ class={cn(
176
+ 'relative flex flex-col rounded-xl border border-border bg-card text-card-foreground kai-elevation-sm',
177
+ local.dense ? 'gap-2.5 p-3.5' : 'gap-4 p-5',
178
+ isInteractive() &&
179
+ 'cursor-pointer no-underline transition-colors hover:border-ring hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
180
+ local.class,
181
+ )}
182
+ {...rest}
183
+ >
184
+ <Show when={local.dismissible}>
185
+ <button
186
+ type="button"
187
+ part="dismiss"
188
+ aria-label="Dismiss"
189
+ onClick={dismiss}
190
+ class="absolute right-2.5 top-2.5 z-10 shrink-0 rounded p-1 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
191
+ >
192
+ <X class="size-3.5" aria-hidden="true" />
193
+ </button>
194
+ </Show>
195
+
196
+ <Show when={local.media}>
197
+ <div class="overflow-hidden rounded-lg">{local.media}</div>
198
+ </Show>
199
+
200
+ {inner}
201
+ </Dynamic>
202
+ </Show>
109
203
  );
110
204
  }
@@ -1,4 +1,11 @@
1
- import { type JSX, splitProps, children as resolveChildren, For, Show } from 'solid-js';
1
+ import {
2
+ type JSX,
3
+ type Accessor,
4
+ splitProps,
5
+ createSignal,
6
+ For,
7
+ Show,
8
+ } from 'solid-js';
2
9
  import { cn } from '../utils/cn';
3
10
  import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '../ui/collapsible';
4
11
  import { ChevronDown, Circle } from 'lucide-solid';
@@ -114,6 +121,15 @@ export interface ChainOfThoughtStepProps {
114
121
  children: JSX.Element;
115
122
  class?: string;
116
123
  isLast?: boolean;
124
+ /** Controlled open state. When provided, the step's Collapsible is driven by
125
+ * this value (the parent owns it — used by the Accordion model below). When
126
+ * OMITTED the step stays uncontrolled, preserving the original behaviour
127
+ * (every step independently toggleable, starting closed). */
128
+ open?: boolean;
129
+ /** Called with the desired next open state when the user clicks the trigger.
130
+ * Only meaningful when `open` is also provided (controlled mode); the parent
131
+ * Accordion routes this through its open-set handler. */
132
+ onOpenChange?: (open: boolean) => void;
117
133
  }
118
134
 
119
135
  function ChainOfThoughtStep(props: ChainOfThoughtStepProps) {
@@ -121,6 +137,8 @@ function ChainOfThoughtStep(props: ChainOfThoughtStepProps) {
121
137
  <Collapsible
122
138
  class={cn('group', props.class)}
123
139
  data-last={props.isLast ?? false}
140
+ open={props.open}
141
+ onOpenChange={props.onOpenChange}
124
142
  >
125
143
  {props.children}
126
144
  <div class="flex justify-start group-data-[last=true]:hidden">
@@ -130,10 +148,201 @@ function ChainOfThoughtStep(props: ChainOfThoughtStepProps) {
130
148
  );
131
149
  }
132
150
 
151
+ // --- Accordion model (the kai-chain-of-thought interaction surface) ---
152
+
153
+ /** Open-mode: `multiple` (any number of steps open at once — the historical
154
+ * default) or `single` (at most one open; opening a step closes the others). */
155
+ export type ChainOfThoughtType = 'single' | 'multiple';
156
+
157
+ /**
158
+ * A reasoning step descriptor. `id` is an OPTIONAL stable key — the Accordion's
159
+ * open-set keys use `step.id` when present, else `String(index)`. So a consumer
160
+ * who wants to drive `value`/`defaultValue` (or read `kai-value-change`) by a
161
+ * meaningful key should set `id`; otherwise the step's position is its key.
162
+ */
163
+ export interface ChainOfThoughtStepData {
164
+ /** The step's heading (the always-visible trigger). */
165
+ label: string;
166
+ /** Optional expandable detail. */
167
+ content?: string;
168
+ /** Optional stable key for the open-set/value model (defaults to the index). */
169
+ id?: string;
170
+ }
171
+
172
+ /**
173
+ * Imperative handle exposed via `controllerRef` so the `<kai-chain-of-thought>`
174
+ * facade can drive/observe the Accordion's open set. Index-based to match the
175
+ * `expand(index?)`/`collapse(index?)`/`toggle(index?)` method signatures; the
176
+ * controller resolves the index → step key internally. All setters flow through
177
+ * the same open-set mutate path as user clicks, so they all emit
178
+ * `kai-value-change` via `onValueChange`.
179
+ */
180
+ export interface ChainOfThoughtController {
181
+ /** Current open keys, normalised per `type` (string in single, string[] in multiple). */
182
+ value: Accessor<string | string[]>;
183
+ /** Open one step by index, or ALL steps when `index` is omitted. */
184
+ expand: (index?: number) => void;
185
+ /** Close one step by index, or ALL steps when `index` is omitted. */
186
+ collapse: (index?: number) => void;
187
+ /** Flip one step's open state by index. */
188
+ toggle: (index?: number) => void;
189
+ }
190
+
191
+ export interface ChainOfThoughtAccordionProps {
192
+ /** The reasoning steps to render. */
193
+ steps: ChainOfThoughtStepData[];
194
+ /** Open mode. Default `multiple` (the historical behaviour). */
195
+ type?: ChainOfThoughtType;
196
+ /** Controlled open step key(s). When provided it WINS over internal state. */
197
+ value?: string | string[];
198
+ /** Uncontrolled initial open step key(s). Seeds the internal open-set. */
199
+ defaultValue?: string | string[];
200
+ /** Fired whenever the open set changes (user click OR a controller method).
201
+ * The single emit point — the facade turns this into `kai-value-change`. The
202
+ * payload is a string in `single` mode, a string[] in `multiple` mode. */
203
+ onValueChange?: (value: string | string[]) => void;
204
+ /** Receive the imperative controller once set up (Pattern C). */
205
+ controllerRef?: (api: ChainOfThoughtController) => void;
206
+ class?: string;
207
+ }
208
+
209
+ /** Normalise a `string | string[]` (or undefined) seed to a Set of keys. */
210
+ function toKeySet(value: string | string[] | undefined): Set<string> {
211
+ if (value == null) return new Set();
212
+ return new Set(Array.isArray(value) ? value : [value]);
213
+ }
214
+
215
+ /**
216
+ * The full Accordion-model root used by `<kai-chain-of-thought>`. Lifts each
217
+ * step's open state into a controlled/uncontrolled open-set:
218
+ * - seeded from `defaultValue` (normalised string|string[] → Set);
219
+ * - when `value` is provided it WINS (controlled);
220
+ * - a trigger click updates the set respecting `single` vs `multiple` and emits
221
+ * via `onValueChange` — in single mode opening a step closes the others;
222
+ * - each step's Collapsible is driven (controlled) by its membership in the set.
223
+ *
224
+ * With NO new props (type defaults to `multiple`, no value/defaultValue), every
225
+ * step starts closed and is independently toggleable — identical to the old
226
+ * behaviour, just with the open state lifted here so methods/events can observe
227
+ * and drive it.
228
+ */
229
+ function ChainOfThoughtAccordion(props: ChainOfThoughtAccordionProps) {
230
+ const [internal, setInternal] = createSignal<Set<string>>(toKeySet(props.defaultValue));
231
+
232
+ const type = (): ChainOfThoughtType => props.type ?? 'multiple';
233
+
234
+ // The step key: explicit id when present, else the index as a string.
235
+ const keyOf = (step: ChainOfThoughtStepData, index: number) => step.id ?? String(index);
236
+
237
+ // Controlled `value` wins; otherwise the internal signal owns the open-set.
238
+ const isControlled = () => props.value !== undefined;
239
+ const openSet = (): Set<string> => (isControlled() ? toKeySet(props.value) : internal());
240
+
241
+ const isOpen = (key: string) => openSet().has(key);
242
+
243
+ // Shape the open-set as the public value: a single string (or '') in single
244
+ // mode, a string[] in multiple mode.
245
+ const toValue = (set: Set<string>): string | string[] => {
246
+ if (type() === 'single') {
247
+ const first = set.values().next();
248
+ return first.done ? '' : first.value;
249
+ }
250
+ return [...set];
251
+ };
252
+
253
+ // The single mutate path — shared by trigger clicks and controller methods.
254
+ // Computes the next open-set, writes it (uncontrolled only) and ALWAYS emits
255
+ // onValueChange so both user + programmatic changes flow to kai-value-change.
256
+ const commit = (next: Set<string>) => {
257
+ const cur = openSet();
258
+ // No-op if the set is unchanged (avoids redundant events).
259
+ if (cur.size === next.size && [...cur].every((k) => next.has(k))) return;
260
+ if (!isControlled()) setInternal(next);
261
+ props.onValueChange?.(toValue(next));
262
+ };
263
+
264
+ // Set a single key's open state, respecting single vs multiple.
265
+ const setKeyOpen = (key: string, open: boolean) => {
266
+ if (type() === 'single') {
267
+ commit(open ? new Set([key]) : new Set());
268
+ return;
269
+ }
270
+ const next = new Set(openSet());
271
+ if (open) next.add(key); else next.delete(key);
272
+ commit(next);
273
+ };
274
+
275
+ // Resolve a step index → key (undefined when out of range).
276
+ const keyAt = (index: number): string | undefined => {
277
+ const step = props.steps[index];
278
+ return step ? keyOf(step, index) : undefined;
279
+ };
280
+
281
+ const allKeys = (): string[] => props.steps.map((s, i) => keyOf(s, i));
282
+
283
+ // --- Controller (Pattern C): hand the facade an index-based handle. Every
284
+ // setter routes through commit() so methods + clicks share one emit point.
285
+ props.controllerRef?.({
286
+ value: () => toValue(openSet()),
287
+ expand: (index?: number) => {
288
+ if (index === undefined) {
289
+ // Open all. In single mode only one can be open — keep the last step.
290
+ if (type() === 'single') {
291
+ const keys = allKeys();
292
+ commit(keys.length ? new Set([keys[keys.length - 1]]) : new Set());
293
+ } else {
294
+ commit(new Set(allKeys()));
295
+ }
296
+ return;
297
+ }
298
+ const key = keyAt(index);
299
+ if (key !== undefined) setKeyOpen(key, true);
300
+ },
301
+ collapse: (index?: number) => {
302
+ if (index === undefined) {
303
+ commit(new Set());
304
+ return;
305
+ }
306
+ const key = keyAt(index);
307
+ if (key !== undefined) setKeyOpen(key, false);
308
+ },
309
+ toggle: (index?: number) => {
310
+ if (index === undefined) return;
311
+ const key = keyAt(index);
312
+ if (key !== undefined) setKeyOpen(key, !isOpen(key));
313
+ },
314
+ });
315
+
316
+ return (
317
+ <ChainOfThought class={props.class}>
318
+ <For each={props.steps}>
319
+ {(step, i) => {
320
+ const key = () => keyOf(step, i());
321
+ return (
322
+ <ChainOfThoughtStep
323
+ isLast={i() === props.steps.length - 1}
324
+ open={isOpen(key())}
325
+ onOpenChange={(open) => setKeyOpen(key(), open)}
326
+ >
327
+ <ChainOfThoughtTrigger>{step.label}</ChainOfThoughtTrigger>
328
+ <Show when={step.content}>
329
+ <ChainOfThoughtContent>
330
+ <ChainOfThoughtItem>{step.content}</ChainOfThoughtItem>
331
+ </ChainOfThoughtContent>
332
+ </Show>
333
+ </ChainOfThoughtStep>
334
+ );
335
+ }}
336
+ </For>
337
+ </ChainOfThought>
338
+ );
339
+ }
340
+
133
341
  export {
134
342
  ChainOfThought,
135
343
  ChainOfThoughtStep,
136
344
  ChainOfThoughtTrigger,
137
345
  ChainOfThoughtContent,
138
346
  ChainOfThoughtItem,
347
+ ChainOfThoughtAccordion,
139
348
  };