@kitn.ai/ui 0.20.0 → 0.21.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 (500) 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 +68 -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-D3Y5qiij.js +338 -0
  50. package/dist/create-tween-DGKY1FDY.js +341 -0
  51. package/dist/create-tween-paho5Rh-.js +7 -0
  52. package/dist/custom-elements.json +1681 -454
  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-C-2LvGDG.js} +1 -1
  70. package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-CaYoImEk.js} +1 -1
  71. package/dist/elements/chunks/arrow-left-Bb46H7w9.js +1 -0
  72. package/dist/elements/chunks/artifact-BBIscOxJ.js +1 -0
  73. package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-BB56zoji.js} +1 -1
  74. package/dist/elements/chunks/audio-visualizer-BxOx4THz.js +1 -0
  75. package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-SxLSZ19R.js} +1 -1
  76. package/dist/elements/chunks/{button-ZWNif3--.js → button-CmkUkz6c.js} +1 -1
  77. package/dist/elements/chunks/card-renderer-Db4ffkWv.js +1 -0
  78. package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
  79. package/dist/elements/chunks/chat-thread-B3Ks-cKr.js +1 -0
  80. package/dist/elements/chunks/check-C8desc_h.js +1 -0
  81. package/dist/elements/chunks/chevron-down-DKtc2HNB.js +1 -0
  82. package/dist/elements/chunks/chevron-right-Dw4GPhwL.js +1 -0
  83. package/dist/elements/chunks/choice-card-Q9cYYDyM.js +1 -0
  84. package/dist/elements/chunks/circle-BeokBsvB.js +1 -0
  85. package/dist/elements/chunks/circle-check-wJSqt859.js +1 -0
  86. package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-D0MEfxOM.js} +1 -1
  87. package/dist/elements/chunks/code-block-C8VzAraE.js +1 -0
  88. package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-Cvj61obE.js} +1 -1
  89. package/dist/elements/chunks/{composer-BguS0QL_.js → composer-D86ztyL0.js} +2 -2
  90. package/dist/elements/chunks/confirm-card-BdyDAGLw.js +1 -0
  91. package/dist/elements/chunks/context-WHDl-xSx.js +1 -0
  92. package/dist/elements/chunks/conversation-list-CdR14_Y4.js +1 -0
  93. package/dist/elements/chunks/create-tween-C_A_B638.js +7 -0
  94. package/dist/elements/chunks/{default-input-Cqqilg9t.js → default-input-CSZqUM81.js} +1 -1
  95. package/dist/elements/chunks/define-BzPnNsXg.js +1 -0
  96. package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DVKsoc1F.js} +1 -1
  97. package/dist/elements/chunks/{download-Dsbg3ste.js → download-BjA54IMJ.js} +1 -1
  98. package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-BjVVHWXH.js} +1 -1
  99. package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BNedmqfi.js} +1 -1
  100. package/dist/elements/chunks/embed-DQsl4XEq.js +1 -0
  101. package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-Di-dw2CU.js} +1 -1
  102. package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-B3sAODCV.js} +1 -1
  103. package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-Dgto-bMn.js} +1 -1
  104. package/dist/elements/chunks/{folder-glP5rha4.js → folder-D6rMadFV.js} +1 -1
  105. package/dist/elements/chunks/form-CILxvhQ7.js +1 -0
  106. package/dist/elements/chunks/hover-card-BtiF4XiU.js +1 -0
  107. package/dist/elements/chunks/{icon-BLb8beoi.js → icon-DKkmze3y.js} +1 -1
  108. package/dist/elements/chunks/{info-CIsv92JK.js → info-CWRjZkuy.js} +1 -1
  109. package/dist/elements/chunks/{input-Dds1kFyt.js → input--QJBK6BA.js} +1 -1
  110. package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-C3zuKh8R.js} +1 -1
  111. package/dist/elements/chunks/{link-i4r_k7z4.js → link-BL5JA7Q0.js} +1 -1
  112. package/dist/elements/chunks/link-preview-CU37bbXh.js +1 -0
  113. package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-XmpyxyRH.js} +1 -1
  114. package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-3Gv5UZ_o.js} +1 -1
  115. package/dist/elements/chunks/message-CnBA2OUt.js +1 -0
  116. package/dist/elements/chunks/message-X5NZbq-W.js +1 -0
  117. package/dist/elements/chunks/message-square-CnpAm_P0.js +1 -0
  118. package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-DwyrUTcM.js} +1 -1
  119. package/dist/elements/chunks/{model-switcher-B94YAKsM.js → model-switcher-CclaJEHt.js} +1 -1
  120. package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-Cepd-KcS.js} +1 -1
  121. package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CdwKjrlu.js} +1 -1
  122. package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-DMVXBD3b.js} +1 -1
  123. package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-Di_WZE_5.js} +1 -1
  124. package/dist/elements/chunks/reasoning-D0jMch5u.js +1 -0
  125. package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-1znbL1Gv.js} +1 -1
  126. package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-DVhhgx8n.js} +1 -1
  127. package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-Bs4bFS1P.js} +1 -1
  128. package/dist/elements/chunks/scroll-button-Bet5NLc4.js +1 -0
  129. package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-C5Gh8qGC.js} +1 -1
  130. package/dist/elements/chunks/{settings-B8lmec4D.js → settings-5vmEFg0K.js} +1 -1
  131. package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-BWLB38kz.js} +1 -1
  132. package/dist/elements/chunks/{share-C3lTiYGt.js → share-BZ3G_DaB.js} +1 -1
  133. package/dist/elements/chunks/skeleton-DafPJQTp.js +1 -0
  134. package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
  135. package/dist/elements/chunks/source-D_qj_6uJ.js +1 -0
  136. package/dist/elements/chunks/{star-CSJtGxgv.js → star-Cuv7j2IE.js} +1 -1
  137. package/dist/elements/chunks/{store-CHyW95TP.js → store-C1StPHfW.js} +1 -1
  138. package/dist/elements/chunks/tasks-card-EyoBZfpA.js +1 -0
  139. package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D9gRSh34.js} +1 -1
  140. package/dist/elements/chunks/textarea-DuhtHfcm.js +1 -0
  141. package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-B9hwv43w.js} +1 -1
  142. package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-Bm3DUEDe.js} +1 -1
  143. package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-CDtW7L0z.js} +1 -1
  144. package/dist/elements/chunks/tooltip-eB226mGK.js +1 -0
  145. package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C1SNzTqK.js} +1 -1
  146. package/dist/elements/chunks/use-card-resolution-YZh2MvI5.js +1 -0
  147. package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Cj1F7D0l.js} +2 -2
  148. package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-T9gpt7QE.js} +1 -1
  149. package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-CxU9z-TE.js} +2 -2
  150. package/dist/elements/chunks/{video-B7vmenoC.js → video-CK4R7Rus.js} +1 -1
  151. package/dist/elements/chunks/x-D1qx0aLd.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 +7 -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 +2 -2
  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/slots.d.ts +16 -0
  208. package/dist/elements/source.js +1 -1
  209. package/dist/elements/status.js +1 -1
  210. package/dist/elements/switch.js +1 -1
  211. package/dist/elements/tabs.js +1 -1
  212. package/dist/elements/tasks.js +1 -1
  213. package/dist/elements/text-shimmer.js +1 -1
  214. package/dist/elements/thinking-bar.js +1 -1
  215. package/dist/elements/thread.js +1 -1
  216. package/dist/elements/toast.js +1 -1
  217. package/dist/elements/tool.js +1 -1
  218. package/dist/elements/tooltip.js +1 -1
  219. package/dist/elements/validate-messages.d.ts +25 -0
  220. package/dist/elements/voice-input.js +1 -1
  221. package/dist/elements/voice-output.js +1 -1
  222. package/dist/elements.d.ts +2725 -214
  223. package/dist/index.d.ts +163 -125
  224. package/dist/index.js +6336 -5802
  225. package/dist/index.server.d.ts +5 -0
  226. package/dist/index.server.js +16913 -0
  227. package/dist/kai-provider.es.d.ts +5 -0
  228. package/dist/kai-provider.es.js +79 -77
  229. package/dist/kai.es.d.ts +5 -0
  230. package/dist/kai.es.js +1 -1
  231. package/dist/llms/llms-full.txt +762 -148
  232. package/dist/llms/llms.txt +57 -5
  233. package/dist/mcp.es.js +4010 -770
  234. package/dist/primitives/card-component-types.d.ts +11 -0
  235. package/dist/primitives/card-data-types.d.ts +196 -0
  236. package/dist/primitives/card-host.d.ts +1 -1
  237. package/dist/primitives/card-recovery.d.ts +3 -3
  238. package/dist/primitives/card-registry.d.ts +4 -14
  239. package/dist/primitives/card-resolution.d.ts +1 -1
  240. package/dist/primitives/card-routing.d.ts +1 -1
  241. package/dist/primitives/card-tags.d.ts +11 -0
  242. package/dist/primitives/card-validate-cards.d.ts +57 -0
  243. package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
  244. package/dist/primitives/card-validate-schemas.d.ts +17 -0
  245. package/dist/primitives/card-validate.d.ts +24 -0
  246. package/dist/primitives/create-kai-chat.d.ts +3 -3
  247. package/dist/primitives/embed-providers.d.ts +1 -1
  248. package/dist/primitives/link-preview.d.ts +1 -1
  249. package/dist/primitives/message-feedback.d.ts +2 -2
  250. package/dist/primitives/toast-store.d.ts +1 -1
  251. package/dist/react/index.d.ts +717 -299
  252. package/dist/react/use-kai-chat.d.ts +8 -4
  253. package/dist/react.d.ts +5 -0
  254. package/dist/react.js +417 -316
  255. package/dist/register-impl-bzrZ44ft.js +145 -0
  256. package/dist/remote/host-embed.d.ts +1 -1
  257. package/dist/remote/index.d.ts +2 -2
  258. package/dist/remote/provider-runtime.d.ts +2 -2
  259. package/dist/remote/provider.d.ts +2 -2
  260. package/dist/remote/validate.d.ts +1 -1
  261. package/dist/remote/wire.d.ts +1 -1
  262. package/dist/schemas/artifact.schema.json +92 -0
  263. package/dist/schemas/embed.schema.json +0 -2
  264. package/dist/schemas/from-tool-call.d.ts +48 -0
  265. package/dist/schemas/index.d.ts +48 -0
  266. package/dist/schemas/link.schema.json +0 -2
  267. package/dist/schemas/provider-subsets.d.ts +136 -0
  268. package/dist/schemas/registry.d.ts +140 -0
  269. package/dist/schemas/tool-defs.d.ts +121 -0
  270. package/dist/schemas.d.ts +5 -0
  271. package/dist/schemas.js +717 -0
  272. package/dist/solid-BCJUQqwS.js +22750 -0
  273. package/dist/solid-BdENBcEq.js +20108 -0
  274. package/dist/solid.d.ts +66 -0
  275. package/dist/solid.js +261 -0
  276. package/dist/solid.server.d.ts +5 -0
  277. package/dist/solid.server.js +261 -0
  278. package/dist/state/index.d.ts +14 -4
  279. package/dist/state/messages.d.ts +8 -3
  280. package/dist/state/mock.d.ts +46 -0
  281. package/dist/state/parts.d.ts +85 -0
  282. package/dist/state/stream.d.ts +15 -9
  283. package/dist/state.d.ts +5 -0
  284. package/dist/state.js +253 -84
  285. package/dist/ui/action-icons.d.ts +1 -1
  286. package/dist/ui/collapsible.d.ts +11 -6
  287. package/dist/ui/dropdown.d.ts +47 -42
  288. package/dist/ui/pane-group.d.ts +1 -1
  289. package/dist/variant-aurora-1wMcXRUS.js +381 -0
  290. package/dist/variant-aurora-BKHHiOgL.js +391 -0
  291. package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-DOb_o4pC.js} +1 -1
  292. package/dist/{variant-custom-Dif-IV4u.js → variant-custom-CJGB0Kbw.js} +1 -1
  293. package/dist/variant-custom-Co0CkXJF.js +200 -0
  294. package/dist/variant-custom-DWSSgmd2.js +170 -0
  295. package/dist/variant-wave-C3-45cL6.js +218 -0
  296. package/dist/{variant-wave-DbfNdvSi.js → variant-wave-CEODq-9q.js} +1 -1
  297. package/dist/variant-wave-jdXBnCUZ.js +208 -0
  298. package/dist/wire/chunk.d.ts +171 -0
  299. package/dist/wire/consume.d.ts +26 -0
  300. package/dist/wire/encode.d.ts +148 -0
  301. package/dist/wire/formats/anthropic.d.ts +2 -0
  302. package/dist/wire/formats/openai.d.ts +5 -0
  303. package/dist/wire/index.d.ts +14 -0
  304. package/dist/wire/read.d.ts +26 -0
  305. package/dist/wire/sink-helpers.d.ts +21 -0
  306. package/dist/wire/sse.d.ts +33 -0
  307. package/dist/wire.d.ts +5 -53
  308. package/dist/wire.js +898 -0
  309. package/frameworks/react/index.tsx +183 -154
  310. package/frameworks/react/runtime.tsx +21 -1
  311. package/frameworks/react/use-kai-chat.tsx +11 -1
  312. package/llms-full.txt +762 -148
  313. package/llms.txt +57 -5
  314. package/package.json +65 -7
  315. package/src/agent-tooling/archetypes.ts +96 -0
  316. package/src/agent-tooling/integrations/anthropic.ts +412 -0
  317. package/src/agent-tooling/integrations/cloudflare.ts +136 -52
  318. package/src/agent-tooling/integrations/langgraph.ts +67 -13
  319. package/src/agent-tooling/integrations/mastra.ts +120 -12
  320. package/src/agent-tooling/integrations/mock.ts +43 -8
  321. package/src/agent-tooling/integrations/ollama.ts +61 -24
  322. package/src/agent-tooling/integrations/openai.ts +87 -0
  323. package/src/agent-tooling/integrations/openrouter.ts +56 -8
  324. package/src/agent-tooling/integrations/pi.ts +79 -23
  325. package/src/agent-tooling/integrations/pydantic-ai.ts +24 -2
  326. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +84 -13
  327. package/src/agent-tooling/mcp/manifest.ts +206 -25
  328. package/src/agent-tooling/mcp/tools/debug.ts +13 -7
  329. package/src/agent-tooling/mcp/tools/reference.ts +285 -6
  330. package/src/agent-tooling/mcp/tools/scaffold.ts +3345 -675
  331. package/src/agent-tooling/registry.ts +17 -0
  332. package/src/agent-tooling/types.ts +229 -1
  333. package/src/components/artifact-card.tsx +120 -0
  334. package/src/components/audio-visualizer/index.tsx +9 -0
  335. package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
  336. package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
  337. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
  338. package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
  339. package/src/components/card-fallback.tsx +42 -10
  340. package/src/components/card-renderer.tsx +146 -9
  341. package/src/components/chat-container.tsx +4 -0
  342. package/src/components/chat-thread.tsx +118 -51
  343. package/src/components/choice-card.tsx +17 -29
  344. package/src/components/composer.tsx +12 -2
  345. package/src/components/confirm-card.tsx +24 -20
  346. package/src/components/context.tsx +12 -4
  347. package/src/components/conversation-list.tsx +18 -2
  348. package/src/components/file-tree.tsx +11 -22
  349. package/src/components/file-upload.tsx +14 -8
  350. package/src/components/form.tsx +14 -47
  351. package/src/components/loader.tsx +39 -12
  352. package/src/components/message-skills.tsx +4 -0
  353. package/src/components/message.tsx +294 -46
  354. package/src/components/reasoning.tsx +92 -5
  355. package/src/components/response-compare-types.ts +25 -0
  356. package/src/components/response-compare.tsx +4 -9
  357. package/src/components/source.tsx +50 -24
  358. package/src/components/tasks-card.tsx +10 -33
  359. package/src/components/thread.tsx +84 -42
  360. package/src/components/toast.tsx +11 -5
  361. package/src/components/tool-classify.ts +26 -0
  362. package/src/components/tool-types.ts +31 -1
  363. package/src/elements/agent-card.tsx +11 -11
  364. package/src/elements/artifact.tsx +15 -7
  365. package/src/elements/attachments.tsx +6 -4
  366. package/src/elements/avatar.tsx +1 -1
  367. package/src/elements/button.tsx +3 -3
  368. package/src/elements/card.tsx +2 -2
  369. package/src/elements/cards.tsx +109 -14
  370. package/src/elements/chain-of-thought.tsx +14 -10
  371. package/src/elements/chat-scope-picker.tsx +9 -7
  372. package/src/elements/chat-types.ts +59 -6
  373. package/src/elements/chat-workspace.tsx +50 -9
  374. package/src/elements/chat.tsx +64 -3
  375. package/src/elements/choice.tsx +8 -8
  376. package/src/elements/coachmark.tsx +1 -1
  377. package/src/elements/command.tsx +8 -23
  378. package/src/elements/compare.tsx +7 -6
  379. package/src/elements/compiled.css +1 -1
  380. package/src/elements/composer.tsx +8 -8
  381. package/src/elements/confirm-card.tsx +9 -9
  382. package/src/elements/context-meter.tsx +4 -18
  383. package/src/elements/conversation-list.tsx +14 -9
  384. package/src/elements/default-input.tsx +3 -3
  385. package/src/elements/define.tsx +178 -43
  386. package/src/elements/editable-label.tsx +17 -2
  387. package/src/elements/element-data-types.ts +112 -0
  388. package/src/elements/element-meta.json +1596 -304
  389. package/src/elements/element-types.d.ts +2636 -211
  390. package/src/elements/feedback-bar.tsx +1 -1
  391. package/src/elements/file-tree.tsx +1 -1
  392. package/src/elements/file-upload.tsx +1 -1
  393. package/src/elements/form.tsx +14 -6
  394. package/src/elements/input.tsx +1 -1
  395. package/src/elements/kbd.tsx +1 -1
  396. package/src/elements/menu.tsx +10 -29
  397. package/src/elements/message-skills.tsx +7 -9
  398. package/src/elements/message.tsx +200 -32
  399. package/src/elements/model-switcher.tsx +5 -3
  400. package/src/elements/pane.tsx +1 -1
  401. package/src/elements/prompt-dock.tsx +8 -8
  402. package/src/elements/prompt-input.tsx +10 -10
  403. package/src/elements/prompt-suggestions.tsx +7 -4
  404. package/src/elements/reasoning.tsx +1 -1
  405. package/src/elements/register.ts +9 -0
  406. package/src/elements/resizable.globals.d.ts +36 -0
  407. package/src/elements/resizable.tsx +24 -13
  408. package/src/elements/response-stream.tsx +1 -1
  409. package/src/elements/scroll-button.tsx +1 -1
  410. package/src/elements/search.tsx +6 -1
  411. package/src/elements/segmented.tsx +5 -11
  412. package/src/elements/separator.tsx +1 -1
  413. package/src/elements/slots.ts +57 -24
  414. package/src/elements/source.tsx +21 -22
  415. package/src/elements/switch.tsx +3 -3
  416. package/src/elements/tasks.tsx +5 -4
  417. package/src/elements/thread.tsx +36 -4
  418. package/src/elements/toast.tsx +3 -2
  419. package/src/elements/tool.tsx +1 -1
  420. package/src/elements/validate-messages.ts +76 -0
  421. package/src/elements/voice-input.tsx +7 -7
  422. package/src/elements/voice-output.tsx +4 -4
  423. package/src/index.ts +141 -6
  424. package/src/primitives/card-component-types.ts +63 -0
  425. package/src/primitives/card-data-types.ts +312 -0
  426. package/src/primitives/card-recovery.ts +13 -2
  427. package/src/primitives/card-registry.tsx +34 -23
  428. package/src/primitives/card-schemas/artifact.schema.json +92 -0
  429. package/src/primitives/card-schemas/embed.schema.json +0 -2
  430. package/src/primitives/card-schemas/link.schema.json +0 -2
  431. package/src/primitives/card-tags.ts +61 -0
  432. package/src/primitives/card-validate-cards.ts +255 -0
  433. package/src/primitives/card-validate-generator.testlib.ts +38 -0
  434. package/src/primitives/card-validate-schemas.ts +58 -0
  435. package/src/primitives/card-validate.ts +48 -20
  436. package/src/primitives/create-tween.ts +54 -2
  437. package/src/primitives/message-feedback.ts +3 -2
  438. package/src/primitives/use-audio-analysis.ts +20 -1
  439. package/src/primitives/use-sequencer.ts +20 -1
  440. package/src/schemas/from-tool-call.ts +151 -0
  441. package/src/schemas/index.ts +340 -0
  442. package/src/schemas/provider-subsets.ts +538 -0
  443. package/src/schemas/registry.ts +355 -0
  444. package/src/schemas/tool-defs.ts +492 -0
  445. package/src/solid.ts +150 -0
  446. package/src/state/index.ts +27 -1
  447. package/src/state/messages.ts +21 -4
  448. package/src/state/mock.ts +211 -0
  449. package/src/state/parts.ts +295 -0
  450. package/src/state/stream.ts +56 -58
  451. package/src/ui/collapsible.tsx +17 -5
  452. package/src/ui/dropdown.tsx +46 -12
  453. package/src/ui/overlay.tsx +10 -4
  454. package/src/wire/chunk.ts +201 -0
  455. package/src/wire/consume.ts +440 -0
  456. package/src/wire/encode.ts +474 -0
  457. package/src/wire/formats/anthropic.ts +267 -0
  458. package/src/wire/formats/openai.ts +187 -0
  459. package/src/wire/index.ts +52 -0
  460. package/src/wire/read.ts +152 -0
  461. package/src/wire/sink-helpers.ts +50 -0
  462. package/src/wire/sse.ts +140 -0
  463. package/dist/create-tween-hvpmSuwW.js +0 -7
  464. package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
  465. package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
  466. package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
  467. package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
  468. package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
  469. package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
  470. package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
  471. package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
  472. package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
  473. package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
  474. package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
  475. package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
  476. package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
  477. package/dist/elements/chunks/define-D1din7Ld.js +0 -1
  478. package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
  479. package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
  480. package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
  481. package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
  482. package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
  483. package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
  484. package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
  485. package/dist/elements/chunks/slots--V19HwUb.js +0 -1
  486. package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
  487. package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
  488. package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
  489. package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
  490. package/dist/host-embed.d.ts +0 -33
  491. package/dist/kai-provider.d.ts +0 -2
  492. package/dist/origin.d.ts +0 -5
  493. package/dist/provider-runtime.d.ts +0 -27
  494. package/dist/register-impl-DQyejdfb.js +0 -145
  495. package/dist/validate.d.ts +0 -4
  496. package/dist/version.d.ts +0 -4
  497. package/src/components/component-meta.json +0 -4550
  498. package/src/elements/card-media.jpg +0 -0
  499. package/src/elements/resizable.d.ts +0 -27
  500. package/src/elements/video-poster.jpg +0 -0
@@ -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';
@@ -16,6 +16,8 @@ import type { TriggerDef } from './composer';
16
16
  import type { ChatMessage } from '../elements/chat-types';
17
17
  import type { ProseSize } from '../primitives/chat-config';
18
18
  import type { ModelOption } from '../types';
19
+ import type { CardComponentMap } from '../primitives/card-registry';
20
+ import type { CardSchemaMap } from './card-renderer';
19
21
 
20
22
  export interface ChatThreadContextUsage {
21
23
  usedTokens: number;
@@ -29,9 +31,19 @@ export interface ChatThreadProps {
29
31
  /** Extra classes for the thread root (e.g. `h-full`). */
30
32
  class?: string;
31
33
  /** 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 = [...]`). */
34
+ * ordered `parts`, and optional actions/avatar/feedback. Set as a JS property
35
+ * (`el.messages = [...]`). */
34
36
  messages: ChatMessage[];
37
+ /** Add/override card type -> component entries, forwarded to `CardRenderer`
38
+ * for `card` parts. */
39
+ cardTypes?: CardComponentMap;
40
+ /** JSON Schemas for the card types this app renders, keyed by envelope type,
41
+ * forwarded to `CardRenderer` for `card` parts. The companion of `cardTypes`:
42
+ * that says what DRAWS a card, this says what a VALID one looks like.
43
+ * `createCardRegistry(...).validationSchemas` is exactly this shape. Without it
44
+ * the kit checks its own seven built-ins and leaves your own card type
45
+ * unvalidated. A schema here WINS over a built-in of the same name. */
46
+ cardSchemas?: CardSchemaMap;
35
47
  /** Value of the input. A **string** is controlled (the host owns the text and
36
48
  * updates it on `kai-value-change`). A **ComposerDoc** is a one-time seed that
37
49
  * pre-populates pills; the user then edits freely. Leave unset for uncontrolled. */
@@ -72,8 +84,8 @@ export interface ChatThreadProps {
72
84
  context?: ChatThreadContextUsage;
73
85
  /** Show the scroll-to-bottom button inside the scroll area. Default true. */
74
86
  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. */
87
+ /** Whether the host has `slot="header-start"` content (left of the title). Set
88
+ * by the `<kai-chat>` facade so a custom control forces the header open. */
77
89
  headerStart?: boolean;
78
90
  /** Whether the host has `slot="header-end"` content (right of the controls). */
79
91
  headerEnd?: boolean;
@@ -87,24 +99,24 @@ export interface ChatThreadProps {
87
99
  // content owns its own data/events — a slotted (light-DOM) node
88
100
  // can't read this component's reactive state. That boundary is
89
101
  // 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. */
102
+ /** REPLACE: full custom header in place of the built-in title/model/context bar. */
91
103
  headerFull?: boolean;
92
- /** INJECT left sidebar column (e.g. a conversation list / your own nav). */
104
+ /** INJECT: left sidebar column (e.g. a conversation list / your own nav). */
93
105
  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). */
106
+ /** 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
107
  empty?: boolean;
96
- /** REPLACE full custom composer in place of the built-in prompt input. The
108
+ /** REPLACE: full custom composer in place of the built-in prompt input. The
97
109
  * projected content wires its own submit (the data-flow boundary). */
98
110
  composer?: boolean;
99
- /** INJECT accessory row just above the composer (e.g. extra actions). */
111
+ /** INJECT: accessory row just above the composer (e.g. extra actions). */
100
112
  composerActions?: boolean;
101
- /** INJECT footer row below the composer (disclaimers, token meter, …). */
113
+ /** INJECT: footer row below the composer (disclaimers, token meter, …). */
102
114
  footer?: boolean;
103
115
  /** Show a Search (Globe) button in the input toolbar; fires a `search` event. */
104
116
  search?: boolean;
105
117
  /** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
106
118
  voice?: boolean;
107
- /** Rich entity triggers each `{ char, kind, items }` opens a caret-anchored
119
+ /** Rich entity triggers. Each `{ char, kind, items }` opens a caret-anchored
108
120
  * menu that inserts an atomic pill (`/` skills, `@` agents/plugins). Set as a
109
121
  * JS property; forwarded to the input. */
110
122
  triggers?: TriggerDef[];
@@ -136,9 +148,48 @@ export interface ChatThreadController {
136
148
  scrollToBottom(behavior?: ScrollBehavior): void;
137
149
  }
138
150
 
151
+ /**
152
+ * THE MESSAGE LIST'S KEY, and the reason it is not the message object. This is
153
+ * the canonical note; `thread.tsx` keys its identical list the same way and
154
+ * points here.
155
+ *
156
+ * `<For>` is REFERENCE-keyed, and a streaming assistant message gets a brand
157
+ * new object identity on every delta — `createAssistantStream` rebuilds it as
158
+ * `{ ...prev[i], parts: next }` because a new reference IS the re-render
159
+ * signal. Keyed on the objects, every chunk therefore looks like an entirely
160
+ * new list: the whole message row is torn down and rebuilt, and everything the
161
+ * user did inside it dies with it. Expanding a tool or reasoning panel
162
+ * mid-stream did nothing at all — the disclosure opened and was discarded
163
+ * microseconds later by the next token. (`MessageBody` keys its PARTS by
164
+ * position for exactly this reason; that fix was dead while its parent kept
165
+ * destroying the subtree above it.)
166
+ *
167
+ * So key on `message.id`, which is stable across the object churn: `<For>` over
168
+ * the id array diffs by string value, a delta produces an identical id list,
169
+ * and no row moves. The row then reads its message through `messages[i()]` —
170
+ * `<For>`'s index accessor, which it keeps current across inserts, removals and
171
+ * moves — so the CONTENT keeps updating through accessors while the DOM stays
172
+ * put.
173
+ *
174
+ * WHY NOT `<Index>`, the other way to survive the churn: `<Index>` keys by
175
+ * POSITION, so a row's local state (an open panel, a half-filled form card)
176
+ * belongs to the slot rather than to the message. Appends and tail truncations
177
+ * (regenerate/edit) are fine, but prepending older turns — load-earlier-history,
178
+ * which every real chat grows — shifts every row, and each open disclosure
179
+ * stays behind with the wrong message. Position IS a valid key inside a
180
+ * message, where the folds only ever append or patch a part in place; it is not
181
+ * one for the message list, where the host owns the array and splices it.
182
+ * Both approaches require the row to read through accessors — that is inherent
183
+ * in keeping a row mounted while its object is replaced, not a cost unique to
184
+ * either.
185
+ *
186
+ * Contract: `id` must be unique per message. It already had to be — the
187
+ * feedback/copy state above this list is keyed by it.
188
+ */
139
189
  export function ChatThread(props: ChatThreadProps) {
140
190
  const outer = useChatConfig();
141
191
  const reveal = () => (props.actionsReveal === 'hover' ? 'hover' : 'always');
192
+ const messageKeys = createMemo(() => props.messages.map((m) => m.id));
142
193
  // Feedback (copy + vote) state lives ABOVE the per-message <For>, so streaming
143
194
  // re-renders (a fresh `messages` array ref per chunk) don't wipe it.
144
195
  // The copy/feedback toasts scope to the chat (this thread's root) so they appear
@@ -261,44 +312,60 @@ export function ChatThread(props: ChatThreadProps) {
261
312
  <Show when={props.empty && props.messages.length === 0}>
262
313
  <slot name="empty" />
263
314
  </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
- }}
315
+ {/* Keyed by message id (see the note above this component), so a
316
+ streaming delta updates the row instead of replacing it. */}
317
+ <For each={messageKeys()}>
318
+ {(_id, i) => (
319
+ // The row reads its message through <For>'s index accessor,
320
+ // never through a captured value: the row outlives the delta
321
+ // that replaced its object, so every read below has to go
322
+ // through `m()` for the new content to land. <Show> supplies
323
+ // the non-null accessor and covers the frame where a removal
324
+ // has shortened the array.
325
+ <Show when={props.messages[i()]}>
326
+ {(m) => {
327
+ const body = (
328
+ <MessageBody
329
+ parts={m().parts}
330
+ cardTypes={props.cardTypes}
331
+ cardSchemas={props.cardSchemas}
332
+ isUser={m().role === 'user'}
333
+ markdown={m().role === 'assistant'}
334
+ actions={m().actions}
335
+ actionsReveal={reveal()}
336
+ activeFeedback={feedback.resolveFeedback(m())}
337
+ copied={feedback.isCopied(m().id)}
338
+ onAction={(action) => feedback.handleAction(m(), action)}
339
+ />
340
+ );
341
+ const rowGroup = () => (reveal() === 'hover' ? 'group ' : '');
342
+ return (
343
+ // `role` is the SPEAKER, forwarded on BOTH branches —
344
+ // see the same note in thread.tsx. `Message` turns it
345
+ // into `role="article"` + an `aria-label`; without it the
346
+ // row is a bare div chromium prunes from the
347
+ // accessibility tree as "uninteresting".
348
+ <Show
349
+ when={m().avatar}
350
+ fallback={
351
+ <Message role={m().role} class={`${rowGroup()}${m().role === 'user' ? 'flex-col items-end' : 'flex-col items-start'}`}>
352
+ {body}
353
+ </Message>
354
+ }
355
+ >
356
+ {(av) => (
357
+ <Message role={m().role} class={rowGroup()}>
358
+ <MessageAvatar src={av().src ?? ''} alt={av().alt ?? ''} fallback={av().fallback} />
359
+ <div class={`flex min-w-0 flex-1 flex-col ${m().role === 'user' ? 'items-end' : 'items-start'}`}>
360
+ {body}
361
+ </div>
362
+ </Message>
363
+ )}
364
+ </Show>
365
+ );
366
+ }}
367
+ </Show>
368
+ )}
302
369
  </For>
303
370
  <ChatContainerScrollAnchor />
304
371
  </ChatContainerContent>
@@ -337,7 +404,7 @@ export function ChatThread(props: ChatThreadProps) {
337
404
  </Show>
338
405
  </div>
339
406
  </div>
340
- {/* INJECT footer row below the composer. */}
407
+ {/* INJECT: footer row below the composer. */}
341
408
  <Show when={props.footer}>
342
409
  <div part="footer" class="shrink-0 px-4 pb-3">
343
410
  <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
 
@@ -469,11 +469,21 @@ export function Composer(props: ComposerProps): JSX.Element {
469
469
  const handleBlur = (e: FocusEvent) => { focused = false; clearPillSelection(); props.onBlur?.(e); };
470
470
 
471
471
  onMount(() => {
472
+ // Captured at SETUP and closed over, never re-resolved as a global inside
473
+ // `onCleanup`: cleanup can run after the host removed the DOM globals (a
474
+ // `kai-*` release is deferred one microtask past detachment, so an
475
+ // environment teardown gets in between), and a bare `document` there throws
476
+ // -- from a promise nobody holds, so it lands as an unhandled rejection that
477
+ // fails the run while every test passes. That is how this was found.
478
+ // See tests/components/teardown-without-dom-globals.test.tsx.
479
+ // `ownerDocument` rather than a captured `document`, matching the rest of
480
+ // this file and staying correct for a composer inside an iframe.
481
+ const doc = editable.ownerDocument;
472
482
  const onSelectionChange = () => {
473
483
  if (focused) updateTriggerState();
474
484
  };
475
- document.addEventListener('selectionchange', onSelectionChange);
476
- onCleanup(() => document.removeEventListener('selectionchange', onSelectionChange));
485
+ doc.addEventListener('selectionchange', onSelectionChange);
486
+ onCleanup(() => doc.removeEventListener('selectionchange', onSelectionChange));
477
487
  });
478
488
 
479
489
  // --- Imperative controller (Pattern C): hand the facade a handle over the
@@ -21,29 +21,33 @@ import { useCardHost } from '../primitives/card-host';
21
21
  import { AlertTriangle, Check, X } from 'lucide-solid';
22
22
 
23
23
  // ─────────────────────────────────────────────────────────────────────────────
24
- // Types (confirm.schema.json) — see src/primitives/card-schemas/confirm.schema.json
24
+ // Types (confirm.schema.json) — AUTHORED IN ../primitives/card-data-types.ts.
25
+ //
26
+ // They used to be declared here, and moving them is the fourth instance of the
27
+ // split card-tags.ts and card-component-types.ts already made twice: a type
28
+ // import still has to RESOLVE, so while these lived in a `.tsx` no Node/no-DOM
29
+ // project could name them (TS6142), which meant `@kitn.ai/ui/schemas` could not
30
+ // re-export them and a TypeScript backend building a card envelope got
31
+ // `data: unknown` while Python and Go read the full shape out of the published
32
+ // JSON Schemas. They are also `type` aliases rather than `interface`s now, which
33
+ // is what makes `el.data = myConfirmCardData` compile — see that file's header
34
+ // for both measurements. Re-exported here, so this module's surface is unchanged.
25
35
  // ─────────────────────────────────────────────────────────────────────────────
26
36
 
27
- export type ConfirmActionStyle = 'primary' | 'default' | 'destructive';
28
- export type ConfirmTone = 'default' | 'warning' | 'danger';
37
+ import type {
38
+ ConfirmAction,
39
+ ConfirmActionStyle,
40
+ ConfirmCardData,
41
+ ConfirmTone,
42
+ } from '../primitives/card-data-types';
29
43
 
30
- export interface ConfirmAction {
31
- id: string;
32
- label: string;
33
- style?: ConfirmActionStyle;
34
- payload?: unknown;
35
- default?: boolean;
36
- }
37
-
38
- export interface ConfirmCardData {
39
- heading?: string;
40
- body?: string;
41
- tone?: ConfirmTone;
42
- actions: ConfirmAction[]; // 1..4
43
- dismissible?: boolean;
44
- }
45
-
46
- export type ConfirmCardEnvelope = CardEnvelope<'confirm', ConfirmCardData>;
44
+ export type {
45
+ ConfirmAction,
46
+ ConfirmActionStyle,
47
+ ConfirmCardData,
48
+ ConfirmCardEnvelope,
49
+ ConfirmTone,
50
+ } from '../primitives/card-data-types';
47
51
 
48
52
  export const CONFIRM_CARD_TYPE = 'confirm' as const;
49
53
 
@@ -306,7 +306,15 @@ export interface ContextUsageRowProps {
306
306
  children?: JSX.Element;
307
307
  }
308
308
 
309
- export function ContextInputUsage(props: ContextUsageRowProps) {
309
+ // The four rows share one shape, but each still ships a `<Name>Props` alias: a
310
+ // consumer typing a wrapper around one of them should not have to discover that
311
+ // the shared name exists.
312
+ export type ContextInputUsageProps = ContextUsageRowProps;
313
+ export type ContextOutputUsageProps = ContextUsageRowProps;
314
+ export type ContextReasoningUsageProps = ContextUsageRowProps;
315
+ export type ContextCacheUsageProps = ContextUsageRowProps;
316
+
317
+ export function ContextInputUsage(props: ContextInputUsageProps) {
310
318
  const ctx = useContextValue();
311
319
  return (
312
320
  <Show when={props.children || ctx.inputTokens}>
@@ -320,7 +328,7 @@ export function ContextInputUsage(props: ContextUsageRowProps) {
320
328
  );
321
329
  }
322
330
 
323
- export function ContextOutputUsage(props: ContextUsageRowProps) {
331
+ export function ContextOutputUsage(props: ContextOutputUsageProps) {
324
332
  const ctx = useContextValue();
325
333
  return (
326
334
  <Show when={props.children || ctx.outputTokens}>
@@ -334,7 +342,7 @@ export function ContextOutputUsage(props: ContextUsageRowProps) {
334
342
  );
335
343
  }
336
344
 
337
- export function ContextReasoningUsage(props: ContextUsageRowProps) {
345
+ export function ContextReasoningUsage(props: ContextReasoningUsageProps) {
338
346
  const ctx = useContextValue();
339
347
  return (
340
348
  <Show when={props.children || ctx.reasoningTokens}>
@@ -348,7 +356,7 @@ export function ContextReasoningUsage(props: ContextUsageRowProps) {
348
356
  );
349
357
  }
350
358
 
351
- export function ContextCacheUsage(props: ContextUsageRowProps) {
359
+ export function ContextCacheUsage(props: ContextCacheUsageProps) {
352
360
  const ctx = useContextValue();
353
361
  return (
354
362
  <Show when={props.children || ctx.cacheTokens}>
@@ -16,7 +16,9 @@ import type { ConversationSummary, ConversationGroup } from '../types';
16
16
  * surrounding region (the workspace puts the thread beside it, the standalone
17
17
  * element stands alone). `onExpand` reopens the rail.
18
18
  */
19
- export function CollapsedRail(props: { onExpand: () => void; class?: string }) {
19
+ export interface CollapsedRailProps { onExpand: () => void; class?: string }
20
+
21
+ export function CollapsedRail(props: CollapsedRailProps) {
20
22
  return (
21
23
  <Button
22
24
  variant="ghost" size="icon-sm" aria-label="Open sidebar"
@@ -94,7 +96,21 @@ export function ConversationList(props: ConversationListProps) {
94
96
  return grouped;
95
97
  });
96
98
 
97
- const ungrouped = createMemo(() => groupedConversations().get(undefined) ?? []);
99
+ /**
100
+ * Everything that is NOT filed under a rendered group heading: no `groupId` at
101
+ * all, OR a `groupId` that matches no entry in `groups`.
102
+ *
103
+ * The second half used to be dropped on the floor. `groups` drives the render
104
+ * loop, so a conversation pointing at a group the consumer had not declared
105
+ * (a stale id, a group removed from the array, a filtered/paginated `groups`
106
+ * response) vanished from the sidebar with no error and no empty state — the
107
+ * list just silently held fewer rows than the data it was given. Falling through
108
+ * to "Ungrouped" keeps every conversation the consumer passed in reachable.
109
+ */
110
+ const ungrouped = createMemo(() => {
111
+ const known = new Set((local.groups ?? []).map((g) => g.id));
112
+ return filteredConversations().filter((c) => c.groupId == null || !known.has(c.groupId));
113
+ });
98
114
 
99
115
  return (
100
116
  <div class={cn('flex flex-col h-full bg-sidebar', local.class)}>