@kitn.ai/ui 0.20.1 → 0.22.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 (510) hide show
  1. package/README.md +44 -45
  2. package/dist/components/artifact-card.d.ts +51 -0
  3. package/dist/components/artifact.d.ts +1 -1
  4. package/dist/components/attachments.d.ts +2 -2
  5. package/dist/components/audio-visualizer/index.d.ts +3 -3
  6. package/dist/components/audio-visualizer/labs/lab-choreography.d.ts +59 -0
  7. package/dist/components/audio-visualizer/labs/lab-shaders.d.ts +22 -0
  8. package/dist/components/audio-visualizer/labs/lab-visualizer.d.ts +18 -0
  9. package/dist/components/audio-visualizer/variant-aurora.d.ts +2 -2
  10. package/dist/components/audio-visualizer/variant-bar.d.ts +2 -2
  11. package/dist/components/audio-visualizer/variant-custom.d.ts +2 -2
  12. package/dist/components/audio-visualizer/variant-grid.d.ts +1 -1
  13. package/dist/components/audio-visualizer/variant-radial.d.ts +1 -1
  14. package/dist/components/audio-visualizer/variant-wave.d.ts +1 -1
  15. package/dist/components/card-fallback.d.ts +12 -2
  16. package/dist/components/card-renderer.d.ts +63 -2
  17. package/dist/components/chat-container.d.ts +3 -0
  18. package/dist/components/chat-scope-picker.d.ts +2 -2
  19. package/dist/components/chat-thread.d.ts +77 -18
  20. package/dist/components/choice-card.d.ts +3 -28
  21. package/dist/components/composer-dom.d.ts +1 -1
  22. package/dist/components/composer-highlight.d.ts +1 -1
  23. package/dist/components/composer-history.d.ts +1 -1
  24. package/dist/components/composer.d.ts +1 -1
  25. package/dist/components/confirm-card.d.ts +3 -18
  26. package/dist/components/context.d.ts +8 -4
  27. package/dist/components/conversation-item.d.ts +1 -1
  28. package/dist/components/conversation-list.d.ts +4 -3
  29. package/dist/components/embed.d.ts +3 -3
  30. package/dist/components/file-tree.d.ts +11 -22
  31. package/dist/components/form-widgets.d.ts +1 -1
  32. package/dist/components/form.d.ts +3 -44
  33. package/dist/components/link-preview.d.ts +3 -3
  34. package/dist/components/loader.d.ts +30 -48
  35. package/dist/components/message-skills.d.ts +4 -0
  36. package/dist/components/message.d.ts +84 -21
  37. package/dist/components/model-switcher.d.ts +2 -2
  38. package/dist/components/prompt-input.d.ts +2 -2
  39. package/dist/components/response-compare-types.d.ts +11 -2
  40. package/dist/components/response-compare.d.ts +3 -3
  41. package/dist/components/source.d.ts +12 -3
  42. package/dist/components/tasks-card.d.ts +3 -31
  43. package/dist/components/thread.d.ts +17 -5
  44. package/dist/components/toast.d.ts +2 -2
  45. package/dist/components/tool-classify.d.ts +8 -0
  46. package/dist/components/tool-types.d.ts +29 -1
  47. package/dist/components/tool.d.ts +3 -3
  48. package/dist/components/use-card-resolution.d.ts +1 -1
  49. package/dist/create-tween-COnPwAQN.js +338 -0
  50. package/dist/create-tween-DmTUaXWE.js +341 -0
  51. package/dist/create-tween-qs-jjvNA.js +7 -0
  52. package/dist/custom-elements.json +1726 -463
  53. package/dist/elements/agent-card.js +1 -1
  54. package/dist/elements/artifact.js +1 -1
  55. package/dist/elements/attachments.js +1 -1
  56. package/dist/elements/audio-visualizer.js +1 -1
  57. package/dist/elements/avatar.js +1 -1
  58. package/dist/elements/badge.js +1 -1
  59. package/dist/elements/button.js +1 -1
  60. package/dist/elements/card.js +1 -1
  61. package/dist/elements/cards.js +1 -1
  62. package/dist/elements/chain-of-thought.js +1 -1
  63. package/dist/elements/chat-scope-picker.js +1 -1
  64. package/dist/elements/chat-types.d.ts +70 -10
  65. package/dist/elements/chat-workspace.js +1 -1
  66. package/dist/elements/chat.js +1 -1
  67. package/dist/elements/checkpoint.js +1 -1
  68. package/dist/elements/choice.js +1 -1
  69. package/dist/elements/chunks/{Icon-Tj58ES_d.js → Icon-CHG5z5L7.js} +1 -1
  70. package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-BAtCEEgg.js} +1 -1
  71. package/dist/elements/chunks/arrow-left-CFPTeL1Y.js +1 -0
  72. package/dist/elements/chunks/artifact-fBZOE4SO.js +1 -0
  73. package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-DpYMe5VQ.js} +1 -1
  74. package/dist/elements/chunks/audio-visualizer-DFwzAcuL.js +1 -0
  75. package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-D6zodm6E.js} +1 -1
  76. package/dist/elements/chunks/{button-ZWNif3--.js → button-C42vrGvO.js} +1 -1
  77. package/dist/elements/chunks/card-renderer-DMZsSuvU.js +1 -0
  78. package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
  79. package/dist/elements/chunks/chat-thread-CNNEhol-.js +1 -0
  80. package/dist/elements/chunks/check-7Q3K-iXE.js +1 -0
  81. package/dist/elements/chunks/chevron-down-CpNYLn7E.js +1 -0
  82. package/dist/elements/chunks/chevron-right-DrH85Fha.js +1 -0
  83. package/dist/elements/chunks/choice-card-YoWjpsFV.js +1 -0
  84. package/dist/elements/chunks/circle-C9KRjKYv.js +1 -0
  85. package/dist/elements/chunks/circle-check-Dmi7Y4zw.js +1 -0
  86. package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-QIxXda6k.js} +1 -1
  87. package/dist/elements/chunks/code-block-CqeugReY.js +1 -0
  88. package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-sTv-_5lF.js} +1 -1
  89. package/dist/elements/chunks/{composer-BguS0QL_.js → composer-ldQyeVEB.js} +2 -2
  90. package/dist/elements/chunks/confirm-card-C0RNOwfn.js +1 -0
  91. package/dist/elements/chunks/context-5qgypNHC.js +1 -0
  92. package/dist/elements/chunks/conversation-list-DLzOacXz.js +1 -0
  93. package/dist/elements/chunks/create-tween-V4TXZ93X.js +7 -0
  94. package/dist/elements/chunks/default-input-DeeRSDHc.js +1 -0
  95. package/dist/elements/chunks/define-lE-QvQFn.js +1 -0
  96. package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DZSa9jog.js} +1 -1
  97. package/dist/elements/chunks/{download-Dsbg3ste.js → download-B2ZmnGqW.js} +1 -1
  98. package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-R3B7cSRE.js} +1 -1
  99. package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BmxceY4l.js} +1 -1
  100. package/dist/elements/chunks/embed-BVrtTZPo.js +1 -0
  101. package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-BbdR7YO7.js} +1 -1
  102. package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-D7dijo1A.js} +1 -1
  103. package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-0upJ5jeb.js} +1 -1
  104. package/dist/elements/chunks/{folder-glP5rha4.js → folder-D8z58t7B.js} +1 -1
  105. package/dist/elements/chunks/form-p5BKJNw5.js +1 -0
  106. package/dist/elements/chunks/hover-card-CN9s2md5.js +1 -0
  107. package/dist/elements/chunks/{icon-BLb8beoi.js → icon-CNiKP37E.js} +1 -1
  108. package/dist/elements/chunks/{info-CIsv92JK.js → info-BSnuJiHI.js} +1 -1
  109. package/dist/elements/chunks/{input-Dds1kFyt.js → input-BauDmzxX.js} +1 -1
  110. package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-BlHJaP9b.js} +1 -1
  111. package/dist/elements/chunks/{link-i4r_k7z4.js → link-DEi5iUWC.js} +1 -1
  112. package/dist/elements/chunks/link-preview-BaHyM6W4.js +1 -0
  113. package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-CmJG_Fkq.js} +1 -1
  114. package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-D2jaMpCN.js} +1 -1
  115. package/dist/elements/chunks/message-C5UfXZ2Z.js +1 -0
  116. package/dist/elements/chunks/message-CXWLmmLn.js +1 -0
  117. package/dist/elements/chunks/message-square-hXylwsy0.js +1 -0
  118. package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-C9ytZFMU.js} +1 -1
  119. package/dist/elements/chunks/model-switcher-CivGfD7-.js +1 -0
  120. package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-DJL_sbOm.js} +1 -1
  121. package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CFaB8naM.js} +1 -1
  122. package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-Br5SksfR.js} +1 -1
  123. package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-CMIvtAhU.js} +1 -1
  124. package/dist/elements/chunks/reasoning-DpZcNzLq.js +1 -0
  125. package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-Q8i3T9a_.js} +1 -1
  126. package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-B5IHCBFV.js} +1 -1
  127. package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-eLeydBje.js} +1 -1
  128. package/dist/elements/chunks/scroll-button-DWbz0yA1.js +1 -0
  129. package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-Xic78AD_.js} +1 -1
  130. package/dist/elements/chunks/{settings-B8lmec4D.js → settings-B8H51pAx.js} +1 -1
  131. package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-CsG9eYLe.js} +1 -1
  132. package/dist/elements/chunks/{share-C3lTiYGt.js → share-CNTx0z__.js} +1 -1
  133. package/dist/elements/chunks/skeleton-CLlHTcMI.js +1 -0
  134. package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
  135. package/dist/elements/chunks/source-BoVcU5ok.js +1 -0
  136. package/dist/elements/chunks/{star-CSJtGxgv.js → star-CxQVTVhI.js} +1 -1
  137. package/dist/elements/chunks/{store-CHyW95TP.js → store-CLtS5CUG.js} +1 -1
  138. package/dist/elements/chunks/tasks-card-CzlkLmUt.js +1 -0
  139. package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D5NhpIQ1.js} +1 -1
  140. package/dist/elements/chunks/textarea-x5Uxsmtm.js +1 -0
  141. package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-UobcJnx9.js} +1 -1
  142. package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-BOfs9eXq.js} +1 -1
  143. package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-Be6Dh_dt.js} +1 -1
  144. package/dist/elements/chunks/tooltip-RxMxyCLF.js +1 -0
  145. package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C4yxH2XN.js} +1 -1
  146. package/dist/elements/chunks/use-card-resolution-jva9ehH6.js +1 -0
  147. package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Dx4VRcjS.js} +2 -2
  148. package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-Y1ZTWgEa.js} +1 -1
  149. package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-B9gml9Cg.js} +2 -2
  150. package/dist/elements/chunks/{video-B7vmenoC.js → video-CrPHsytT.js} +1 -1
  151. package/dist/elements/chunks/x-_-OPHept.js +1 -0
  152. package/dist/elements/coachmark.js +1 -1
  153. package/dist/elements/code-block.js +1 -1
  154. package/dist/elements/command.js +1 -1
  155. package/dist/elements/compare.js +1 -1
  156. package/dist/elements/composer.js +1 -1
  157. package/dist/elements/confirm-card.js +1 -1
  158. package/dist/elements/context-meter.js +1 -1
  159. package/dist/elements/conversation-list.js +1 -1
  160. package/dist/elements/default-input.d.ts +34 -7
  161. package/dist/elements/define.d.ts +1 -0
  162. package/dist/elements/dialog.js +1 -1
  163. package/dist/elements/disclosure.d.ts +1 -1
  164. package/dist/elements/editable-label.js +1 -1
  165. package/dist/elements/element-data-types.d.ts +107 -0
  166. package/dist/elements/embed.js +1 -1
  167. package/dist/elements/empty.js +1 -1
  168. package/dist/elements/feedback-bar.js +1 -1
  169. package/dist/elements/file-tree.js +1 -1
  170. package/dist/elements/file-upload.js +1 -1
  171. package/dist/elements/form.js +1 -1
  172. package/dist/elements/hover-card.js +1 -1
  173. package/dist/elements/icon.js +1 -1
  174. package/dist/elements/image.js +1 -1
  175. package/dist/elements/input.js +1 -1
  176. package/dist/elements/kbd.js +1 -1
  177. package/dist/elements/link-preview.js +1 -1
  178. package/dist/elements/loader.js +1 -1
  179. package/dist/elements/markdown.js +1 -1
  180. package/dist/elements/menu.js +1 -1
  181. package/dist/elements/message-skills.js +1 -1
  182. package/dist/elements/message.js +1 -1
  183. package/dist/elements/model-switcher.js +1 -1
  184. package/dist/elements/nav.js +1 -1
  185. package/dist/elements/notice.js +1 -1
  186. package/dist/elements/pane-group.js +1 -1
  187. package/dist/elements/pane.js +1 -1
  188. package/dist/elements/popover.js +1 -1
  189. package/dist/elements/progress-bar.js +1 -1
  190. package/dist/elements/prompt-dock.js +1 -1
  191. package/dist/elements/prompt-input.js +1 -1
  192. package/dist/elements/prompt-suggestions.js +1 -1
  193. package/dist/elements/reasoning.js +1 -1
  194. package/dist/elements/register.d.ts +8 -6
  195. package/dist/elements/remote.js +1 -1
  196. package/dist/elements/resizable.js +1 -1
  197. package/dist/elements/response-stream.js +1 -1
  198. package/dist/elements/screen.js +1 -1
  199. package/dist/elements/scroll-area.js +1 -1
  200. package/dist/elements/scroll-button.js +1 -1
  201. package/dist/elements/search.js +1 -1
  202. package/dist/elements/segmented.js +1 -1
  203. package/dist/elements/separator.js +1 -1
  204. package/dist/elements/setting-item.js +1 -1
  205. package/dist/elements/settings-group.js +1 -1
  206. package/dist/elements/skeleton.js +1 -1
  207. package/dist/elements/slot-text.d.ts +51 -0
  208. package/dist/elements/slots.d.ts +21 -0
  209. package/dist/elements/source.js +1 -1
  210. package/dist/elements/status.js +1 -1
  211. package/dist/elements/switch.js +1 -1
  212. package/dist/elements/tabs.js +1 -1
  213. package/dist/elements/tasks.js +1 -1
  214. package/dist/elements/text-shimmer.js +1 -1
  215. package/dist/elements/thinking-bar.js +1 -1
  216. package/dist/elements/thread.js +1 -1
  217. package/dist/elements/toast.js +1 -1
  218. package/dist/elements/tool.js +1 -1
  219. package/dist/elements/tooltip.js +1 -1
  220. package/dist/elements/validate-messages.d.ts +25 -0
  221. package/dist/elements/voice-input.js +1 -1
  222. package/dist/elements/voice-output.js +1 -1
  223. package/dist/elements.d.ts +2734 -217
  224. package/dist/index.d.ts +163 -125
  225. package/dist/index.js +6572 -5990
  226. package/dist/index.server.d.ts +5 -0
  227. package/dist/index.server.js +5403 -4860
  228. package/dist/kai-provider.es.d.ts +5 -0
  229. package/dist/kai-provider.es.js +79 -77
  230. package/dist/kai.es.d.ts +5 -0
  231. package/dist/kai.es.js +1 -1
  232. package/dist/llms/llms-full.txt +774 -151
  233. package/dist/llms/llms.txt +57 -5
  234. package/dist/mcp.es.js +4756 -782
  235. package/dist/primitives/card-component-types.d.ts +11 -0
  236. package/dist/primitives/card-data-types.d.ts +196 -0
  237. package/dist/primitives/card-host.d.ts +1 -1
  238. package/dist/primitives/card-recovery.d.ts +3 -3
  239. package/dist/primitives/card-registry.d.ts +4 -14
  240. package/dist/primitives/card-resolution.d.ts +1 -1
  241. package/dist/primitives/card-routing.d.ts +1 -1
  242. package/dist/primitives/card-tags.d.ts +11 -0
  243. package/dist/primitives/card-validate-cards.d.ts +57 -0
  244. package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
  245. package/dist/primitives/card-validate-schemas.d.ts +17 -0
  246. package/dist/primitives/card-validate.d.ts +24 -0
  247. package/dist/primitives/create-kai-chat.d.ts +3 -3
  248. package/dist/primitives/embed-providers.d.ts +1 -1
  249. package/dist/primitives/link-preview.d.ts +1 -1
  250. package/dist/primitives/message-feedback.d.ts +2 -2
  251. package/dist/primitives/toast-store.d.ts +1 -1
  252. package/dist/react/index.d.ts +730 -302
  253. package/dist/react/use-kai-chat.d.ts +8 -4
  254. package/dist/react.d.ts +5 -0
  255. package/dist/react.js +418 -317
  256. package/dist/register-impl-AxIlqjo7.js +145 -0
  257. package/dist/remote/host-embed.d.ts +1 -1
  258. package/dist/remote/index.d.ts +2 -2
  259. package/dist/remote/provider-runtime.d.ts +2 -2
  260. package/dist/remote/provider.d.ts +2 -2
  261. package/dist/remote/validate.d.ts +1 -1
  262. package/dist/remote/wire.d.ts +1 -1
  263. package/dist/schemas/artifact.schema.json +92 -0
  264. package/dist/schemas/embed.schema.json +0 -2
  265. package/dist/schemas/from-tool-call.d.ts +48 -0
  266. package/dist/schemas/index.d.ts +48 -0
  267. package/dist/schemas/link.schema.json +0 -2
  268. package/dist/schemas/provider-subsets.d.ts +136 -0
  269. package/dist/schemas/registry.d.ts +140 -0
  270. package/dist/schemas/tool-defs.d.ts +121 -0
  271. package/dist/schemas.d.ts +5 -0
  272. package/dist/schemas.js +717 -0
  273. package/dist/solid-BGYSJyHG.js +22960 -0
  274. package/dist/solid-Dt2nEwfl.js +20194 -0
  275. package/dist/solid.d.ts +66 -0
  276. package/dist/solid.js +261 -0
  277. package/dist/solid.server.d.ts +5 -0
  278. package/dist/solid.server.js +261 -0
  279. package/dist/state/index.d.ts +14 -4
  280. package/dist/state/messages.d.ts +8 -3
  281. package/dist/state/mock.d.ts +46 -0
  282. package/dist/state/parts.d.ts +85 -0
  283. package/dist/state/stream.d.ts +15 -9
  284. package/dist/state.d.ts +5 -0
  285. package/dist/state.js +253 -84
  286. package/dist/ui/action-icons.d.ts +1 -1
  287. package/dist/ui/collapsible.d.ts +11 -6
  288. package/dist/ui/dropdown.d.ts +47 -42
  289. package/dist/ui/pane-group.d.ts +1 -1
  290. package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-B44X69AG.js} +1 -1
  291. package/dist/variant-aurora-BRhV8Flk.js +391 -0
  292. package/dist/variant-aurora-Ds5ou9BH.js +381 -0
  293. package/dist/variant-custom-B20FhtYj.js +170 -0
  294. package/dist/{variant-custom-Dif-IV4u.js → variant-custom-BvA1Schl.js} +1 -1
  295. package/dist/variant-custom-D6Rtvneb.js +200 -0
  296. package/dist/{variant-wave-DbfNdvSi.js → variant-wave-BUQtkryq.js} +1 -1
  297. package/dist/variant-wave-BVYnqy9L.js +218 -0
  298. package/dist/variant-wave-DXC6_LcH.js +208 -0
  299. package/dist/wire/chunk.d.ts +171 -0
  300. package/dist/wire/consume.d.ts +26 -0
  301. package/dist/wire/encode.d.ts +221 -0
  302. package/dist/wire/files.d.ts +116 -0
  303. package/dist/wire/formats/anthropic.d.ts +2 -0
  304. package/dist/wire/formats/openai.d.ts +5 -0
  305. package/dist/wire/index.d.ts +16 -0
  306. package/dist/wire/media-types.d.ts +106 -0
  307. package/dist/wire/read.d.ts +26 -0
  308. package/dist/wire/sink-helpers.d.ts +21 -0
  309. package/dist/wire/sse.d.ts +33 -0
  310. package/dist/wire.d.ts +5 -53
  311. package/dist/wire.js +1126 -0
  312. package/frameworks/react/index.tsx +191 -158
  313. package/frameworks/react/runtime.tsx +21 -1
  314. package/frameworks/react/use-kai-chat.tsx +11 -1
  315. package/llms-full.txt +774 -151
  316. package/llms.txt +57 -5
  317. package/package.json +62 -8
  318. package/src/agent-tooling/archetypes.ts +127 -0
  319. package/src/agent-tooling/integrations/anthropic.ts +436 -0
  320. package/src/agent-tooling/integrations/cloudflare.ts +141 -52
  321. package/src/agent-tooling/integrations/langgraph.ts +80 -13
  322. package/src/agent-tooling/integrations/mastra.ts +161 -12
  323. package/src/agent-tooling/integrations/mock.ts +47 -8
  324. package/src/agent-tooling/integrations/ollama.ts +68 -24
  325. package/src/agent-tooling/integrations/openai.ts +89 -0
  326. package/src/agent-tooling/integrations/openrouter.ts +58 -8
  327. package/src/agent-tooling/integrations/pi.ts +104 -23
  328. package/src/agent-tooling/integrations/pydantic-ai.ts +61 -5
  329. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +120 -13
  330. package/src/agent-tooling/mcp/manifest.ts +206 -25
  331. package/src/agent-tooling/mcp/tools/debug.ts +13 -7
  332. package/src/agent-tooling/mcp/tools/reference.ts +285 -6
  333. package/src/agent-tooling/mcp/tools/scaffold.ts +4152 -661
  334. package/src/agent-tooling/registry.ts +74 -0
  335. package/src/agent-tooling/types.ts +361 -1
  336. package/src/components/artifact-card.tsx +120 -0
  337. package/src/components/audio-visualizer/index.tsx +9 -0
  338. package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
  339. package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
  340. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
  341. package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
  342. package/src/components/card-fallback.tsx +42 -10
  343. package/src/components/card-renderer.tsx +146 -9
  344. package/src/components/chat-container.tsx +4 -0
  345. package/src/components/chat-thread.tsx +128 -52
  346. package/src/components/choice-card.tsx +17 -29
  347. package/src/components/composer.tsx +12 -2
  348. package/src/components/confirm-card.tsx +24 -20
  349. package/src/components/context.tsx +30 -16
  350. package/src/components/conversation-item.tsx +6 -0
  351. package/src/components/conversation-list.tsx +18 -2
  352. package/src/components/file-tree.tsx +11 -22
  353. package/src/components/file-upload.tsx +14 -8
  354. package/src/components/form.tsx +14 -47
  355. package/src/components/loader.tsx +39 -12
  356. package/src/components/message-skills.tsx +4 -0
  357. package/src/components/message.tsx +294 -46
  358. package/src/components/model-switcher.tsx +38 -8
  359. package/src/components/reasoning.tsx +92 -5
  360. package/src/components/response-compare-types.ts +25 -0
  361. package/src/components/response-compare.tsx +4 -9
  362. package/src/components/source.tsx +50 -24
  363. package/src/components/tasks-card.tsx +10 -33
  364. package/src/components/thread.tsx +84 -42
  365. package/src/components/toast.tsx +17 -6
  366. package/src/components/tool-classify.ts +26 -0
  367. package/src/components/tool-types.ts +31 -1
  368. package/src/elements/agent-card.tsx +11 -11
  369. package/src/elements/artifact.tsx +15 -7
  370. package/src/elements/attachments.tsx +6 -4
  371. package/src/elements/avatar.tsx +1 -1
  372. package/src/elements/button.tsx +29 -6
  373. package/src/elements/card.tsx +2 -2
  374. package/src/elements/cards.tsx +109 -14
  375. package/src/elements/chain-of-thought.tsx +14 -10
  376. package/src/elements/chat-scope-picker.tsx +9 -7
  377. package/src/elements/chat-types.ts +59 -6
  378. package/src/elements/chat-workspace.tsx +50 -9
  379. package/src/elements/chat.tsx +90 -3
  380. package/src/elements/choice.tsx +8 -8
  381. package/src/elements/coachmark.tsx +1 -1
  382. package/src/elements/command.tsx +8 -23
  383. package/src/elements/compare.tsx +7 -6
  384. package/src/elements/compiled.css +1 -1
  385. package/src/elements/composer.tsx +8 -8
  386. package/src/elements/confirm-card.tsx +9 -9
  387. package/src/elements/context-meter.tsx +4 -18
  388. package/src/elements/conversation-list.tsx +14 -9
  389. package/src/elements/default-input.tsx +131 -8
  390. package/src/elements/define.tsx +178 -43
  391. package/src/elements/editable-label.tsx +17 -2
  392. package/src/elements/element-data-types.ts +112 -0
  393. package/src/elements/element-meta.json +1624 -306
  394. package/src/elements/element-types.d.ts +2645 -214
  395. package/src/elements/feedback-bar.tsx +1 -1
  396. package/src/elements/file-tree.tsx +1 -1
  397. package/src/elements/file-upload.tsx +1 -1
  398. package/src/elements/form.tsx +14 -6
  399. package/src/elements/input.tsx +1 -1
  400. package/src/elements/kbd.tsx +1 -1
  401. package/src/elements/menu.tsx +40 -32
  402. package/src/elements/message-skills.tsx +7 -9
  403. package/src/elements/message.tsx +200 -32
  404. package/src/elements/model-switcher.tsx +5 -3
  405. package/src/elements/pane.tsx +1 -1
  406. package/src/elements/prompt-dock.tsx +8 -8
  407. package/src/elements/prompt-input.tsx +10 -10
  408. package/src/elements/prompt-suggestions.tsx +7 -4
  409. package/src/elements/reasoning.tsx +1 -1
  410. package/src/elements/register.ts +9 -0
  411. package/src/elements/resizable.globals.d.ts +36 -0
  412. package/src/elements/resizable.tsx +24 -13
  413. package/src/elements/response-stream.tsx +1 -1
  414. package/src/elements/scroll-button.tsx +1 -1
  415. package/src/elements/search.tsx +6 -1
  416. package/src/elements/segmented.tsx +5 -11
  417. package/src/elements/separator.tsx +1 -1
  418. package/src/elements/slot-text.ts +73 -0
  419. package/src/elements/slots.ts +67 -24
  420. package/src/elements/source.tsx +21 -22
  421. package/src/elements/switch.tsx +3 -3
  422. package/src/elements/tasks.tsx +5 -4
  423. package/src/elements/thread.tsx +36 -4
  424. package/src/elements/toast.tsx +3 -2
  425. package/src/elements/tool.tsx +1 -1
  426. package/src/elements/validate-messages.ts +76 -0
  427. package/src/elements/voice-input.tsx +7 -7
  428. package/src/elements/voice-output.tsx +4 -4
  429. package/src/index.ts +145 -6
  430. package/src/primitives/card-component-types.ts +63 -0
  431. package/src/primitives/card-data-types.ts +312 -0
  432. package/src/primitives/card-recovery.ts +13 -2
  433. package/src/primitives/card-registry.tsx +34 -23
  434. package/src/primitives/card-schemas/artifact.schema.json +92 -0
  435. package/src/primitives/card-schemas/embed.schema.json +0 -2
  436. package/src/primitives/card-schemas/link.schema.json +0 -2
  437. package/src/primitives/card-tags.ts +61 -0
  438. package/src/primitives/card-validate-cards.ts +255 -0
  439. package/src/primitives/card-validate-generator.testlib.ts +38 -0
  440. package/src/primitives/card-validate-schemas.ts +58 -0
  441. package/src/primitives/card-validate.ts +48 -20
  442. package/src/primitives/create-tween.ts +54 -2
  443. package/src/primitives/message-feedback.ts +3 -2
  444. package/src/primitives/use-audio-analysis.ts +20 -1
  445. package/src/primitives/use-sequencer.ts +20 -1
  446. package/src/schemas/from-tool-call.ts +151 -0
  447. package/src/schemas/index.ts +340 -0
  448. package/src/schemas/provider-subsets.ts +538 -0
  449. package/src/schemas/registry.ts +355 -0
  450. package/src/schemas/tool-defs.ts +492 -0
  451. package/src/solid.ts +150 -0
  452. package/src/state/index.ts +27 -1
  453. package/src/state/messages.ts +21 -4
  454. package/src/state/mock.ts +211 -0
  455. package/src/state/parts.ts +295 -0
  456. package/src/state/stream.ts +56 -58
  457. package/src/ui/collapsible.tsx +17 -5
  458. package/src/ui/dropdown.tsx +46 -12
  459. package/src/ui/overlay.tsx +10 -4
  460. package/src/wire/chunk.ts +201 -0
  461. package/src/wire/consume.ts +440 -0
  462. package/src/wire/encode.ts +689 -0
  463. package/src/wire/files.ts +342 -0
  464. package/src/wire/formats/anthropic.ts +267 -0
  465. package/src/wire/formats/openai.ts +187 -0
  466. package/src/wire/index.ts +73 -0
  467. package/src/wire/media-types.ts +344 -0
  468. package/src/wire/read.ts +152 -0
  469. package/src/wire/sink-helpers.ts +50 -0
  470. package/src/wire/sse.ts +140 -0
  471. package/dist/create-tween-hvpmSuwW.js +0 -7
  472. package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
  473. package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
  474. package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
  475. package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
  476. package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
  477. package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
  478. package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
  479. package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
  480. package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
  481. package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
  482. package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
  483. package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
  484. package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
  485. package/dist/elements/chunks/default-input-Cqqilg9t.js +0 -1
  486. package/dist/elements/chunks/define-D1din7Ld.js +0 -1
  487. package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
  488. package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
  489. package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
  490. package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
  491. package/dist/elements/chunks/model-switcher-B94YAKsM.js +0 -1
  492. package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
  493. package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
  494. package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
  495. package/dist/elements/chunks/slots--V19HwUb.js +0 -1
  496. package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
  497. package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
  498. package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
  499. package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
  500. package/dist/host-embed.d.ts +0 -33
  501. package/dist/kai-provider.d.ts +0 -2
  502. package/dist/origin.d.ts +0 -5
  503. package/dist/provider-runtime.d.ts +0 -27
  504. package/dist/register-impl-DQyejdfb.js +0 -145
  505. package/dist/validate.d.ts +0 -4
  506. package/dist/version.d.ts +0 -4
  507. package/src/components/component-meta.json +0 -4550
  508. package/src/elements/card-media.jpg +0 -0
  509. package/src/elements/resizable.d.ts +0 -27
  510. package/src/elements/video-poster.jpg +0 -0
@@ -382,6 +382,35 @@ export function ShaderCanvas(props: ShaderCanvasProps): JSX.Element {
382
382
  let drawFrame: ((now: number) => void) | null = null;
383
383
  /** The pending animation frame id, or 0 when the loop is stopped. */
384
384
  let raf = 0;
385
+ /**
386
+ * `cancelAnimationFrame`, captured at SETUP.
387
+ *
388
+ * `stopLoop()` below runs at dispose (via `release()`), and dispose is not
389
+ * guaranteed to happen while the page that mounted this canvas is still
390
+ * standing -- `component-register`'s `disconnectedCallback` defers a
391
+ * microtask, and a test environment tears its DOM globals down in between.
392
+ * A bare `cancelAnimationFrame` there throws from a promise nobody holds, so
393
+ * it surfaces as an unhandled rejection that fails a run in which every test
394
+ * passed. See tests/components/teardown-without-dom-globals.test.tsx.
395
+ *
396
+ * The FUNCTION, not the view. The `const win = window` capture that fixes a
397
+ * bare `document` does nothing here: `window === globalThis` -- measured, in
398
+ * jsdom and in real Chromium/WebKit alike -- and the teardown deletes these
399
+ * keys off that very object, so `win.cancelAnimationFrame` is undefined by
400
+ * the time cleanup runs. It only trades the ReferenceError for a TypeError.
401
+ * `.bind` pins the receiver the WebIDL operation is specified on; Chromium
402
+ * and WebKit both accept a detached call (measured), so the bind is belt and
403
+ * braces against an engine that does not, at zero cost.
404
+ *
405
+ * GUARDED because "setup" for this component is its body, and a server render
406
+ * executes component bodies. Node has no `cancelAnimationFrame` at all, so an
407
+ * unguarded capture here would trade the disposal crash for an SSR crash --
408
+ * measured, not hypothesised. Nothing can be scheduled without
409
+ * `requestAnimationFrame` either, so the no-op fallback is exactly right.
410
+ */
411
+ const cancelFrame = typeof cancelAnimationFrame === 'function'
412
+ ? cancelAnimationFrame.bind(globalThis)
413
+ : () => {};
385
414
 
386
415
  /** The assembled source and the uniform snapshot it was assembled from. */
387
416
  let build: { source: string; uniforms: Record<string, UniformSpec> } | null = null;
@@ -460,7 +489,8 @@ export function ShaderCanvas(props: ShaderCanvasProps): JSX.Element {
460
489
 
461
490
  const stopLoop = () => {
462
491
  if (raf === 0) return;
463
- cancelAnimationFrame(raf);
492
+ // Captured at setup -- see `cancelFrame`. This line runs at dispose.
493
+ cancelFrame(raf);
464
494
  raf = 0;
465
495
  // Bank what was drawn so `iTime` resumes from here, not from zero.
466
496
  elapsedMs += Math.max(0, performance.now() - runStartedAt);
@@ -1,28 +1,60 @@
1
1
  // src/components/card-fallback.tsx
2
- // Rendered by the dispatcher when an envelope's `type` has no registered card.
2
+ // Rendered by the dispatcher when an envelope cannot be rendered: its `type` has no
3
+ // registered card, or its `data` failed HARD validation against the built-in schema
4
+ // for that type (see primitives/card-validate-cards.ts for what "hard" means).
3
5
  // Visual: reuses the Card chrome so it sits naturally in a card stream.
4
- import type { JSX } from 'solid-js';
6
+ import { Show, type JSX } from 'solid-js';
5
7
  import { Card } from './card';
6
8
  import { AlertTriangle } from 'lucide-solid';
7
9
 
8
10
  export interface CardFallbackProps {
9
- /** The unrecognized envelope type, shown to aid debugging. */
11
+ /** The envelope type, shown to aid debugging. */
10
12
  type: string;
11
13
  /** The envelope id (for parity with cards; not displayed). */
12
14
  cardId?: string;
15
+ /**
16
+ * Why this card could not render, when the type WAS known but the data was not
17
+ * usable. Carries the failing field paths verbatim, e.g.
18
+ * `(root).actions: fewer than minItems 1`.
19
+ *
20
+ * Absent means the older case: an unrecognized type. The two are told apart by
21
+ * this prop rather than by a mode flag so a caller cannot ask for the diagnostic
22
+ * and forget to supply the diagnosis.
23
+ */
24
+ reason?: string;
13
25
  }
14
26
 
15
- /** Inert, themed fallback for an unsupported card type. Emits nothing itself
27
+ /** Inert, themed fallback for a card that cannot render. Emits nothing itself:
16
28
  * the dispatcher emits the contract `error` event alongside rendering this. */
17
29
  export function CardFallback(props: CardFallbackProps): JSX.Element {
18
30
  return (
19
31
  <Card>
20
- <div role="alert" class="flex items-center gap-2 p-4 text-sm text-muted-foreground">
21
- <AlertTriangle class="size-4 shrink-0 text-destructive dark:text-red-400" aria-hidden="true" />
22
- <span>
23
- Unsupported card type: <code class="font-mono">{props.type}</code>
24
- </span>
25
- </div>
32
+ <Show
33
+ when={props.reason}
34
+ fallback={
35
+ <div data-card-fallback role="alert" class="flex items-center gap-2 p-4 text-sm text-muted-foreground">
36
+ <AlertTriangle class="size-4 shrink-0 text-destructive dark:text-red-400" aria-hidden="true" />
37
+ <span>
38
+ Unsupported card type: <code class="font-mono">{props.type}</code>
39
+ </span>
40
+ </div>
41
+ }
42
+ >
43
+ {(reason) => (
44
+ <div
45
+ data-card-fallback
46
+ data-card-invalid
47
+ role="alert"
48
+ class="flex items-start gap-2 p-4 text-sm text-muted-foreground"
49
+ >
50
+ <AlertTriangle class="size-4 shrink-0 translate-y-0.5 text-destructive dark:text-red-400" aria-hidden="true" />
51
+ <span class="min-w-0">
52
+ Invalid <code class="font-mono">{props.type}</code> card:{' '}
53
+ <code data-card-invalid-reason class="font-mono break-words">{reason()}</code>
54
+ </span>
55
+ </div>
56
+ )}
57
+ </Show>
26
58
  </Card>
27
59
  );
28
60
  }
@@ -2,17 +2,85 @@
2
2
  // The Solid single-envelope dispatcher: pick the card for envelope.type and render it
3
3
  // with the envelope spread onto its props. Routing uses the ambient CardProvider
4
4
  // (useCardHost). Unknown type → CardFallback + a one-shot contract `error` emit.
5
- import { createMemo, untrack, Show, type JSX } from 'solid-js';
5
+ // Invalid data two-tier validation, mirroring the remote transport; see below.
6
+ import { createEffect, createMemo, untrack, Show, type JSX } from 'solid-js';
6
7
  import { Dynamic } from 'solid-js/web';
7
8
  import type { CardEnvelope } from '../primitives/card-contract';
8
9
  import { useCardHost } from '../primitives/card-host';
9
- import { mergeCardComponents, type CardComponentMap } from '../primitives/card-registry';
10
+ import { BUILTIN_CARD_COMPONENTS, mergeCardComponents, type CardComponentMap } from '../primitives/card-registry';
11
+ import type { JsonSchema } from '../primitives/card-validate';
12
+ import { cardValidationMessage, validateCardData, type CardValidationReport } from '../primitives/card-validate-cards';
10
13
  import { CardFallback } from './card-fallback';
11
14
 
15
+ /**
16
+ * Consumer card schemas by envelope type: `{ 'pricing-table': pricingSchema }`.
17
+ *
18
+ * `object`, NOT `JsonSchema`, and that is measured rather than lazy. A real schema
19
+ * is either imported from a `.json` file — where TypeScript widens `"type": "object"`
20
+ * to `string`, which `JsonSchema`'s literal union rejects — or written out with the
21
+ * `$schema` / `title` / `description` / `additionalProperties` keywords that
22
+ * `JsonSchema` does not describe at all, which excess-property checking rejects.
23
+ * Both are the ordinary case, so the tighter type would make the normal way of
24
+ * supplying a schema a compile error. `src/schemas/index.ts` widens `CardSchema`
25
+ * for exactly this reason and says so at length (lines 92-110 there).
26
+ *
27
+ * The value is handed straight to `validateCardData`, which reads only the keywords
28
+ * `validateAgainstSchema` implements and ignores the rest.
29
+ */
30
+ export type CardSchemaMap = Record<string, object>;
31
+
12
32
  export interface CardRendererProps {
13
33
  envelope: CardEnvelope;
14
34
  /** Add/override type→component entries (merged over the built-ins). */
15
35
  types?: CardComponentMap;
36
+ /**
37
+ * JSON Schemas for the card types this app renders, keyed by `envelope.type`.
38
+ * `createCardRegistry(...).validationSchemas` is exactly this shape.
39
+ *
40
+ * The companion of `types`, and the half that was missing: `types` says WHAT
41
+ * draws a `pricing-table`, `schemas` says what a VALID one looks like. Without it
42
+ * the kit checks its own seven built-ins and leaves the developer's own card —
43
+ * the one the app actually cares about — as the only unvalidated thing on screen.
44
+ *
45
+ * A schema given here WINS over a built-in of the same name, matching
46
+ * `mergeCardComponents`, where the consumer's entry is spread over ours.
47
+ */
48
+ schemas?: CardSchemaMap;
49
+ /**
50
+ * Validate `envelope.data` against the built-in schema for `envelope.type` before
51
+ * rendering. Default `true`.
52
+ *
53
+ * ON IN PRODUCTION TOO, DELIBERATELY. The obvious alternative, stripping the check
54
+ * from production builds, inverts the point: a model emitting a bad shape is a
55
+ * production failure mode, so stripping it means the developer's USERS get the
56
+ * broken card while the developer's laptop looks fine. The cost is the projected
57
+ * schema data for all seven card types, measured by building this package twice
58
+ * (once with the projection stubbed to `{}`) rather than estimated: +834 B gzip on
59
+ * `dist/index.js` and +775 B gzip on the elements register bundle. That is below
60
+ * the noise floor of a package that already ships a Solid runtime and `marked`.
61
+ *
62
+ * Set `false` to opt out. A type with no schema at all — no built-in, and none
63
+ * supplied through `schemas` — is never validated either way.
64
+ */
65
+ validateCards?: boolean;
66
+ }
67
+
68
+ /**
69
+ * True when the consumer registered a schema for this type.
70
+ *
71
+ * Spelled out rather than left to `validateCardData` because it is a GATE, not a
72
+ * lookup: a consumer schema is the one thing that authorises validating a renderer
73
+ * that is not ours. `hasOwnProperty` (not `in`) so a type named `toString` cannot
74
+ * pick up `Object.prototype`, and the `typeof` check so a `{ 'x': null }` entry
75
+ * reads as "not registered" instead of matching and then validating against
76
+ * nothing. The same predicate lives in elements/cards.tsx for `<kai-cards>`; the
77
+ * two dispatchers already duplicate this rule (they compare component identity vs
78
+ * tag identity), so keep them in step.
79
+ */
80
+ export function hasConsumerSchema(schemas: CardSchemaMap | undefined, type: string): boolean {
81
+ if (!schemas || !Object.prototype.hasOwnProperty.call(schemas, type)) return false;
82
+ const s = schemas[type];
83
+ return typeof s === 'object' && s !== null;
16
84
  }
17
85
 
18
86
  export function CardRenderer(props: CardRendererProps): JSX.Element {
@@ -20,12 +88,74 @@ export function CardRenderer(props: CardRendererProps): JSX.Element {
20
88
  const map = createMemo(() => mergeCardComponents(props.types));
21
89
  const entry = createMemo(() => map()[props.envelope.type]);
22
90
 
91
+ // MIRRORS src/remote/provider-runtime.ts:139-147. That transport runs
92
+ // `validateAgainstSchema(renderer.schema, envelope.data)` and, on failure, renders
93
+ // a placeholder and emits `{ kind: 'error', cardId, message }`. This is the same
94
+ // behaviour on the native path, split into two tiers so a card that renders
95
+ // acceptably today is reported without being replaced. Keep the two in step.
96
+ //
97
+ // WHAT AUTHORISES A CHECK IS A SCHEMA THAT DESCRIBES WHAT IS ON SCREEN.
98
+ //
99
+ // `types` lets a consumer replace a built-in type's renderer with their own
100
+ // (`types={{ confirm: MyConfirm }}`), and `confirm.schema.json` describes OUR
101
+ // ConfirmCard's data, not theirs. Validating a replaced renderer's payload against
102
+ // our schema would reject shapes that are correct for the component actually on
103
+ // screen. So OUR schema applies only to OUR component: the identity check is
104
+ // against BUILTIN_CARD_COMPONENTS, the same object `mergeCardComponents` puts in
105
+ // the map when nothing overrode the type, and the same one elements/message.tsx
106
+ // reuses for a non-overridden built-in.
107
+ //
108
+ // A schema the CONSUMER registered is the other way round: they wrote it about
109
+ // their own card, and it is the shape their model was told to emit, so it applies
110
+ // whichever component draws the type. That covers the case the identity check can
111
+ // never reach — a `pricing-table` that is nobody's built-in — and it re-enables
112
+ // the check on an overridden built-in, where the objection was our schema and not
113
+ // the checking.
114
+ const report = createMemo<CardValidationReport | null>(() => {
115
+ if (props.validateCards === false) return null;
116
+ if (
117
+ !hasConsumerSchema(props.schemas, props.envelope.type) &&
118
+ entry() !== BUILTIN_CARD_COMPONENTS[props.envelope.type]
119
+ ) {
120
+ return null;
121
+ }
122
+ // `schemas` is passed even when the gate opened on renderer identity, so a
123
+ // consumer schema for a built-in type wins over ours — `schemaFor`'s rule.
124
+ return validateCardData(props.envelope.type, props.envelope.data, props.schemas as Record<string, JsonSchema> | undefined);
125
+ });
126
+
127
+ // One `error` per distinct problem, both tiers. Keyed on the message rather than
128
+ // on mount because streaming hands this component a NEW envelope object per chunk
129
+ // (that is the documented way to re-render), so an identity-triggered emit would
130
+ // fire once per chunk for one bad card.
131
+ let lastEmitted: string | null = null;
132
+ createEffect(() => {
133
+ const r = report();
134
+ if (!entry()) return; // an unknown TYPE reports itself, once, in UnknownCard
135
+ if (!r || r.ok) {
136
+ lastEmitted = null;
137
+ return;
138
+ }
139
+ const message = cardValidationMessage(r);
140
+ if (message === lastEmitted) return;
141
+ lastEmitted = message;
142
+ host?.emit({ kind: 'error', cardId: props.envelope.id, message });
143
+ });
144
+
23
145
  return (
24
- <Show
25
- when={entry()}
26
- fallback={<UnknownCard envelope={props.envelope} />}
27
- >
28
- {(comp) => <Dynamic component={comp()} envelope={props.envelope} host={host} />}
146
+ <Show when={entry()} fallback={<UnknownCard envelope={props.envelope} />}>
147
+ {(comp) => (
148
+ <Show
149
+ when={report()?.tier === 'hard'}
150
+ // SOFT and valid both take this branch: the card renders exactly as it
151
+ // does today. The soft failure is reported through the effect above and
152
+ // changes nothing on screen, which is the whole point of the tiering.
153
+ fallback={<Dynamic component={comp()} envelope={props.envelope} host={host} />}
154
+ >
155
+ {/* HARD: there is nothing to draw, so name the field instead. */}
156
+ <CardFallback type={props.envelope.type} cardId={props.envelope.id} reason={report()!.summary} />
157
+ </Show>
158
+ )}
29
159
  </Show>
30
160
  );
31
161
  }
@@ -46,7 +176,14 @@ function UnknownCard(props: { envelope: CardEnvelope }): JSX.Element {
46
176
  /** Function sugar: renderCard(env) ≡ <CardRenderer envelope={env} />. */
47
177
  export function renderCard(
48
178
  envelope: CardEnvelope,
49
- opts?: { types?: CardComponentMap },
179
+ opts?: { types?: CardComponentMap; schemas?: CardSchemaMap; validateCards?: boolean },
50
180
  ): JSX.Element {
51
- return <CardRenderer envelope={envelope} types={opts?.types} />;
181
+ return (
182
+ <CardRenderer
183
+ envelope={envelope}
184
+ types={opts?.types}
185
+ schemas={opts?.schemas}
186
+ validateCards={opts?.validateCards}
187
+ />
188
+ );
52
189
  }
@@ -74,6 +74,10 @@ function ChatContainerScrollAnchor(props: ChatContainerScrollAnchorProps) {
74
74
  );
75
75
  }
76
76
 
77
+ /** `ChatContainer` is the exported alias of `ChatContainerRoot`; its props type
78
+ * is aliased too, so the name a consumer imports always has a matching Props. */
79
+ export type ChatContainerProps = ChatContainerRootProps;
80
+
77
81
  export {
78
82
  ChatContainerRoot as ChatContainer,
79
83
  ChatContainerRoot,
@@ -1,4 +1,4 @@
1
- import { createSignal, createEffect, For, Show, onMount } from 'solid-js';
1
+ import { createSignal, createEffect, createMemo, For, Show, onMount } from 'solid-js';
2
2
  import { ChatConfig, useChatConfig } from '../primitives/chat-config';
3
3
  import { type ComposerDoc, normalizeValue, serializeToText } from '../primitives/composer-model';
4
4
  import { ChatContainer, ChatContainerContent, ChatContainerScrollAnchor } from './chat-container';
@@ -11,11 +11,14 @@ import {
11
11
  Context, ContextTrigger, ContextContent, ContextContentHeader,
12
12
  ContextContentBody, ContextContentFooter, ContextInputUsage, ContextOutputUsage,
13
13
  } from './context';
14
- import { DefaultPromptInput } from '../elements/default-input';
14
+ import { DefaultPromptInput, type RejectedAttachment } from '../elements/default-input';
15
+ import type { MediaTypeFilter } from '../wire/media-types';
15
16
  import type { TriggerDef } from './composer';
16
17
  import type { ChatMessage } from '../elements/chat-types';
17
18
  import type { ProseSize } from '../primitives/chat-config';
18
19
  import type { ModelOption } from '../types';
20
+ import type { CardComponentMap } from '../primitives/card-registry';
21
+ import type { CardSchemaMap } from './card-renderer';
19
22
 
20
23
  export interface ChatThreadContextUsage {
21
24
  usedTokens: number;
@@ -29,9 +32,19 @@ export interface ChatThreadProps {
29
32
  /** Extra classes for the thread root (e.g. `h-full`). */
30
33
  class?: string;
31
34
  /** The full message thread to render, newest last. Each entry carries its role,
32
- * content, and optional reasoning/tools/attachments/actions. Set as a JS
33
- * property (`el.messages = [...]`). */
35
+ * ordered `parts`, and optional actions/avatar/feedback. Set as a JS property
36
+ * (`el.messages = [...]`). */
34
37
  messages: ChatMessage[];
38
+ /** Add/override card type -> component entries, forwarded to `CardRenderer`
39
+ * for `card` parts. */
40
+ cardTypes?: CardComponentMap;
41
+ /** JSON Schemas for the card types this app renders, keyed by envelope type,
42
+ * forwarded to `CardRenderer` for `card` parts. The companion of `cardTypes`:
43
+ * that says what DRAWS a card, this says what a VALID one looks like.
44
+ * `createCardRegistry(...).validationSchemas` is exactly this shape. Without it
45
+ * the kit checks its own seven built-ins and leaves your own card type
46
+ * unvalidated. A schema here WINS over a built-in of the same name. */
47
+ cardSchemas?: CardSchemaMap;
35
48
  /** Value of the input. A **string** is controlled (the host owns the text and
36
49
  * updates it on `kai-value-change`). A **ComposerDoc** is a one-time seed that
37
50
  * pre-populates pills; the user then edits freely. Leave unset for uncontrolled. */
@@ -72,8 +85,8 @@ export interface ChatThreadProps {
72
85
  context?: ChatThreadContextUsage;
73
86
  /** Show the scroll-to-bottom button inside the scroll area. Default true. */
74
87
  scrollButton?: boolean;
75
- /** Whether the host has `slot="header-start"` content (left of the title)
76
- * set by the `<kai-chat>` facade so a custom control forces the header open. */
88
+ /** Whether the host has `slot="header-start"` content (left of the title). Set
89
+ * by the `<kai-chat>` facade so a custom control forces the header open. */
77
90
  headerStart?: boolean;
78
91
  /** Whether the host has `slot="header-end"` content (right of the controls). */
79
92
  headerEnd?: boolean;
@@ -87,24 +100,31 @@ export interface ChatThreadProps {
87
100
  // content owns its own data/events — a slotted (light-DOM) node
88
101
  // can't read this component's reactive state. That boundary is
89
102
  // the whole reason `messages` stays a data prop, not a slot.
90
- /** REPLACE full custom header in place of the built-in title/model/context bar. */
103
+ /** REPLACE: full custom header in place of the built-in title/model/context bar. */
91
104
  headerFull?: boolean;
92
- /** INJECT left sidebar column (e.g. a conversation list / your own nav). */
105
+ /** INJECT: left sidebar column (e.g. a conversation list / your own nav). */
93
106
  sidebar?: boolean;
94
- /** REPLACE custom zero-state rendered in the message area while the thread is empty (replaces the empty message list only; the composer and its suggestions still render). */
107
+ /** REPLACE: custom zero-state rendered in the message area while the thread is empty (replaces the empty message list only; the composer and its suggestions still render). */
95
108
  empty?: boolean;
96
- /** REPLACE full custom composer in place of the built-in prompt input. The
109
+ /** REPLACE: full custom composer in place of the built-in prompt input. The
97
110
  * projected content wires its own submit (the data-flow boundary). */
98
111
  composer?: boolean;
99
- /** INJECT accessory row just above the composer (e.g. extra actions). */
112
+ /** INJECT: accessory row just above the composer (e.g. extra actions). */
100
113
  composerActions?: boolean;
101
- /** INJECT footer row below the composer (disclaimers, token meter, …). */
114
+ /** INJECT: footer row below the composer (disclaimers, token meter, …). */
102
115
  footer?: boolean;
116
+ /** Which attachment media types the user may stage, in HTML `accept` syntax
117
+ * (`'image/*,application/pdf'`). Omitted means no filter. Narrowed by what the
118
+ * encoders can actually send — the same string, and the same resolver, as
119
+ * `toOpenAIMessages(msgs, { accept })`. */
120
+ accept?: MediaTypeFilter;
121
+ /** Files the composer refused because `accept` excluded them. */
122
+ onAttachmentsRejected?: (rejected: RejectedAttachment[]) => void;
103
123
  /** Show a Search (Globe) button in the input toolbar; fires a `search` event. */
104
124
  search?: boolean;
105
125
  /** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
106
126
  voice?: boolean;
107
- /** Rich entity triggers each `{ char, kind, items }` opens a caret-anchored
127
+ /** Rich entity triggers. Each `{ char, kind, items }` opens a caret-anchored
108
128
  * menu that inserts an atomic pill (`/` skills, `@` agents/plugins). Set as a
109
129
  * JS property; forwarded to the input. */
110
130
  triggers?: TriggerDef[];
@@ -136,9 +156,48 @@ export interface ChatThreadController {
136
156
  scrollToBottom(behavior?: ScrollBehavior): void;
137
157
  }
138
158
 
159
+ /**
160
+ * THE MESSAGE LIST'S KEY, and the reason it is not the message object. This is
161
+ * the canonical note; `thread.tsx` keys its identical list the same way and
162
+ * points here.
163
+ *
164
+ * `<For>` is REFERENCE-keyed, and a streaming assistant message gets a brand
165
+ * new object identity on every delta — `createAssistantStream` rebuilds it as
166
+ * `{ ...prev[i], parts: next }` because a new reference IS the re-render
167
+ * signal. Keyed on the objects, every chunk therefore looks like an entirely
168
+ * new list: the whole message row is torn down and rebuilt, and everything the
169
+ * user did inside it dies with it. Expanding a tool or reasoning panel
170
+ * mid-stream did nothing at all — the disclosure opened and was discarded
171
+ * microseconds later by the next token. (`MessageBody` keys its PARTS by
172
+ * position for exactly this reason; that fix was dead while its parent kept
173
+ * destroying the subtree above it.)
174
+ *
175
+ * So key on `message.id`, which is stable across the object churn: `<For>` over
176
+ * the id array diffs by string value, a delta produces an identical id list,
177
+ * and no row moves. The row then reads its message through `messages[i()]` —
178
+ * `<For>`'s index accessor, which it keeps current across inserts, removals and
179
+ * moves — so the CONTENT keeps updating through accessors while the DOM stays
180
+ * put.
181
+ *
182
+ * WHY NOT `<Index>`, the other way to survive the churn: `<Index>` keys by
183
+ * POSITION, so a row's local state (an open panel, a half-filled form card)
184
+ * belongs to the slot rather than to the message. Appends and tail truncations
185
+ * (regenerate/edit) are fine, but prepending older turns — load-earlier-history,
186
+ * which every real chat grows — shifts every row, and each open disclosure
187
+ * stays behind with the wrong message. Position IS a valid key inside a
188
+ * message, where the folds only ever append or patch a part in place; it is not
189
+ * one for the message list, where the host owns the array and splices it.
190
+ * Both approaches require the row to read through accessors — that is inherent
191
+ * in keeping a row mounted while its object is replaced, not a cost unique to
192
+ * either.
193
+ *
194
+ * Contract: `id` must be unique per message. It already had to be — the
195
+ * feedback/copy state above this list is keyed by it.
196
+ */
139
197
  export function ChatThread(props: ChatThreadProps) {
140
198
  const outer = useChatConfig();
141
199
  const reveal = () => (props.actionsReveal === 'hover' ? 'hover' : 'always');
200
+ const messageKeys = createMemo(() => props.messages.map((m) => m.id));
142
201
  // Feedback (copy + vote) state lives ABOVE the per-message <For>, so streaming
143
202
  // re-renders (a fresh `messages` array ref per chunk) don't wipe it.
144
203
  // The copy/feedback toasts scope to the chat (this thread's root) so they appear
@@ -261,44 +320,60 @@ export function ChatThread(props: ChatThreadProps) {
261
320
  <Show when={props.empty && props.messages.length === 0}>
262
321
  <slot name="empty" />
263
322
  </Show>
264
- <For each={props.messages}>
265
- {(m) => {
266
- const body = (
267
- <MessageBody
268
- content={m.content}
269
- reasoning={m.reasoning}
270
- tools={m.tools}
271
- attachments={m.attachments}
272
- isUser={m.role === 'user'}
273
- markdown={m.role === 'assistant'}
274
- actions={m.actions}
275
- actionsReveal={reveal()}
276
- activeFeedback={feedback.resolveFeedback(m)}
277
- copied={feedback.isCopied(m.id)}
278
- onAction={(action) => feedback.handleAction(m, action)}
279
- />
280
- );
281
- const rowGroup = reveal() === 'hover' ? 'group ' : '';
282
- return (
283
- <Show
284
- when={m.avatar}
285
- fallback={
286
- <Message class={`${rowGroup}${m.role === 'user' ? 'flex-col items-end' : 'flex-col items-start'}`}>
287
- {body}
288
- </Message>
289
- }
290
- >
291
- {(av) => (
292
- <Message class={rowGroup}>
293
- <MessageAvatar src={av().src ?? ''} alt={av().alt ?? ''} fallback={av().fallback} />
294
- <div class={`flex min-w-0 flex-1 flex-col ${m.role === 'user' ? 'items-end' : 'items-start'}`}>
295
- {body}
296
- </div>
297
- </Message>
298
- )}
299
- </Show>
300
- );
301
- }}
323
+ {/* Keyed by message id (see the note above this component), so a
324
+ streaming delta updates the row instead of replacing it. */}
325
+ <For each={messageKeys()}>
326
+ {(_id, i) => (
327
+ // The row reads its message through <For>'s index accessor,
328
+ // never through a captured value: the row outlives the delta
329
+ // that replaced its object, so every read below has to go
330
+ // through `m()` for the new content to land. <Show> supplies
331
+ // the non-null accessor and covers the frame where a removal
332
+ // has shortened the array.
333
+ <Show when={props.messages[i()]}>
334
+ {(m) => {
335
+ const body = (
336
+ <MessageBody
337
+ parts={m().parts}
338
+ cardTypes={props.cardTypes}
339
+ cardSchemas={props.cardSchemas}
340
+ isUser={m().role === 'user'}
341
+ markdown={m().role === 'assistant'}
342
+ actions={m().actions}
343
+ actionsReveal={reveal()}
344
+ activeFeedback={feedback.resolveFeedback(m())}
345
+ copied={feedback.isCopied(m().id)}
346
+ onAction={(action) => feedback.handleAction(m(), action)}
347
+ />
348
+ );
349
+ const rowGroup = () => (reveal() === 'hover' ? 'group ' : '');
350
+ return (
351
+ // `role` is the SPEAKER, forwarded on BOTH branches —
352
+ // see the same note in thread.tsx. `Message` turns it
353
+ // into `role="article"` + an `aria-label`; without it the
354
+ // row is a bare div chromium prunes from the
355
+ // accessibility tree as "uninteresting".
356
+ <Show
357
+ when={m().avatar}
358
+ fallback={
359
+ <Message role={m().role} class={`${rowGroup()}${m().role === 'user' ? 'flex-col items-end' : 'flex-col items-start'}`}>
360
+ {body}
361
+ </Message>
362
+ }
363
+ >
364
+ {(av) => (
365
+ <Message role={m().role} class={rowGroup()}>
366
+ <MessageAvatar src={av().src ?? ''} alt={av().alt ?? ''} fallback={av().fallback} />
367
+ <div class={`flex min-w-0 flex-1 flex-col ${m().role === 'user' ? 'items-end' : 'items-start'}`}>
368
+ {body}
369
+ </div>
370
+ </Message>
371
+ )}
372
+ </Show>
373
+ );
374
+ }}
375
+ </Show>
376
+ )}
302
377
  </For>
303
378
  <ChatContainerScrollAnchor />
304
379
  </ChatContainerContent>
@@ -325,6 +400,7 @@ export function ChatThread(props: ChatThreadProps) {
325
400
  <DefaultPromptInput
326
401
  value={current()} placeholder={props.placeholder} loading={props.loading === true}
327
402
  suggestions={visibleSuggestions()} attachments={attachments()}
403
+ accept={props.accept} onAttachmentsRejected={props.onAttachmentsRejected}
328
404
  search={props.search === true} voice={props.voice === true}
329
405
  triggers={props.triggers} kindIcons={props.kindIcons}
330
406
  onValueChange={handleChange} onSubmit={handleSubmit} onSuggestionClick={handleSuggestionClick}
@@ -337,7 +413,7 @@ export function ChatThread(props: ChatThreadProps) {
337
413
  </Show>
338
414
  </div>
339
415
  </div>
340
- {/* INJECT footer row below the composer. */}
416
+ {/* INJECT: footer row below the composer. */}
341
417
  <Show when={props.footer}>
342
418
  <div part="footer" class="shrink-0 px-4 pb-3">
343
419
  <div class="mx-auto max-w-3xl text-center text-xs text-muted-foreground"><slot name="footer" /></div>
@@ -24,37 +24,25 @@ import { useCardHost } from '../primitives/card-host';
24
24
  import { Check, X } from 'lucide-solid';
25
25
 
26
26
  // ─────────────────────────────────────────────────────────────────────────────
27
- // Types (choice.schema.json) — see src/primitives/card-schemas/choice.schema.json
27
+ // Types (choice.schema.json) — AUTHORED IN ../primitives/card-data-types.ts.
28
+ // See the note in confirm-card.tsx, or that file's header, for why they left a
29
+ // `.tsx` and why they are `type` aliases. Re-exported here unchanged.
28
30
  // ─────────────────────────────────────────────────────────────────────────────
29
31
 
30
- export interface ChoiceOptionMedia {
31
- image?: string;
32
- imageAlt?: string;
33
- icon?: string;
34
- }
35
-
36
- export interface ChoiceOption {
37
- id: string; // required, unique within the card
38
- label: string; // required
39
- description?: string;
40
- media?: ChoiceOptionMedia;
41
- meta?: string; // trailing freeform text (e.g. price/badge)
42
- recommended?: boolean; // renders a "Recommended" pill
43
- disabled?: boolean; // not selectable; skipped in keyboard nav
44
- payload?: unknown; // echoed back in the emitted action
45
- }
46
-
47
- export type ChoiceAllowOther = boolean | { label?: string; placeholder?: string };
48
-
49
- export interface ChoiceCardData {
50
- prompt?: string; // optional question/body above the options
51
- options: ChoiceOption[]; // 1..N
52
- allowOther?: ChoiceAllowOther; // free-text escape
53
- submitLabel?: string; // label for the Submit button (default 'Submit')
54
- dismissible?: boolean; // show a close affordance that emits `dismiss`
55
- }
56
-
57
- export type ChoiceCardEnvelope = CardEnvelope<'choice', ChoiceCardData>;
32
+ import type {
33
+ ChoiceAllowOther,
34
+ ChoiceCardData,
35
+ ChoiceOption,
36
+ ChoiceOptionMedia,
37
+ } from '../primitives/card-data-types';
38
+
39
+ export type {
40
+ ChoiceAllowOther,
41
+ ChoiceCardData,
42
+ ChoiceCardEnvelope,
43
+ ChoiceOption,
44
+ ChoiceOptionMedia,
45
+ } from '../primitives/card-data-types';
58
46
 
59
47
  export const CHOICE_CARD_TYPE = 'choice' as const;
60
48