@kitn.ai/ui 0.25.1 → 0.26.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 (409) hide show
  1. package/README.md +5 -7
  2. package/dist/components/attachment-types.d.ts +46 -1
  3. package/dist/components/attachments.d.ts +31 -0
  4. package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
  5. package/dist/components/audio-visualizer/index.d.ts +15 -1
  6. package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
  7. package/dist/components/chat-thread.d.ts +3 -41
  8. package/dist/components/code-block.d.ts +22 -0
  9. package/dist/components/conversation-item.d.ts +54 -1
  10. package/dist/components/conversation-list.d.ts +65 -0
  11. package/dist/components/form.d.ts +42 -0
  12. package/dist/components/message.d.ts +7 -0
  13. package/dist/components/toast.d.ts +2 -1
  14. package/dist/components/voice-input.d.ts +8 -0
  15. package/dist/components/voice-output.d.ts +12 -1
  16. package/dist/components/workspace-shell.d.ts +87 -0
  17. package/dist/{create-tween-B5Y4Im8G.js → create-tween-BFT6c-Y8.js} +1 -1
  18. package/dist/{create-tween-4z1XYLkR.js → create-tween-CJNlzoaO.js} +1 -1
  19. package/dist/{create-tween-CUzOlaMS.js → create-tween-CQED4T0g.js} +1 -1
  20. package/dist/custom-elements.json +2931 -2572
  21. package/dist/diagnostics/hook.d.ts +81 -0
  22. package/dist/diagnostics/index.d.ts +7 -0
  23. package/dist/diagnostics/report-request.d.ts +61 -0
  24. package/dist/diagnostics.d.ts +5 -0
  25. package/dist/diagnostics.js +209 -0
  26. package/dist/elements/agent-card.js +1 -1
  27. package/dist/elements/artifact.js +1 -1
  28. package/dist/elements/attachments.js +1 -1
  29. package/dist/elements/audio-visualizer.js +1 -1
  30. package/dist/elements/autoloader.js +1 -1
  31. package/dist/elements/avatar.js +1 -1
  32. package/dist/elements/badge.js +1 -1
  33. package/dist/elements/button.js +1 -1
  34. package/dist/elements/card.js +1 -1
  35. package/dist/elements/cards.js +1 -1
  36. package/dist/elements/chain-of-thought.js +1 -1
  37. package/dist/elements/chat-scope-picker.js +1 -1
  38. package/dist/elements/chat-workspace.js +1 -1
  39. package/dist/elements/chat.js +1 -1
  40. package/dist/elements/checkpoint.js +1 -1
  41. package/dist/elements/choice.js +1 -1
  42. package/dist/elements/chunks/{Icon-b5A8hlHs.js → Icon--Y8vsFal.js} +1 -1
  43. package/dist/elements/chunks/action-icons-D9u3OzYW.js +1 -0
  44. package/dist/elements/chunks/arrow-left-CVh1TeN7.js +1 -0
  45. package/dist/elements/chunks/{artifact-BSuPYFHY.js → artifact-BkYcfIlZ.js} +1 -1
  46. package/dist/elements/chunks/attachments-DwfNlubq.js +1 -0
  47. package/dist/elements/chunks/audio-visualizer-D7lB6mvR.js +1 -0
  48. package/dist/elements/chunks/{badge-TIMiJFKc.js → badge-Q0-Cmwzz.js} +1 -1
  49. package/dist/elements/chunks/{button-B9okqG73.js → button-flUbeufF.js} +1 -1
  50. package/dist/elements/chunks/{card-renderer-CSoTL_e1.js → card-renderer-3Dyl-FpN.js} +1 -1
  51. package/dist/elements/chunks/check-B5kBPKeo.js +1 -0
  52. package/dist/elements/chunks/chevron-down-DZ1GS39h.js +1 -0
  53. package/dist/elements/chunks/chevron-right-CvP7OUd2.js +1 -0
  54. package/dist/elements/chunks/{choice-card-CGMc3kek.js → choice-card-D4QUIOWZ.js} +1 -1
  55. package/dist/elements/chunks/circle-CDf9g6MQ.js +1 -0
  56. package/dist/elements/chunks/circle-check-CA47Nqbz.js +1 -0
  57. package/dist/elements/chunks/{circle-x-mhH7pOK2.js → circle-x-CJ_HVbC8.js} +1 -1
  58. package/dist/elements/chunks/code-block-BdLiTitd.js +1 -0
  59. package/dist/elements/chunks/collapsible-BzbXpPzr.js +1 -0
  60. package/dist/elements/chunks/{composer-CZZmtYOT.js → composer-Byve-n8K.js} +2 -2
  61. package/dist/elements/chunks/{confirm-card-BWjf2-xo.js → confirm-card-B0flE3MN.js} +1 -1
  62. package/dist/elements/chunks/context-VLIL3sJ-.js +1 -0
  63. package/dist/elements/chunks/controllable-DTwTIlwp.js +1 -0
  64. package/dist/elements/chunks/conversation-item-BDztezOG.js +1 -0
  65. package/dist/elements/chunks/copy-Nf-kqxXH.js +1 -0
  66. package/dist/elements/chunks/{create-tween-CUrM6D_o.js → create-tween-c4Z8TDYz.js} +2 -2
  67. package/dist/elements/chunks/default-input-C2iLFq0b.js +1 -0
  68. package/dist/elements/chunks/define-wjHR1MkU.js +1 -0
  69. package/dist/elements/chunks/disclosure-CcNbyLy0.js +1 -0
  70. package/dist/elements/chunks/{download-DnCyWFJE.js → download-C4V7tBdb.js} +1 -1
  71. package/dist/elements/chunks/dropdown-BgWTthUx.js +1 -0
  72. package/dist/elements/chunks/{ellipsis-CVxqfsDZ.js → ellipsis-7gU33mL2.js} +1 -1
  73. package/dist/elements/chunks/embed-D3KewsIJ.js +1 -0
  74. package/dist/elements/chunks/{external-link-DG2sRdQL.js → external-link-CSOEsNlx.js} +1 -1
  75. package/dist/elements/chunks/{file-text-D0J9F6M7.js → file-text-D1G8khle.js} +1 -1
  76. package/dist/elements/chunks/{file-tree-yGWNK0Js.js → file-tree-zvB2i7P9.js} +1 -1
  77. package/dist/elements/chunks/{folder-NV1pi_ud.js → folder-WW2LGLid.js} +1 -1
  78. package/dist/elements/chunks/form-CQirdC4W.js +1 -0
  79. package/dist/elements/chunks/hover-card-CJJkfHTK.js +1 -0
  80. package/dist/elements/chunks/icon-Cfgmf4fa.js +1 -0
  81. package/dist/elements/chunks/{info-C8r5Q6cc.js → info-DX3YMOt6.js} +1 -1
  82. package/dist/elements/chunks/input-BtW9U6Sq.js +1 -0
  83. package/dist/elements/chunks/{kbd-OGJDUT-O.js → kbd-LGXwuT_V.js} +1 -1
  84. package/dist/elements/chunks/{link-BdsfLB7b.js → link-2My69BqT.js} +1 -1
  85. package/dist/elements/chunks/{link-preview-Cf5KOP-i.js → link-preview-OQ9IEq0f.js} +1 -1
  86. package/dist/elements/chunks/{loader-B5QzK_8_.js → loader-iOxc_Tu-.js} +1 -1
  87. package/dist/elements/chunks/{markdown-DbozyYqa.js → markdown-DgBFaGAz.js} +1 -1
  88. package/dist/elements/chunks/{message-DtXKPqiR.js → message-VMZohrXx.js} +1 -1
  89. package/dist/elements/chunks/message-circle-CpXMM-_n.js +1 -0
  90. package/dist/elements/chunks/message-square-DxcWtETQ.js +1 -0
  91. package/dist/elements/chunks/message-tZmV2A3p.js +1 -0
  92. package/dist/elements/chunks/{minimize-2-SegJ_oku.js → minimize-2-DPIUKTrT.js} +1 -1
  93. package/dist/elements/chunks/{model-switcher-D7JrFFHp.js → model-switcher-DsT8tFB0.js} +1 -1
  94. package/dist/elements/chunks/{overlay-CV7z2YuX.js → overlay-DuiwdXjm.js} +1 -1
  95. package/dist/elements/chunks/{paperclip-Bg0ITU3b.js → paperclip-CRZVp4z_.js} +1 -1
  96. package/dist/elements/chunks/{progress-bar-D1_rzt6N.js → progress-bar-DNy66e_E.js} +1 -1
  97. package/dist/elements/chunks/{prompt-suggestion-D5Sm8xF1.js → prompt-suggestion-Bn64Nl7L.js} +1 -1
  98. package/dist/elements/chunks/reasoning-Emn9N4-T.js +1 -0
  99. package/dist/elements/chunks/{resizable-Dpzjlvp4.js → resizable-D181b4fM.js} +1 -1
  100. package/dist/elements/chunks/{rotate-cw-DzVDJOA1.js → rotate-cw-CPWe37D9.js} +1 -1
  101. package/dist/elements/chunks/{scroll-area-93A1bbeG.js → scroll-area-ClZkZxIk.js} +1 -1
  102. package/dist/elements/chunks/scroll-button-DPE-t3x_.js +1 -0
  103. package/dist/elements/chunks/{separator-DH2r_ZSZ.js → separator-CeJHQqLW.js} +1 -1
  104. package/dist/elements/chunks/{settings-BFMOd6yB.js → settings-CqaHgJF9.js} +1 -1
  105. package/dist/elements/chunks/{settings-group-B3VZKIcz.js → settings-group-CjqjLCOn.js} +1 -1
  106. package/dist/elements/chunks/share-CyfnNEqp.js +1 -0
  107. package/dist/elements/chunks/{skeleton-N8BuWYmf.js → skeleton-D8akBnKS.js} +1 -1
  108. package/dist/elements/chunks/slots-DaNjX0rc.js +1 -0
  109. package/dist/elements/chunks/{source-w9V0NnBn.js → source-UWCvmVak.js} +1 -1
  110. package/dist/elements/chunks/{star-DHvACxtm.js → star-DoHFAikQ.js} +1 -1
  111. package/dist/elements/chunks/store-Cjq0qwYN.js +1 -0
  112. package/dist/elements/chunks/{tasks-card-lPxoodlA.js → tasks-card-CmZ5xh6v.js} +1 -1
  113. package/dist/elements/chunks/{text-shimmer-BY_rI3s5.js → text-shimmer-DYn09zrz.js} +1 -1
  114. package/dist/elements/chunks/textarea-skiGHURM.js +1 -0
  115. package/dist/elements/chunks/{thumbs-up-DQe0hvkZ.js → thumbs-up-DX2Ijk3H.js} +1 -1
  116. package/dist/elements/chunks/{toast-store-VUZZBhzH.js → toast-store-CFnQpThX.js} +1 -1
  117. package/dist/elements/chunks/{tool-C1YtxwPu.js → tool-DKQ2D1DP.js} +1 -1
  118. package/dist/elements/chunks/{tooltip-BqwnFWhn.js → tooltip-B0G99y8w.js} +1 -1
  119. package/dist/elements/chunks/{triangle-alert-B6or7F5u.js → triangle-alert-F9cyUEyy.js} +1 -1
  120. package/dist/elements/chunks/{use-card-resolution-DJg32jGH.js → use-card-resolution-DoA2f_EU.js} +1 -1
  121. package/dist/elements/chunks/{variant-aurora-QXTZPyST.js → variant-aurora-qt6Zct8M.js} +2 -2
  122. package/dist/elements/chunks/variant-custom-tJp22_BQ.js +1 -0
  123. package/dist/{variant-wave-BwzrHD1s.js → elements/chunks/variant-wave-CaQ5ti39.js} +2 -2
  124. package/dist/elements/chunks/x-B6UnOiLw.js +1 -0
  125. package/dist/elements/coachmark.js +1 -1
  126. package/dist/elements/code-block.js +1 -1
  127. package/dist/elements/command.js +1 -1
  128. package/dist/elements/compare.js +1 -1
  129. package/dist/elements/composer.js +1 -1
  130. package/dist/elements/confirm-card.js +1 -1
  131. package/dist/elements/context-meter.js +1 -1
  132. package/dist/elements/conversation-item.d.ts +1 -0
  133. package/dist/elements/conversation-item.js +1 -0
  134. package/dist/elements/conversation-list.js +1 -1
  135. package/dist/elements/default-input.d.ts +3 -3
  136. package/dist/elements/define.d.ts +27 -0
  137. package/dist/elements/diagnostic-events.d.ts +74 -0
  138. package/dist/elements/dialog.js +1 -1
  139. package/dist/elements/dock.d.ts +1 -0
  140. package/dist/elements/dock.js +149 -0
  141. package/dist/elements/dropdown.d.ts +1 -0
  142. package/dist/elements/dropdown.js +1 -0
  143. package/dist/elements/editable-label.js +1 -1
  144. package/dist/elements/element-diagnostics.d.ts +81 -0
  145. package/dist/elements/embed.js +1 -1
  146. package/dist/elements/empty.js +1 -1
  147. package/dist/elements/feedback-bar.js +1 -1
  148. package/dist/elements/file-tree.js +1 -1
  149. package/dist/elements/file-upload.js +1 -1
  150. package/dist/elements/form.js +1 -1
  151. package/dist/elements/hover-card.js +1 -1
  152. package/dist/elements/icon.js +1 -1
  153. package/dist/elements/image.js +1 -1
  154. package/dist/elements/input.js +1 -1
  155. package/dist/elements/kbd.js +1 -1
  156. package/dist/elements/link-preview.js +1 -1
  157. package/dist/elements/loader.js +1 -1
  158. package/dist/elements/markdown.js +1 -1
  159. package/dist/elements/menu.js +1 -1
  160. package/dist/elements/message-skills.js +1 -1
  161. package/dist/elements/message.js +1 -1
  162. package/dist/elements/model-switcher.js +1 -1
  163. package/dist/elements/nav.js +1 -1
  164. package/dist/elements/notice.js +1 -1
  165. package/dist/elements/pane-group.js +1 -1
  166. package/dist/elements/pane.js +1 -1
  167. package/dist/elements/popover.js +1 -1
  168. package/dist/elements/progress-bar.js +1 -1
  169. package/dist/elements/prompt-dock.js +1 -1
  170. package/dist/elements/prompt-input.js +1 -1
  171. package/dist/elements/prompt-suggestions.js +1 -1
  172. package/dist/elements/reasoning.js +1 -1
  173. package/dist/elements/register.d.ts +2 -0
  174. package/dist/elements/remote.js +1 -1
  175. package/dist/elements/resizable.js +1 -1
  176. package/dist/elements/response-stream.js +3 -3
  177. package/dist/elements/screen.js +1 -1
  178. package/dist/elements/scroll-area.js +1 -1
  179. package/dist/elements/scroll-button.js +1 -1
  180. package/dist/elements/search.js +1 -1
  181. package/dist/elements/segmented.js +1 -1
  182. package/dist/elements/separator.js +1 -1
  183. package/dist/elements/setting-item.js +1 -1
  184. package/dist/elements/settings-group.js +1 -1
  185. package/dist/elements/skeleton.js +1 -1
  186. package/dist/elements/slots.d.ts +35 -3
  187. package/dist/elements/source.js +1 -1
  188. package/dist/elements/status.js +1 -1
  189. package/dist/elements/switch.js +1 -1
  190. package/dist/elements/tabs.js +1 -1
  191. package/dist/elements/tasks.js +1 -1
  192. package/dist/elements/text-shimmer.js +1 -1
  193. package/dist/elements/thinking-bar.js +1 -1
  194. package/dist/elements/thread.js +1 -1
  195. package/dist/elements/toast.js +1 -1
  196. package/dist/elements/tool.js +1 -1
  197. package/dist/elements/tooltip.js +1 -1
  198. package/dist/elements/voice-input.js +1 -1
  199. package/dist/elements/voice-output.js +1 -1
  200. package/dist/elements.d.ts +232 -148
  201. package/dist/index.d.ts +2 -0
  202. package/dist/index.js +5456 -4931
  203. package/dist/index.server.js +4142 -3669
  204. package/dist/kai-provider.es.js +40 -40
  205. package/dist/kai.es.js +1 -1
  206. package/dist/llms/llms-full.txt +184 -69
  207. package/dist/llms/llms.txt +3 -3
  208. package/dist/mcp.es.js +2669 -546
  209. package/dist/primitives/use-resize-observer.d.ts +23 -0
  210. package/dist/primitives/use-speech-recognition.d.ts +8 -0
  211. package/dist/primitives/use-text-stream.d.ts +5 -0
  212. package/dist/react/index.d.ts +115 -266
  213. package/dist/react.js +247 -221
  214. package/dist/register-impl-DpE7icIb.js +293 -0
  215. package/dist/schemas/index.d.ts +1 -1
  216. package/dist/schemas/tool-defs.d.ts +35 -0
  217. package/dist/schemas.js +403 -310
  218. package/dist/{solid-BMuDmFo_.js → solid-BtdqNZI7.js} +8435 -7394
  219. package/dist/{solid-CGYNdLRR.js → solid-CnavwBye.js} +5987 -5035
  220. package/dist/solid.d.ts +5 -0
  221. package/dist/solid.js +210 -203
  222. package/dist/solid.server.js +210 -203
  223. package/dist/state/index.d.ts +4 -0
  224. package/dist/state/persistence.d.ts +51 -0
  225. package/dist/state/stream.d.ts +50 -0
  226. package/dist/state/threads.d.ts +71 -0
  227. package/dist/state.js +269 -169
  228. package/dist/types.d.ts +4 -2
  229. package/dist/ui/dock.d.ts +109 -0
  230. package/dist/ui/hover-card.d.ts +44 -0
  231. package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-B8II3-I1.js} +27 -27
  232. package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CMNWOSJR.js} +28 -28
  233. package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CcuDMWwQ.js} +2 -2
  234. package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-BdvYC7af.js} +40 -40
  235. package/dist/{variant-custom-BhS9wIpU.js → variant-custom-C714mqKl.js} +65 -65
  236. package/dist/variant-custom-L16JLLZy.js +1 -0
  237. package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-2S4La2Ka.js} +2 -2
  238. package/dist/{variant-wave-CbVsmgwT.js → variant-wave-D7ruiA1W.js} +25 -25
  239. package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-ecwykeyL.js} +21 -21
  240. package/dist/wire/chunk.d.ts +47 -0
  241. package/dist/wire/diagnostics.d.ts +575 -0
  242. package/dist/wire/encode-probe.d.ts +70 -0
  243. package/dist/wire/encode.d.ts +24 -0
  244. package/dist/wire/index.d.ts +2 -0
  245. package/dist/wire/sse.d.ts +10 -2
  246. package/dist/wire.js +917 -480
  247. package/frameworks/react/index.tsx +132 -93
  248. package/llms-full.txt +184 -69
  249. package/llms.txt +3 -3
  250. package/package.json +27 -7
  251. package/src/agent-tooling/archetypes.ts +41 -9
  252. package/src/agent-tooling/catalog/README.md +676 -0
  253. package/src/agent-tooling/catalog/catalog-types.ts +231 -0
  254. package/src/agent-tooling/catalog/fabrications.ts +96 -0
  255. package/src/agent-tooling/catalog/invariants.ts +284 -0
  256. package/src/agent-tooling/catalog/labs-titles.ts +114 -0
  257. package/src/agent-tooling/catalog/scenarios.ts +87 -0
  258. package/src/agent-tooling/catalog/surfaces.ts +275 -0
  259. package/src/agent-tooling/integrations/anthropic.ts +7 -1
  260. package/src/agent-tooling/integrations/cloudflare.ts +43 -2
  261. package/src/agent-tooling/integrations/langgraph.ts +7 -1
  262. package/src/agent-tooling/integrations/mastra.ts +7 -1
  263. package/src/agent-tooling/integrations/mock.ts +92 -17
  264. package/src/agent-tooling/integrations/ollama.ts +7 -1
  265. package/src/agent-tooling/integrations/openai.ts +7 -1
  266. package/src/agent-tooling/integrations/openrouter.ts +7 -1
  267. package/src/agent-tooling/integrations/pi.ts +56 -1
  268. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
  269. package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
  270. package/src/agent-tooling/mcp/manifest.ts +100 -1
  271. package/src/agent-tooling/mcp/server.ts +61 -5
  272. package/src/agent-tooling/mcp/tools/reference.ts +440 -4
  273. package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
  274. package/src/agent-tooling/mcp/tools/theme.ts +146 -30
  275. package/src/agent-tooling/mcp/validate-args.ts +141 -0
  276. package/src/agent-tooling/registry.ts +19 -7
  277. package/src/agent-tooling/route-emit.ts +26 -2
  278. package/src/components/attachment-types.ts +47 -1
  279. package/src/components/attachments.tsx +169 -22
  280. package/src/components/audio-visualizer/fit-scale.ts +112 -0
  281. package/src/components/audio-visualizer/index.tsx +80 -3
  282. package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
  283. package/src/components/audio-visualizer/variant-bar.tsx +38 -6
  284. package/src/components/audio-visualizer/variant-custom.tsx +8 -2
  285. package/src/components/audio-visualizer/variant-grid.tsx +16 -11
  286. package/src/components/audio-visualizer/variant-radial.tsx +15 -10
  287. package/src/components/audio-visualizer/variant-wave.tsx +7 -2
  288. package/src/components/chat-container.tsx +4 -1
  289. package/src/components/chat-thread.tsx +36 -7
  290. package/src/components/code-block.tsx +79 -3
  291. package/src/components/composer.tsx +8 -1
  292. package/src/components/conversation-item.tsx +114 -4
  293. package/src/components/conversation-list.tsx +212 -12
  294. package/src/components/embed.tsx +4 -0
  295. package/src/components/form.tsx +41 -14
  296. package/src/components/message.tsx +75 -5
  297. package/src/components/response-stream.tsx +13 -7
  298. package/src/components/screen.tsx +5 -0
  299. package/src/components/scroll-button.tsx +7 -0
  300. package/src/components/toast.tsx +11 -5
  301. package/src/components/voice-input.tsx +35 -3
  302. package/src/components/voice-output.tsx +46 -6
  303. package/src/components/workspace-shell.tsx +285 -0
  304. package/src/diagnostics/hook.ts +352 -0
  305. package/src/diagnostics/index.ts +82 -0
  306. package/src/diagnostics/report-request.ts +296 -0
  307. package/src/elements/attachments.tsx +6 -7
  308. package/src/elements/audio-visualizer.tsx +48 -4
  309. package/src/elements/chat-workspace.tsx +159 -287
  310. package/src/elements/chat.tsx +12 -9
  311. package/src/elements/code-block.tsx +19 -1
  312. package/src/elements/command.tsx +4 -1
  313. package/src/elements/compiled.css +1 -1
  314. package/src/elements/conversation-item.tsx +80 -0
  315. package/src/elements/conversation-list.tsx +60 -11
  316. package/src/elements/default-input.tsx +5 -5
  317. package/src/elements/define.tsx +168 -8
  318. package/src/elements/diagnostic-events.ts +114 -0
  319. package/src/elements/dialog.tsx +47 -0
  320. package/src/elements/disclosure.ts +34 -12
  321. package/src/elements/dock.tsx +204 -0
  322. package/src/elements/dropdown.tsx +123 -0
  323. package/src/elements/element-diagnostics.ts +392 -0
  324. package/src/elements/element-manifest.json +12 -0
  325. package/src/elements/element-meta.json +672 -439
  326. package/src/elements/element-nonscalar.json +170 -0
  327. package/src/elements/element-types.d.ts +232 -148
  328. package/src/elements/menu.tsx +13 -0
  329. package/src/elements/prompt-dock.tsx +2 -1
  330. package/src/elements/prompt-input.tsx +8 -8
  331. package/src/elements/register-impl.ts +46 -0
  332. package/src/elements/register.ts +28 -0
  333. package/src/elements/resizable.tsx +52 -25
  334. package/src/elements/scroll-button.tsx +5 -0
  335. package/src/elements/slots.ts +107 -12
  336. package/src/elements/styles.css +30 -6
  337. package/src/elements/thinking-bar.tsx +16 -7
  338. package/src/elements/toast.tsx +3 -0
  339. package/src/elements/tool.tsx +26 -5
  340. package/src/elements/voice-input.tsx +11 -2
  341. package/src/elements/voice-output.tsx +16 -3
  342. package/src/index.ts +2 -0
  343. package/src/primitives/use-resize-observer.ts +33 -0
  344. package/src/primitives/use-speech-recognition.ts +12 -1
  345. package/src/primitives/use-stick-to-bottom.ts +34 -2
  346. package/src/primitives/use-text-stream.ts +33 -2
  347. package/src/remote/provider-runtime.ts +6 -3
  348. package/src/schemas/index.ts +1 -0
  349. package/src/schemas/tool-defs.ts +297 -6
  350. package/src/solid.ts +5 -0
  351. package/src/state/index.ts +12 -0
  352. package/src/state/persistence.ts +180 -0
  353. package/src/state/stream.ts +71 -4
  354. package/src/state/threads.ts +142 -0
  355. package/src/types.ts +4 -2
  356. package/src/ui/dialog.tsx +6 -0
  357. package/src/ui/dock.tsx +445 -0
  358. package/src/ui/dropdown.tsx +67 -23
  359. package/src/ui/hover-card.tsx +156 -6
  360. package/src/ui/input.tsx +29 -10
  361. package/src/ui/resizable.tsx +11 -0
  362. package/src/wire/chunk.ts +47 -0
  363. package/src/wire/consume.ts +185 -26
  364. package/src/wire/diagnostics.ts +727 -0
  365. package/src/wire/encode-probe.ts +214 -0
  366. package/src/wire/encode.ts +267 -14
  367. package/src/wire/formats/anthropic.ts +8 -1
  368. package/src/wire/formats/openai.ts +5 -0
  369. package/src/wire/index.ts +22 -0
  370. package/src/wire/read.ts +250 -6
  371. package/src/wire/sse.ts +16 -3
  372. package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
  373. package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
  374. package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
  375. package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
  376. package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
  377. package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
  378. package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
  379. package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
  380. package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
  381. package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
  382. package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
  383. package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
  384. package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
  385. package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
  386. package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
  387. package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
  388. package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
  389. package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
  390. package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
  391. package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
  392. package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
  393. package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
  394. package/dist/elements/chunks/input-DrseEjhp.js +0 -1
  395. package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
  396. package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
  397. package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
  398. package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
  399. package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
  400. package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
  401. package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
  402. package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
  403. package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
  404. package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
  405. package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
  406. package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
  407. package/dist/register-impl-DGmGzrEM.js +0 -145
  408. package/dist/variant-custom-MkTNTycd.js +0 -1
  409. package/src/primitives/card-validate-generator.testlib.ts +0 -38
package/README.md CHANGED
@@ -30,11 +30,7 @@ npm install solid-js
30
30
 
31
31
  ### Option A — Web components (any framework / plain HTML)
32
32
 
33
- Build the bundle, then import it as a side-effect (it registers the custom elements):
34
-
35
- ```bash
36
- npm run build # emits dist/kai.es.js
37
- ```
33
+ `npm install` ships the built bundle, so there is nothing to compile. Import it once as a side effect and every element registers itself:
38
34
 
39
35
  ```html
40
36
  <body style="height: 100vh; margin: 0;">
@@ -78,7 +74,9 @@ The element bundle is a self-contained ES module — load it directly from [jsDe
78
74
  <kai-chat></kai-chat>
79
75
  ```
80
76
 
81
- The URLs above track the **latest** release — handy for trying things out. **For production, pin an exact version** (e.g. `@kitn.ai/ui@0.25.0/dist/kai.es.js`): pinned URLs are immutable and cached far more aggressively, and — since this package is pre-1.0 — pinning shields you from breaking changes in a future minor release.
77
+ <!-- x-release-please-start-version -->
78
+ The URLs above track the **latest** release — handy for trying things out. **For production, pin an exact version** (e.g. `@kitn.ai/ui@0.26.0/dist/kai.es.js`): pinned URLs are immutable and cached far more aggressively, and — since this package is pre-1.0 — pinning shields you from breaking changes in a future minor release.
79
+ <!-- x-release-please-end -->
82
80
 
83
81
  > **Pin `0.25.0` or newer.** Every version from `0.14.1` through `0.24.0` is covered by a [critical security advisory](https://github.com/kitn-ai/ui/security/advisories) and is deprecated on npm. `npm install` warns you about a deprecated version; **a CDN fetch does not**, so an old pinned URL in a page keeps serving the vulnerable bundle silently.
84
82
 
@@ -193,7 +191,7 @@ The components are deliberately **transport-agnostic**: `<kai-chat>` just render
193
191
  </script>
194
192
  ```
195
193
 
196
- Key point: `chat.messages` has to be reassigned with a **new array containing a new object** for the streaming message on each chunk. That's what triggers the re-render. Mutating the existing object in place won't update the view. `createAssistantStream` is what satisfies that rule for you; if you drive `messages` by hand, you own it.
194
+ Key point, and it is not only a streaming rule: `chat.messages` has to be reassigned with a **new array**, and any message you changed has to be a **new object**. The array reference is what tells the element something changed — reassigning the same array is a no-op even if you swapped a message inside it. The new message object is what makes the change visible, because the list keys its rows by item identity. Adds, removes and reorders need only the fresh array; editing an existing message needs both, and mutating one in place won't update the view even inside a fresh array. `createAssistantStream` satisfies both halves for you; if you drive `messages` by hand — editing a message, renaming a conversation — you own it.
197
195
 
198
196
  `@kitn.ai/ui/wire` also ships `readAnthropicStream`, the helpers for a multi-round tool loop, and a seam for a custom wire format. See the [wire adapter recipe](https://ui.kitn.ai/guides/recipes/wire-adapter/).
199
197
 
@@ -8,5 +8,50 @@ export interface AttachmentData {
8
8
  url?: string;
9
9
  title?: string;
10
10
  }
11
- export type AttachmentMediaCategory = 'image' | 'video' | 'audio' | 'document' | 'source' | 'unknown';
11
+ /**
12
+ * How an attachment is DRAWN, derived from what the wire can do with it.
13
+ *
14
+ * ★ THIS IS NOT A LIST OF MEDIA TYPES, and that is the whole point. It used to
15
+ * be one in disguise (`image | video | audio | document`, decided by a prefix
16
+ * switch in `attachments.tsx`), and it disagreed with `wire/media-types.ts` in
17
+ * both directions: SVG and MP4 drew a convincing preview that then threw at
18
+ * encode time, while PDF and text — the formats the wire handles best — drew
19
+ * the same anonymous icon as a `.zip`. `getMediaCategory` now asks the media
20
+ * policy and returns what it says, so there is one declaration and this is a
21
+ * projection of it.
22
+ *
23
+ * `image` / `document` / `text` ARE `EncodableKind` from `wire/media-types.ts`.
24
+ * They are spelled out rather than imported because this module is deliberately
25
+ * JSX-free and dependency-free for the framework-neutral consumers; the runtime
26
+ * agreement is pinned by `attachment-media-policy.test.tsx`, whose drift guard
27
+ * derives both sides from the policy.
28
+ *
29
+ * `video` and `audio` are GONE on purpose. No wire this kit ships can carry
30
+ * either, so they were categories whose only job was to make an unsendable file
31
+ * look sendable; both now land in `unsendable`.
32
+ */
33
+ export type AttachmentMediaCategory =
34
+ /** A raster image in one of the four formats both APIs document. */
35
+ 'image'
36
+ /** A PDF. Named for the Anthropic `document` block it becomes. */
37
+ | 'document'
38
+ /** Text content — `text/*`, JSON, XML, YAML. Rides as text on both wires. */
39
+ | 'text'
40
+ /** A RAG citation chip, not something the user staged. Never encoded. */
41
+ | 'source'
42
+ /**
43
+ * No wire format this kit ships can carry this type — SVG, BMP, video, audio,
44
+ * archives. That is a fact about the kit's encoders, knowable at render time
45
+ * and true of every provider, which is why it is safe to show the user.
46
+ *
47
+ * It is NOT "this will fail to send": the renderer does not know which
48
+ * provider a thread is bound for, and must not pretend to.
49
+ */
50
+ | 'unsendable'
51
+ /**
52
+ * Nobody named this file, so nothing is known yet. Its bytes decide it (see
53
+ * `classifyAttachment`), and they have not been read at render time. Marking
54
+ * it `unsendable` would be a guess wearing a fact's clothes.
55
+ */
56
+ | 'unknown';
12
57
  export type AttachmentVariant = 'grid' | 'inline' | 'list';
@@ -1,7 +1,38 @@
1
1
  import { JSX } from 'solid-js';
2
2
  import { AttachmentData, AttachmentMediaCategory, AttachmentVariant } from './attachment-types.js';
3
3
  export type { AttachmentData, AttachmentMediaCategory, AttachmentVariant } from './attachment-types.js';
4
+ /**
5
+ * What to DRAW for this attachment, asked of the one module that knows.
6
+ *
7
+ * ★ NO MEDIA TYPES APPEAR BELOW, and that is the fix. This function used to be
8
+ * a prefix switch — `image/` → image, `video/` → video, `application/` or
9
+ * `text/` → document — a second list of media types in a repo whose media-type
10
+ * declaration says, at its own definition, "if you find yourself writing a
11
+ * second list of media types anywhere in this repo, delete it and read this".
12
+ * It had drifted in both directions: `image/svg+xml` came back `image` and drew
13
+ * a real thumbnail for a format the encoder refuses outright, while
14
+ * `application/json` came back `document` when the wire carries it as text.
15
+ *
16
+ * `DEFAULT_MEDIA_POLICY` and not a narrowed one, on purpose. A developer's
17
+ * `accept` filter says what a user may STAGE; it says nothing about what an
18
+ * already-staged attachment IS, and narrowing here would redraw a message
19
+ * retroactively when a host changed a prop. The default policy is the kit's
20
+ * full capability set, so `unsupported` against it means exactly "no wire
21
+ * format this kit ships can carry this" — which is the only claim about
22
+ * sendability the renderer can honestly make without knowing the provider.
23
+ */
4
24
  export declare const getMediaCategory: (data: AttachmentData) => AttachmentMediaCategory;
25
+ /**
26
+ * The fact, written out, for the one category where the kit knows something the
27
+ * user needs to know.
28
+ *
29
+ * A FACT ABOUT THE FORMATS, NOT A PREDICTION ABOUT THIS REQUEST. At render time
30
+ * the kit does not know which provider the thread is bound for, so "this will
31
+ * fail" is not its to say. What it does know is that no wire format it ships
32
+ * has any representation for the type at all, which is true of every provider
33
+ * and is what this sentence claims.
34
+ */
35
+ export declare const getUnsendableNote: (data: AttachmentData) => string | undefined;
5
36
  export declare const getAttachmentLabel: (data: AttachmentData) => string;
6
37
  interface AttachmentsContextValue {
7
38
  variant: AttachmentVariant;
@@ -0,0 +1,50 @@
1
+ import { Accessor } from 'solid-js';
2
+ /**
3
+ * Fit-to-container scaling (owner finding, rung 2): the size tiers are fixed
4
+ * px by design (`sizes.ts`), so a container narrower than a tier's natural
5
+ * footprint used to CLIP the picture at its edges. The dispatcher now scales
6
+ * the WHOLE visualization down proportionally to fit, and renders byte-equal
7
+ * to the designed px metrics whenever the container is at least natural size.
8
+ *
9
+ * Why a transform-scale wrapper rather than relative units or reflow:
10
+ * - The px tiers ARE the design (LiveKit parity); switching the geometry to
11
+ * relative units would change rendering at every container size, not just
12
+ * the overflowing ones.
13
+ * - Reflowing (fewer bars, smaller gaps) changes what the animation MEANS;
14
+ * proportional scale keeps the exact picture, just smaller.
15
+ * - A transform also covers the shader variants for free: their canvases size
16
+ * their backing store from layout size (`clientWidth`), which a transform
17
+ * does not change, so the bitmap stays at full natural resolution and only
18
+ * the composited output shrinks. No variant needs an exception.
19
+ *
20
+ * Both sizes are MEASURED (one ResizeObserver watching both wrappers), never
21
+ * restated from `sizes.ts`: the natural footprint depends on variant, size
22
+ * tier, barCount/count/radius and the consumer's own part styling, and a
23
+ * hand-derived width table would rot the day any of those moved.
24
+ *
25
+ * Where ResizeObserver does not exist (SSR, jsdom) this is inert: scale stays
26
+ * 1 and nothing is written, which is exactly the pre-fix rendering.
27
+ */
28
+ export declare function computeFitScale(available: number | undefined, natural: number | undefined): number;
29
+ export interface FitScale {
30
+ /** Ref for the outer wrapper (the host box, clamped by `max-width: 100%`). */
31
+ observeOuter: (el: HTMLElement) => void;
32
+ /** Ref for the inner wrapper (the natural-size content that gets scaled). */
33
+ observeInner: (el: HTMLElement) => void;
34
+ /** 1 when the content fits; the proportional shrink factor when it does not. */
35
+ scale: Accessor<number>;
36
+ /** The scaled content height in px while shrunk, else undefined: the outer
37
+ * wrapper adopts it so the page never reserves the unscaled height. */
38
+ scaledHeight: Accessor<number | undefined>;
39
+ }
40
+ /**
41
+ * One ResizeObserver over both wrappers. ResizeObserver reports layout
42
+ * (border-box/content-box) sizes, which CSS transforms do not affect, so the
43
+ * inner wrapper keeps reporting its NATURAL size even while scaled, and the
44
+ * outer wrapper (whose `max-width: 100%` is what lets a narrow parent
45
+ * actually constrain it) reports the width the container is offering. The
46
+ * ratio of the two is the scale, with `computeFitScale` guarding the
47
+ * unmeasured/degenerate cases to 1 so a bad reading can never blank the
48
+ * element.
49
+ */
50
+ export declare function createFitScale(): FitScale;
@@ -52,10 +52,24 @@ export interface AudioVisualizerProps {
52
52
  complexity?: number;
53
53
  /** Setting this makes the element an announced image instead of decorative. */
54
54
  label?: string;
55
+ /** Live audio to analyze. NOTE: amplitude renders only while
56
+ * `state === 'speaking'` unless `listeningAmplitude` is set; every other
57
+ * state plays its scripted animation and the analysis output is unused. */
55
58
  stream?: MediaStream;
59
+ /** Same speaking-only amplitude rule as `stream`: see `listeningAmplitude`. */
56
60
  audioElement?: HTMLMediaElement;
57
- /** Pre-computed levels. Set this and no AudioContext is ever constructed. */
61
+ /** Pre-computed levels. Set this and no AudioContext is ever constructed.
62
+ * Same speaking-only amplitude rule as `stream`: see `listeningAmplitude`. */
58
63
  bands?: number[];
64
+ /**
65
+ * Opt in to rendering live amplitude during `listening` as well, using the
66
+ * same presentation as `speaking`. Default off, which keeps LiveKit parity:
67
+ * amplitude (from `stream`, `audioElement` or `bands`) renders only while
68
+ * `state === 'speaking'`. Set this to show a real mic-level picture while
69
+ * the USER is the one talking. Forwarded to every variant; see
70
+ * `amplitudeRenderState` in variant-bar for the single mapping they share.
71
+ */
72
+ listeningAmplitude?: boolean;
59
73
  /**
60
74
  * Custom fragment shader for `variant="custom"`. See `ShaderSpec` for the
61
75
  * full contract -- most importantly, `fragment` MUST output premultiplied
@@ -5,8 +5,18 @@ import { VisualizerSize } from './sizes.js';
5
5
  export interface VariantProps {
6
6
  state: VisualizerState;
7
7
  size: VisualizerSize;
8
- /** Multiband levels, 0..1. Only read while `state === 'speaking'`. */
8
+ /** Multiband levels, 0..1. Only read while `state === 'speaking'`, or also
9
+ * while `'listening'` when `listeningAmplitude` is set. */
9
10
  bands: number[];
11
+ /**
12
+ * Opt in to rendering live amplitude during `listening` as well: the
13
+ * variant presents `listening` exactly as it presents `speaking` (live
14
+ * levels, lit geometry) while the host keeps reporting the real state via
15
+ * `data-kai-state`. Default off, which preserves LiveKit parity: amplitude
16
+ * renders only while `state === 'speaking'`, and every other state plays
17
+ * its scripted sequence. See `amplitudeRenderState`.
18
+ */
19
+ listeningAmplitude?: boolean;
10
20
  /** `prefers-reduced-motion`: pin the sequencer at its first frame. */
11
21
  frozen: boolean;
12
22
  /** Overrides the inherited `currentColor` the bars are painted with. */
@@ -41,6 +51,19 @@ export interface VariantProps {
41
51
  * `packages/shadcn/components/agents-ui/agent-audio-visualizer-bar.tsx`
42
52
  * (Apache License 2.0).
43
53
  */
54
+ /**
55
+ * The state the RENDERING follows, as opposed to the state the consumer set.
56
+ *
57
+ * `listeningAmplitude` opts the `listening` state into the full speaking
58
+ * presentation: every variant routes its state-driven decisions (levels,
59
+ * highlight sequence, thresholds, volume overrides) through this ONE mapping
60
+ * rather than each patching its own `=== 'speaking'` gates, so the opt-in
61
+ * cannot half-apply within a variant. The host's `data-kai-state` deliberately
62
+ * keeps the REAL state, so CSS hooks and consumers observing the element are
63
+ * never lied to. With the flag absent or false this is the identity mapping,
64
+ * which is what keeps the default byte-identical to LiveKit parity.
65
+ */
66
+ export declare function amplitudeRenderState(state: VisualizerState, listeningAmplitude: boolean | undefined): VisualizerState;
44
67
  export declare function BarVisualizer(props: VariantProps & {
45
68
  barCount?: number;
46
69
  }): JSX.Element;
@@ -98,8 +98,8 @@ export interface ChatThreadProps {
98
98
  accept?: MediaTypeFilter;
99
99
  /** Files the composer refused because `accept` excluded them. */
100
100
  onAttachmentsRejected?: (rejected: RejectedAttachment[]) => void;
101
- /** Show a Search (Globe) button in the input toolbar; fires a `search` event. */
102
- search?: boolean;
101
+ /** Show a web-search (Globe) button in the input toolbar; calls `onWebSearch`. */
102
+ webSearch?: boolean;
103
103
  /** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
104
104
  voice?: boolean;
105
105
  /** Rich entity triggers. Each `{ char, kind, items }` opens a caret-anchored
@@ -120,7 +120,7 @@ export interface ChatThreadProps {
120
120
  onSuggestionClick?: (value: string) => void;
121
121
  onModelChange?: (modelId: string) => void;
122
122
  onMessageAction?: (detail: MessageActionDetail) => void;
123
- onSearch?: () => void;
123
+ onWebSearch?: () => void;
124
124
  onVoice?: () => void;
125
125
  /** Receive the imperative controller once mounted. The kai-chat facade forwards
126
126
  * these as element methods (focus/clear/send/scrollToBottom). */
@@ -134,42 +134,4 @@ export interface ChatThreadController {
134
134
  send(): void;
135
135
  scrollToBottom(behavior?: ScrollBehavior): void;
136
136
  }
137
- /**
138
- * THE MESSAGE LIST'S KEY, and the reason it is not the message object. This is
139
- * the canonical note; `thread.tsx` keys its identical list the same way and
140
- * points here.
141
- *
142
- * `<For>` is REFERENCE-keyed, and a streaming assistant message gets a brand
143
- * new object identity on every delta — `createAssistantStream` rebuilds it as
144
- * `{ ...prev[i], parts: next }` because a new reference IS the re-render
145
- * signal. Keyed on the objects, every chunk therefore looks like an entirely
146
- * new list: the whole message row is torn down and rebuilt, and everything the
147
- * user did inside it dies with it. Expanding a tool or reasoning panel
148
- * mid-stream did nothing at all — the disclosure opened and was discarded
149
- * microseconds later by the next token. (`MessageBody` keys its PARTS by
150
- * position for exactly this reason; that fix was dead while its parent kept
151
- * destroying the subtree above it.)
152
- *
153
- * So key on `message.id`, which is stable across the object churn: `<For>` over
154
- * the id array diffs by string value, a delta produces an identical id list,
155
- * and no row moves. The row then reads its message through `messages[i()]` —
156
- * `<For>`'s index accessor, which it keeps current across inserts, removals and
157
- * moves — so the CONTENT keeps updating through accessors while the DOM stays
158
- * put.
159
- *
160
- * WHY NOT `<Index>`, the other way to survive the churn: `<Index>` keys by
161
- * POSITION, so a row's local state (an open panel, a half-filled form card)
162
- * belongs to the slot rather than to the message. Appends and tail truncations
163
- * (regenerate/edit) are fine, but prepending older turns — load-earlier-history,
164
- * which every real chat grows — shifts every row, and each open disclosure
165
- * stays behind with the wrong message. Position IS a valid key inside a
166
- * message, where the folds only ever append or patch a part in place; it is not
167
- * one for the message list, where the host owns the array and splices it.
168
- * Both approaches require the row to read through accessors — that is inherent
169
- * in keeping a row mounted while its object is replaced, not a cost unique to
170
- * either.
171
- *
172
- * Contract: `id` must be unique per message. It already had to be — the
173
- * feedback/copy state above this list is keyed by it.
174
- */
175
137
  export declare function ChatThread(props: ChatThreadProps): import("solid-js").JSX.Element;
@@ -1,6 +1,28 @@
1
1
  import { JSX } from 'solid-js';
2
2
  export interface CodeBlockProps extends JSX.HTMLAttributes<HTMLDivElement> {
3
3
  children?: JSX.Element;
4
+ /**
5
+ * Render a copy button in a header row above the code.
6
+ *
7
+ * DEFAULT OFF, and deliberately. This component is shared by three surfaces —
8
+ * `markdown.tsx` (every fenced block in every assistant message), `artifact.tsx`
9
+ * (the code panel), and the `<kai-code-block>` facade — and only the ELEMENT is
10
+ * documented as shipping a copy button. Defaulting on would add one to every code
11
+ * block in every message, a kit-wide visible change. The facade opts in; the other
12
+ * two are untouched, which `tests/elements/code-block.test.tsx` pins both
13
+ * behaviourally and at the call sites.
14
+ */
15
+ copy?: boolean;
16
+ /**
17
+ * The exact text the copy button puts on the clipboard.
18
+ *
19
+ * Named for what it DOES rather than what it holds, because the distinction is the
20
+ * whole contract: by the time the button is clicked the source has been through
21
+ * Shiki, so anything read back out of the DOM is either `<span>`-laden HTML or a
22
+ * `textContent` reconstruction — and the reconstruction is where tabs, blank lines
23
+ * and trailing newlines quietly change. This prop never goes near the highlighter.
24
+ */
25
+ copyText?: string;
4
26
  }
5
27
  declare function CodeBlock(props: CodeBlockProps): JSX.Element;
6
28
  export interface CodeBlockCodeProps extends JSX.HTMLAttributes<HTMLDivElement> {
@@ -1,3 +1,4 @@
1
+ import { JSX } from 'solid-js';
1
2
  import { ConversationSummary } from '../types.js';
2
3
  export interface ConversationItemProps {
3
4
  conversation: ConversationSummary;
@@ -14,4 +15,56 @@ export interface ConversationItemProps {
14
15
  * ticking clock. Returns '' for a missing or unparseable date.
15
16
  */
16
17
  export declare function relativeTimeShort(iso?: string, now?: number): string;
17
- export declare function ConversationItem(props: ConversationItemProps): import("solid-js").JSX.Element;
18
+ /**
19
+ * The slotted-item shape rendered by `<kai-conversation-item>` — the composed
20
+ * row of the consumer-owned loop (spec 2026-08-20 § 2a). Distinct from the
21
+ * data-mode `ConversationItem` above, which batteries mode keeps rendering
22
+ * unchanged: this one takes REGIONS, not a `ConversationSummary`.
23
+ *
24
+ * Regions map 1:1 to the element's slots: `children` = the default slot (the
25
+ * title), plus `leading`, `meta`, and `menu`. The `menu` region takes the
26
+ * consumer's OWN popover (rename / fork / archive live there); the component
27
+ * provides only the region plus focus and ARIA plumbing, never a declarative
28
+ * actions prop. Activation semantics live in the CONTAINER
29
+ * (`createConversationItemsController` in conversation-list.tsx): this row is
30
+ * presentational, and the `data-kai-item-menu` marker on the menu region is what
31
+ * the container's activation guard keys off so a click in the consumer's menu
32
+ * never also selects the row.
33
+ *
34
+ * ARIA contract for direct Solid use: the row renders `role="listitem"` holding
35
+ * a `role="button"` body (`aria-current` marks the active row, the same dialect
36
+ * as the batteries-mode row above) with the menu as the body's tabbable SIBLING
37
+ * (ratified 2026-08-20; axe `nested-interactive` bans focusable descendants of
38
+ * a control, and `aria-required-children` outlaws non-option content anywhere
39
+ * inside a listbox, which is why the vocabulary is not listbox/option). Render
40
+ * your loop's rows inside a `role="list"` ancestor; element mode has no such
41
+ * burden: `<kai-conversations>`' items region provides it for slotted items.
42
+ */
43
+ export interface SlottedConversationItemProps {
44
+ /** The row's identity, handed to the container's selection contract. In the
45
+ * element this is the `conversation-id` attribute (host `id` is the fallback). */
46
+ conversationId?: string;
47
+ /** Selected state. Reflected as `aria-current` on the row body and a
48
+ * `data-active` styling hook on the row; the container drives it from its
49
+ * `activeId`. */
50
+ active?: boolean;
51
+ /** Dense single-line row padding. */
52
+ compact?: boolean;
53
+ /** Leading region before the title (an icon or avatar). */
54
+ leading?: JSX.Element;
55
+ /** Meta region under the title (a timestamp or status line). */
56
+ meta?: JSX.Element;
57
+ /** Your own row menu (a popover trigger). Never selects the row. */
58
+ menu?: JSX.Element;
59
+ /** The title (the element's default slot). */
60
+ children?: JSX.Element;
61
+ /** Set when the element HOST carries the row-group semantics: the inner row
62
+ * then renders `role="presentation"` so the accessibility tree sees one
63
+ * group (the host), never two. The `kai-conversation-item` facade sets it;
64
+ * Solid consumers rendering the component directly leave it off and the row
65
+ * itself is the group. */
66
+ hostSemantics?: boolean;
67
+ class?: string;
68
+ }
69
+ export declare function SlottedConversationItem(props: SlottedConversationItemProps): JSX.Element;
70
+ export declare function ConversationItem(props: ConversationItemProps): JSX.Element;
@@ -13,6 +13,57 @@ export interface CollapsedRailProps {
13
13
  class?: string;
14
14
  }
15
15
  export declare function CollapsedRail(props: CollapsedRailProps): JSX.Element;
16
+ /** Read a conversation item's identity the way the container does: the
17
+ * `conversationId` property, else the `conversation-id` attribute, else the
18
+ * host `id`. The property is what a framework loop sets; the attributes are
19
+ * the plain-HTML spellings (host `id` matches the `<kai-conversation>`
20
+ * data-carrier precedent). */
21
+ export declare function readConversationItemId(el: Element): string;
22
+ export interface ConversationItemsControllerOptions {
23
+ /** The current slotted item hosts, in DOM order. */
24
+ getItems: () => HTMLElement[];
25
+ /** The container's active conversation id. */
26
+ getActiveId: () => string | undefined;
27
+ /** Item activation (click / Enter / Space). Surfaces as
28
+ * `kai-conversation-select` on the element. */
29
+ onSelect: (id: string) => void;
30
+ }
31
+ export interface ConversationItemsController {
32
+ /** Re-derive the ARIA/roving-tabindex bookkeeping over the current items.
33
+ * Call on slotchange / child mutation and whenever `activeId` changes. */
34
+ sync(): void;
35
+ /** The container's items-region click handler (composed-path aware). */
36
+ handleClick(e: MouseEvent): void;
37
+ /** The container's items-region keydown handler (arrows, Home/End,
38
+ * Enter/Space). */
39
+ handleKeyDown(e: KeyboardEvent): void;
40
+ }
41
+ /**
42
+ * The parent-item contract of item mode (spec 2026-08-20 § 2a), as a pure-DOM
43
+ * controller so it is host-agnostic: the `kai-conversations` facade wires it over
44
+ * its slotted `kai-conversation-item` children, and the jsdom contract tests
45
+ * drive it over plain nodes. Solid context cannot cross the element boundary
46
+ * (each facade is its own Solid root), so the channel is DOM traversal by
47
+ * construction:
48
+ *
49
+ * - selection flows container to item — exactly one item's BODY node (the
50
+ * shadow body of a `kai-conversation-item`, else the node itself; see
51
+ * `bodyOf`) is `aria-current="true"`, plus the `active` property on the
52
+ * host for the item's own styling hook;
53
+ * - `role="button"` is ensured on each item's body node (an authored role is
54
+ * left alone);
55
+ * - roving tabindex — exactly one body node `tabindex="0"` (the active
56
+ * item's, else the first's), the rest `-1`, re-derived on every `sync()`;
57
+ * menu content keeps its natural tab order (it is the body's SIBLING, per
58
+ * the ratified 2026-08-20 sibling restructure);
59
+ * - activation (click / Enter / Space) calls `onSelect` with the item's id, and
60
+ * is SUPPRESSED when the composed path crosses the item's `menu` region
61
+ * (light-DOM `slot="menu"` content or the shadow `data-kai-item-menu`
62
+ * wrapper), so the consumer's own popover never also selects the row;
63
+ * - ArrowUp/ArrowDown/Home/End move focus item-to-item, tabindex following the
64
+ * focused item.
65
+ */
66
+ export declare function createConversationItemsController(opts: ConversationItemsControllerOptions): ConversationItemsController;
16
67
  export interface ConversationListProps {
17
68
  groups: ConversationGroup[];
18
69
  conversations: ConversationSummary[];
@@ -34,6 +85,20 @@ export interface ConversationListProps {
34
85
  /** Receive the imperative controller once mounted. The `kai-conversations`
35
86
  * facade uses it to focus / clear the internal search input. */
36
87
  controllerRef?: (controller: ConversationListController) => void;
88
+ /** Item mode (spec 2026-08-20 § 2a): the consumer's OWN rows, rendered inside
89
+ * a list region in place of the data rows. When set, the built-in search
90
+ * filter, grouping and empty/no-match states do not apply — the consumer's
91
+ * loop owns them — while the chrome (header, search box, new-chat, footer)
92
+ * still renders and `onSearchChange` still reports queries. The
93
+ * `kai-conversations` facade passes its default `<slot>` here when it detects
94
+ * `kai-conversation-item` children. */
95
+ items?: JSX.Element;
96
+ /** Keydown handler for the item-mode list region (the facade wires
97
+ * `createConversationItemsController.handleKeyDown`). */
98
+ itemsKeyDown?: (e: KeyboardEvent) => void;
99
+ /** Click handler for the item-mode list region (the facade wires
100
+ * `createConversationItemsController.handleClick`). */
101
+ itemsClick?: (e: MouseEvent) => void;
37
102
  class?: string;
38
103
  }
39
104
  /** Imperative handle exposed via `controllerRef` — surfaces the internal search
@@ -94,3 +94,45 @@ export interface FormProps {
94
94
  * `kai-card` CustomEvent.
95
95
  */
96
96
  export declare function Form(props: FormProps): JSX.Element;
97
+ interface FieldRowProps {
98
+ fieldKey: string;
99
+ field: FormField;
100
+ required: boolean;
101
+ inlineMax: number;
102
+ value: () => unknown;
103
+ error: () => string | undefined;
104
+ disabled: boolean;
105
+ onInput: (value: unknown) => void;
106
+ onBlur: () => void;
107
+ }
108
+ /**
109
+ * The widget prop bag for one field row. **Internal** — exported only so the
110
+ * subscription contract can be pinned directly
111
+ * (`tests/components/form-field-subscriptions.test.tsx`); it is not re-exported
112
+ * from the package.
113
+ *
114
+ * GETTERS, NOT AN OBJECT FACTORY, and this is the whole point (K-D12b). The
115
+ * factory read `props.value()` alongside everything else and rebuilt the bag on
116
+ * every call, so a reader of ANY prop — `invalid`, say — subscribed to `value`
117
+ * too and re-ran on every keystroke. Combined with the `<input>` being rebuilt
118
+ * when its class expression re-ran (K-D12a), that is what made typing into a
119
+ * `kai-form` text field lose focus after each character.
120
+ *
121
+ * One stable object with one getter per prop: each reader tracks exactly the
122
+ * signal behind the prop it read. The object identity is constant, so the
123
+ * `WidgetSwitch` prop below never changes either.
124
+ */
125
+ export declare function fieldCommon(props: FieldRowProps, id: string, placeholder: () => string | undefined, label: () => string, describedBy?: () => string | undefined): ReturnType<FieldRowCommon>;
126
+ type FieldRowCommon = () => {
127
+ id: string;
128
+ value: unknown;
129
+ field: FormField;
130
+ disabled: boolean;
131
+ placeholder?: string;
132
+ required: boolean;
133
+ invalid: boolean;
134
+ describedBy?: string;
135
+ label: string;
136
+ onInput: (v: unknown) => void;
137
+ onBlur: () => void;
138
+ };
@@ -123,6 +123,13 @@ export interface MessageBodyProps {
123
123
  activeFeedback?: FeedbackVote;
124
124
  /** When true, the copy button shows its "copied" check icon. */
125
125
  copied?: boolean;
126
+ /** Whether this message is the one currently streaming. Forwarded to each
127
+ * reasoning part's `<Reasoning>` so the disclosure auto-opens while the
128
+ * model is thinking and settles back once the stream ends (F-21: without it
129
+ * the user watches a static collapsed "Reasoning" label for the whole
130
+ * thinking window). The caller owns the definition of "streaming" — for
131
+ * `ChatThread` that is `loading` + being the last assistant message. */
132
+ isStreaming?: boolean;
126
133
  /** INJECT slot projected at the TOP of the body — above the reasoning / tools /
127
134
  * content. A per-message header: a model-name label, a role + timestamp line.
128
135
  * In the `<kai-message>` shadow this is `<slot name="before-body" />`. */
@@ -53,6 +53,7 @@ export declare function Toast(props: ToastProps): import("solid-js").JSX.Element
53
53
  * Renders a stack of toasts at a viewport edge. Caps the visible count at `max`
54
54
  * (newest on top); overflow waits in a queue and is promoted as visible toasts
55
55
  * leave. `aria-live=polite` / `role=region` so screen readers announce new
56
- * toasts without stealing focus.
56
+ * toasts without stealing focus. Your app chrome must stay below the toast
57
+ * layer; set `--kai-toast-z` (default 100) on the host to move the layer.
57
58
  */
58
59
  export declare function ToastRegion(props: ToastRegionProps): import("solid-js").JSX.Element;
@@ -30,5 +30,13 @@ export interface VoiceInputProps {
30
30
  /** Receive the imperative controller once mounted. The `<kai-voice-input>`
31
31
  * facade forwards these as element methods (start/stop). */
32
32
  controllerRef?: (controller: VoiceInputController) => void;
33
+ /** A recognition session failed or produced nothing. `error` is the platform
34
+ * error code, the thrown exception's name, or `no-result` when the session
35
+ * ended with no error and no text. The facade maps this to kai-voice-error. */
36
+ onError?: (detail: {
37
+ source: 'recognition';
38
+ error: string;
39
+ message: string;
40
+ }) => void;
33
41
  }
34
42
  export declare function VoiceInput(props: VoiceInputProps): import("solid-js").JSX.Element;
@@ -20,8 +20,19 @@ export interface VoiceOutputProps {
20
20
  /** TTS model seam: given text, return an audio Blob to play. When set, the
21
21
  * native speechSynthesis path is bypassed. Mirrors VoiceInput's `onTranscribe`. */
22
22
  onSynthesize?: (text: string) => Promise<Blob>;
23
- /** Fires whenever playback starts or stops. */
23
+ /** Fires whenever playback starts or stops. `speaking: true` means audio has
24
+ * actually started (utterance.onstart on the native path; audio.play()
25
+ * resolving on the model path), not that speak() was called. */
24
26
  onSpeakingChange?: (speaking: boolean) => void;
27
+ /** Synthesis failed: a native utterance error, a rejecting `onSynthesize`, or
28
+ * audio playback failing to start. `error` is the platform error code or the
29
+ * exception's name. Deliberate cancellation (stop(), a new speak()) is not a
30
+ * failure and does not fire. The facade maps this to kai-voice-error. */
31
+ onError?: (detail: {
32
+ source: 'synthesis';
33
+ error: string;
34
+ message: string;
35
+ }) => void;
25
36
  /** Fires once the model path resolves audio (model path only). */
26
37
  onSynthesized?: (blob: Blob) => void;
27
38
  disabled?: boolean;