@kitn.ai/ui 0.17.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 (494) hide show
  1. package/README.md +28 -5
  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 +37 -12
  9. package/dist/components/choice-card.d.ts +26 -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/file-tree.d.ts +15 -0
  19. package/dist/components/form.d.ts +34 -0
  20. package/dist/components/markdown.d.ts +3 -0
  21. package/dist/components/message.d.ts +12 -0
  22. package/dist/components/model-switcher.d.ts +7 -0
  23. package/dist/components/prompt-input.d.ts +13 -4
  24. package/dist/components/prompt-suggestion.d.ts +9 -1
  25. package/dist/components/reasoning.d.ts +14 -1
  26. package/dist/components/response-compare.d.ts +14 -0
  27. package/dist/components/screen.d.ts +46 -0
  28. package/dist/components/tasks-card.d.ts +49 -1
  29. package/dist/components/toast.d.ts +12 -2
  30. package/dist/components/tool-types.d.ts +10 -0
  31. package/dist/components/tool.d.ts +8 -8
  32. package/dist/components/voice-input.d.ts +27 -0
  33. package/dist/components/voice-output.d.ts +33 -0
  34. package/dist/core-C8fzo39E.js +12 -0
  35. package/dist/custom-elements.json +8392 -1860
  36. package/dist/elements/agent-card.d.ts +1 -0
  37. package/dist/elements/agent-card.js +1 -0
  38. package/dist/elements/artifact.js +1 -1
  39. package/dist/elements/attachments.js +1 -1
  40. package/dist/elements/autoloader.js +1 -1
  41. package/dist/elements/avatar.d.ts +1 -0
  42. package/dist/elements/avatar.js +1 -0
  43. package/dist/elements/badge.d.ts +1 -0
  44. package/dist/elements/badge.js +1 -0
  45. package/dist/elements/button.d.ts +1 -0
  46. package/dist/elements/button.js +1 -0
  47. package/dist/elements/card.js +1 -1
  48. package/dist/elements/cards.js +1 -1
  49. package/dist/elements/chain-of-thought.js +1 -1
  50. package/dist/elements/chat-scope-picker.js +1 -1
  51. package/dist/elements/chat-types.d.ts +47 -0
  52. package/dist/elements/chat-workspace.js +1 -1
  53. package/dist/elements/chat.js +1 -1
  54. package/dist/elements/checkpoint.js +1 -1
  55. package/dist/elements/choice.js +1 -1
  56. package/dist/elements/chunks/Icon-DLLBeLG5.js +1 -0
  57. package/dist/elements/chunks/action-icons-UB4V5FNq.js +1 -0
  58. package/dist/elements/chunks/arrow-left-C-gwlUmx.js +1 -0
  59. package/dist/elements/chunks/attachments-CgbErf1r.js +1 -0
  60. package/dist/elements/chunks/badge-DTQpgB6N.js +1 -0
  61. package/dist/elements/chunks/button-XDUM6lXs.js +1 -0
  62. package/dist/elements/chunks/card-CBlFZ-7I.js +1 -0
  63. package/dist/elements/chunks/chat-thread-BIhmqwNL.js +1 -0
  64. package/dist/elements/chunks/check-hGUoVIKb.js +1 -0
  65. package/dist/elements/chunks/chevron-down-DDoaSUtO.js +1 -0
  66. package/dist/elements/chunks/chevron-right-CdGEUakB.js +1 -0
  67. package/dist/elements/chunks/circle-BKAZdmzb.js +1 -0
  68. package/dist/elements/chunks/circle-check-CmYG-y3I.js +1 -0
  69. package/dist/elements/chunks/circle-x-DewgAEym.js +1 -0
  70. package/dist/elements/chunks/cn-CU7UAWzk.js +1 -0
  71. package/dist/elements/chunks/code-block-DedwxwFI.js +1 -0
  72. package/dist/elements/chunks/collapsible-FfgmTHtM.js +1 -0
  73. package/dist/elements/chunks/composer-Cfbw5HKT.js +65 -0
  74. package/dist/elements/chunks/context-DwJmxlRE.js +1 -0
  75. package/dist/elements/chunks/conversation-list-DsCQMfPq.js +1 -0
  76. package/dist/elements/chunks/default-input-BuqsoENC.js +1 -0
  77. package/dist/elements/chunks/define-rGsaWsLL.js +1 -0
  78. package/dist/elements/chunks/disclosure-CwoAkpMH.js +1 -0
  79. package/dist/elements/chunks/download-DJ06G3Bz.js +1 -0
  80. package/dist/elements/chunks/dropdown-BFg8ri_R.js +1 -0
  81. package/dist/elements/chunks/ellipsis-dlN6YvGw.js +1 -0
  82. package/dist/elements/chunks/external-link-DBX2lc_R.js +1 -0
  83. package/dist/elements/chunks/file-text-HfwUx4F9.js +1 -0
  84. package/dist/elements/chunks/file-tree-C6Toj71u.js +1 -0
  85. package/dist/elements/chunks/folder-MYuqa9Xp.js +1 -0
  86. package/dist/elements/chunks/hover-card-gE7iyXdW.js +1 -0
  87. package/dist/elements/chunks/icon-Vqz4ivrY.js +1 -0
  88. package/dist/elements/chunks/{index-CTBEJeo6.js → index-BCRhi84a.js} +1 -1
  89. package/dist/elements/chunks/info-B_toFS0l.js +1 -0
  90. package/dist/elements/chunks/input-gzbHYZvQ.js +1 -0
  91. package/dist/elements/chunks/kbd-n49c4KZm.js +1 -0
  92. package/dist/elements/chunks/link-CKPs9yM0.js +1 -0
  93. package/dist/elements/chunks/loader-q6MamGus.js +1 -0
  94. package/dist/elements/chunks/{markdown-CMTGx9r8.js → markdown-DBrcqq4S.js} +9 -9
  95. package/dist/elements/chunks/message-CTqaYI0s.js +1 -0
  96. package/dist/elements/chunks/message-feedback-Cvzm03w0.js +1 -0
  97. package/dist/elements/chunks/message-square-CUg9eRCi.js +1 -0
  98. package/dist/elements/chunks/minimize-2-l2klxP6K.js +1 -0
  99. package/dist/elements/chunks/model-switcher-CmyjiHO1.js +1 -0
  100. package/dist/elements/chunks/overlay-C8sOYqjT.js +1 -0
  101. package/dist/elements/chunks/paperclip-42Ia7xHD.js +1 -0
  102. package/dist/elements/chunks/progress-bar-tzKMWAwO.js +1 -0
  103. package/dist/elements/chunks/prompt-suggestion-D3F6vP_A.js +1 -0
  104. package/dist/elements/chunks/reasoning-BJNmmyov.js +1 -0
  105. package/dist/elements/chunks/resizable-BRsk19qI.js +1 -0
  106. package/dist/elements/chunks/rotate-cw-BgcggLHC.js +1 -0
  107. package/dist/elements/chunks/scroll-area-CgGbFFbT.js +1 -0
  108. package/dist/elements/chunks/separator-8hM4GdHE.js +1 -0
  109. package/dist/elements/chunks/settings-CaUIYOg-.js +1 -0
  110. package/dist/elements/chunks/settings-group-CYjOVIZQ.js +1 -0
  111. package/dist/elements/chunks/share-BTYM3Ddt.js +1 -0
  112. package/dist/elements/chunks/skeleton-DsTYrPUe.js +1 -0
  113. package/dist/elements/chunks/slots-DkZ-Cmha.js +1 -0
  114. package/dist/elements/chunks/star-9R3LBQ9n.js +1 -0
  115. package/dist/elements/chunks/store-CYl5Y0_u.js +1 -0
  116. package/dist/elements/chunks/text-shimmer-CaYGv8Se.js +1 -0
  117. package/dist/elements/chunks/textarea-NEr2SLcD.js +1 -0
  118. package/dist/elements/chunks/thumbs-up-DZYZkFfc.js +1 -0
  119. package/dist/elements/chunks/toast-store-ClxVZRcc.js +1 -0
  120. package/dist/elements/chunks/tool-CHZOmoe9.js +1 -0
  121. package/dist/elements/chunks/tooltip-WpHElELC.js +1 -0
  122. package/dist/elements/chunks/triangle-alert-BlS80PUg.js +1 -0
  123. package/dist/elements/chunks/use-card-resolution-VSVoxDeJ.js +1 -0
  124. package/dist/elements/chunks/video-DxTPZqT8.js +1 -0
  125. package/dist/elements/chunks/x-C1qtndxn.js +1 -0
  126. package/dist/elements/coachmark.d.ts +1 -0
  127. package/dist/elements/coachmark.js +1 -0
  128. package/dist/elements/code-block.js +1 -1
  129. package/dist/elements/command.d.ts +1 -0
  130. package/dist/elements/command.js +1 -0
  131. package/dist/elements/compare.js +1 -1
  132. package/dist/elements/composer.d.ts +1 -0
  133. package/dist/elements/composer.js +1 -0
  134. package/dist/elements/confirm-card.js +1 -1
  135. package/dist/elements/context-meter.js +1 -1
  136. package/dist/elements/conversation-list.js +1 -1
  137. package/dist/elements/css.d.ts +1 -0
  138. package/dist/elements/default-input.d.ts +51 -0
  139. package/dist/elements/define.d.ts +53 -0
  140. package/dist/elements/dialog.d.ts +1 -0
  141. package/dist/elements/dialog.js +1 -0
  142. package/dist/elements/disclosure.d.ts +37 -0
  143. package/dist/elements/editable-label.d.ts +1 -0
  144. package/dist/elements/editable-label.js +1 -0
  145. package/dist/elements/embed.js +1 -1
  146. package/dist/elements/empty.js +1 -1
  147. package/dist/elements/feedback-bar.js +1 -1
  148. package/dist/elements/file-tree.js +1 -1
  149. package/dist/elements/file-upload.js +1 -1
  150. package/dist/elements/form.js +1 -1
  151. package/dist/elements/hover-card.d.ts +1 -0
  152. package/dist/elements/hover-card.js +1 -0
  153. package/dist/elements/icon.d.ts +1 -0
  154. package/dist/elements/icon.js +1 -0
  155. package/dist/elements/image.js +1 -1
  156. package/dist/elements/input.d.ts +1 -0
  157. package/dist/elements/input.js +1 -0
  158. package/dist/elements/kbd.d.ts +1 -0
  159. package/dist/elements/kbd.js +1 -0
  160. package/dist/elements/link-preview.js +1 -1
  161. package/dist/elements/loader.js +1 -1
  162. package/dist/elements/markdown.js +1 -1
  163. package/dist/elements/menu.d.ts +1 -0
  164. package/dist/elements/menu.js +1 -0
  165. package/dist/elements/message-skills.js +1 -1
  166. package/dist/elements/message.js +1 -1
  167. package/dist/elements/model-switcher.js +1 -1
  168. package/dist/elements/nav.d.ts +1 -0
  169. package/dist/elements/nav.js +1 -0
  170. package/dist/elements/notice.d.ts +1 -0
  171. package/dist/elements/notice.js +1 -0
  172. package/dist/elements/pane-group.d.ts +1 -0
  173. package/dist/elements/pane-group.js +1 -0
  174. package/dist/elements/pane.d.ts +1 -0
  175. package/dist/elements/pane.js +1 -0
  176. package/dist/elements/popover.js +1 -1
  177. package/dist/elements/progress-bar.d.ts +1 -0
  178. package/dist/elements/progress-bar.js +1 -0
  179. package/dist/elements/prompt-dock.d.ts +1 -0
  180. package/dist/elements/prompt-dock.js +1 -0
  181. package/dist/elements/prompt-input.js +1 -1
  182. package/dist/elements/prompt-suggestions.js +1 -1
  183. package/dist/elements/reasoning.js +1 -1
  184. package/dist/elements/register-impl.d.ts +0 -0
  185. package/dist/elements/register.d.ts +13 -0
  186. package/dist/elements/remote.d.ts +1 -0
  187. package/dist/elements/remote.js +1 -0
  188. package/dist/elements/resizable.js +1 -1
  189. package/dist/elements/response-stream.js +2 -2
  190. package/dist/elements/screen.d.ts +1 -0
  191. package/dist/elements/screen.js +1 -0
  192. package/dist/elements/scroll-area.d.ts +1 -0
  193. package/dist/elements/scroll-area.js +1 -0
  194. package/dist/elements/scroll-button.js +1 -1
  195. package/dist/elements/search.d.ts +1 -0
  196. package/dist/elements/search.js +1 -0
  197. package/dist/elements/segmented.d.ts +1 -0
  198. package/dist/elements/segmented.js +1 -0
  199. package/dist/elements/separator.d.ts +1 -0
  200. package/dist/elements/separator.js +1 -0
  201. package/dist/elements/setting-item.d.ts +1 -0
  202. package/dist/elements/setting-item.js +1 -0
  203. package/dist/elements/settings-group.d.ts +1 -0
  204. package/dist/elements/settings-group.js +1 -0
  205. package/dist/elements/skeleton.d.ts +1 -0
  206. package/dist/elements/skeleton.js +1 -0
  207. package/dist/elements/slots.d.ts +176 -0
  208. package/dist/elements/source.js +1 -1
  209. package/dist/elements/status.d.ts +1 -0
  210. package/dist/elements/status.js +1 -0
  211. package/dist/elements/switch.js +1 -1
  212. package/dist/elements/tabs.d.ts +1 -0
  213. package/dist/elements/tabs.js +1 -0
  214. package/dist/elements/tasks.js +1 -1
  215. package/dist/elements/text-shimmer.js +1 -1
  216. package/dist/elements/thinking-bar.js +1 -1
  217. package/dist/elements/toast.js +1 -1
  218. package/dist/elements/tool.js +1 -1
  219. package/dist/elements/tooltip.d.ts +1 -0
  220. package/dist/elements/tooltip.js +1 -0
  221. package/dist/elements/voice-input.js +1 -1
  222. package/dist/elements/voice-output.d.ts +1 -0
  223. package/dist/elements/voice-output.js +1 -0
  224. package/dist/elements.d.ts +733 -58
  225. package/dist/engine-javascript-C1x7zo1_.js +141 -0
  226. package/dist/index.d.ts +3 -2
  227. package/dist/index.js +8256 -5703
  228. package/dist/kai.es.js +1 -0
  229. package/dist/llms/llms-full.txt +1146 -64
  230. package/dist/llms/llms.txt +4 -4
  231. package/dist/mcp.es.js +62 -7
  232. package/dist/primitives/composer-model.d.ts +22 -0
  233. package/dist/primitives/composer-triggers.d.ts +19 -0
  234. package/dist/primitives/create-kai-chat.d.ts +38 -0
  235. package/dist/primitives/toast-store.d.ts +32 -2
  236. package/dist/primitives/use-speech-recognition.d.ts +26 -0
  237. package/dist/react/index.d.ts +1085 -97
  238. package/dist/react/runtime.d.ts +9 -2
  239. package/dist/react/use-kai-chat.d.ts +41 -0
  240. package/dist/react.js +572 -193
  241. package/dist/register-impl-CYWGkgbI.js +141 -0
  242. package/dist/schemas/tasks.schema.json +2 -2
  243. package/dist/state/index.d.ts +4 -0
  244. package/dist/state/messages.d.ts +11 -0
  245. package/dist/state/stream.d.ts +22 -0
  246. package/dist/state/suggestions.d.ts +4 -0
  247. package/dist/state.js +117 -0
  248. package/dist/theme.tokens.css +87 -0
  249. package/dist/types.d.ts +4 -0
  250. package/dist/ui/agent-card.d.ts +61 -0
  251. package/dist/ui/button.d.ts +5 -2
  252. package/dist/ui/card.d.ts +61 -0
  253. package/dist/ui/collapsible.d.ts +15 -1
  254. package/dist/ui/command.d.ts +33 -0
  255. package/dist/ui/dialog.d.ts +42 -0
  256. package/dist/ui/dropdown.d.ts +95 -1
  257. package/dist/ui/editable-label.d.ts +26 -0
  258. package/dist/ui/hover-card.d.ts +13 -1
  259. package/dist/ui/icon.d.ts +18 -0
  260. package/dist/ui/input.d.ts +33 -0
  261. package/dist/ui/kbd.d.ts +21 -0
  262. package/dist/ui/nav.d.ts +62 -0
  263. package/dist/ui/notice.d.ts +31 -0
  264. package/dist/ui/overlay.d.ts +12 -0
  265. package/dist/ui/pane-grid.d.ts +58 -0
  266. package/dist/ui/pane-group.d.ts +75 -0
  267. package/dist/ui/pane.d.ts +78 -0
  268. package/dist/ui/popover.d.ts +11 -1
  269. package/dist/ui/progress-bar.d.ts +23 -0
  270. package/dist/ui/prompt-dock.d.ts +96 -0
  271. package/dist/ui/resizable.d.ts +12 -0
  272. package/dist/ui/scroll-area.d.ts +4 -0
  273. package/dist/ui/segmented.d.ts +28 -0
  274. package/dist/ui/settings-group.d.ts +32 -0
  275. package/dist/ui/skeleton.d.ts +12 -0
  276. package/dist/ui/stat.d.ts +16 -0
  277. package/dist/ui/status.d.ts +13 -0
  278. package/dist/ui/switch.d.ts +8 -1
  279. package/dist/ui/tabs.d.ts +35 -0
  280. package/dist/ui/tooltip.d.ts +19 -1
  281. package/frameworks/react/index.tsx +1042 -157
  282. package/frameworks/react/runtime.tsx +30 -1
  283. package/frameworks/react/use-kai-chat.tsx +87 -0
  284. package/llms-full.txt +1146 -64
  285. package/llms.txt +4 -4
  286. package/package.json +23 -8
  287. package/src/agent-tooling/mcp/manifest.ts +14 -0
  288. package/src/agent-tooling/mcp/tools/debug.ts +10 -6
  289. package/src/agent-tooling/mcp/tools/reference.ts +32 -0
  290. package/src/agent-tooling/mcp/tools/scaffold.ts +30 -1
  291. package/src/components/artifact.tsx +98 -13
  292. package/src/components/attachment-types.ts +13 -0
  293. package/src/components/attachments.tsx +4 -14
  294. package/src/components/card.tsx +109 -15
  295. package/src/components/chain-of-thought.tsx +210 -1
  296. package/src/components/chat-scope-picker.tsx +13 -3
  297. package/src/components/chat-thread.tsx +225 -116
  298. package/src/components/choice-card.tsx +103 -11
  299. package/src/components/coachmark.tsx +245 -0
  300. package/src/components/component-meta.json +691 -102
  301. package/src/components/composer-dom.ts +159 -0
  302. package/src/components/composer-highlight.ts +242 -0
  303. package/src/components/composer-history.ts +82 -0
  304. package/src/components/composer.tsx +979 -0
  305. package/src/components/confirm-card.tsx +54 -1
  306. package/src/components/conversation-item.tsx +69 -7
  307. package/src/components/conversation-list.tsx +134 -37
  308. package/src/components/file-tree.tsx +197 -47
  309. package/src/components/form-widgets.tsx +9 -7
  310. package/src/components/form.tsx +140 -17
  311. package/src/components/image.tsx +3 -5
  312. package/src/components/link-preview.tsx +5 -4
  313. package/src/components/markdown.tsx +5 -2
  314. package/src/components/message.tsx +23 -4
  315. package/src/components/model-switcher.tsx +19 -4
  316. package/src/components/prompt-input.tsx +46 -75
  317. package/src/components/prompt-suggestion.tsx +48 -16
  318. package/src/components/reasoning.tsx +29 -6
  319. package/src/components/response-compare.tsx +31 -2
  320. package/src/components/screen.tsx +176 -0
  321. package/src/components/tasks-card.tsx +342 -21
  322. package/src/components/toast.tsx +119 -31
  323. package/src/components/tool-types.ts +10 -0
  324. package/src/components/tool.tsx +22 -16
  325. package/src/components/voice-input.tsx +102 -19
  326. package/src/components/voice-output.tsx +202 -0
  327. package/src/elements/agent-card.tsx +67 -0
  328. package/src/elements/artifact.tsx +53 -4
  329. package/src/elements/attachments.tsx +1 -0
  330. package/src/elements/avatar.tsx +33 -0
  331. package/src/elements/badge.tsx +32 -0
  332. package/src/elements/button.tsx +126 -0
  333. package/src/elements/card-media.jpg +0 -0
  334. package/src/elements/card.tsx +124 -31
  335. package/src/elements/cards.tsx +81 -7
  336. package/src/elements/chain-of-thought.tsx +77 -32
  337. package/src/elements/chat-scope-picker.tsx +35 -8
  338. package/src/elements/chat-types.ts +2 -2
  339. package/src/elements/chat-workspace.tsx +153 -41
  340. package/src/elements/chat.tsx +45 -23
  341. package/src/elements/choice.tsx +71 -19
  342. package/src/elements/coachmark.tsx +90 -0
  343. package/src/elements/command.tsx +209 -0
  344. package/src/elements/compare.tsx +24 -2
  345. package/src/elements/compiled.css +1 -1
  346. package/src/elements/composer.tsx +151 -0
  347. package/src/elements/confirm-card.tsx +36 -11
  348. package/src/elements/conversation-list.tsx +80 -11
  349. package/src/elements/default-input.tsx +75 -53
  350. package/src/elements/define.tsx +47 -6
  351. package/src/elements/dialog.tsx +98 -0
  352. package/src/elements/disclosure.ts +73 -0
  353. package/src/elements/editable-label.tsx +125 -0
  354. package/src/elements/element-manifest.json +129 -1
  355. package/src/elements/element-meta.json +5562 -1336
  356. package/src/elements/element-types.d.ts +732 -57
  357. package/src/elements/file-tree.tsx +12 -2
  358. package/src/elements/file-upload.tsx +1 -1
  359. package/src/elements/form.tsx +54 -11
  360. package/src/elements/hover-card.tsx +80 -0
  361. package/src/elements/icon-names.json +48 -0
  362. package/src/elements/icon.tsx +41 -0
  363. package/src/elements/input.tsx +203 -0
  364. package/src/elements/kbd.tsx +45 -0
  365. package/src/elements/menu.tsx +217 -0
  366. package/src/elements/message.tsx +58 -11
  367. package/src/elements/model-switcher.tsx +27 -1
  368. package/src/elements/nav.tsx +87 -0
  369. package/src/elements/notice.tsx +53 -0
  370. package/src/elements/pane-group.tsx +119 -0
  371. package/src/elements/pane.tsx +129 -0
  372. package/src/elements/popover.tsx +39 -17
  373. package/src/elements/progress-bar.tsx +39 -0
  374. package/src/elements/prompt-dock.tsx +81 -0
  375. package/src/elements/prompt-input.tsx +112 -63
  376. package/src/elements/prompt-suggestions.tsx +25 -9
  377. package/src/elements/reasoning.tsx +30 -9
  378. package/src/elements/register-impl.ts +38 -3
  379. package/src/elements/register.ts +11 -3
  380. package/src/elements/resizable.tsx +48 -11
  381. package/src/elements/screen.tsx +90 -0
  382. package/src/elements/scroll-area.tsx +33 -0
  383. package/src/elements/search.tsx +180 -0
  384. package/src/elements/segmented.tsx +135 -0
  385. package/src/elements/separator.tsx +36 -0
  386. package/src/elements/setting-item.tsx +50 -0
  387. package/src/elements/settings-group.tsx +37 -0
  388. package/src/elements/skeleton.tsx +45 -0
  389. package/src/elements/slots.ts +607 -0
  390. package/src/elements/source.tsx +13 -6
  391. package/src/elements/status.tsx +42 -0
  392. package/src/elements/switch.tsx +89 -14
  393. package/src/elements/tabs.tsx +95 -0
  394. package/src/elements/tasks.tsx +70 -14
  395. package/src/elements/toast.tsx +17 -2
  396. package/src/elements/tool.tsx +43 -9
  397. package/src/elements/tooltip.tsx +85 -0
  398. package/src/elements/video-poster.jpg +0 -0
  399. package/src/elements/voice-input.tsx +66 -16
  400. package/src/elements/voice-output.tsx +69 -0
  401. package/src/index.ts +5 -2
  402. package/src/primitives/card-schemas/tasks.schema.json +2 -2
  403. package/src/primitives/composer-model.ts +49 -0
  404. package/src/primitives/composer-triggers.ts +35 -0
  405. package/src/primitives/create-kai-chat.ts +66 -0
  406. package/src/primitives/toast-store.ts +43 -2
  407. package/src/primitives/use-speech-recognition.ts +135 -0
  408. package/src/state/index.ts +6 -0
  409. package/src/state/messages.ts +36 -0
  410. package/src/state/stream.ts +92 -0
  411. package/src/state/suggestions.ts +9 -0
  412. package/src/types.ts +4 -0
  413. package/src/ui/agent-card.tsx +212 -0
  414. package/src/ui/button.tsx +16 -5
  415. package/src/ui/card.tsx +245 -0
  416. package/src/ui/collapsible.tsx +30 -4
  417. package/src/ui/command.tsx +99 -0
  418. package/src/ui/dialog.tsx +217 -0
  419. package/src/ui/dropdown.tsx +375 -13
  420. package/src/ui/editable-label.tsx +112 -0
  421. package/src/ui/hover-card.tsx +28 -5
  422. package/src/ui/icon.tsx +110 -0
  423. package/src/ui/input.tsx +132 -0
  424. package/src/ui/kbd.tsx +118 -0
  425. package/src/ui/nav.tsx +282 -0
  426. package/src/ui/notice.tsx +84 -0
  427. package/src/ui/overlay.tsx +108 -2
  428. package/src/ui/pane-grid.tsx +116 -0
  429. package/src/ui/pane-group.tsx +280 -0
  430. package/src/ui/pane.tsx +193 -0
  431. package/src/ui/popover.tsx +16 -2
  432. package/src/ui/progress-bar.tsx +83 -0
  433. package/src/ui/prompt-dock.tsx +145 -0
  434. package/src/ui/resizable.tsx +69 -1
  435. package/src/ui/scroll-area.tsx +14 -3
  436. package/src/ui/segmented.tsx +109 -0
  437. package/src/ui/settings-group.tsx +67 -0
  438. package/src/ui/skeleton.tsx +62 -5
  439. package/src/ui/stat.tsx +41 -0
  440. package/src/ui/status.tsx +48 -0
  441. package/src/ui/switch.tsx +49 -23
  442. package/src/ui/tabs.tsx +168 -0
  443. package/src/ui/tooltip.tsx +52 -8
  444. package/theme.css +113 -0
  445. package/dist/components/slash-command.d.ts +0 -15
  446. package/dist/elements/chunks/Icon-D_Zmwmwh.js +0 -1
  447. package/dist/elements/chunks/action-icons-PVfIvcuG.js +0 -1
  448. package/dist/elements/chunks/attachments-Dp6vqtrc.js +0 -1
  449. package/dist/elements/chunks/button-CKyYD9G4.js +0 -1
  450. package/dist/elements/chunks/card-Cjho-PYk.js +0 -1
  451. package/dist/elements/chunks/chat-thread-RT808iPf.js +0 -1
  452. package/dist/elements/chunks/check-CDYaMYrM.js +0 -1
  453. package/dist/elements/chunks/chevron-down-3SBiq8HF.js +0 -1
  454. package/dist/elements/chunks/chevron-right-k2UFlIVK.js +0 -1
  455. package/dist/elements/chunks/cn-Dogu6PbV.js +0 -1
  456. package/dist/elements/chunks/code-block-BYUmfFSd.js +0 -1
  457. package/dist/elements/chunks/collapsible-BiNwdOn6.js +0 -1
  458. package/dist/elements/chunks/context-18g6oi4J.js +0 -1
  459. package/dist/elements/chunks/conversation-list-D9Y0NVdo.js +0 -1
  460. package/dist/elements/chunks/default-input-CSAoYYMd.js +0 -1
  461. package/dist/elements/chunks/download-D8K0Je8r.js +0 -1
  462. package/dist/elements/chunks/dropdown-bUs2iTia.js +0 -1
  463. package/dist/elements/chunks/external-link-6mA0wxab.js +0 -1
  464. package/dist/elements/chunks/file-text-DnaYk3Dm.js +0 -1
  465. package/dist/elements/chunks/file-tree-CgtRLlxK.js +0 -1
  466. package/dist/elements/chunks/hover-card-CIGw62gP.js +0 -1
  467. package/dist/elements/chunks/link-BlGSeoPy.js +0 -1
  468. package/dist/elements/chunks/loader-DvhR4qa0.js +0 -1
  469. package/dist/elements/chunks/message-CDZwXjhL.js +0 -1
  470. package/dist/elements/chunks/message-feedback-CYx81QiA.js +0 -1
  471. package/dist/elements/chunks/model-switcher-O_tUhoOT.js +0 -1
  472. package/dist/elements/chunks/overlay-DydXYMQW.js +0 -1
  473. package/dist/elements/chunks/prompt-suggestion-ldlWV5kd.js +0 -1
  474. package/dist/elements/chunks/reasoning-2PUnHlmr.js +0 -1
  475. package/dist/elements/chunks/resizable-B5WNbSeb.js +0 -1
  476. package/dist/elements/chunks/star-t9tI-P0q.js +0 -1
  477. package/dist/elements/chunks/store-C6rIooZz.js +0 -1
  478. package/dist/elements/chunks/text-shimmer-uh9V_KUh.js +0 -1
  479. package/dist/elements/chunks/textarea-3nm6Ia9U.js +0 -1
  480. package/dist/elements/chunks/thumbs-up-BBtguGld.js +0 -1
  481. package/dist/elements/chunks/toast-store-D8cL3fWl.js +0 -1
  482. package/dist/elements/chunks/tool-GJ3vwp1U.js +0 -1
  483. package/dist/elements/chunks/tooltip-DwKAz4XR.js +0 -1
  484. package/dist/elements/chunks/triangle-alert-Ct1zt2JL.js +0 -1
  485. package/dist/elements/chunks/use-card-resolution-D-_0Alzy.js +0 -1
  486. package/dist/elements/chunks/video-mVYiM99-.js +0 -1
  487. package/dist/elements/chunks/x-CzxL27Ch.js +0 -1
  488. package/dist/kitn-chat.es.js +0 -1
  489. package/dist/register-JyTglR6m.js +0 -1
  490. package/dist/register-impl-C6wlSsts.js +0 -77
  491. package/src/components/slash-command.tsx +0 -232
  492. package/src/elements/framework-usage.json +0 -588
  493. /package/dist/{kitn-chat-provider.d.ts → kai-provider.d.ts} +0 -0
  494. /package/dist/{kitn-chat-provider.es.js → kai-provider.es.js} +0 -0
@@ -30,8 +30,30 @@ export interface FileTreeFile {
30
30
  language?: string;
31
31
  /** Kind — drives the icon + whether Code applies. */
32
32
  type?: 'html' | 'pdf' | 'image' | 'other';
33
+ /** Lines added vs the base. Rendered as a trailing `+N` stat (success hue,
34
+ * tabular-nums). Only shown when present; omit for a plain file row. */
35
+ additions?: number;
36
+ /** Lines removed vs the base. Rendered as a trailing `-N` stat (error hue). */
37
+ deletions?: number;
38
+ /** Change status vs the base. Drives a small trailing status letter in the
39
+ * conventional VCS hue (added=green, modified=amber, deleted=red,
40
+ * renamed=blue, untracked=muted). Only shown when present. */
41
+ status?: 'added' | 'modified' | 'deleted' | 'renamed' | 'untracked';
33
42
  }
34
43
 
44
+ /** A change status a file can carry. */
45
+ export type FileStatus = NonNullable<FileTreeFile['status']>;
46
+
47
+ /** Per-status letter, accessible word, and tool-hue text class. Conventional VCS
48
+ * colors so a changed-files view reads at a glance. */
49
+ const STATUS_META: Record<FileStatus, { letter: string; label: string; class: string }> = {
50
+ added: { letter: 'A', label: 'added', class: 'text-tool-green' },
51
+ modified: { letter: 'M', label: 'modified', class: 'text-tool-amber' },
52
+ deleted: { letter: 'D', label: 'deleted', class: 'text-tool-red' },
53
+ renamed: { letter: 'R', label: 'renamed', class: 'text-tool-blue' },
54
+ untracked: { letter: 'U', label: 'untracked', class: 'text-muted-foreground' },
55
+ };
56
+
35
57
  /** A folder node in the built tree. */
36
58
  export interface FileTreeFolderNode {
37
59
  kind: 'folder';
@@ -134,6 +156,48 @@ function iconFor(type: FileTreeFile['type']) {
134
156
  }
135
157
  }
136
158
 
159
+ /** Every folder path in the tree (render order) — drives collapse-all/expand-all. */
160
+ function collectFolderPaths(nodes: FileTreeNode[], out: string[] = []): string[] {
161
+ for (const n of nodes) {
162
+ if (n.kind === 'folder') {
163
+ out.push(n.path);
164
+ collectFolderPaths(n.children, out);
165
+ }
166
+ }
167
+ return out;
168
+ }
169
+
170
+ /** Every leaf file in the tree — drives the summary header totals. */
171
+ function collectFiles(nodes: FileTreeNode[], out: FileTreeFile[] = []): FileTreeFile[] {
172
+ for (const n of nodes) {
173
+ if (n.kind === 'folder') collectFiles(n.children, out);
174
+ else out.push(n.file);
175
+ }
176
+ return out;
177
+ }
178
+
179
+ /** Whether a file carries any diff metadata (status / additions / deletions). */
180
+ function hasDiffMeta(file: FileTreeFile): boolean {
181
+ return file.status !== undefined || file.additions !== undefined || file.deletions !== undefined;
182
+ }
183
+
184
+ /**
185
+ * Accessible name for a changed-file row: the file name plus its status and diff
186
+ * stats, e.g. `App.tsx, modified, 3 additions, 1 deletion`. Returns `undefined`
187
+ * when the file has no diff metadata, so a plain row keeps its text content as
188
+ * the accessible name (unchanged behavior).
189
+ */
190
+ function fileAccessibleName(name: string, file: FileTreeFile): string | undefined {
191
+ if (!hasDiffMeta(file)) return undefined;
192
+ const parts: string[] = [];
193
+ if (file.status) parts.push(STATUS_META[file.status].label);
194
+ if (file.additions !== undefined)
195
+ parts.push(`${file.additions} addition${file.additions === 1 ? '' : 's'}`);
196
+ if (file.deletions !== undefined)
197
+ parts.push(`${file.deletions} deletion${file.deletions === 1 ? '' : 's'}`);
198
+ return [name, ...parts].join(', ');
199
+ }
200
+
137
201
  export interface FileTreeProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onSelect'> {
138
202
  /** Flat file list; folders are derived from `/`-delimited paths. */
139
203
  files: FileTreeFile[];
@@ -143,6 +207,10 @@ export interface FileTreeProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>,
143
207
  onSelect?: (path: string, file: FileTreeFile) => void;
144
208
  /** Folder paths expanded by default. When omitted, all folders start open. */
145
209
  defaultExpanded?: string[];
210
+ /** Show a summary header above the tree: the changed-file count, the summed
211
+ * `+additions / -deletions`, and a Collapse-all/Expand-all toggle wired to the
212
+ * folder-expand state. Off by default (no header — unchanged behavior). */
213
+ summary?: boolean;
146
214
  }
147
215
 
148
216
  /**
@@ -157,6 +225,7 @@ export function FileTree(props: FileTreeProps): JSX.Element {
157
225
  'activeFile',
158
226
  'onSelect',
159
227
  'defaultExpanded',
228
+ 'summary',
160
229
  'class',
161
230
  ]);
162
231
 
@@ -196,6 +265,23 @@ export function FileTree(props: FileTreeProps): JSX.Element {
196
265
  }
197
266
  };
198
267
 
268
+ // Summary header derivations (only read when `summary` is set).
269
+ const folderPaths = createMemo(() => collectFolderPaths(tree()));
270
+ const leafFiles = createMemo(() => collectFiles(tree()));
271
+ const fileCount = () => leafFiles().length;
272
+ const totalAdds = () => leafFiles().reduce((sum, f) => sum + (f.additions ?? 0), 0);
273
+ const totalDels = () => leafFiles().reduce((sum, f) => sum + (f.deletions ?? 0), 0);
274
+ const anyOpen = () => folderPaths().some((p) => isOpen(p));
275
+ const collapseAll = () => {
276
+ if (defaultAllOpen()) setClosedSet(new Set<string>(folderPaths()));
277
+ else setOpenSet(new Set<string>());
278
+ };
279
+ const expandAll = () => {
280
+ if (defaultAllOpen()) setClosedSet(new Set<string>());
281
+ else setOpenSet(new Set<string>(folderPaths()));
282
+ };
283
+ const toggleAll = () => (anyOpen() ? collapseAll() : expandAll());
284
+
199
285
  const [focusedPath, setFocusedPath] = createSignal<string | undefined>();
200
286
 
201
287
  const selectFile = (node: FileTreeFileNode) => {
@@ -247,27 +333,59 @@ export function FileTree(props: FileTreeProps): JSX.Element {
247
333
  };
248
334
 
249
335
  return (
250
- <div
251
- role="tree"
252
- aria-label="Files"
253
- class={cn('text-sm select-none', local.class)}
254
- {...rest}
255
- >
256
- <For each={tree()}>
257
- {(node) => (
258
- <TreeNode
259
- node={node}
260
- depth={0}
261
- isOpen={isOpen}
262
- toggle={toggle}
263
- activeFile={() => local.activeFile}
264
- focusedPath={focusedPath}
265
- onSelectFile={selectFile}
266
- onKeyDown={onKeyDown}
267
- />
268
- )}
269
- </For>
270
- </div>
336
+ <>
337
+ <Show when={local.summary}>
338
+ <div
339
+ part="summary"
340
+ class="flex items-center gap-2 px-2 py-1.5 text-xs text-muted-foreground border-b border-border"
341
+ >
342
+ <span class="font-medium text-foreground">
343
+ {fileCount()} {fileCount() === 1 ? 'file' : 'files'} changed
344
+ </span>
345
+ <Show when={totalAdds() > 0}>
346
+ <span class="text-tool-green tabular-nums">+{totalAdds()}</span>
347
+ </Show>
348
+ <Show when={totalDels() > 0}>
349
+ <span class="text-tool-red tabular-nums">-{totalDels()}</span>
350
+ </Show>
351
+ <Show when={folderPaths().length > 0}>
352
+ <button
353
+ type="button"
354
+ class={cn(
355
+ 'ml-auto rounded-md px-1.5 py-0.5 font-medium text-muted-foreground outline-none',
356
+ 'hover:bg-muted hover:text-foreground',
357
+ 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset',
358
+ )}
359
+ aria-label={anyOpen() ? 'Collapse all folders' : 'Expand all folders'}
360
+ onClick={toggleAll}
361
+ >
362
+ {anyOpen() ? 'Collapse all' : 'Expand all'}
363
+ </button>
364
+ </Show>
365
+ </div>
366
+ </Show>
367
+ <div
368
+ role="tree"
369
+ aria-label="Files"
370
+ class={cn('text-sm select-none', local.class)}
371
+ {...rest}
372
+ >
373
+ <For each={tree()}>
374
+ {(node) => (
375
+ <TreeNode
376
+ node={node}
377
+ depth={0}
378
+ isOpen={isOpen}
379
+ toggle={toggle}
380
+ activeFile={() => local.activeFile}
381
+ focusedPath={focusedPath}
382
+ onSelectFile={selectFile}
383
+ onKeyDown={onKeyDown}
384
+ />
385
+ )}
386
+ </For>
387
+ </div>
388
+ </>
271
389
  );
272
390
  }
273
391
 
@@ -300,32 +418,64 @@ function TreeNode(props: TreeNodeProps): JSX.Element {
300
418
  return (
301
419
  <Show
302
420
  when={props.node.kind === 'folder'}
303
- fallback={
304
- <div
305
- role="treeitem"
306
- aria-selected={isActive()}
307
- data-tree-path={props.node.path}
308
- data-tree-kind="file"
309
- tabindex={tabIndex()}
310
- class={cn(
311
- 'flex items-center gap-1.5 rounded-md py-1 pr-2 cursor-pointer outline-none',
312
- 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset',
313
- isActive()
314
- ? 'bg-primary/10 text-foreground font-medium'
315
- : 'text-muted-foreground hover:bg-muted hover:text-foreground',
316
- )}
317
- style={indent()}
318
- onClick={() => props.onSelectFile(props.node as FileTreeFileNode)}
319
- onKeyDown={(e) => props.onKeyDown(e, props.node)}
320
- >
321
- <span class="w-3.5 shrink-0" />
322
- {(() => {
323
- const Icon = iconFor((props.node as FileTreeFileNode).file.type);
324
- return <Icon size={14} class="shrink-0 opacity-70" aria-hidden="true" />;
325
- })()}
326
- <span class="truncate">{node().name}</span>
327
- </div>
328
- }
421
+ fallback={(() => {
422
+ const file = (props.node as FileTreeFileNode).file;
423
+ const ariaName = fileAccessibleName(node().name, file);
424
+ const hasMeta = ariaName !== undefined;
425
+ const status = file.status ? STATUS_META[file.status] : undefined;
426
+ return (
427
+ <div
428
+ role="treeitem"
429
+ aria-selected={isActive()}
430
+ aria-label={ariaName}
431
+ data-tree-path={props.node.path}
432
+ data-tree-kind="file"
433
+ tabindex={tabIndex()}
434
+ class={cn(
435
+ 'flex items-center gap-1.5 rounded-md py-1 pr-2 cursor-pointer outline-none',
436
+ 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset',
437
+ isActive()
438
+ ? 'bg-primary/10 text-foreground font-medium'
439
+ : 'text-muted-foreground hover:bg-muted hover:text-foreground',
440
+ )}
441
+ style={indent()}
442
+ onClick={() => props.onSelectFile(props.node as FileTreeFileNode)}
443
+ onKeyDown={(e) => props.onKeyDown(e, props.node)}
444
+ >
445
+ <span class="w-3.5 shrink-0" />
446
+ {(() => {
447
+ const Icon = iconFor(file.type);
448
+ return <Icon size={14} class="shrink-0 opacity-70" aria-hidden="true" />;
449
+ })()}
450
+ <span class={cn('truncate', hasMeta && 'min-w-0 flex-1')}>{node().name}</span>
451
+ <Show when={hasMeta}>
452
+ <span
453
+ class="ml-auto flex items-center gap-1.5 shrink-0 pl-2 text-xs"
454
+ aria-hidden="true"
455
+ >
456
+ <Show when={file.additions !== undefined}>
457
+ <span part="stat-additions" class="text-tool-green tabular-nums">
458
+ +{file.additions}
459
+ </span>
460
+ </Show>
461
+ <Show when={file.deletions !== undefined}>
462
+ <span part="stat-deletions" class="text-tool-red tabular-nums">
463
+ -{file.deletions}
464
+ </span>
465
+ </Show>
466
+ <Show when={status}>
467
+ <span
468
+ part="status"
469
+ class={cn('w-3 text-center font-mono font-semibold', status!.class)}
470
+ >
471
+ {status!.letter}
472
+ </span>
473
+ </Show>
474
+ </span>
475
+ </Show>
476
+ </div>
477
+ );
478
+ })()}
329
479
  >
330
480
  <div role="none">
331
481
  <div
@@ -1,6 +1,7 @@
1
1
  import { type JSX, For, Show, createSignal } from 'solid-js';
2
2
  import { cn } from '../utils/cn';
3
3
  import { Textarea } from '../ui/textarea';
4
+ import { Input, FIELD_BASE as inputBase } from '../ui/input';
4
5
  import { Star } from 'lucide-solid';
5
6
  import type { FormField } from './form';
6
7
 
@@ -19,9 +20,6 @@ export interface WidgetProps {
19
20
  onBlur: () => void;
20
21
  }
21
22
 
22
- const inputBase =
23
- 'w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:pointer-events-none';
24
-
25
23
  function ariaProps(p: WidgetProps) {
26
24
  return {
27
25
  'aria-required': p.required || undefined,
@@ -52,20 +50,24 @@ export function TextWidget(
52
50
  return 'text';
53
51
  }
54
52
  };
53
+ // Render the bare `Input` control (no label/hint/error): `kai-form`'s FieldRow
54
+ // already supplies the label, description, and inline error around the widget.
55
+ // `Input` owns the field styling (its `FIELD_BASE` is the former `inputBase`),
56
+ // so the rendered input is byte-identical to the old raw `<input>`.
55
57
  return (
56
- <input
58
+ <Input
57
59
  id={props.id}
58
60
  data-control
59
61
  type={inputType()}
60
- class={cn(inputBase, props.invalid && 'border-destructive dark:border-red-400/70')}
61
62
  value={(props.value as string) ?? ''}
62
63
  placeholder={props.placeholder}
64
+ invalid={props.invalid}
63
65
  disabled={props.disabled}
64
66
  minLength={props.field.minLength}
65
67
  maxLength={props.field.maxLength}
66
68
  {...ariaProps(props)}
67
- onInput={(e) => props.onInput(e.currentTarget.value)}
68
- onBlur={props.onBlur}
69
+ onValueInput={(value) => props.onInput(value)}
70
+ onValueChange={() => props.onBlur()}
69
71
  />
70
72
  );
71
73
  }
@@ -8,6 +8,7 @@ import {
8
8
  createMemo,
9
9
  createEffect,
10
10
  on,
11
+ onMount,
11
12
  ErrorBoundary,
12
13
  } from 'solid-js';
13
14
  import { createStore, produce, unwrap } from 'solid-js/store';
@@ -355,6 +356,25 @@ export function buildResult(
355
356
  // The <Form> component.
356
357
  // ─────────────────────────────────────────────────────────────────────────────
357
358
 
359
+ /** Imperative handle exposed via `controllerRef` — surfaces the form's latent
360
+ * capabilities (focus the first/first-invalid control, validate, programmatic
361
+ * send, reset to defaults, dismiss/reopen) so the `<kai-form>` facade can forward
362
+ * them as instance methods. */
363
+ export interface FormController {
364
+ /** Focus the first control, or the first INVALID control after a failed validation. */
365
+ focus(options?: FocusOptions): void;
366
+ /** Run full validation + submit: focus the first invalid field on failure, else emit `submit`. */
367
+ send(): void;
368
+ /** Run client-side validation now and return per-field errors WITHOUT submitting. */
369
+ validate(): { valid: boolean; errors?: Record<string, string> };
370
+ /** Re-seed from each field's `default` and clear errors. */
371
+ reset(): void;
372
+ /** Trigger the dismiss path (emit `dismiss` + collapse to the re-openable stub). */
373
+ dismiss(): void;
374
+ /** Re-open a dismissed card from its stub (emit `reopen`). */
375
+ reopen(): void;
376
+ }
377
+
358
378
  export interface FormProps {
359
379
  /** The form definition (CardEnvelope.data). */
360
380
  data?: FormDefinition;
@@ -370,6 +390,16 @@ export interface FormProps {
370
390
  class?: string;
371
391
  /** When set, render the chromed read-only view instead of the form inputs. */
372
392
  resolution?: CardResolution;
393
+ /** Controlled field values — when set, this wins over internal/uncontrolled state. */
394
+ values?: Record<string, unknown>;
395
+ /** Initial values overlaying the schema defaults (uncontrolled seed). */
396
+ defaultValues?: Record<string, unknown>;
397
+ /** Disable all fields + submit. */
398
+ disabled?: boolean;
399
+ /** Fires on input with the current coerced values + validity (distinct from submit). */
400
+ onValuesChange?: (payload: { values: Record<string, unknown>; valid: boolean }) => void;
401
+ /** Receive the imperative controller once mounted. */
402
+ controllerRef?: (controller: FormController) => void;
373
403
  }
374
404
 
375
405
  const DEFAULT_FORM: FormDefinition = { type: 'object', properties: {} };
@@ -383,7 +413,10 @@ const DEFAULT_FORM: FormDefinition = { type: 'object', properties: {} };
383
413
  */
384
414
  export function Form(props: FormProps): JSX.Element {
385
415
  const merged = mergeProps({ cardId: 'kai-form' }, props);
386
- const [local] = splitProps(merged, ['data', 'cardId', 'heading', 'host', 'hostElement', 'class', 'resolution']);
416
+ const [local] = splitProps(merged, [
417
+ 'data', 'cardId', 'heading', 'host', 'hostElement', 'class', 'resolution',
418
+ 'values', 'defaultValues', 'disabled', 'onValuesChange', 'controllerRef',
419
+ ]);
387
420
 
388
421
  const ctxHost = useCardHost();
389
422
 
@@ -414,7 +447,34 @@ export function Form(props: FormProps): JSX.Element {
414
447
  const [values, setValues] = createStore<Record<string, unknown>>({});
415
448
  const [errors, setErrors] = createStore<Record<string, string>>({});
416
449
 
450
+ // Build the seed value map: schema `default`s, overlaid by `defaultValues`, then
451
+ // (when controlled) the live `values` prop. The controlled prop always wins.
452
+ const seedMap = (d: FormDefinition): Record<string, unknown> => {
453
+ const next: Record<string, unknown> = {};
454
+ for (const [key, field] of Object.entries(d.properties ?? {})) {
455
+ if (field.default !== undefined) next[key] = field.default;
456
+ else if (field.type === 'array') next[key] = [];
457
+ }
458
+ if (local.defaultValues) Object.assign(next, local.defaultValues);
459
+ if (local.values) Object.assign(next, local.values);
460
+ return next;
461
+ };
462
+
417
463
  const seed = (d: FormDefinition): void => {
464
+ const next = seedMap(d);
465
+ setValues(produce((s) => {
466
+ for (const k of Object.keys(s)) delete s[k];
467
+ Object.assign(s, next);
468
+ }));
469
+ setErrors(produce((s) => {
470
+ for (const k of Object.keys(s)) delete s[k];
471
+ }));
472
+ };
473
+
474
+ // Reset to defaults only (ignore the controlled `values`/`defaultValues` overlay):
475
+ // re-seed from each field's schema `default` and clear errors. Used by reset().
476
+ const seedDefaults = (): void => {
477
+ const d = def();
418
478
  const next: Record<string, unknown> = {};
419
479
  for (const [key, field] of Object.entries(d.properties ?? {})) {
420
480
  if (field.default !== undefined) next[key] = field.default;
@@ -432,6 +492,16 @@ export function Form(props: FormProps): JSX.Element {
432
492
  // Reseed whenever a NEW valid definition arrives.
433
493
  createEffect(on(() => local.data, () => { if (envelopeValid().ok) seed(def()); }));
434
494
 
495
+ // Controlled values: when the consumer drives `values`, mirror it into the store
496
+ // (the controlled prop wins over local edits). Deferred so mount's seed runs first.
497
+ createEffect(on(() => local.values, (v) => {
498
+ if (!v) return;
499
+ setValues(produce((s) => {
500
+ for (const k of Object.keys(s)) delete s[k];
501
+ Object.assign(s, v);
502
+ }));
503
+ }, { defer: true }));
504
+
435
505
  // ready + error lifecycle emits.
436
506
  createEffect(on(envelopeValid, (state) => {
437
507
  if (state.ok) emit({ kind: 'ready', cardId: local.cardId });
@@ -450,6 +520,12 @@ export function Form(props: FormProps): JSX.Element {
450
520
  const setField = (key: string, raw: unknown): void => {
451
521
  setValues(key, raw);
452
522
  if (errors[key]) setErrors(key, undefined as unknown as string);
523
+ // Fire the live change signal: current coerced values + validity (distinct from
524
+ // the terminal submit). Validation here is read-only — it doesn't surface errors.
525
+ const snapshot = unwrap(values) as Record<string, unknown>;
526
+ const out = buildResult(def(), snapshot);
527
+ const { valid } = validateForm(def(), snapshot);
528
+ local.onValuesChange?.({ values: out, valid });
453
529
  };
454
530
 
455
531
  const validateField = (key: string): void => {
@@ -462,12 +538,21 @@ export function Form(props: FormProps): JSX.Element {
462
538
  setErrors(key, single.fieldErrors[key]);
463
539
  };
464
540
 
465
- const onSubmit = (e: Event): void => {
466
- e.preventDefault();
467
- if (res.isResolved()) return;
468
- // Capture the <form> synchronously — `e.currentTarget` is nulled out once the
469
- // event has finished dispatching (so it can't be read in a later microtask).
470
- const formEl = e.currentTarget as HTMLElement | null;
541
+ // Resolve the root to query controls inside (the live <form> when called from the
542
+ // submit event, else the host element's shadow root for a programmatic call).
543
+ const queryRoot = (formEl?: HTMLElement | null): ParentNode =>
544
+ formEl?.closest('form') ?? formEl ?? local.hostElement?.shadowRoot ?? document;
545
+
546
+ // Focus the control for a field key (the `[data-control]` inside its `[data-field]`).
547
+ const focusControl = (key: string, root: ParentNode, options?: FocusOptions): void => {
548
+ root.querySelector<HTMLElement>(`[data-field="${cssEscape(key)}"] [data-control]`)?.focus(options);
549
+ };
550
+
551
+ // Run full validation; on failure surface the per-field errors + focus the first
552
+ // invalid control and return false; on success emit `submit` + resolve. Shared by
553
+ // the form's onSubmit handler and the controller's send().
554
+ const runSubmit = (formEl?: HTMLElement | null): boolean => {
555
+ if (res.isResolved()) return false;
471
556
  const snapshot = unwrap(values);
472
557
  const result = validateForm(def(), snapshot as Record<string, unknown>);
473
558
  setErrors(produce((s) => {
@@ -476,18 +561,23 @@ export function Form(props: FormProps): JSX.Element {
476
561
  }));
477
562
  if (!result.valid) {
478
563
  const firstBad = keys().find((k) => result.fieldErrors[k]);
479
- if (firstBad && local.hostElement) {
480
- // Focus the first invalid control (light-DOM query inside shadow root).
481
- queueMicrotask(() => {
482
- const root: ParentNode = formEl?.closest('form') ?? formEl ?? document;
483
- root.querySelector<HTMLElement>(`[data-field="${cssEscape(firstBad)}"] [data-control]`)?.focus();
484
- });
564
+ if (firstBad) {
565
+ const root = queryRoot(formEl);
566
+ queueMicrotask(() => focusControl(firstBad, root));
485
567
  }
486
- return;
568
+ return false;
487
569
  }
488
570
  const out = buildResult(def(), snapshot as Record<string, unknown>);
489
571
  emit({ kind: 'submit', cardId: local.cardId, data: out });
490
572
  res.setLocal({ kind: 'submit', data: out });
573
+ return true;
574
+ };
575
+
576
+ const onSubmit = (e: Event): void => {
577
+ e.preventDefault();
578
+ // Capture the <form> synchronously — `e.currentTarget` is nulled out once the
579
+ // event has finished dispatching (so it can't be read in a later microtask).
580
+ runSubmit(e.currentTarget as HTMLElement | null);
491
581
  };
492
582
 
493
583
  // Dismiss: emit `dismiss` AND optimistically flip to a `dismissed` resolution so
@@ -499,6 +589,39 @@ export function Form(props: FormProps): JSX.Element {
499
589
  };
500
590
  const onReopen = (): void => emit({ kind: 'reopen', cardId: local.cardId });
501
591
 
592
+ const disabled = (): boolean => local.disabled === true;
593
+
594
+ // Imperative controller (Pattern C): hand the facade a handle over the form's
595
+ // latent capabilities. focus targets the first control (or the first INVALID one
596
+ // after a failed validation); validate runs the existing validateForm and surfaces
597
+ // the per-field errors WITHOUT submitting; send runs the same path as the Submit
598
+ // button; reset re-seeds from schema defaults; dismiss/reopen drive the stub.
599
+ onMount(() => {
600
+ local.controllerRef?.({
601
+ focus: (options) => {
602
+ const root = queryRoot();
603
+ const firstBad = keys().find((k) => errors[k]);
604
+ const target = firstBad ?? keys()[0];
605
+ if (target) focusControl(target, root, options);
606
+ },
607
+ send: () => { runSubmit(); },
608
+ validate: () => {
609
+ const snapshot = unwrap(values) as Record<string, unknown>;
610
+ const result = validateForm(def(), snapshot);
611
+ setErrors(produce((s) => {
612
+ for (const k of Object.keys(s)) delete s[k];
613
+ Object.assign(s, result.fieldErrors);
614
+ }));
615
+ return result.valid
616
+ ? { valid: true }
617
+ : { valid: false, errors: result.fieldErrors };
618
+ },
619
+ reset: () => seedDefaults(),
620
+ dismiss: () => onDismiss(),
621
+ reopen: () => onReopen(),
622
+ });
623
+ });
624
+
502
625
  const actions = createMemo(() => def()['x-kai-actions'] ?? []);
503
626
  const submitLabel = () => def()['x-kai-submitLabel'] ?? 'Submit';
504
627
  const dismissible = () => def()['x-kai-dismissible'] === true;
@@ -556,7 +679,7 @@ export function Form(props: FormProps): JSX.Element {
556
679
  </Button>
557
680
  )}
558
681
  </For>
559
- <Button type="submit" form={formId()}>
682
+ <Button type="submit" form={formId()} disabled={disabled()}>
560
683
  {submitLabel()}
561
684
  </Button>
562
685
  </div>
@@ -583,7 +706,7 @@ export function Form(props: FormProps): JSX.Element {
583
706
  inlineMax={inlineMax()}
584
707
  value={() => values[key]}
585
708
  error={() => errors[key]}
586
- disabled={false}
709
+ disabled={disabled()}
587
710
  onInput={(v) => setField(key, v)}
588
711
  onBlur={() => validateField(key)}
589
712
  />
@@ -678,7 +801,7 @@ function FieldRow(props: FieldRowProps): JSX.Element {
678
801
  ['fieldset', 'repeater', 'checkbox-group', 'multiselect', 'radio', 'taglist'].includes(widget());
679
802
 
680
803
  return (
681
- <div class="flex flex-col gap-2 rounded-xl bg-muted/40 p-3.5" data-field={props.fieldKey}>
804
+ <div class="flex flex-col gap-2 rounded-xl bg-surface p-3.5" data-field={props.fieldKey}>
682
805
  <Show when={!isGrouped()}>
683
806
  <label for={id} class="text-sm font-medium text-foreground">
684
807
  {label()}
@@ -1,5 +1,6 @@
1
1
  import { type JSX, splitProps, createSignal, createEffect, onCleanup, Show } from 'solid-js';
2
2
  import { cn } from '../utils/cn';
3
+ import { Skeleton } from '../ui/skeleton';
3
4
 
4
5
  export interface GeneratedImageLike {
5
6
  base64?: string;
@@ -44,13 +45,10 @@ function Image(props: ImageProps) {
44
45
  <Show
45
46
  when={src()}
46
47
  fallback={
47
- <div
48
+ <Skeleton
48
49
  aria-label={local.alt}
49
50
  role="img"
50
- class={cn(
51
- 'h-auto max-w-full animate-pulse overflow-hidden rounded-md bg-muted',
52
- local.class
53
- )}
51
+ class={cn('h-auto max-w-full overflow-hidden', local.class)}
54
52
  />
55
53
  }
56
54
  >
@@ -10,6 +10,7 @@ import {
10
10
  Show,
11
11
  } from 'solid-js';
12
12
  import { cn } from '../utils/cn';
13
+ import { Skeleton } from '../ui/skeleton';
13
14
  import { Link as LinkIcon } from 'lucide-solid';
14
15
  import type { CardEvent } from '../primitives/card-contract';
15
16
  import {
@@ -153,11 +154,11 @@ export function LinkPreview(props: LinkPreviewProps): JSX.Element {
153
154
  >
154
155
  <Show when={loading()}>
155
156
  {/* Skeleton while the bare-URL fetcher resolves. */}
156
- <div class="aspect-[16/9] w-full animate-pulse bg-muted" />
157
+ <Skeleton class="aspect-[16/9] w-full rounded-none" />
157
158
  <div class="space-y-2 p-3">
158
- <div class="h-3 w-1/3 animate-pulse rounded bg-muted" />
159
- <div class="h-4 w-2/3 animate-pulse rounded bg-muted" />
160
- <div class="h-3 w-full animate-pulse rounded bg-muted" />
159
+ <Skeleton class="h-3 w-1/3" />
160
+ <Skeleton class="h-4 w-2/3" />
161
+ <Skeleton class="h-3 w-full" />
161
162
  </div>
162
163
  </Show>
163
164
  <Show when={!loading()}>
@@ -11,6 +11,9 @@ export interface MarkdownProps {
11
11
  id?: string;
12
12
  class?: string;
13
13
  codeTheme?: string;
14
+ /** Optional `::part` name(s) to expose on the rendered root. Lets callers
15
+ * (e.g. the message bubble) surface a styleable part through the shadow. */
16
+ part?: string;
14
17
  }
15
18
 
16
19
  interface ParsedBlock {
@@ -49,13 +52,13 @@ function MarkdownBlock(props: { content: string }) {
49
52
  }
50
53
 
51
54
  function Markdown(props: MarkdownProps) {
52
- const [local] = splitProps(props, ['content', 'id', 'class', 'codeTheme']);
55
+ const [local] = splitProps(props, ['content', 'id', 'class', 'codeTheme', 'part']);
53
56
  const config = useChatConfig();
54
57
  const blockId = () => local.id ?? createUniqueId();
55
58
  const blocks = createMemo(() => parseMarkdownIntoBlocks(local.content));
56
59
 
57
60
  return (
58
- <div class={cn('chat-markdown max-w-none break-words whitespace-normal [&>div:first-child>p:first-child]:mt-0 [&>div:last-child>p:last-child]:mb-0', textClass(config.proseSize()), local.class)}>
61
+ <div part={local.part} class={cn('chat-markdown max-w-none break-words whitespace-normal [&>div:first-child>p:first-child]:mt-0 [&>div:last-child>p:last-child]:mb-0', textClass(config.proseSize()), local.class)}>
59
62
  <For each={blocks()}>
60
63
  {(block) => (
61
64
  <Switch>