@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
@@ -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,12 +8,14 @@ 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';
14
15
  import { cn } from '../utils/cn';
15
16
  import { Button } from '../ui/button';
16
17
  import { Card } from './card';
18
+ import { DismissedStub } from './dismissed-stub';
17
19
  import {
18
20
  validateAgainstSchema,
19
21
  type JsonSchema,
@@ -354,6 +356,25 @@ export function buildResult(
354
356
  // The <Form> component.
355
357
  // ─────────────────────────────────────────────────────────────────────────────
356
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
+
357
378
  export interface FormProps {
358
379
  /** The form definition (CardEnvelope.data). */
359
380
  data?: FormDefinition;
@@ -369,6 +390,16 @@ export interface FormProps {
369
390
  class?: string;
370
391
  /** When set, render the chromed read-only view instead of the form inputs. */
371
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;
372
403
  }
373
404
 
374
405
  const DEFAULT_FORM: FormDefinition = { type: 'object', properties: {} };
@@ -382,7 +413,10 @@ const DEFAULT_FORM: FormDefinition = { type: 'object', properties: {} };
382
413
  */
383
414
  export function Form(props: FormProps): JSX.Element {
384
415
  const merged = mergeProps({ cardId: 'kai-form' }, props);
385
- 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
+ ]);
386
420
 
387
421
  const ctxHost = useCardHost();
388
422
 
@@ -413,7 +447,34 @@ export function Form(props: FormProps): JSX.Element {
413
447
  const [values, setValues] = createStore<Record<string, unknown>>({});
414
448
  const [errors, setErrors] = createStore<Record<string, string>>({});
415
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
+
416
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();
417
478
  const next: Record<string, unknown> = {};
418
479
  for (const [key, field] of Object.entries(d.properties ?? {})) {
419
480
  if (field.default !== undefined) next[key] = field.default;
@@ -431,23 +492,40 @@ export function Form(props: FormProps): JSX.Element {
431
492
  // Reseed whenever a NEW valid definition arrives.
432
493
  createEffect(on(() => local.data, () => { if (envelopeValid().ok) seed(def()); }));
433
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
+
434
505
  // ready + error lifecycle emits.
435
506
  createEffect(on(envelopeValid, (state) => {
436
507
  if (state.ok) emit({ kind: 'ready', cardId: local.cardId });
437
508
  else emit({ kind: 'error', cardId: local.cardId, message: state.message });
438
509
  }));
439
510
 
440
- // Surface the resolved state for host styling.
511
+ // Surface the resolved state for host styling. Only a `submit` resolution is the
512
+ // "submitted" state; a deferred `dismissed` (or `expired`) is not.
441
513
  createEffect(() => {
442
514
  const el = local.hostElement;
443
515
  if (!el) return;
444
- if (res.isResolved()) el.setAttribute('data-kai-resolved', 'submitted');
516
+ if (res.resolution()?.kind === 'submit') el.setAttribute('data-kai-resolved', 'submitted');
445
517
  else el.removeAttribute('data-kai-resolved');
446
518
  });
447
519
 
448
520
  const setField = (key: string, raw: unknown): void => {
449
521
  setValues(key, raw);
450
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 });
451
529
  };
452
530
 
453
531
  const validateField = (key: string): void => {
@@ -460,12 +538,21 @@ export function Form(props: FormProps): JSX.Element {
460
538
  setErrors(key, single.fieldErrors[key]);
461
539
  };
462
540
 
463
- const onSubmit = (e: Event): void => {
464
- e.preventDefault();
465
- if (res.isResolved()) return;
466
- // Capture the <form> synchronously — `e.currentTarget` is nulled out once the
467
- // event has finished dispatching (so it can't be read in a later microtask).
468
- 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;
469
556
  const snapshot = unwrap(values);
470
557
  const result = validateForm(def(), snapshot as Record<string, unknown>);
471
558
  setErrors(produce((s) => {
@@ -474,20 +561,67 @@ export function Form(props: FormProps): JSX.Element {
474
561
  }));
475
562
  if (!result.valid) {
476
563
  const firstBad = keys().find((k) => result.fieldErrors[k]);
477
- if (firstBad && local.hostElement) {
478
- // Focus the first invalid control (light-DOM query inside shadow root).
479
- queueMicrotask(() => {
480
- const root: ParentNode = formEl?.closest('form') ?? formEl ?? document;
481
- root.querySelector<HTMLElement>(`[data-field="${cssEscape(firstBad)}"] [data-control]`)?.focus();
482
- });
564
+ if (firstBad) {
565
+ const root = queryRoot(formEl);
566
+ queueMicrotask(() => focusControl(firstBad, root));
483
567
  }
484
- return;
568
+ return false;
485
569
  }
486
570
  const out = buildResult(def(), snapshot as Record<string, unknown>);
487
571
  emit({ kind: 'submit', cardId: local.cardId, data: out });
488
572
  res.setLocal({ kind: 'submit', data: out });
573
+ return true;
489
574
  };
490
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);
581
+ };
582
+
583
+ // Dismiss: emit `dismiss` AND optimistically flip to a `dismissed` resolution so
584
+ // the form collapses to its re-openable stub immediately.
585
+ const onDismiss = (): void => {
586
+ if (res.isResolved()) return;
587
+ emit({ kind: 'dismiss', cardId: local.cardId });
588
+ res.setLocal({ kind: 'dismissed' });
589
+ };
590
+ const onReopen = (): void => emit({ kind: 'reopen', cardId: local.cardId });
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
+
491
625
  const actions = createMemo(() => def()['x-kai-actions'] ?? []);
492
626
  const submitLabel = () => def()['x-kai-submitLabel'] ?? 'Submit';
493
627
  const dismissible = () => def()['x-kai-dismissible'] === true;
@@ -509,6 +643,12 @@ export function Form(props: FormProps): JSX.Element {
509
643
  return <Card heading={local.heading} errorMessage="The form failed to render." />;
510
644
  }}
511
645
  >
646
+ <Show
647
+ when={!res.isDeferred()}
648
+ fallback={
649
+ <DismissedStub type="form" title={local.heading ?? def().title} onReopen={onReopen} />
650
+ }
651
+ >
512
652
  <Card
513
653
  heading={local.heading ?? def().title}
514
654
  description={def().description}
@@ -522,7 +662,7 @@ export function Form(props: FormProps): JSX.Element {
522
662
  <Button
523
663
  type="button"
524
664
  variant="ghost"
525
- onClick={() => emit({ kind: 'dismiss', cardId: local.cardId })}
665
+ onClick={onDismiss}
526
666
  >
527
667
  Dismiss
528
668
  </Button>
@@ -539,7 +679,7 @@ export function Form(props: FormProps): JSX.Element {
539
679
  </Button>
540
680
  )}
541
681
  </For>
542
- <Button type="submit" form={formId()}>
682
+ <Button type="submit" form={formId()} disabled={disabled()}>
543
683
  {submitLabel()}
544
684
  </Button>
545
685
  </div>
@@ -566,7 +706,7 @@ export function Form(props: FormProps): JSX.Element {
566
706
  inlineMax={inlineMax()}
567
707
  value={() => values[key]}
568
708
  error={() => errors[key]}
569
- disabled={false}
709
+ disabled={disabled()}
570
710
  onInput={(v) => setField(key, v)}
571
711
  onBlur={() => validateField(key)}
572
712
  />
@@ -575,6 +715,7 @@ export function Form(props: FormProps): JSX.Element {
575
715
  </form>
576
716
  </Show>
577
717
  </Card>
718
+ </Show>
578
719
  </ErrorBoundary>
579
720
  </Show>
580
721
  );
@@ -660,7 +801,7 @@ function FieldRow(props: FieldRowProps): JSX.Element {
660
801
  ['fieldset', 'repeater', 'checkbox-group', 'multiselect', 'radio', 'taglist'].includes(widget());
661
802
 
662
803
  return (
663
- <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}>
664
805
  <Show when={!isGrouped()}>
665
806
  <label for={id} class="text-sm font-medium text-foreground">
666
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
  >