@kitn.ai/ui 0.20.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (510) hide show
  1. package/README.md +44 -45
  2. package/dist/components/artifact-card.d.ts +51 -0
  3. package/dist/components/artifact.d.ts +1 -1
  4. package/dist/components/attachments.d.ts +2 -2
  5. package/dist/components/audio-visualizer/index.d.ts +3 -3
  6. package/dist/components/audio-visualizer/labs/lab-choreography.d.ts +59 -0
  7. package/dist/components/audio-visualizer/labs/lab-shaders.d.ts +22 -0
  8. package/dist/components/audio-visualizer/labs/lab-visualizer.d.ts +18 -0
  9. package/dist/components/audio-visualizer/variant-aurora.d.ts +2 -2
  10. package/dist/components/audio-visualizer/variant-bar.d.ts +2 -2
  11. package/dist/components/audio-visualizer/variant-custom.d.ts +2 -2
  12. package/dist/components/audio-visualizer/variant-grid.d.ts +1 -1
  13. package/dist/components/audio-visualizer/variant-radial.d.ts +1 -1
  14. package/dist/components/audio-visualizer/variant-wave.d.ts +1 -1
  15. package/dist/components/card-fallback.d.ts +12 -2
  16. package/dist/components/card-renderer.d.ts +63 -2
  17. package/dist/components/chat-container.d.ts +3 -0
  18. package/dist/components/chat-scope-picker.d.ts +2 -2
  19. package/dist/components/chat-thread.d.ts +77 -18
  20. package/dist/components/choice-card.d.ts +3 -28
  21. package/dist/components/composer-dom.d.ts +1 -1
  22. package/dist/components/composer-highlight.d.ts +1 -1
  23. package/dist/components/composer-history.d.ts +1 -1
  24. package/dist/components/composer.d.ts +1 -1
  25. package/dist/components/confirm-card.d.ts +3 -18
  26. package/dist/components/context.d.ts +8 -4
  27. package/dist/components/conversation-item.d.ts +1 -1
  28. package/dist/components/conversation-list.d.ts +4 -3
  29. package/dist/components/embed.d.ts +3 -3
  30. package/dist/components/file-tree.d.ts +11 -22
  31. package/dist/components/form-widgets.d.ts +1 -1
  32. package/dist/components/form.d.ts +3 -44
  33. package/dist/components/link-preview.d.ts +3 -3
  34. package/dist/components/loader.d.ts +30 -48
  35. package/dist/components/message-skills.d.ts +4 -0
  36. package/dist/components/message.d.ts +84 -21
  37. package/dist/components/model-switcher.d.ts +2 -2
  38. package/dist/components/prompt-input.d.ts +2 -2
  39. package/dist/components/response-compare-types.d.ts +11 -2
  40. package/dist/components/response-compare.d.ts +3 -3
  41. package/dist/components/source.d.ts +12 -3
  42. package/dist/components/tasks-card.d.ts +3 -31
  43. package/dist/components/thread.d.ts +17 -5
  44. package/dist/components/toast.d.ts +2 -2
  45. package/dist/components/tool-classify.d.ts +8 -0
  46. package/dist/components/tool-types.d.ts +29 -1
  47. package/dist/components/tool.d.ts +3 -3
  48. package/dist/components/use-card-resolution.d.ts +1 -1
  49. package/dist/create-tween-COnPwAQN.js +338 -0
  50. package/dist/create-tween-DmTUaXWE.js +341 -0
  51. package/dist/create-tween-qs-jjvNA.js +7 -0
  52. package/dist/custom-elements.json +1726 -463
  53. package/dist/elements/agent-card.js +1 -1
  54. package/dist/elements/artifact.js +1 -1
  55. package/dist/elements/attachments.js +1 -1
  56. package/dist/elements/audio-visualizer.js +1 -1
  57. package/dist/elements/avatar.js +1 -1
  58. package/dist/elements/badge.js +1 -1
  59. package/dist/elements/button.js +1 -1
  60. package/dist/elements/card.js +1 -1
  61. package/dist/elements/cards.js +1 -1
  62. package/dist/elements/chain-of-thought.js +1 -1
  63. package/dist/elements/chat-scope-picker.js +1 -1
  64. package/dist/elements/chat-types.d.ts +70 -10
  65. package/dist/elements/chat-workspace.js +1 -1
  66. package/dist/elements/chat.js +1 -1
  67. package/dist/elements/checkpoint.js +1 -1
  68. package/dist/elements/choice.js +1 -1
  69. package/dist/elements/chunks/{Icon-Tj58ES_d.js → Icon-CHG5z5L7.js} +1 -1
  70. package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-BAtCEEgg.js} +1 -1
  71. package/dist/elements/chunks/arrow-left-CFPTeL1Y.js +1 -0
  72. package/dist/elements/chunks/artifact-fBZOE4SO.js +1 -0
  73. package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-DpYMe5VQ.js} +1 -1
  74. package/dist/elements/chunks/audio-visualizer-DFwzAcuL.js +1 -0
  75. package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-D6zodm6E.js} +1 -1
  76. package/dist/elements/chunks/{button-ZWNif3--.js → button-C42vrGvO.js} +1 -1
  77. package/dist/elements/chunks/card-renderer-DMZsSuvU.js +1 -0
  78. package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
  79. package/dist/elements/chunks/chat-thread-CNNEhol-.js +1 -0
  80. package/dist/elements/chunks/check-7Q3K-iXE.js +1 -0
  81. package/dist/elements/chunks/chevron-down-CpNYLn7E.js +1 -0
  82. package/dist/elements/chunks/chevron-right-DrH85Fha.js +1 -0
  83. package/dist/elements/chunks/choice-card-YoWjpsFV.js +1 -0
  84. package/dist/elements/chunks/circle-C9KRjKYv.js +1 -0
  85. package/dist/elements/chunks/circle-check-Dmi7Y4zw.js +1 -0
  86. package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-QIxXda6k.js} +1 -1
  87. package/dist/elements/chunks/code-block-CqeugReY.js +1 -0
  88. package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-sTv-_5lF.js} +1 -1
  89. package/dist/elements/chunks/{composer-BguS0QL_.js → composer-ldQyeVEB.js} +2 -2
  90. package/dist/elements/chunks/confirm-card-C0RNOwfn.js +1 -0
  91. package/dist/elements/chunks/context-5qgypNHC.js +1 -0
  92. package/dist/elements/chunks/conversation-list-DLzOacXz.js +1 -0
  93. package/dist/elements/chunks/create-tween-V4TXZ93X.js +7 -0
  94. package/dist/elements/chunks/default-input-DeeRSDHc.js +1 -0
  95. package/dist/elements/chunks/define-lE-QvQFn.js +1 -0
  96. package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DZSa9jog.js} +1 -1
  97. package/dist/elements/chunks/{download-Dsbg3ste.js → download-B2ZmnGqW.js} +1 -1
  98. package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-R3B7cSRE.js} +1 -1
  99. package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BmxceY4l.js} +1 -1
  100. package/dist/elements/chunks/embed-BVrtTZPo.js +1 -0
  101. package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-BbdR7YO7.js} +1 -1
  102. package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-D7dijo1A.js} +1 -1
  103. package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-0upJ5jeb.js} +1 -1
  104. package/dist/elements/chunks/{folder-glP5rha4.js → folder-D8z58t7B.js} +1 -1
  105. package/dist/elements/chunks/form-p5BKJNw5.js +1 -0
  106. package/dist/elements/chunks/hover-card-CN9s2md5.js +1 -0
  107. package/dist/elements/chunks/{icon-BLb8beoi.js → icon-CNiKP37E.js} +1 -1
  108. package/dist/elements/chunks/{info-CIsv92JK.js → info-BSnuJiHI.js} +1 -1
  109. package/dist/elements/chunks/{input-Dds1kFyt.js → input-BauDmzxX.js} +1 -1
  110. package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-BlHJaP9b.js} +1 -1
  111. package/dist/elements/chunks/{link-i4r_k7z4.js → link-DEi5iUWC.js} +1 -1
  112. package/dist/elements/chunks/link-preview-BaHyM6W4.js +1 -0
  113. package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-CmJG_Fkq.js} +1 -1
  114. package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-D2jaMpCN.js} +1 -1
  115. package/dist/elements/chunks/message-C5UfXZ2Z.js +1 -0
  116. package/dist/elements/chunks/message-CXWLmmLn.js +1 -0
  117. package/dist/elements/chunks/message-square-hXylwsy0.js +1 -0
  118. package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-C9ytZFMU.js} +1 -1
  119. package/dist/elements/chunks/model-switcher-CivGfD7-.js +1 -0
  120. package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-DJL_sbOm.js} +1 -1
  121. package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CFaB8naM.js} +1 -1
  122. package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-Br5SksfR.js} +1 -1
  123. package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-CMIvtAhU.js} +1 -1
  124. package/dist/elements/chunks/reasoning-DpZcNzLq.js +1 -0
  125. package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-Q8i3T9a_.js} +1 -1
  126. package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-B5IHCBFV.js} +1 -1
  127. package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-eLeydBje.js} +1 -1
  128. package/dist/elements/chunks/scroll-button-DWbz0yA1.js +1 -0
  129. package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-Xic78AD_.js} +1 -1
  130. package/dist/elements/chunks/{settings-B8lmec4D.js → settings-B8H51pAx.js} +1 -1
  131. package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-CsG9eYLe.js} +1 -1
  132. package/dist/elements/chunks/{share-C3lTiYGt.js → share-CNTx0z__.js} +1 -1
  133. package/dist/elements/chunks/skeleton-CLlHTcMI.js +1 -0
  134. package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
  135. package/dist/elements/chunks/source-BoVcU5ok.js +1 -0
  136. package/dist/elements/chunks/{star-CSJtGxgv.js → star-CxQVTVhI.js} +1 -1
  137. package/dist/elements/chunks/{store-CHyW95TP.js → store-CLtS5CUG.js} +1 -1
  138. package/dist/elements/chunks/tasks-card-CzlkLmUt.js +1 -0
  139. package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D5NhpIQ1.js} +1 -1
  140. package/dist/elements/chunks/textarea-x5Uxsmtm.js +1 -0
  141. package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-UobcJnx9.js} +1 -1
  142. package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-BOfs9eXq.js} +1 -1
  143. package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-Be6Dh_dt.js} +1 -1
  144. package/dist/elements/chunks/tooltip-RxMxyCLF.js +1 -0
  145. package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C4yxH2XN.js} +1 -1
  146. package/dist/elements/chunks/use-card-resolution-jva9ehH6.js +1 -0
  147. package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Dx4VRcjS.js} +2 -2
  148. package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-Y1ZTWgEa.js} +1 -1
  149. package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-B9gml9Cg.js} +2 -2
  150. package/dist/elements/chunks/{video-B7vmenoC.js → video-CrPHsytT.js} +1 -1
  151. package/dist/elements/chunks/x-_-OPHept.js +1 -0
  152. package/dist/elements/coachmark.js +1 -1
  153. package/dist/elements/code-block.js +1 -1
  154. package/dist/elements/command.js +1 -1
  155. package/dist/elements/compare.js +1 -1
  156. package/dist/elements/composer.js +1 -1
  157. package/dist/elements/confirm-card.js +1 -1
  158. package/dist/elements/context-meter.js +1 -1
  159. package/dist/elements/conversation-list.js +1 -1
  160. package/dist/elements/default-input.d.ts +34 -7
  161. package/dist/elements/define.d.ts +1 -0
  162. package/dist/elements/dialog.js +1 -1
  163. package/dist/elements/disclosure.d.ts +1 -1
  164. package/dist/elements/editable-label.js +1 -1
  165. package/dist/elements/element-data-types.d.ts +107 -0
  166. package/dist/elements/embed.js +1 -1
  167. package/dist/elements/empty.js +1 -1
  168. package/dist/elements/feedback-bar.js +1 -1
  169. package/dist/elements/file-tree.js +1 -1
  170. package/dist/elements/file-upload.js +1 -1
  171. package/dist/elements/form.js +1 -1
  172. package/dist/elements/hover-card.js +1 -1
  173. package/dist/elements/icon.js +1 -1
  174. package/dist/elements/image.js +1 -1
  175. package/dist/elements/input.js +1 -1
  176. package/dist/elements/kbd.js +1 -1
  177. package/dist/elements/link-preview.js +1 -1
  178. package/dist/elements/loader.js +1 -1
  179. package/dist/elements/markdown.js +1 -1
  180. package/dist/elements/menu.js +1 -1
  181. package/dist/elements/message-skills.js +1 -1
  182. package/dist/elements/message.js +1 -1
  183. package/dist/elements/model-switcher.js +1 -1
  184. package/dist/elements/nav.js +1 -1
  185. package/dist/elements/notice.js +1 -1
  186. package/dist/elements/pane-group.js +1 -1
  187. package/dist/elements/pane.js +1 -1
  188. package/dist/elements/popover.js +1 -1
  189. package/dist/elements/progress-bar.js +1 -1
  190. package/dist/elements/prompt-dock.js +1 -1
  191. package/dist/elements/prompt-input.js +1 -1
  192. package/dist/elements/prompt-suggestions.js +1 -1
  193. package/dist/elements/reasoning.js +1 -1
  194. package/dist/elements/register.d.ts +8 -6
  195. package/dist/elements/remote.js +1 -1
  196. package/dist/elements/resizable.js +1 -1
  197. package/dist/elements/response-stream.js +1 -1
  198. package/dist/elements/screen.js +1 -1
  199. package/dist/elements/scroll-area.js +1 -1
  200. package/dist/elements/scroll-button.js +1 -1
  201. package/dist/elements/search.js +1 -1
  202. package/dist/elements/segmented.js +1 -1
  203. package/dist/elements/separator.js +1 -1
  204. package/dist/elements/setting-item.js +1 -1
  205. package/dist/elements/settings-group.js +1 -1
  206. package/dist/elements/skeleton.js +1 -1
  207. package/dist/elements/slot-text.d.ts +51 -0
  208. package/dist/elements/slots.d.ts +21 -0
  209. package/dist/elements/source.js +1 -1
  210. package/dist/elements/status.js +1 -1
  211. package/dist/elements/switch.js +1 -1
  212. package/dist/elements/tabs.js +1 -1
  213. package/dist/elements/tasks.js +1 -1
  214. package/dist/elements/text-shimmer.js +1 -1
  215. package/dist/elements/thinking-bar.js +1 -1
  216. package/dist/elements/thread.js +1 -1
  217. package/dist/elements/toast.js +1 -1
  218. package/dist/elements/tool.js +1 -1
  219. package/dist/elements/tooltip.js +1 -1
  220. package/dist/elements/validate-messages.d.ts +25 -0
  221. package/dist/elements/voice-input.js +1 -1
  222. package/dist/elements/voice-output.js +1 -1
  223. package/dist/elements.d.ts +2734 -217
  224. package/dist/index.d.ts +163 -125
  225. package/dist/index.js +6572 -5990
  226. package/dist/index.server.d.ts +5 -0
  227. package/dist/index.server.js +5403 -4860
  228. package/dist/kai-provider.es.d.ts +5 -0
  229. package/dist/kai-provider.es.js +79 -77
  230. package/dist/kai.es.d.ts +5 -0
  231. package/dist/kai.es.js +1 -1
  232. package/dist/llms/llms-full.txt +774 -151
  233. package/dist/llms/llms.txt +57 -5
  234. package/dist/mcp.es.js +4756 -782
  235. package/dist/primitives/card-component-types.d.ts +11 -0
  236. package/dist/primitives/card-data-types.d.ts +196 -0
  237. package/dist/primitives/card-host.d.ts +1 -1
  238. package/dist/primitives/card-recovery.d.ts +3 -3
  239. package/dist/primitives/card-registry.d.ts +4 -14
  240. package/dist/primitives/card-resolution.d.ts +1 -1
  241. package/dist/primitives/card-routing.d.ts +1 -1
  242. package/dist/primitives/card-tags.d.ts +11 -0
  243. package/dist/primitives/card-validate-cards.d.ts +57 -0
  244. package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
  245. package/dist/primitives/card-validate-schemas.d.ts +17 -0
  246. package/dist/primitives/card-validate.d.ts +24 -0
  247. package/dist/primitives/create-kai-chat.d.ts +3 -3
  248. package/dist/primitives/embed-providers.d.ts +1 -1
  249. package/dist/primitives/link-preview.d.ts +1 -1
  250. package/dist/primitives/message-feedback.d.ts +2 -2
  251. package/dist/primitives/toast-store.d.ts +1 -1
  252. package/dist/react/index.d.ts +730 -302
  253. package/dist/react/use-kai-chat.d.ts +8 -4
  254. package/dist/react.d.ts +5 -0
  255. package/dist/react.js +418 -317
  256. package/dist/register-impl-AxIlqjo7.js +145 -0
  257. package/dist/remote/host-embed.d.ts +1 -1
  258. package/dist/remote/index.d.ts +2 -2
  259. package/dist/remote/provider-runtime.d.ts +2 -2
  260. package/dist/remote/provider.d.ts +2 -2
  261. package/dist/remote/validate.d.ts +1 -1
  262. package/dist/remote/wire.d.ts +1 -1
  263. package/dist/schemas/artifact.schema.json +92 -0
  264. package/dist/schemas/embed.schema.json +0 -2
  265. package/dist/schemas/from-tool-call.d.ts +48 -0
  266. package/dist/schemas/index.d.ts +48 -0
  267. package/dist/schemas/link.schema.json +0 -2
  268. package/dist/schemas/provider-subsets.d.ts +136 -0
  269. package/dist/schemas/registry.d.ts +140 -0
  270. package/dist/schemas/tool-defs.d.ts +121 -0
  271. package/dist/schemas.d.ts +5 -0
  272. package/dist/schemas.js +717 -0
  273. package/dist/solid-BGYSJyHG.js +22960 -0
  274. package/dist/solid-Dt2nEwfl.js +20194 -0
  275. package/dist/solid.d.ts +66 -0
  276. package/dist/solid.js +261 -0
  277. package/dist/solid.server.d.ts +5 -0
  278. package/dist/solid.server.js +261 -0
  279. package/dist/state/index.d.ts +14 -4
  280. package/dist/state/messages.d.ts +8 -3
  281. package/dist/state/mock.d.ts +46 -0
  282. package/dist/state/parts.d.ts +85 -0
  283. package/dist/state/stream.d.ts +15 -9
  284. package/dist/state.d.ts +5 -0
  285. package/dist/state.js +253 -84
  286. package/dist/ui/action-icons.d.ts +1 -1
  287. package/dist/ui/collapsible.d.ts +11 -6
  288. package/dist/ui/dropdown.d.ts +47 -42
  289. package/dist/ui/pane-group.d.ts +1 -1
  290. package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-B44X69AG.js} +1 -1
  291. package/dist/variant-aurora-BRhV8Flk.js +391 -0
  292. package/dist/variant-aurora-Ds5ou9BH.js +381 -0
  293. package/dist/variant-custom-B20FhtYj.js +170 -0
  294. package/dist/{variant-custom-Dif-IV4u.js → variant-custom-BvA1Schl.js} +1 -1
  295. package/dist/variant-custom-D6Rtvneb.js +200 -0
  296. package/dist/{variant-wave-DbfNdvSi.js → variant-wave-BUQtkryq.js} +1 -1
  297. package/dist/variant-wave-BVYnqy9L.js +218 -0
  298. package/dist/variant-wave-DXC6_LcH.js +208 -0
  299. package/dist/wire/chunk.d.ts +171 -0
  300. package/dist/wire/consume.d.ts +26 -0
  301. package/dist/wire/encode.d.ts +221 -0
  302. package/dist/wire/files.d.ts +116 -0
  303. package/dist/wire/formats/anthropic.d.ts +2 -0
  304. package/dist/wire/formats/openai.d.ts +5 -0
  305. package/dist/wire/index.d.ts +16 -0
  306. package/dist/wire/media-types.d.ts +106 -0
  307. package/dist/wire/read.d.ts +26 -0
  308. package/dist/wire/sink-helpers.d.ts +21 -0
  309. package/dist/wire/sse.d.ts +33 -0
  310. package/dist/wire.d.ts +5 -53
  311. package/dist/wire.js +1126 -0
  312. package/frameworks/react/index.tsx +191 -158
  313. package/frameworks/react/runtime.tsx +21 -1
  314. package/frameworks/react/use-kai-chat.tsx +11 -1
  315. package/llms-full.txt +774 -151
  316. package/llms.txt +57 -5
  317. package/package.json +62 -8
  318. package/src/agent-tooling/archetypes.ts +127 -0
  319. package/src/agent-tooling/integrations/anthropic.ts +436 -0
  320. package/src/agent-tooling/integrations/cloudflare.ts +141 -52
  321. package/src/agent-tooling/integrations/langgraph.ts +80 -13
  322. package/src/agent-tooling/integrations/mastra.ts +161 -12
  323. package/src/agent-tooling/integrations/mock.ts +47 -8
  324. package/src/agent-tooling/integrations/ollama.ts +68 -24
  325. package/src/agent-tooling/integrations/openai.ts +89 -0
  326. package/src/agent-tooling/integrations/openrouter.ts +58 -8
  327. package/src/agent-tooling/integrations/pi.ts +104 -23
  328. package/src/agent-tooling/integrations/pydantic-ai.ts +61 -5
  329. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +120 -13
  330. package/src/agent-tooling/mcp/manifest.ts +206 -25
  331. package/src/agent-tooling/mcp/tools/debug.ts +13 -7
  332. package/src/agent-tooling/mcp/tools/reference.ts +285 -6
  333. package/src/agent-tooling/mcp/tools/scaffold.ts +4152 -661
  334. package/src/agent-tooling/registry.ts +74 -0
  335. package/src/agent-tooling/types.ts +361 -1
  336. package/src/components/artifact-card.tsx +120 -0
  337. package/src/components/audio-visualizer/index.tsx +9 -0
  338. package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
  339. package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
  340. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
  341. package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
  342. package/src/components/card-fallback.tsx +42 -10
  343. package/src/components/card-renderer.tsx +146 -9
  344. package/src/components/chat-container.tsx +4 -0
  345. package/src/components/chat-thread.tsx +128 -52
  346. package/src/components/choice-card.tsx +17 -29
  347. package/src/components/composer.tsx +12 -2
  348. package/src/components/confirm-card.tsx +24 -20
  349. package/src/components/context.tsx +30 -16
  350. package/src/components/conversation-item.tsx +6 -0
  351. package/src/components/conversation-list.tsx +18 -2
  352. package/src/components/file-tree.tsx +11 -22
  353. package/src/components/file-upload.tsx +14 -8
  354. package/src/components/form.tsx +14 -47
  355. package/src/components/loader.tsx +39 -12
  356. package/src/components/message-skills.tsx +4 -0
  357. package/src/components/message.tsx +294 -46
  358. package/src/components/model-switcher.tsx +38 -8
  359. package/src/components/reasoning.tsx +92 -5
  360. package/src/components/response-compare-types.ts +25 -0
  361. package/src/components/response-compare.tsx +4 -9
  362. package/src/components/source.tsx +50 -24
  363. package/src/components/tasks-card.tsx +10 -33
  364. package/src/components/thread.tsx +84 -42
  365. package/src/components/toast.tsx +17 -6
  366. package/src/components/tool-classify.ts +26 -0
  367. package/src/components/tool-types.ts +31 -1
  368. package/src/elements/agent-card.tsx +11 -11
  369. package/src/elements/artifact.tsx +15 -7
  370. package/src/elements/attachments.tsx +6 -4
  371. package/src/elements/avatar.tsx +1 -1
  372. package/src/elements/button.tsx +29 -6
  373. package/src/elements/card.tsx +2 -2
  374. package/src/elements/cards.tsx +109 -14
  375. package/src/elements/chain-of-thought.tsx +14 -10
  376. package/src/elements/chat-scope-picker.tsx +9 -7
  377. package/src/elements/chat-types.ts +59 -6
  378. package/src/elements/chat-workspace.tsx +50 -9
  379. package/src/elements/chat.tsx +90 -3
  380. package/src/elements/choice.tsx +8 -8
  381. package/src/elements/coachmark.tsx +1 -1
  382. package/src/elements/command.tsx +8 -23
  383. package/src/elements/compare.tsx +7 -6
  384. package/src/elements/compiled.css +1 -1
  385. package/src/elements/composer.tsx +8 -8
  386. package/src/elements/confirm-card.tsx +9 -9
  387. package/src/elements/context-meter.tsx +4 -18
  388. package/src/elements/conversation-list.tsx +14 -9
  389. package/src/elements/default-input.tsx +131 -8
  390. package/src/elements/define.tsx +178 -43
  391. package/src/elements/editable-label.tsx +17 -2
  392. package/src/elements/element-data-types.ts +112 -0
  393. package/src/elements/element-meta.json +1624 -306
  394. package/src/elements/element-types.d.ts +2645 -214
  395. package/src/elements/feedback-bar.tsx +1 -1
  396. package/src/elements/file-tree.tsx +1 -1
  397. package/src/elements/file-upload.tsx +1 -1
  398. package/src/elements/form.tsx +14 -6
  399. package/src/elements/input.tsx +1 -1
  400. package/src/elements/kbd.tsx +1 -1
  401. package/src/elements/menu.tsx +40 -32
  402. package/src/elements/message-skills.tsx +7 -9
  403. package/src/elements/message.tsx +200 -32
  404. package/src/elements/model-switcher.tsx +5 -3
  405. package/src/elements/pane.tsx +1 -1
  406. package/src/elements/prompt-dock.tsx +8 -8
  407. package/src/elements/prompt-input.tsx +10 -10
  408. package/src/elements/prompt-suggestions.tsx +7 -4
  409. package/src/elements/reasoning.tsx +1 -1
  410. package/src/elements/register.ts +9 -0
  411. package/src/elements/resizable.globals.d.ts +36 -0
  412. package/src/elements/resizable.tsx +24 -13
  413. package/src/elements/response-stream.tsx +1 -1
  414. package/src/elements/scroll-button.tsx +1 -1
  415. package/src/elements/search.tsx +6 -1
  416. package/src/elements/segmented.tsx +5 -11
  417. package/src/elements/separator.tsx +1 -1
  418. package/src/elements/slot-text.ts +73 -0
  419. package/src/elements/slots.ts +67 -24
  420. package/src/elements/source.tsx +21 -22
  421. package/src/elements/switch.tsx +3 -3
  422. package/src/elements/tasks.tsx +5 -4
  423. package/src/elements/thread.tsx +36 -4
  424. package/src/elements/toast.tsx +3 -2
  425. package/src/elements/tool.tsx +1 -1
  426. package/src/elements/validate-messages.ts +76 -0
  427. package/src/elements/voice-input.tsx +7 -7
  428. package/src/elements/voice-output.tsx +4 -4
  429. package/src/index.ts +145 -6
  430. package/src/primitives/card-component-types.ts +63 -0
  431. package/src/primitives/card-data-types.ts +312 -0
  432. package/src/primitives/card-recovery.ts +13 -2
  433. package/src/primitives/card-registry.tsx +34 -23
  434. package/src/primitives/card-schemas/artifact.schema.json +92 -0
  435. package/src/primitives/card-schemas/embed.schema.json +0 -2
  436. package/src/primitives/card-schemas/link.schema.json +0 -2
  437. package/src/primitives/card-tags.ts +61 -0
  438. package/src/primitives/card-validate-cards.ts +255 -0
  439. package/src/primitives/card-validate-generator.testlib.ts +38 -0
  440. package/src/primitives/card-validate-schemas.ts +58 -0
  441. package/src/primitives/card-validate.ts +48 -20
  442. package/src/primitives/create-tween.ts +54 -2
  443. package/src/primitives/message-feedback.ts +3 -2
  444. package/src/primitives/use-audio-analysis.ts +20 -1
  445. package/src/primitives/use-sequencer.ts +20 -1
  446. package/src/schemas/from-tool-call.ts +151 -0
  447. package/src/schemas/index.ts +340 -0
  448. package/src/schemas/provider-subsets.ts +538 -0
  449. package/src/schemas/registry.ts +355 -0
  450. package/src/schemas/tool-defs.ts +492 -0
  451. package/src/solid.ts +150 -0
  452. package/src/state/index.ts +27 -1
  453. package/src/state/messages.ts +21 -4
  454. package/src/state/mock.ts +211 -0
  455. package/src/state/parts.ts +295 -0
  456. package/src/state/stream.ts +56 -58
  457. package/src/ui/collapsible.tsx +17 -5
  458. package/src/ui/dropdown.tsx +46 -12
  459. package/src/ui/overlay.tsx +10 -4
  460. package/src/wire/chunk.ts +201 -0
  461. package/src/wire/consume.ts +440 -0
  462. package/src/wire/encode.ts +689 -0
  463. package/src/wire/files.ts +342 -0
  464. package/src/wire/formats/anthropic.ts +267 -0
  465. package/src/wire/formats/openai.ts +187 -0
  466. package/src/wire/index.ts +73 -0
  467. package/src/wire/media-types.ts +344 -0
  468. package/src/wire/read.ts +152 -0
  469. package/src/wire/sink-helpers.ts +50 -0
  470. package/src/wire/sse.ts +140 -0
  471. package/dist/create-tween-hvpmSuwW.js +0 -7
  472. package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
  473. package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
  474. package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
  475. package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
  476. package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
  477. package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
  478. package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
  479. package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
  480. package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
  481. package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
  482. package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
  483. package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
  484. package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
  485. package/dist/elements/chunks/default-input-Cqqilg9t.js +0 -1
  486. package/dist/elements/chunks/define-D1din7Ld.js +0 -1
  487. package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
  488. package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
  489. package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
  490. package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
  491. package/dist/elements/chunks/model-switcher-B94YAKsM.js +0 -1
  492. package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
  493. package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
  494. package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
  495. package/dist/elements/chunks/slots--V19HwUb.js +0 -1
  496. package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
  497. package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
  498. package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
  499. package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
  500. package/dist/host-embed.d.ts +0 -33
  501. package/dist/kai-provider.d.ts +0 -2
  502. package/dist/origin.d.ts +0 -5
  503. package/dist/provider-runtime.d.ts +0 -27
  504. package/dist/register-impl-DQyejdfb.js +0 -145
  505. package/dist/validate.d.ts +0 -4
  506. package/dist/version.d.ts +0 -4
  507. package/src/components/component-meta.json +0 -4550
  508. package/src/elements/card-media.jpg +0 -0
  509. package/src/elements/resizable.d.ts +0 -27
  510. package/src/elements/video-poster.jpg +0 -0
@@ -31,7 +31,7 @@ export function parseKaiTriggerItemElement(n: Element, char: string, kind: strin
31
31
  }
32
32
 
33
33
  interface Props extends Record<string, unknown> {
34
- /** Controlled value string or a full ComposerDoc (set as JS property). */
34
+ /** Controlled value: a string or a full ComposerDoc (set as JS property). */
35
35
  value?: string | ComposerDoc;
36
36
  /** Placeholder text shown when the composer is empty. */
37
37
  placeholder?: string;
@@ -43,9 +43,9 @@ interface Props extends Record<string, unknown> {
43
43
  maxHeight?: number | string;
44
44
  /** Whether pressing Enter (without Shift) submits. Default true. */
45
45
  submitOnEnter?: boolean;
46
- /** Trigger definitions set as a JS property. */
46
+ /** Trigger definitions. Set as a JS property. */
47
47
  triggers?: TriggerDef[];
48
- /** Keyword highlight rules set as a JS property. */
48
+ /** Keyword highlight rules. Set as a JS property. */
49
49
  highlights?: HighlightRule[];
50
50
  /** Default icon per entity kind (kind → image URL/data-URI) for items without
51
51
  * their own `icon`. Overrides the built-in agent/plugin glyphs. JS property. */
@@ -67,7 +67,7 @@ interface Events {
67
67
  /** The active trigger was dismissed (Escape, space, or outside click). */
68
68
  'kai-trigger-close': Record<string, never>;
69
69
  /** The composer gained focus. `focus`/`blur` are NOT composed natively, so they
70
- * don't escape the shadow root these re-expose them on the host. (For
70
+ * don't escape the shadow root; these re-expose them on the host. (For
71
71
  * `keydown`/`paste`/`focusin`/`focusout`, listen NATIVELY on `<kai-composer>`:
72
72
  * they're composed and already cross the shadow boundary.) */
73
73
  'kai-focus': { originalEvent: FocusEvent };
@@ -100,7 +100,7 @@ defineWebComponent<Props, Events>('kai-composer', {
100
100
  let controller: ComposerController | undefined;
101
101
  expose({
102
102
  /** Focus the editable element. `focus`/`blur` are NOT composed natively, so a
103
- * host-level focus() can't reach the editable inside the shadow root this is
103
+ * host-level focus() can't reach the editable inside the shadow root. This is
104
104
  * the only way to focus the composer programmatically. */
105
105
  focus: (options?: FocusOptions) => controller?.focus(options),
106
106
  /** Blur the editable element. */
@@ -108,9 +108,9 @@ defineWebComponent<Props, Events>('kai-composer', {
108
108
  /** Empty the composer to a blank doc (resets the internal value + history
109
109
  * baseline; fires kai-value-change). */
110
110
  clear: () => controller?.clear(),
111
- /** Submit the current content programmatically same path as Enter (fires
112
- * kai-submit). Named `send`, not `submit`, to match the shared vocabulary and
113
- * avoid any submit collision. */
111
+ /** Submit the current content programmatically, on the same path as Enter
112
+ * (fires kai-submit). Named `send`, not `submit`, to match the shared
113
+ * vocabulary and avoid any submit collision. */
114
114
  send: () => controller?.send(),
115
115
  /** Insert an atomic entity pill (skill/agent/plugin) at the caret without
116
116
  * typing a trigger (fires kai-entity-add). */
@@ -6,13 +6,13 @@ interface Props extends Record<string, unknown> {
6
6
  /** The confirm definition (the CardEnvelope.data). Set as a JS PROPERTY:
7
7
  * `el.data = { body, tone, actions:[…] }`. Import `ConfirmCardData` from
8
8
  * `@kitn.ai/ui` for the full shape. */
9
- data?: Record<string, unknown>;
9
+ data?: ConfirmCardData;
10
10
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
11
11
  cardId?: string;
12
12
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
13
13
  heading?: string;
14
- /** Focus the default action on mount (off by default no focus-stealing).
15
- * Attribute: `autofocus`. */
14
+ /** Focus the default action on mount (off by default, so nothing steals
15
+ * focus). Attribute: `autofocus`. */
16
16
  autofocus?: boolean;
17
17
  /** Set when the user resolved this card; renders the read-only view. Property:
18
18
  * `el.resolution = { kind:'action', action:'…' }`. */
@@ -48,22 +48,22 @@ defineWebComponent<Props>(
48
48
  // `dismiss`/`reopen`).
49
49
  let controller: ConfirmController | undefined;
50
50
  expose({
51
- /** Focus the default action button (or the first action if none is default)
52
- * the same target `autofocus` focuses on mount, but on demand. */
51
+ /** Focus the default action button (or the first action if none is default).
52
+ * The same target `autofocus` focuses on mount, but on demand. */
53
53
  focus: (options?: FocusOptions) => controller?.focus(options),
54
- /** Activate an action by id emits the `action` verb on kai-card and resolves
54
+ /** Activate an action by id: emits the `action` verb on kai-card and resolves
55
55
  * the card (single-shot). With no id, invokes the default action. */
56
56
  confirm: (actionId?: string) => controller?.confirm(actionId),
57
- /** Trigger the dismiss path emits `dismiss` on kai-card and optimistically
57
+ /** Trigger the dismiss path: emits `dismiss` on kai-card and optimistically
58
58
  * collapses the card to its re-openable stub. */
59
59
  dismiss: () => controller?.dismiss(),
60
- /** Re-open a dismissed card from its stub emits `reopen` on kai-card. */
60
+ /** Re-open a dismissed card from its stub: emits `reopen` on kai-card. */
61
61
  reopen: () => controller?.reopen(),
62
62
  });
63
63
 
64
64
  return (
65
65
  <ConfirmCard
66
- data={props.data as ConfirmCardData | undefined}
66
+ data={props.data}
67
67
  cardId={props.cardId ?? (element.id || 'kai-confirm')}
68
68
  heading={props.heading}
69
69
  autofocus={flag('autofocus')}
@@ -16,27 +16,13 @@ import {
16
16
  DEFAULT_DANGER_THRESHOLD,
17
17
  type ContextSeverity,
18
18
  } from '../components/context';
19
-
20
- interface ContextUsage {
21
- /** Tokens consumed so far in the context window (drives the meter fill). */
22
- usedTokens: number;
23
- /** The model's total context-window size in tokens. */
24
- maxTokens: number;
25
- /** Tokens attributed to the prompt/input, shown in the breakdown. */
26
- inputTokens?: number;
27
- /** Tokens attributed to the generated output, shown in the breakdown. */
28
- outputTokens?: number;
29
- /** Tokens attributed to reasoning/thinking, shown when present. */
30
- reasoningTokens?: number;
31
- /** Tokens served from prompt cache, shown when present. */
32
- cacheTokens?: number;
33
- /** Estimated cost in dollars for this usage, shown in the footer. */
34
- estimatedCost?: number;
35
- }
19
+ // Public shape of the `context` prop; lives in ./element-data-types so the ROOT
20
+ // entry can re-export it (see that file's header).
21
+ import type { KaiContextUsage } from './element-data-types';
36
22
 
37
23
  interface Props extends Record<string, unknown> {
38
24
  /** Token-usage data. Set as a JS property. */
39
- context?: ContextUsage;
25
+ context?: KaiContextUsage;
40
26
  /**
41
27
  * Fraction (0–1) above which the meter turns yellow.
42
28
  * Defaults to `0.7` (70%).
@@ -6,13 +6,18 @@ import { ConversationList, CollapsedRail, type ConversationListController } from
6
6
  import type { ConversationGroup, ConversationSummary, ConversationScope } from '../types';
7
7
 
8
8
  interface Props extends Record<string, unknown> {
9
- /** Pre-bucketed conversation groups (e.g. "Today", "Yesterday"), each with its
10
- * own conversations. Use this when you want to control the grouping/headers
11
- * yourself; otherwise pass a flat `conversations` array. Set as a JS property. */
12
- groups: ConversationGroup[];
13
- /** A flat list of conversation summaries; the component buckets them by recency
14
- * for you. Ignored when `groups` is provided. Set as a JS property. */
15
- conversations: ConversationSummary[];
9
+ /** The list's section headers (`{ id, name, sortOrder, createdAt }`), rendered
10
+ * in array order. A group carries no conversations of its own; it is matched
11
+ * against `conversations` by id, so the two props are complementary rather
12
+ * than alternatives. Omit for an ungrouped list. Set as a JS property. */
13
+ groups?: ConversationGroup[];
14
+ /** Every conversation the list renders, flat. Each one is filed under the group
15
+ * whose `id` equals its `groupId`; one with no `groupId`, or with a `groupId`
16
+ * matching no entry in `groups`, falls into a trailing "Ungrouped" section, so
17
+ * nothing you pass in is ever dropped. There is no recency bucketing. Set as a
18
+ * JS property. Omit to supply them as `<kai-conversation>` light-DOM children
19
+ * instead, or for the empty state. */
20
+ conversations?: ConversationSummary[];
16
21
  /** The id of the currently-open conversation, highlighted in the list. */
17
22
  activeId?: string;
18
23
  /** Controlled collapsed state. Set as a JS property (`el.collapsed = true`) to
@@ -115,7 +120,7 @@ defineWebComponent<Props, Events>('kai-conversations', {
115
120
  /** Clear the internal search query (resets the list filter) and fire
116
121
  * kai-search with an empty string. */
117
122
  clear: () => controller?.clearSearch(),
118
- /** Programmatically select a conversation by id mirror of the
123
+ /** Programmatically select a conversation by id. The mirror of the
119
124
  * kai-conversation-select event (a convenience over driving `activeId`). */
120
125
  select: (id: string) => dispatch('kai-conversation-select', { id }),
121
126
  /** Collapse the rail to its floating reopen button (fires `kai-collapse-toggle`). */
@@ -132,7 +137,7 @@ defineWebComponent<Props, Events>('kai-conversations', {
132
137
  fallback={<CollapsedRail onExpand={() => setCollapsedTo(false)} />}
133
138
  >
134
139
  <ConversationList
135
- groups={props.groups}
140
+ groups={props.groups ?? []}
136
141
  conversations={allConversations()}
137
142
  activeId={props.activeId}
138
143
  onSelect={(id) => dispatch('kai-conversation-select', { id })}
@@ -16,6 +16,32 @@ import {
16
16
  } from '../components/attachments';
17
17
  import { actionIcon } from '../ui/action-icons';
18
18
  import type { CustomAction } from './chat-types';
19
+ // The LEAF module, deliberately not the `../wire` barrel: this needs the media
20
+ // declaration, and importing the barrel would pull the whole stream adapter into
21
+ // the elements bundle for a table of strings. Sharing the module itself (rather
22
+ // than copying the list into the composer) is the entire point -- a second list
23
+ // here is the drift this design exists to prevent.
24
+ import { resolveMediaPolicy, type MediaTypeFilter } from '../wire/media-types';
25
+ // Also a leaf, and imported for the same reason: when the browser cannot name a
26
+ // file, the question "can this be sent?" is answered by decoding its bytes, and
27
+ // this module is where that decode already lives. Asking IT rather than growing
28
+ // a second decoder here is what keeps the composer's answer and the encoder's
29
+ // answer the same answer, byte for byte, instead of two that agree today.
30
+ import { classifyAttachment } from '../wire/files';
31
+
32
+ /** One file the composer refused, as facts rather than as a message. The kit
33
+ * says what happened; the application decides what the user reads. */
34
+ export interface RejectedAttachment {
35
+ filename: string;
36
+ /** The browser's media type for the file, or `''` when it could not tell. An
37
+ * empty one is not by itself why a file was rejected: an unnamed file is
38
+ * decided by decoding its bytes, so `''` here means the decode is what said
39
+ * no (binary), or that your `accept` left no text type for it to land in. */
40
+ mediaType: string;
41
+ /** `'filtered'` = this kit could have sent it, your `accept` excluded it.
42
+ * `'unsupported'` = no API takes this as message content at all. */
43
+ reason: 'filtered' | 'unsupported';
44
+ }
19
45
 
20
46
  export interface DefaultPromptInputProps {
21
47
  /** String = controlled text mirror; ComposerDoc = a seed that pre-populates pills. */
@@ -31,14 +57,27 @@ export interface DefaultPromptInputProps {
31
57
  * `onAttachmentsChange` is provided (e.g. when a `+` menu already covers
32
58
  * file-attach). Defaults to `true`. */
33
59
  attach?: boolean;
60
+ /** Which attachment media types the user may stage, in HTML `accept` syntax
61
+ * (`'image/*,application/pdf'`). Omitted means no filter, exactly as before.
62
+ *
63
+ * NARROWED BY WHAT THE ENCODERS CAN SEND: `'image/*'` here resolves to the
64
+ * four image formats both APIs actually accept, not to every image type the
65
+ * OS will offer. It is the same string, resolved by the same function against
66
+ * the same declaration, as `toOpenAIMessages(msgs, { accept })` -- so a file
67
+ * the picker allows is a file the wire can carry. */
68
+ accept?: MediaTypeFilter;
69
+ /** Files that were dropped because `accept` excluded them. Carries the facts
70
+ * (name, media type, reason) and renders nothing itself: what the user should
71
+ * see is the application's call, not the kit's. */
72
+ onAttachmentsRejected?: (rejected: RejectedAttachment[]) => void;
34
73
  /** Show a Search (Globe) button in the left toolbar; calls `onSearch`. */
35
74
  search?: boolean;
36
75
  /** Show a Voice (Mic) button in the left toolbar; calls `onVoice`. */
37
76
  voice?: boolean;
38
77
  /** Send-button visibility. `'always'` (default) always shows it; `'auto'` shows
39
- * it only when there's text/attachments (an empty composer hides it — Enter
40
- * still submits). To hide it entirely (Enter-only), it's pure CSS:
41
- * `::part(send){display:none}` no prop needed. Restyle via `::part(send)`.
78
+ * it only when there's text/attachments (an empty composer hides it, though
79
+ * Enter still submits). To hide it entirely (Enter-only), it's pure CSS:
80
+ * `::part(send){display:none}`, no prop needed. Restyle via `::part(send)`.
42
81
  * The Stop button (stoppable + loading) is unaffected. */
43
82
  submit?: 'always' | 'auto';
44
83
  onValueChange: (v: string) => void;
@@ -64,7 +103,23 @@ export interface DefaultPromptInputProps {
64
103
  onComposerChange?: (change: ComposerChange) => void;
65
104
  }
66
105
 
67
- function fileToAttachment(file: File): AttachmentData {
106
+ /** The staged file as a `data:` URI.
107
+ *
108
+ * NOT `URL.createObjectURL`. An object URL resolves only inside the tab that
109
+ * minted it, so it renders a perfect thumbnail here and is meaningless to
110
+ * anything downstream — `toOpenAIMessages` / `toAnthropicMessages` refuse it,
111
+ * and before they refused it the attachment reached the model as nothing at
112
+ * all. A data URI previews identically and is the one form both providers
113
+ * actually take. */
114
+ const readAsDataUrl = (file: File): Promise<string> =>
115
+ new Promise((resolve, reject) => {
116
+ const reader = new FileReader();
117
+ reader.onload = () => resolve(String(reader.result));
118
+ reader.onerror = () => reject(reader.error ?? new Error(`Could not read ${file.name}`));
119
+ reader.readAsDataURL(file);
120
+ });
121
+
122
+ async function fileToAttachment(file: File): Promise<AttachmentData> {
68
123
  const id =
69
124
  typeof crypto !== 'undefined' && crypto.randomUUID
70
125
  ? crypto.randomUUID()
@@ -74,7 +129,9 @@ function fileToAttachment(file: File): AttachmentData {
74
129
  type: 'file',
75
130
  filename: file.name,
76
131
  mediaType: file.type || undefined,
77
- url: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,
132
+ // EVERY file, not just images. A document used to get no `url` at all,
133
+ // which left it unencodable for exactly the same reason a blob: URL is.
134
+ url: await readAsDataUrl(file),
78
135
  };
79
136
  }
80
137
 
@@ -83,9 +140,67 @@ export function DefaultPromptInput(props: DefaultPromptInputProps) {
83
140
  const attachments = () => props.attachments ?? [];
84
141
  const canAttach = () => !!props.onAttachmentsChange;
85
142
 
86
- const addFiles = (files: FileList | null) => {
143
+ // The SAME resolver the encoders call, on the same declaration. Recomputed
144
+ // reactively so a host that swaps `accept` at runtime moves the picker hint
145
+ // and the filter together.
146
+ const mediaPolicy = () => resolveMediaPolicy({ accept: props.accept });
147
+
148
+ const addFiles = async (files: FileList | null) => {
87
149
  if (!files?.length || !props.onAttachmentsChange) return;
88
- props.onAttachmentsChange([...attachments(), ...Array.from(files).map(fileToAttachment)]);
150
+
151
+ // The `accept` ATTRIBUTE below is only a hint — every OS dialog offers an
152
+ // "All Files" escape, and it does not apply to drag-and-drop at all. So the
153
+ // filter that actually holds is this one, in JS, on the files as staged.
154
+ const policy = mediaPolicy();
155
+ const picked = Array.from(files);
156
+ // No `accept` means no filtering, which is what every existing consumer gets
157
+ // today. Opting in is what turns the picker into a guarantee.
158
+ const decisions = picked.map((file) =>
159
+ props.accept === undefined ? undefined : policy.decide(file.type),
160
+ );
161
+ // Read a file when its media type says yes, and ALSO when its media type
162
+ // says nothing at all: `undetermined` means the browser could not name it,
163
+ // and the only honest way to answer is to look at the bytes. Reading is the
164
+ // cost of that answer, so it is spent only on files that could still be
165
+ // staged — a file the filter already rejected is never read.
166
+ const needsBytes = (d: (typeof decisions)[number]): boolean =>
167
+ d === undefined || d.status === 'allowed' || d.status === 'undetermined';
168
+ // One parallel pass, index-aligned with `picked`, so the staged order is the
169
+ // PICK order rather than whichever file finished reading first.
170
+ const read = await Promise.all(
171
+ picked.map((file, i) => (needsBytes(decisions[i]) ? fileToAttachment(file) : undefined)),
172
+ );
173
+
174
+ const staged: AttachmentData[] = [];
175
+ const rejected: RejectedAttachment[] = [];
176
+ picked.forEach((file, i) => {
177
+ const decision = decisions[i];
178
+ const attachment = read[i];
179
+ if (decision === undefined || decision.status === 'allowed') {
180
+ if (attachment) staged.push(attachment);
181
+ return;
182
+ }
183
+ if (decision.status === 'undetermined') {
184
+ // The bytes are in hand now, so the encoder can answer for real. Its
185
+ // verdict IS the composer's verdict: staging anything it would refuse is
186
+ // the exact defect (#186) this whole design exists to prevent.
187
+ if (attachment && classifyAttachment(attachment, policy).status === 'encodable') {
188
+ staged.push(attachment);
189
+ } else {
190
+ // Not `filtered`: the kit could not encode this either, so pointing
191
+ // the developer at their own `accept` would send them the wrong way.
192
+ rejected.push({ filename: file.name, mediaType: file.type, reason: 'unsupported' });
193
+ }
194
+ return;
195
+ }
196
+ rejected.push({ filename: file.name, mediaType: file.type, reason: decision.status });
197
+ });
198
+
199
+ if (rejected.length > 0) props.onAttachmentsRejected?.(rejected);
200
+ if (staged.length === 0) return;
201
+ // Re-read `attachments()` AFTER the await — a second drop while these were
202
+ // being read would otherwise be overwritten by this call's stale snapshot.
203
+ props.onAttachmentsChange?.([...attachments(), ...staged]);
89
204
  };
90
205
  const removeAttachment = (id: string) =>
91
206
  props.onAttachmentsChange?.(attachments().filter((a) => a.id !== id));
@@ -156,8 +271,16 @@ export function DefaultPromptInput(props: DefaultPromptInputProps) {
156
271
  type="file"
157
272
  multiple
158
273
  class="hidden"
274
+ // Derived from the same policy as the filter above, never spelled
275
+ // out again. `accept="image/*"` narrows to the image formats the
276
+ // wire can actually carry, so the OS dialog greys out the SVG
277
+ // that would otherwise 400 at request time.
278
+ accept={props.accept === undefined ? undefined : mediaPolicy().accept}
159
279
  onChange={(e) => {
160
- addFiles(e.currentTarget.files);
280
+ // Reading is async now, so this is deliberately not awaited.
281
+ // `addFiles` captures the FileList synchronously, before its
282
+ // first await, so clearing the input below cannot race it.
283
+ void addFiles(e.currentTarget.files);
161
284
  e.currentTarget.value = ''; // allow re-picking the same file
162
285
  }}
163
286
  />
@@ -96,6 +96,147 @@ function toAttr(name: string): string {
96
96
  return name.replace(/([A-Z])/g, '-$1').toLowerCase();
97
97
  }
98
98
 
99
+ /**
100
+ * Every property name the built-in element prototype chain exposes as an ACCESSOR
101
+ * — `role`, `hidden`, `autofocus`, `dir`, `title`, `className`, … — i.e. the
102
+ * reflected IDL attributes plus the handful of read-only accessors. Methods
103
+ * (`focus`, `append`) are plain data properties and are deliberately not included:
104
+ * assigning over one shadows it on the instance but destroys nothing on the DOM.
105
+ *
106
+ * DERIVED, not listed. A hand-written list is a list someone has to remember to
107
+ * extend: the collision set is (this element's prop names) × (whatever the browser
108
+ * reflects), and both sides move — the kit adds props, and the platform keeps
109
+ * adding globals (`inert`, `popover`, `writingSuggestions` are all recent). Reading
110
+ * it off the live prototype chain means a prop that starts colliding tomorrow is
111
+ * protected the day it lands, in whichever engines have shipped the accessor,
112
+ * without a code change. It also self-limits: in an engine that has NOT shipped a
113
+ * given accessor (jsdom has no `autofocus`) the name is simply not a collision
114
+ * there, which is exactly right.
115
+ *
116
+ * Computed once — the chain does not change at runtime.
117
+ */
118
+ let globalAccessorNames: Set<string> | undefined;
119
+ export function reflectedGlobalPropNames(): Set<string> {
120
+ if (globalAccessorNames) return globalAccessorNames;
121
+ const names = new Set<string>();
122
+ let proto: object | null =
123
+ typeof HTMLElement === 'undefined' ? null : (HTMLElement.prototype as object);
124
+ while (proto && proto !== Object.prototype) {
125
+ for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(proto))) {
126
+ if (descriptor.get || descriptor.set) names.add(name);
127
+ }
128
+ proto = Object.getPrototypeOf(proto) as object | null;
129
+ }
130
+ globalAccessorNames = names;
131
+ return names;
132
+ }
133
+
134
+ /**
135
+ * Replace inherited reflected accessors with plain, non-reflecting per-instance
136
+ * stores on this element class's prototype. Setting the property then only STORES
137
+ * the value — component-register still reads it back to seed the styling prop, and
138
+ * later shadows this with its own (also non-reflecting) instance accessor — and it
139
+ * can never touch a host attribute.
140
+ */
141
+ function installNonReflectingProps(proto: object, keys: readonly string[]): void {
142
+ for (const key of keys) {
143
+ const store = Symbol(`kai-prop:${key}`);
144
+ Object.defineProperty(proto, key, {
145
+ get(this: Record<symbol, unknown>) {
146
+ return this[store];
147
+ },
148
+ set(this: Record<symbol, unknown>, value: unknown) {
149
+ this[store] = value;
150
+ },
151
+ enumerable: true,
152
+ configurable: true,
153
+ });
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Run `define` (solid-element's `customElement`, which calls
159
+ * `customElements.define` internally) with `keys` already shadowed on the element
160
+ * class — BEFORE the registry ever sees it.
161
+ *
162
+ * THE ORDER IS THE WHOLE POINT. `customElements.define()` synchronously upgrades
163
+ * every matching element the parser has already produced, and component-register's
164
+ * constructor runs `this[prop] = undefined` for every declared prop. If the
165
+ * non-reflecting accessor is installed AFTER the define call, that assignment lands
166
+ * on the NATIVE setter, which for a reflected IDL attribute means the author's
167
+ * markup is destroyed before a line of facade code runs — `undefined` coerces to
168
+ * `null`/`false` and the setter removes the attribute. Measured in chromium from
169
+ * parsed HTML (`node scripts/probe-upgrade-attribute-loss.mjs`):
170
+ *
171
+ * <kai-message role="user"> → role attr gone, el.role "assistant",
172
+ * the row renders as "Assistant message"
173
+ * <kai-resizable-item hidden> → hidden attr gone, el.hidden false, the
174
+ * panel is slotted and RENDERS VISIBLE
175
+ * <kai-confirm autofocus> → autofocus attr gone, nothing focused
176
+ *
177
+ * Two of those are silent: `hidden` and `autofocus` are legitimate global
178
+ * attributes, so no a11y rule and no console warning fires — the value simply
179
+ * evaporates. Only `role` was loud enough to get noticed.
180
+ *
181
+ * WHY A TRANSIENT REGISTRY WRAP and not something tidier: the class does not exist
182
+ * until `register()` builds it, and `register()` defines it in the same statement.
183
+ * component-register can take a `BaseElement` (a prototype we control) or a
184
+ * `customElements`-alike (a registry we control) via its third argument — either
185
+ * would be cleaner — but solid-element's `customElement()` calls `register(tag,
186
+ * props)` with no options and forwards nothing, and `component-register` is not a
187
+ * declared dependency of this package, so reaching past solid-element to call
188
+ * `register` ourselves would ship an undeclared import to consumers. Intercepting
189
+ * the one `define` call is the smallest seam that exists.
190
+ *
191
+ * The wrap is synchronous, restored in a `finally`, delegates to whatever was
192
+ * installed (so a polyfilled or consumer-wrapped registry keeps working), acts only
193
+ * on OUR tag, and is re-entrancy safe: a facade that defines another element during
194
+ * its upgrade nests a second wrap and unwinds it in order.
195
+ */
196
+ function defineWithNonReflectingProps<T>(
197
+ tag: string,
198
+ keys: readonly string[],
199
+ define: () => T,
200
+ ): { result: T; handled: boolean } {
201
+ // `handled` = nothing more for the caller to do. With no colliding props there is
202
+ // nothing to install at all, so that case is trivially handled.
203
+ if (keys.length === 0) return { result: define(), handled: true };
204
+ const registry = customElements;
205
+ const hadOwnDefine = Object.prototype.hasOwnProperty.call(registry, 'define');
206
+ const inner = registry.define;
207
+ let handled = false;
208
+ let restore: (() => void) | undefined;
209
+ try {
210
+ registry.define = function (
211
+ this: CustomElementRegistry,
212
+ name: string,
213
+ constructor: CustomElementConstructor,
214
+ options?: ElementDefinitionOptions,
215
+ ) {
216
+ if (name === tag) {
217
+ installNonReflectingProps(constructor.prototype, keys);
218
+ handled = true;
219
+ }
220
+ return inner.call(this, name, constructor, options);
221
+ };
222
+ restore = () => {
223
+ if (hadOwnDefine) registry.define = inner;
224
+ else delete (registry as unknown as Record<string, unknown>).define;
225
+ };
226
+ } catch {
227
+ // A frozen registry (SES lockdown, a sealed polyfill, a hardened extension
228
+ // page): we cannot get ahead of define() there. Leave `handled` false so the
229
+ // caller installs on the returned class instead — the OLD, too-late timing, but
230
+ // registration itself still succeeds. Getting this wrong the other way would
231
+ // take all 80 elements down in that environment to fix three props in it.
232
+ }
233
+ try {
234
+ return { result: define(), handled };
235
+ } finally {
236
+ restore?.();
237
+ }
238
+ }
239
+
99
240
  /** Underlying flag resolution; see `WebComponentContext.flag`. */
100
241
  function resolveFlag(element: HTMLElement, value: unknown, attribute: string): boolean {
101
242
  if (value === true) return true;
@@ -148,27 +289,33 @@ export function defineWebComponent<P extends Record<string, unknown>, E = Record
148
289
  }
149
290
  }
150
291
 
151
- // Props that NAME a reflected global IDL accessor (today: only `role`, from
152
- // ARIAMixin's Element.prototype.role). Unlike RESERVED (which throws), these are
153
- // allowed as plain styling props, but they must NEVER reach the host as an
154
- // attribute: kai-message uses role='user'|'assistant' to pick its layout, and
155
- // those are not valid ARIA roles, so a reflected role="user" / role="assistant"
156
- // fails axe's aria-roles rule. The native IDL setter reflects to an attribute
157
- // whenever the property is set BEFORE connectedCallback (e.g. in a framework
158
- // `ref`: `el.role = 'user'`), which is the window before component-register
159
- // installs its own (non-reflecting) per-instance accessor in initializeProps. We
160
- // shadow the native accessor on the element prototype with a plain non-reflecting
161
- // one, so setting the property only STORES the value (component-register still
162
- // reads it back for styling) and never touches a host attribute.
163
- const NON_REFLECTING = ['role'];
292
+ // Every element gets a `theme` property/attribute. It drives a `.dark` class on
293
+ // an inner wrapper, which the injected kit CSS already styles — so dark mode
294
+ // works in standalone Shadow-DOM usage with no token duplication.
295
+ //
296
+ // The `as` annotation is load-bearing, not decoration: scripts/gen-element-api.mjs
297
+ // reads THIS object literal to learn which props every element gets for free, and
298
+ // takes each one's type and doc comment off the checker. Without it the inferred
299
+ // type is the literal `"auto"`. Add a universal prop here and it appears in
300
+ // element-meta.json, the generated .d.ts, custom-elements.json and llms-full.txt
301
+ // for all 79 elements, with no second list to update.
302
+ const defaults = {
303
+ /** Color mode (`auto` follows prefers-color-scheme). */
304
+ theme: 'auto' as 'light' | 'dark' | 'auto',
305
+ ...propDefaults,
306
+ };
164
307
 
165
- // Every element gets a `theme` property/attribute: 'light' | 'dark' | 'auto'
166
- // (default 'auto' = follow the OS `prefers-color-scheme`). It drives a `.dark`
167
- // class on an inner wrapper, which the injected kit CSS already styles — so dark
168
- // mode works in standalone Shadow-DOM usage with no token duplication.
169
- const defaults = { theme: 'auto', ...propDefaults };
308
+ // Props that NAME a reflected global IDL accessor (today, across all 80 elements:
309
+ // `role` on kai-message, `hidden` on kai-resizable-item, `autofocus` on
310
+ // kai-confirm). Unlike RESERVED (which throws), these are legitimate domain props
311
+ // `role` is the SPEAKER, `hidden` and `autofocus` mean what they say — and they
312
+ // stay usable, but the native accessor must not be the one that receives them.
313
+ // See defineWithNonReflectingProps for what the native setter does to the author's
314
+ // markup if it is, and why the shadowing has to be in place before the registry
315
+ // sees the class. RESERVED names never get here; they already threw above.
316
+ const shadowedProps = Object.keys(defaults).filter((key) => reflectedGlobalPropNames().has(key));
170
317
 
171
- const Ctor = customElement(tag, defaults, (props: typeof defaults, options: { element: object }) => {
318
+ const renderFacade = (props: typeof defaults, options: { element: object }) => {
172
319
  const element = options.element as HTMLElement;
173
320
  let portalNode!: HTMLDivElement;
174
321
 
@@ -239,30 +386,18 @@ export function defineWebComponent<P extends Record<string, unknown>, E = Record
239
386
  </div>
240
387
  </>
241
388
  );
242
- });
389
+ };
390
+
391
+ const { result: Ctor, handled } = defineWithNonReflectingProps(tag, shadowedProps, () =>
392
+ customElement(tag, defaults, renderFacade),
393
+ );
243
394
 
244
- // Shadow any reflected native IDL accessor this element re-uses as a prop (see
245
- // NON_REFLECTING) with a plain, non-reflecting store on the element prototype.
246
- // This must live on the prototype (not the instance) so it is already in place
247
- // when the value is set before connectedCallback, the window in which the native
248
- // setter would otherwise reflect it to a host attribute. component-register reads
249
- // the property back during initializeProps to seed the styling prop, and later
250
- // shadows this with its own (also non-reflecting) per-instance accessor.
395
+ // Belt and braces for the one path the wrap cannot see: if `customElements.define`
396
+ // was never reached for this tag (a registry that defines lazily, or a future
397
+ // solid-element that stops calling it synchronously), install on the returned class
398
+ // instead. That is the OLD, too-late timing it still protects every element
399
+ // constructed from here on, it just cannot rescue an already-upgraded one. Skipped
400
+ // in the normal case, where the accessors are already on this same prototype.
251
401
  const proto = (Ctor as unknown as { prototype?: object } | undefined)?.prototype;
252
- if (proto) {
253
- for (const key of NON_REFLECTING) {
254
- if (!(key in defaults)) continue;
255
- const store = Symbol(`kai-prop:${key}`);
256
- Object.defineProperty(proto, key, {
257
- get(this: Record<symbol, unknown>) {
258
- return this[store];
259
- },
260
- set(this: Record<symbol, unknown>, value: unknown) {
261
- this[store] = value;
262
- },
263
- enumerable: true,
264
- configurable: true,
265
- });
266
- }
267
- }
402
+ if (!handled && proto) installNonReflectingProps(proto, shadowedProps);
268
403
  }
@@ -3,7 +3,7 @@ import { defineWebComponent } from './define';
3
3
  import { EditableLabel } from '../ui/editable-label';
4
4
 
5
5
  interface Props extends Record<string, unknown> {
6
- /** The label text settable and reflected to the `value` attribute. Read
6
+ /** The label text. Settable and reflected to the `value` attribute. Read
7
7
  * `el.value` for live state. */
8
8
  value?: string;
9
9
  /** Controlled edit state. `el.editing = true` opens the field; reflected to the
@@ -104,7 +104,22 @@ defineWebComponent<Props, Events>('kai-editable-label', {
104
104
  }
105
105
  };
106
106
 
107
- expose({ edit: enter, commit, cancel });
107
+ expose({
108
+ /** Switch the label into its editing field, which autofocuses and selects the
109
+ * current text. Same entry point as a user double-click, and a no-op while
110
+ * `disabled`. Commit with `commit()` or by blurring, abandon with `cancel()`
111
+ * or Escape. */
112
+ edit: enter,
113
+ /** Close the field and keep what was typed, exactly as blurring it does.
114
+ * `kai-rename` fires only when the text actually changed, so committing an
115
+ * untouched field is silent. A no-op while the field is closed. */
116
+ commit,
117
+ /** Abandon the edit, exactly as Escape does: the original text is restored,
118
+ * the field closes and `kai-cancel` fires. `kai-rename` never fires, even if
119
+ * the field was edited. Also works when `editing` was set programmatically
120
+ * and the field has not rendered yet. */
121
+ cancel,
122
+ });
108
123
 
109
124
  return (
110
125
  <>