@kitn.ai/ui 0.26.0 → 0.28.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 (488) hide show
  1. package/README.md +25 -6
  2. package/bin/mcp.js +16 -3
  3. package/bin/route.js +23 -0
  4. package/bin/route.test.js +58 -0
  5. package/dist/{elements/chunks/Icon--Y8vsFal.js → Icon-DObhsRtm.js} +1 -1
  6. package/dist/action-icons-CcgNN2oT.js +1 -0
  7. package/dist/arrow-down-CjRU3JgV.js +1 -0
  8. package/dist/arrow-left-Bnpb1UEW.js +1 -0
  9. package/dist/artifact-Bwt7OgcR.js +1 -0
  10. package/dist/assets/dev-5XzXYR21.js +91 -0
  11. package/dist/attachments-Qr4kg-2b.js +1 -0
  12. package/dist/{elements/chunks/audio-visualizer-D7lB6mvR.js → audio-visualizer-DToYdfSc.js} +1 -1
  13. package/dist/badge-CdEu_jn2.js +1 -0
  14. package/dist/bash-InADTalH.js +1 -6
  15. package/dist/bell-DQAAOpDz.js +1 -0
  16. package/dist/{elements/chunks/button-flUbeufF.js → button-DkuOnGLy.js} +1 -1
  17. package/dist/card-renderer-Jx70m6_l.js +1 -0
  18. package/dist/card-routing-0YidER_3.js +1 -0
  19. package/dist/check-njkVdZu2.js +1 -0
  20. package/dist/checkbox-D7iRB3nL.js +1 -0
  21. package/dist/checkbox-group-B2_rpoPM.js +1 -0
  22. package/dist/chevron-down-B72s6PXR.js +1 -0
  23. package/dist/chevron-right-BatuHzUm.js +1 -0
  24. package/dist/choice-card-m0BYqqOK.js +1 -0
  25. package/dist/circle-check-B62pODB3.js +1 -0
  26. package/dist/circle-x-BPG0iPst.js +1 -0
  27. package/dist/circle-zx0ByzY9.js +1 -0
  28. package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
  29. package/dist/code-block-ClMLKb7C.js +1 -0
  30. package/dist/{elements/chunks/collapsible-BzbXpPzr.js → collapsible-B4DlWicL.js} +1 -1
  31. package/dist/components/attachment-types.d.ts +7 -0
  32. package/dist/components/audio-visualizer/fit-scale.d.ts +1 -1
  33. package/dist/components/card-renderer.d.ts +13 -0
  34. package/dist/components/card.d.ts +4 -0
  35. package/dist/components/chat-thread.d.ts +135 -2
  36. package/dist/components/conversation-item.d.ts +35 -7
  37. package/dist/components/conversation-list.d.ts +17 -5
  38. package/dist/components/conversation-panel.d.ts +29 -0
  39. package/dist/components/form-widgets.d.ts +67 -2
  40. package/dist/components/form.d.ts +48 -1
  41. package/dist/components/home-panel.d.ts +34 -0
  42. package/dist/components/message.d.ts +25 -3
  43. package/dist/components/reasoning.d.ts +6 -0
  44. package/dist/components/scroll-button.d.ts +11 -0
  45. package/dist/components/thread.d.ts +5 -0
  46. package/dist/components/tool-types.d.ts +9 -0
  47. package/dist/components/widget-tab-bar.d.ts +16 -0
  48. package/dist/composer-sQ0HcvyG.js +106 -0
  49. package/dist/confirm-card-rQWDQsnC.js +1 -0
  50. package/dist/construct-cli.es.js +1337 -0
  51. package/dist/{elements/chunks/context-VLIL3sJ-.js → context-0kg4ZX1J.js} +1 -1
  52. package/dist/controllable-CnGpMF6r.js +1 -0
  53. package/dist/conversation-item-BxwQLaFJ.js +1 -0
  54. package/dist/conversation-list-BCfGN1JU.js +1 -0
  55. package/dist/copy-n9u9rqpi.js +1 -0
  56. package/dist/core-AYMC6_lb.js +12 -5874
  57. package/dist/{create-tween-BFT6c-Y8.js → create-tween-C-q-JCM-.js} +1 -1
  58. package/dist/{create-tween-CJNlzoaO.js → create-tween-CRrJXxBd.js} +1 -1
  59. package/dist/{create-tween-CQED4T0g.js → create-tween-DZ3RRDHD.js} +1 -1
  60. package/dist/{elements/chunks/create-tween-c4Z8TDYz.js → create-tween-uPkArjIG.js} +2 -2
  61. package/dist/css-M7EaDHN_.js +1 -6
  62. package/dist/custom-elements.json +1433 -202
  63. package/dist/default-input-CaMrJkgH.js +1 -0
  64. package/dist/define-By-uL588.js +1 -0
  65. package/dist/define.d.ts +4 -0
  66. package/dist/define.js +510 -0
  67. package/dist/define.server.d.ts +5 -0
  68. package/dist/define.server.js +506 -0
  69. package/dist/{elements/chunks/disclosure-CcNbyLy0.js → disclosure-WUImBbDv.js} +1 -1
  70. package/dist/{elements/chunks/dropdown-BgWTthUx.js → dropdown-wZVeJOMd.js} +1 -1
  71. package/dist/elements/agent-card.js +1 -1
  72. package/dist/elements/artifact.js +1 -1
  73. package/dist/elements/attachments.js +1 -1
  74. package/dist/elements/audio-visualizer.js +1 -1
  75. package/dist/elements/autoloader.js +1 -1
  76. package/dist/elements/avatar.js +1 -1
  77. package/dist/elements/badge.js +1 -1
  78. package/dist/elements/button.js +1 -1
  79. package/dist/elements/card.js +1 -1
  80. package/dist/elements/cards.js +1 -1
  81. package/dist/elements/chain-of-thought.js +1 -1
  82. package/dist/elements/chat-scope-picker.js +1 -1
  83. package/dist/elements/chat-workspace.js +1 -1
  84. package/dist/elements/chat.js +1 -1
  85. package/dist/elements/checkbox-group.d.ts +1 -0
  86. package/dist/elements/checkbox-group.js +1 -0
  87. package/dist/elements/checkbox.d.ts +1 -0
  88. package/dist/elements/checkbox.js +1 -0
  89. package/dist/elements/checkpoint.js +1 -1
  90. package/dist/elements/choice.js +1 -1
  91. package/dist/elements/coachmark.js +1 -1
  92. package/dist/elements/code-block.js +1 -1
  93. package/dist/elements/command.js +1 -1
  94. package/dist/elements/compare.js +1 -1
  95. package/dist/elements/composer.js +1 -1
  96. package/dist/elements/confirm-card.js +1 -1
  97. package/dist/elements/context-meter.js +1 -1
  98. package/dist/elements/conversation-item.js +1 -1
  99. package/dist/elements/conversation-list.js +1 -1
  100. package/dist/elements/define-entry.d.ts +12 -0
  101. package/dist/elements/dialog.js +1 -1
  102. package/dist/elements/dock.js +102 -8
  103. package/dist/elements/dropdown.js +1 -1
  104. package/dist/elements/editable-label.js +1 -1
  105. package/dist/elements/element-data-types.d.ts +37 -0
  106. package/dist/elements/embed.js +1 -1
  107. package/dist/elements/empty.js +1 -1
  108. package/dist/elements/feedback-bar.js +1 -1
  109. package/dist/elements/file-tree.js +1 -1
  110. package/dist/elements/file-upload.js +1 -1
  111. package/dist/elements/form.js +1 -1
  112. package/dist/elements/hover-card.js +1 -1
  113. package/dist/elements/icon.js +1 -1
  114. package/dist/elements/image.js +1 -1
  115. package/dist/elements/input.js +1 -1
  116. package/dist/elements/kbd.js +1 -1
  117. package/dist/elements/link-preview.js +1 -1
  118. package/dist/elements/loader.js +1 -1
  119. package/dist/elements/markdown.js +1 -1
  120. package/dist/elements/menu.js +1 -1
  121. package/dist/elements/message-skills.js +1 -1
  122. package/dist/elements/message.js +1 -1
  123. package/dist/elements/model-switcher.js +1 -1
  124. package/dist/elements/nav.js +1 -1
  125. package/dist/elements/notice.js +1 -1
  126. package/dist/elements/pane-grid.d.ts +1 -0
  127. package/dist/elements/pane-grid.js +1 -0
  128. package/dist/elements/pane-group.js +1 -1
  129. package/dist/elements/pane.js +1 -1
  130. package/dist/elements/popover.js +1 -1
  131. package/dist/elements/progress-bar.js +1 -1
  132. package/dist/elements/prompt-dock.js +1 -1
  133. package/dist/elements/prompt-input.js +1 -1
  134. package/dist/elements/prompt-suggestions.js +1 -1
  135. package/dist/elements/radio-group.d.ts +1 -0
  136. package/dist/elements/radio-group.js +1 -0
  137. package/dist/elements/reasoning.js +1 -1
  138. package/dist/elements/remote.js +1 -1
  139. package/dist/elements/resizable.js +1 -1
  140. package/dist/elements/response-stream.js +2 -2
  141. package/dist/elements/screen.js +1 -1
  142. package/dist/elements/scroll-area.js +1 -1
  143. package/dist/elements/scroll-button.js +1 -1
  144. package/dist/elements/search.js +1 -1
  145. package/dist/elements/segmented.js +1 -1
  146. package/dist/elements/select.d.ts +1 -0
  147. package/dist/elements/select.js +1 -0
  148. package/dist/elements/separator.js +1 -1
  149. package/dist/elements/setting-item.js +1 -1
  150. package/dist/elements/settings-group.js +1 -1
  151. package/dist/elements/skeleton.js +1 -1
  152. package/dist/elements/slider.d.ts +1 -0
  153. package/dist/elements/slider.js +1 -0
  154. package/dist/elements/slots.d.ts +1 -1
  155. package/dist/elements/source.js +1 -1
  156. package/dist/elements/status.js +1 -1
  157. package/dist/elements/switch.js +1 -1
  158. package/dist/elements/tabs.js +1 -1
  159. package/dist/elements/tasks.js +1 -1
  160. package/dist/elements/text-shimmer.js +1 -1
  161. package/dist/elements/thinking-bar.js +1 -1
  162. package/dist/elements/thread.js +1 -1
  163. package/dist/elements/toast.js +1 -1
  164. package/dist/elements/tool.js +1 -1
  165. package/dist/elements/tooltip.js +1 -1
  166. package/dist/elements/voice-input.js +1 -1
  167. package/dist/elements/voice-output.js +1 -1
  168. package/dist/elements.d.ts +380 -26
  169. package/dist/{elements/chunks/ellipsis-7gU33mL2.js → ellipsis-CbOpJnU3.js} +1 -1
  170. package/dist/embed-CrfjkbUG.js +1 -0
  171. package/dist/engine-javascript-vq0WuIJl.js +14 -2516
  172. package/dist/{elements/chunks/external-link-CSOEsNlx.js → external-link-ByXAwBBQ.js} +1 -1
  173. package/dist/file-text-DMib2qwt.js +1 -0
  174. package/dist/{elements/chunks/file-tree-zvB2i7P9.js → file-tree-CJqM2aP7.js} +1 -1
  175. package/dist/folder-_BAb929C.js +1 -0
  176. package/dist/form-DSZgZQZk.js +1 -0
  177. package/dist/github-dark-dimmed-DUshB20C.js +1 -4
  178. package/dist/github-light-JYsPkUQd.js +1 -4
  179. package/dist/hover-card-BQbul9eq.js +1 -0
  180. package/dist/html-CPZ3oZQ7.js +1 -10
  181. package/dist/icon-CdcfRhYI.js +1 -0
  182. package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
  183. package/dist/index.d.ts +4 -2
  184. package/dist/index.js +7474 -5857
  185. package/dist/index.server.js +5965 -4762
  186. package/dist/info-DsFjGuI_.js +1 -0
  187. package/dist/input-DKbmkMos.js +1 -0
  188. package/dist/javascript-C25yR2R2.js +1 -6
  189. package/dist/json-DxJze_jm.js +1 -6
  190. package/dist/kai.es.js +1 -1
  191. package/dist/kbd-PvdGhX4s.js +1 -0
  192. package/dist/{elements/chunks/link-preview-OQ9IEq0f.js → link-preview-DbM1866N.js} +1 -1
  193. package/dist/link-zF8PzWfF.js +1 -0
  194. package/dist/loader-BM4CEz4G.js +1 -0
  195. package/dist/{elements/chunks/markdown-DgBFaGAz.js → markdown-LMvmAyBk.js} +7 -7
  196. package/dist/mcp.es.js +1361 -81
  197. package/dist/message-BSsgvHvf.js +1 -0
  198. package/dist/message-BYZECyek.js +1 -0
  199. package/dist/message-circle-DruwObfX.js +1 -0
  200. package/dist/message-square-BG6q6mlm.js +1 -0
  201. package/dist/{elements/chunks/minimize-2-DPIUKTrT.js → minimize-2-BQRoBV1V.js} +1 -1
  202. package/dist/{elements/chunks/model-switcher-DsT8tFB0.js → model-switcher-CMpCOYcB.js} +1 -1
  203. package/dist/{elements/chunks/overlay-DuiwdXjm.js → overlay-Cb-MkyDa.js} +1 -1
  204. package/dist/panel-right-i8warFxT.js +1 -0
  205. package/dist/paperclip-ByY3UVO6.js +1 -0
  206. package/dist/play-BvKKq3fX.js +1 -0
  207. package/dist/primitives/card-data-types.d.ts +49 -0
  208. package/dist/primitives/card-routing.d.ts +1 -33
  209. package/dist/primitives/card-validate.d.ts +2 -2
  210. package/dist/primitives/chat-config.d.ts +29 -2
  211. package/dist/primitives/conversation-store.d.ts +54 -0
  212. package/dist/primitives/field-mask.d.ts +96 -0
  213. package/dist/primitives/field-semantics.d.ts +36 -0
  214. package/dist/primitives/input-mask.d.ts +46 -0
  215. package/dist/primitives/url-scheme-policy.d.ts +34 -0
  216. package/dist/primitives/use-resize-observer.d.ts +2 -2
  217. package/dist/progress-bar-jVAHvMUf.js +1 -0
  218. package/dist/prompt-suggestion-CD-qtNgC.js +1 -0
  219. package/dist/radio-BsoBMhkF.js +1 -0
  220. package/dist/react/index.d.ts +586 -19
  221. package/dist/react.js +223 -187
  222. package/dist/reasoning-4kBPtlTx.js +1 -0
  223. package/dist/register-impl-B1hLkrI8.js +424 -0
  224. package/dist/{elements/chunks/resizable-D181b4fM.js → resizable-uPmY9VY-.js} +1 -1
  225. package/dist/rotate-ccw-B2xn93py.js +1 -0
  226. package/dist/rotate-cw-CMVLoCyH.js +1 -0
  227. package/dist/schemas/form.schema.json +21 -1
  228. package/dist/schemas/tool-defs.d.ts +11 -1
  229. package/dist/schemas.js +81 -81
  230. package/dist/scroll-area-BqLUo8ZZ.js +1 -0
  231. package/dist/scroll-button-DslXiJyL.js +1 -0
  232. package/dist/select-2cLRtPGT.js +1 -0
  233. package/dist/{elements/chunks/separator-CeJHQqLW.js → separator-BWQtNJcM.js} +1 -1
  234. package/dist/{elements/chunks/settings-CqaHgJF9.js → settings-BBR63jLg.js} +1 -1
  235. package/dist/{elements/chunks/settings-group-CjqjLCOn.js → settings-group-B2ciy-Iw.js} +1 -1
  236. package/dist/{elements/chunks/skeleton-D8akBnKS.js → skeleton-CkHI-HgC.js} +1 -1
  237. package/dist/slider-DDR9U-i0.js +1 -0
  238. package/dist/{solid-BtdqNZI7.js → solid-BzCFdL25.js} +9771 -7594
  239. package/dist/{solid-CnavwBye.js → solid-DruVOXnf.js} +7893 -6183
  240. package/dist/solid.d.ts +16 -2
  241. package/dist/solid.js +256 -243
  242. package/dist/solid.server.js +256 -243
  243. package/dist/source-GEnFF528.js +1 -0
  244. package/dist/star-CilHq8GP.js +1 -0
  245. package/dist/state/index.d.ts +1 -1
  246. package/dist/state/mock.d.ts +32 -2
  247. package/dist/state.js +133 -108
  248. package/dist/store-Ciyk6eZG.js +1 -0
  249. package/dist/svelte--5p79yCD.js +1 -15
  250. package/dist/switch-KkeQIbuH.js +1 -0
  251. package/dist/tasks-card-BrN4bggJ.js +1 -0
  252. package/dist/text-shimmer-Dcj1MHuq.js +1 -0
  253. package/dist/textarea-Dpvbngbg.js +1 -0
  254. package/dist/theme.tokens.css +110 -13
  255. package/dist/{elements/chunks/thumbs-up-DX2Ijk3H.js → thumbs-up-CsT9Wzb9.js} +1 -1
  256. package/dist/toast-store-tgK6MuMv.js +1 -0
  257. package/dist/{elements/chunks/tool-DKQ2D1DP.js → tool-DzUjIBT8.js} +1 -1
  258. package/dist/{elements/chunks/tooltip-B0G99y8w.js → tooltip-Bkkg_4t0.js} +1 -1
  259. package/dist/trash-2-jBKxjGGQ.js +1 -0
  260. package/dist/{elements/chunks/triangle-alert-F9cyUEyy.js → triangle-alert-DlQ68ppf.js} +1 -1
  261. package/dist/tsx-B8rCNbgL.js +1 -6
  262. package/dist/types.d.ts +48 -3
  263. package/dist/typescript-RycA9KXf.js +1 -6
  264. package/dist/ui/checkbox-group.d.ts +83 -0
  265. package/dist/ui/checkbox.d.ts +40 -0
  266. package/dist/ui/dock.d.ts +51 -0
  267. package/dist/ui/icon.d.ts +3 -0
  268. package/dist/ui/input.d.ts +48 -2
  269. package/dist/ui/radio.d.ts +85 -0
  270. package/dist/ui/select.d.ts +71 -0
  271. package/dist/ui/slider.d.ts +65 -0
  272. package/dist/ui/switch.d.ts +9 -2
  273. package/dist/upload-TcAHXv9W.js +1 -0
  274. package/dist/url-scheme-policy-DR5DgvkM.js +1 -0
  275. package/dist/use-card-resolution-DaZqAdWf.js +1 -0
  276. package/dist/{elements/chunks/variant-aurora-qt6Zct8M.js → variant-aurora-BhLuYxJR.js} +1 -1
  277. package/dist/{variant-aurora-B8II3-I1.js → variant-aurora-CNmfHhcx.js} +2 -2
  278. package/dist/{variant-aurora-CMNWOSJR.js → variant-aurora-D6GsdTPA.js} +2 -2
  279. package/dist/{variant-aurora-CcuDMWwQ.js → variant-aurora-DbW_OWL2.js} +1 -1
  280. package/dist/{elements/chunks/variant-custom-tJp22_BQ.js → variant-custom-BRvaNHSi.js} +1 -1
  281. package/dist/{variant-custom-L16JLLZy.js → variant-custom-BvYHR62b.js} +1 -1
  282. package/dist/{variant-custom-BdvYC7af.js → variant-custom-DMTrEO5w.js} +2 -2
  283. package/dist/{variant-custom-C714mqKl.js → variant-custom-DV2x254s.js} +2 -2
  284. package/dist/{variant-wave-ecwykeyL.js → variant-wave-BNMGrDfj.js} +2 -2
  285. package/dist/{variant-wave-2S4La2Ka.js → variant-wave-BpDd8t3Z.js} +1 -1
  286. package/dist/{variant-wave-D7ruiA1W.js → variant-wave-CyBueXj9.js} +2 -2
  287. package/dist/{elements/chunks/variant-wave-CaQ5ti39.js → variant-wave-sp-qwsdI.js} +1 -1
  288. package/dist/vue-BmIZj4XD.js +1 -33
  289. package/dist/x-Z5UntkIJ.js +1 -0
  290. package/frameworks/react/index.tsx +210 -22
  291. package/llms-full.txt +1184 -22
  292. package/llms.txt +6 -3
  293. package/package.json +17 -4
  294. package/src/agent-tooling/README.md +192 -0
  295. package/src/agent-tooling/catalog/invariants.ts +3 -3
  296. package/src/agent-tooling/catalog/surfaces.ts +72 -0
  297. package/src/agent-tooling/construct/cli-entry.ts +8 -0
  298. package/src/agent-tooling/construct/cli.ts +147 -0
  299. package/src/agent-tooling/construct/codegen.ts +1635 -0
  300. package/src/agent-tooling/construct/construct.v1.schema.json +319 -0
  301. package/src/agent-tooling/construct/dev.ts +148 -0
  302. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +7 -0
  303. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +44 -0
  304. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +32 -0
  305. package/src/agent-tooling/construct/schema.ts +452 -0
  306. package/src/agent-tooling/mcp/server.ts +2 -1
  307. package/src/agent-tooling/mcp/tools/construct.ts +130 -0
  308. package/src/agent-tooling/mcp/tools/debug.ts +126 -4
  309. package/src/agent-tooling/mcp/tools/reference.ts +104 -3
  310. package/src/agent-tooling/recipes/composed-thread.ts +714 -0
  311. package/src/agent-tooling/recipes/index.ts +22 -0
  312. package/src/agent-tooling/recipes/types.ts +32 -0
  313. package/src/agent-tooling/registry.ts +5 -0
  314. package/src/components/attachment-types.ts +7 -0
  315. package/src/components/audio-visualizer/fit-scale.ts +1 -1
  316. package/src/components/card-renderer.tsx +39 -8
  317. package/src/components/card.tsx +10 -2
  318. package/src/components/chat-scope-picker.tsx +2 -2
  319. package/src/components/chat-thread.tsx +602 -43
  320. package/src/components/choice-card.tsx +102 -37
  321. package/src/components/coachmark.tsx +1 -1
  322. package/src/components/code-block.tsx +2 -2
  323. package/src/components/composer.tsx +89 -15
  324. package/src/components/confirm-card.tsx +20 -4
  325. package/src/components/conversation-item.tsx +56 -9
  326. package/src/components/conversation-list.tsx +25 -10
  327. package/src/components/conversation-panel.tsx +118 -0
  328. package/src/components/empty.tsx +14 -3
  329. package/src/components/file-tree.tsx +5 -1
  330. package/src/components/form-widgets.tsx +257 -195
  331. package/src/components/form.tsx +297 -9
  332. package/src/components/home-panel.tsx +159 -0
  333. package/src/components/loader.tsx +12 -5
  334. package/src/components/message-skills.tsx +1 -1
  335. package/src/components/message.tsx +71 -11
  336. package/src/components/prompt-input.tsx +7 -1
  337. package/src/components/prompt-suggestion.tsx +4 -1
  338. package/src/components/reasoning.tsx +30 -3
  339. package/src/components/response-compare.tsx +2 -2
  340. package/src/components/scroll-button.tsx +56 -4
  341. package/src/components/source.tsx +6 -1
  342. package/src/components/tasks-card.tsx +58 -16
  343. package/src/components/thread.tsx +10 -1
  344. package/src/components/toast.tsx +1 -1
  345. package/src/components/tool-types.ts +9 -0
  346. package/src/components/widget-tab-bar.tsx +73 -0
  347. package/src/elements/attachments.tsx +3 -1
  348. package/src/elements/chat-workspace.tsx +1 -1
  349. package/src/elements/chat.tsx +115 -5
  350. package/src/elements/checkbox-group.tsx +164 -0
  351. package/src/elements/checkbox.tsx +134 -0
  352. package/src/elements/compiled.css +1 -1
  353. package/src/elements/composer.tsx +12 -1
  354. package/src/elements/conversation-item.tsx +62 -22
  355. package/src/elements/conversation-list.tsx +2 -2
  356. package/src/elements/define-entry.ts +12 -0
  357. package/src/elements/dock.tsx +11 -0
  358. package/src/elements/element-data-types.ts +40 -0
  359. package/src/elements/element-manifest.json +24 -0
  360. package/src/elements/element-meta.json +767 -34
  361. package/src/elements/element-nonscalar.json +17 -0
  362. package/src/elements/element-types.d.ts +380 -26
  363. package/src/elements/feedback-bar.tsx +5 -0
  364. package/src/elements/icon-names.json +29 -0
  365. package/src/elements/input.tsx +312 -23
  366. package/src/elements/message.tsx +2 -0
  367. package/src/elements/pane-grid.tsx +111 -0
  368. package/src/elements/prompt-input.tsx +8 -2
  369. package/src/elements/radio-group.tsx +129 -0
  370. package/src/elements/register-impl.ts +6 -0
  371. package/src/elements/scroll-button.tsx +54 -6
  372. package/src/elements/select.tsx +165 -0
  373. package/src/elements/slider.tsx +171 -0
  374. package/src/elements/slots.ts +1 -1
  375. package/src/elements/styles.css +89 -3
  376. package/src/elements/thread.tsx +2 -0
  377. package/src/elements/toast.tsx +14 -1
  378. package/src/index.ts +5 -2
  379. package/src/primitives/card-data-types.ts +49 -0
  380. package/src/primitives/card-host.tsx +4 -2
  381. package/src/primitives/card-routing.ts +7 -56
  382. package/src/primitives/card-schemas/form.schema.json +21 -1
  383. package/src/primitives/card-validate.ts +10 -2
  384. package/src/primitives/chat-config.tsx +33 -6
  385. package/src/primitives/conversation-store.ts +248 -0
  386. package/src/primitives/field-mask.ts +256 -0
  387. package/src/primitives/field-semantics.ts +115 -0
  388. package/src/primitives/input-mask.ts +853 -0
  389. package/src/primitives/toast-store.ts +66 -4
  390. package/src/primitives/url-scheme-policy.ts +70 -0
  391. package/src/primitives/use-resize-observer.ts +2 -2
  392. package/src/schemas/tool-defs.ts +12 -2
  393. package/src/solid.ts +20 -2
  394. package/src/state/index.ts +1 -1
  395. package/src/state/mock.ts +92 -6
  396. package/src/types.ts +45 -3
  397. package/src/ui/agent-card.tsx +2 -2
  398. package/src/ui/avatar.tsx +1 -1
  399. package/src/ui/checkbox-group.tsx +153 -0
  400. package/src/ui/checkbox.tsx +63 -0
  401. package/src/ui/dock.tsx +192 -7
  402. package/src/ui/icon.tsx +53 -0
  403. package/src/ui/input.tsx +249 -9
  404. package/src/ui/kbd.tsx +1 -1
  405. package/src/ui/nav.tsx +1 -1
  406. package/src/ui/pane-group.tsx +2 -2
  407. package/src/ui/radio.tsx +150 -0
  408. package/src/ui/select.tsx +168 -0
  409. package/src/ui/slider.tsx +178 -0
  410. package/src/ui/switch.tsx +49 -18
  411. package/src/utils/cn.ts +11 -5
  412. package/theme.css +112 -13
  413. package/dist/elements/chunks/action-icons-D9u3OzYW.js +0 -1
  414. package/dist/elements/chunks/arrow-left-CVh1TeN7.js +0 -1
  415. package/dist/elements/chunks/artifact-BkYcfIlZ.js +0 -1
  416. package/dist/elements/chunks/attachments-DwfNlubq.js +0 -1
  417. package/dist/elements/chunks/badge-Q0-Cmwzz.js +0 -1
  418. package/dist/elements/chunks/bash-InADTalH.js +0 -1
  419. package/dist/elements/chunks/card-renderer-3Dyl-FpN.js +0 -1
  420. package/dist/elements/chunks/card-routing-BK09BdmL.js +0 -1
  421. package/dist/elements/chunks/check-B5kBPKeo.js +0 -1
  422. package/dist/elements/chunks/chevron-down-DZ1GS39h.js +0 -1
  423. package/dist/elements/chunks/chevron-right-CvP7OUd2.js +0 -1
  424. package/dist/elements/chunks/choice-card-D4QUIOWZ.js +0 -1
  425. package/dist/elements/chunks/circle-CDf9g6MQ.js +0 -1
  426. package/dist/elements/chunks/circle-check-CA47Nqbz.js +0 -1
  427. package/dist/elements/chunks/circle-x-CJ_HVbC8.js +0 -1
  428. package/dist/elements/chunks/code-block-BdLiTitd.js +0 -1
  429. package/dist/elements/chunks/composer-Byve-n8K.js +0 -69
  430. package/dist/elements/chunks/confirm-card-B0flE3MN.js +0 -1
  431. package/dist/elements/chunks/controllable-DTwTIlwp.js +0 -1
  432. package/dist/elements/chunks/conversation-item-BDztezOG.js +0 -1
  433. package/dist/elements/chunks/copy-Nf-kqxXH.js +0 -1
  434. package/dist/elements/chunks/core-AYMC6_lb.js +0 -12
  435. package/dist/elements/chunks/css-M7EaDHN_.js +0 -1
  436. package/dist/elements/chunks/default-input-C2iLFq0b.js +0 -1
  437. package/dist/elements/chunks/define-wjHR1MkU.js +0 -1
  438. package/dist/elements/chunks/download-C4V7tBdb.js +0 -1
  439. package/dist/elements/chunks/embed-D3KewsIJ.js +0 -1
  440. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +0 -141
  441. package/dist/elements/chunks/file-text-D1G8khle.js +0 -1
  442. package/dist/elements/chunks/folder-WW2LGLid.js +0 -1
  443. package/dist/elements/chunks/form-CQirdC4W.js +0 -1
  444. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +0 -1
  445. package/dist/elements/chunks/github-light-JYsPkUQd.js +0 -1
  446. package/dist/elements/chunks/hover-card-CJJkfHTK.js +0 -1
  447. package/dist/elements/chunks/html-CPZ3oZQ7.js +0 -1
  448. package/dist/elements/chunks/icon-Cfgmf4fa.js +0 -1
  449. package/dist/elements/chunks/info-DX3YMOt6.js +0 -1
  450. package/dist/elements/chunks/input-BtW9U6Sq.js +0 -1
  451. package/dist/elements/chunks/javascript-C25yR2R2.js +0 -1
  452. package/dist/elements/chunks/json-DxJze_jm.js +0 -1
  453. package/dist/elements/chunks/kbd-LGXwuT_V.js +0 -1
  454. package/dist/elements/chunks/link-2My69BqT.js +0 -1
  455. package/dist/elements/chunks/loader-iOxc_Tu-.js +0 -1
  456. package/dist/elements/chunks/message-VMZohrXx.js +0 -1
  457. package/dist/elements/chunks/message-circle-CpXMM-_n.js +0 -1
  458. package/dist/elements/chunks/message-square-DxcWtETQ.js +0 -1
  459. package/dist/elements/chunks/message-tZmV2A3p.js +0 -1
  460. package/dist/elements/chunks/paperclip-CRZVp4z_.js +0 -1
  461. package/dist/elements/chunks/progress-bar-DNy66e_E.js +0 -1
  462. package/dist/elements/chunks/prompt-suggestion-Bn64Nl7L.js +0 -1
  463. package/dist/elements/chunks/reasoning-Emn9N4-T.js +0 -1
  464. package/dist/elements/chunks/rotate-cw-CPWe37D9.js +0 -1
  465. package/dist/elements/chunks/scroll-area-ClZkZxIk.js +0 -1
  466. package/dist/elements/chunks/scroll-button-DPE-t3x_.js +0 -1
  467. package/dist/elements/chunks/share-CyfnNEqp.js +0 -1
  468. package/dist/elements/chunks/source-UWCvmVak.js +0 -1
  469. package/dist/elements/chunks/star-DoHFAikQ.js +0 -1
  470. package/dist/elements/chunks/store-Cjq0qwYN.js +0 -1
  471. package/dist/elements/chunks/svelte--5p79yCD.js +0 -1
  472. package/dist/elements/chunks/tasks-card-CmZ5xh6v.js +0 -1
  473. package/dist/elements/chunks/text-shimmer-DYn09zrz.js +0 -1
  474. package/dist/elements/chunks/textarea-skiGHURM.js +0 -1
  475. package/dist/elements/chunks/toast-store-CFnQpThX.js +0 -1
  476. package/dist/elements/chunks/tsx-B8rCNbgL.js +0 -1
  477. package/dist/elements/chunks/typescript-RycA9KXf.js +0 -1
  478. package/dist/elements/chunks/use-card-resolution-DoA2f_EU.js +0 -1
  479. package/dist/elements/chunks/vue-BmIZj4XD.js +0 -1
  480. package/dist/elements/chunks/x-B6UnOiLw.js +0 -1
  481. package/dist/llms/llms-full.txt +0 -2949
  482. package/dist/llms/llms.txt +0 -156
  483. package/dist/register-impl-DpE7icIb.js +0 -293
  484. package/dist/ui/stat.d.ts +0 -16
  485. package/src/ui/stat.tsx +0 -41
  486. /package/dist/{elements/chunks/card-tags-D8lZ-C_U.js → card-tags-D8lZ-C_U.js} +0 -0
  487. /package/dist/{elements/chunks/link-preview-DNILK391.js → link-preview-DNILK391.js} +0 -0
  488. /package/dist/{elements/chunks/slots-DaNjX0rc.js → slots-DaNjX0rc.js} +0 -0
@@ -305,7 +305,7 @@ export interface KaiArtifactElement extends HTMLElement {
305
305
  export interface KaiAttachmentsElement extends HTMLElement {
306
306
  /** Color mode (`auto` follows prefers-color-scheme). */
307
307
  theme?: "light" | "dark" | "auto";
308
- /** The attachments to render. Omit (or pass an empty array) for the empty state, which shows `emptyText` if set and nothing otherwise. Set as a JS property (array). */
308
+ /** The attachments to render. Omit (or pass an empty array) for the empty state, which shows `emptyText` if set and nothing otherwise. Set as a JS property (array). Each item's `url` must be a `data:` URI or an https URL, never `URL.createObjectURL`: a `blob:` URL previews here but the wire encoders (`toOpenAIMessages`/`toAnthropicMessages`) refuse it. */
309
309
  items: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
310
310
  /** Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. */
311
311
  variant?: "grid" | "inline" | "list";
@@ -440,7 +440,7 @@ export interface KaiCardsElement extends HTMLElement {
440
440
  /** JSON Schemas for the card types this app renders, keyed by envelope type. The companion of `types`, which says what DRAWS a card while this says what a VALID one looks like. An OBJECT, so it is a JS property only: `el.schemas = { 'pricing-table': pricingSchema }`, never an attribute. `createCardRegistry(...).validationSchemas` is exactly this shape. Without it the kit validates its own seven built-ins and leaves your own card type, the one your app actually cares about, as the only unchecked thing on screen. A schema here WINS over a built-in of the same name, matching `mergeCardTags`, where your entry is spread over ours. Typed `Record<string, object>` rather than `Record<string, JsonSchema>` deliberately: an imported `.json` schema widens `"type"` to `string`, and an authored one carries `$schema`/`title`/`description`/`additionalProperties`, so the tighter type would reject both of the normal ways to supply one. See `CardSchemaMap` in components/card-renderer.tsx. */
441
441
  schemas?: Record<string, object>;
442
442
  /** Optional CardPolicy handling child events. Property: `el.policy`. */
443
- policy?: { onSubmit?: (cardId: string, data: unknown) => void; onAction?: (cardId: string, action: string, payload?: unknown) => void; onSendPrompt?: (text: string, opts: { mode: "compose" | "send"; context?: unknown; }) => void; onOpen?: (url: string, target: "tab" | "artifact") => void; onState?: (cardId: string, patch: unknown) => void; onDismiss?: (cardId: string) => void; onReopen?: (cardId: string) => void; onError?: (cardId: string, message: string) => void; maxSendPromptMode?: "compose" | "send" };
443
+ policy?: { onSubmit?: ((cardId: string, data: unknown) => void); onAction?: ((cardId: string, action: string, payload?: unknown) => void); onSendPrompt?: ((text: string, opts: { mode: "compose" | "send"; context?: unknown }) => void); onOpen?: ((url: string, target: "tab" | "artifact") => void); onState?: ((cardId: string, patch: unknown) => void); onDismiss?: ((cardId: string) => void); onReopen?: ((cardId: string) => void); onError?: ((cardId: string, message: string) => void); maxSendPromptMode?: "compose" | "send" };
444
444
  /** Validate each envelope's `data` against the schema for its type before rendering it, using a built-in's own schema or yours from `schemas`. Default `true`; set `validate-cards="false"` (or `el.validateCards = false`) to opt out. A hard failure (wrong type, a missing required field) renders a diagnostic naming the field instead of the card; a soft failure (bounds) renders the card unchanged. Both emit a contract `error` event. On in production too: a model emitting a bad shape is a production failure mode, so stripping the check there would hide it from exactly the person who needs to see it. */
445
445
  validateCards?: boolean;
446
446
  /** Programmatically resolve a child card by id: set that envelope's `resolution` so the child re-renders into its read-only/resolved view. The imperative twin of the consumer mutating the cards array. No-op for an unknown id. */
@@ -491,6 +491,10 @@ export interface KaiChatElement extends HTMLElement {
491
491
  codeTheme?: string;
492
492
  /** Enable Shiki syntax highlighting in code blocks. Turn off to render plain `<pre>` blocks (lighter, no highlighter load). Default true. */
493
493
  codeHighlight?: boolean;
494
+ /** How `reasoning` parts render across the thread. `'full'` (default) is the current collapsible-disclosure behavior; `'compact'` shows only a shimmer loader while a reasoning part streams and nothing once it settles (no expandable detail); `'off'` renders reasoning parts not at all. Forwarded to every `MessageBody` as `reasoningMode`. */
495
+ reasoning?: "full" | "compact" | "off";
496
+ /** Seeds the reasoning disclosure open AND keeps it tracking the stream (open while streaming, closes when it settles): the pre-Task-19f `full` behavior. Default false/absent: the panel starts closed (just the "Thinking" shimmer chip) and only opens on click, the current default (owner ruling, 2026-08-26). Meaningless when `reasoning` is `'compact'` or `'off'`. Forwarded to every `MessageBody` as `reasoningDefaultOpen`. */
497
+ reasoningOpen?: boolean;
494
498
  /** Optional header title shown on the left of the header. */
495
499
  chatTitle?: string;
496
500
  /** Optional model list. When set (>1 model) a ModelSwitcher is shown in the header and a `kai-model-change` event fires on selection. */
@@ -517,6 +521,8 @@ export interface KaiChatElement extends HTMLElement {
517
521
  composerActions?: boolean;
518
522
  /** INJECT: footer row below the composer (disclaimers, token meter, …). */
519
523
  footer?: boolean;
524
+ /** When `false`, hides the built-in paperclip attach button. Defaults to `true` (undeclared keeps today's behavior: attach visible), matching `DefaultPromptInput`'s own default: only an explicit `false` hides it. */
525
+ attach?: boolean;
520
526
  /** Show a web-search (Globe) button in the input toolbar; calls `onWebSearch`. */
521
527
  webSearch?: boolean;
522
528
  /** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
@@ -535,6 +541,12 @@ export interface KaiChatElement extends HTMLElement {
535
541
  cardTypes?: Record<string, string>;
536
542
  /** JSON Schemas for the card types this app renders, keyed by envelope type. The companion of `cardTypes`, which says what DRAWS a card while this says what a VALID one looks like. An OBJECT, so it is a JS property only: `el.cardSchemas = { 'pricing-table': pricingSchema }`, never an attribute. `createCardRegistry(...).validationSchemas` is exactly this shape. Without it the kit validates its own seven built-ins and leaves your own card type, the one your app actually cares about, as the only unchecked thing on screen. A schema here WINS over a built-in of the same name. Typed `Record<string, object>` rather than `Record<string, JsonSchema>` deliberately: an imported `.json` schema widens `"type"` to `string`, and an authored one carries `$schema`/`title`/`description`/`additionalProperties`, so the tighter type would reject both of the normal ways to supply one. */
537
543
  cardSchemas?: Record<string, object>;
544
+ /** Turns on the prior-conversations list (a list-toggle button in the header, plus a second list view sharing the panel, C-1). Attribute- settable like every other boolean flag on this element: `<kai-chat conversations>`. Requires `store`. A row select, "new conversation," or the visitor's mount-time auto-restore all deliver their messages the same way: listen for `kai-conversation-load` and set `el.messages` from `event.detail.messages` (a fresh array): this element does not update `messages` for you. Set with no `store`, the underlying `ChatThread` decides loudly (one console.error) and stays visually off; this facade always supplies its own internal load handler (the `kai-conversation-load` dispatch below), so the second ChatThread guard, missing `onConversationLoad`, never trips here, even for a consumer who never listens for the event. Default false. */
545
+ conversations?: boolean;
546
+ /** The adapter this thread persists conversations through: an object of three functions (`list`/`load`/`save`; `ConversationStore`, exported from `@kitn.ai/ui`'s `primitives/conversation-store`). A JS PROPERTY ONLY: `el.store = myAdapter`. It can never be an attribute, since a function-bearing object has no HTML string form, the same reasoning that keeps `messages`/`cardSchemas` property-only (the kai- contract: array/object props are JS properties, never attributes). Two built-ins ship: `localStorageStore(name, userId?)` and `fetchStore(url, userId?)`. */
547
+ store?: { list: () => Promise<{ id: string; title: string; groupId?: string; scope?: { type: "document" | "collection"; documentId?: string; filters?: { tags?: string[]; authors?: string[]; contentType?: "transcript" | "markdown"; dateRange?: { from: string; to: string } } }; messageCount: number; lastMessageAt?: string; updatedAt: string; trailing?: string; lastReadAt?: string }[]>; load: (id: string) => Promise<{ id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" }[]>; save: (id: string, messages: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" }[]) => Promise<void>; markRead?: ((id: string) => Promise<void>) };
548
+ /** Turns on the widget home screen (Intercom-pattern): the panel boots into a `home` view, with a greeting, most-recent-conversation card, a "new conversation" CTA, and host-defined links, plus a Home/Messages tab bar for switching back to the thread. An OBJECT, so it is a JS property only: `el.home = { greeting: { title: 'Hey' }, links: [...] }`, never an attribute (the kai- contract: array/object props are JS properties). A `links` entry with no `href` fires `kai-home-link` with that entry when tapped, rather than navigating; one WITH `href` opens it directly (only when the URL passes the kit's own scheme allowlist). Omit for the no-home widget (chat view only, unchanged). */
549
+ home?: { greeting?: { title?: string; subtitle?: string }; recentConversation?: boolean; newConversation?: { label?: string }; links?: { label: string; href?: string; description?: string; icon?: string }[] };
538
550
  /** Focus the composer, meaning the contenteditable (or textarea) inside the shadow root. A native `focus()` on the host lands on the host itself and never reaches it, so this is the only way to focus the input programmatically. */
539
551
  focus(options?: FocusOptions): void;
540
552
  /** Blur whatever currently holds focus inside the shadow root. The companion to `focus()`, for the same reason: a native `blur()` on the host misses the real focus target. */
@@ -547,6 +559,48 @@ export interface KaiChatElement extends HTMLElement {
547
559
  scrollToBottom(behavior?: "auto" | "instant" | "smooth"): void;
548
560
  }
549
561
 
562
+ export interface KaiCheckboxElement extends HTMLElement {
563
+ /** Color mode (`auto` follows prefers-color-scheme). */
564
+ theme?: "light" | "dark" | "auto";
565
+ /** Controlled checked state. Settable and reflected to the `checked` attribute. `el.checked = true` (or `<kai-checkbox checked>`) drives it; ticking the box updates it and fires `kai-change`. Read `el.checked` for live state. */
566
+ checked?: boolean;
567
+ /** Initial checked state on mount (uncontrolled seed). Bare attribute (`<kai-checkbox default-checked>`) turns it on. */
568
+ defaultChecked?: boolean;
569
+ /** The mixed state, for a parent box whose children are partly ticked. Visual plus an accessibility hint: the box still reports `checked === false`. */
570
+ indeterminate?: boolean;
571
+ /** Disable interaction. */
572
+ disabled?: boolean;
573
+ /** Set the native `required` attribute, and nothing more. Whether an unticked box is an error is the application's rule, not the kit's. */
574
+ required?: boolean;
575
+ /** Accessible label. The visible text beside the box is the consumer's to render. */
576
+ label?: string;
577
+ /** Form-control name (paired with `value`). */
578
+ name?: string;
579
+ /** Submitted value when checked (paired with `name`). Defaults to `'on'`. */
580
+ value?: string;
581
+ /** Flip the box and fire `kai-change` (no-op while disabled). */
582
+ toggle(): void;
583
+ /** Focus the inner input (the host element can't reach it). */
584
+ focus(options?: FocusOptions): void;
585
+ }
586
+
587
+ export interface KaiCheckboxGroupElement extends HTMLElement {
588
+ /** Color mode (`auto` follows prefers-color-scheme). */
589
+ theme?: "light" | "dark" | "auto";
590
+ /** The choices, top to bottom. Set as a JS PROPERTY (array), never an attribute. Rendered in full: the kit never truncates, re-orders or de-duplicates them. */
591
+ options: { value: string; label: string; description?: undefined | string; disabled?: undefined | boolean }[];
592
+ /** The FIRST selected value. Settable and reflected to the `value` attribute, so `:host([value])` and `el.value` see live state, and a seed can be written in markup. Writing it makes that the whole selection; to read or drive the rest, use `el.values`. */
593
+ value?: string;
594
+ /** The shared form-control name every box carries, so `FormData.getAll(name)` reads the whole selection back under one key. NO DEFAULT, unlike `<kai-radio-group>`. A radio set needs a shared `name` for the browser to make it exclusive and arrow-navigable, so one is generated when none is given; checkboxes are independent controls and behave correctly with no name at all. Generating one here would submit the selection under a random key, which is worse than submitting nothing. The element is NOT form-associated (no `ElementInternals`, no `setFormValue()`), the same known gap `<kai-input>` records: the boxes live in a shadow root, so a surrounding `<form>` collects nothing from them whether or not `name` is set. Read `el.values`. The name still lands on every inner input, so it is right the day form association arrives. */
595
+ name?: string;
596
+ /** Disable every row. Individual rows carry their own `disabled`. */
597
+ disabled?: boolean;
598
+ /** Accessible name for the group. */
599
+ label?: string;
600
+ /** Focus the group's first box. Not "the first ticked one", which is `<kai-radio-group>`'s rule: a radio group is ONE tab stop that lands on the selection, while every checkbox here is its own tab stop, so the entry point is simply the top of the list. */
601
+ focus(options?: FocusOptions): void;
602
+ }
603
+
550
604
  export interface KaiCheckpointElement extends HTMLElement {
551
605
  /** Color mode (`auto` follows prefers-color-scheme). */
552
606
  theme?: "light" | "dark" | "auto";
@@ -741,9 +795,9 @@ export interface KaiContextElement extends HTMLElement {
741
795
  export interface KaiConversationItemElement extends HTMLElement {
742
796
  /** Color mode (`auto` follows prefers-color-scheme). */
743
797
  theme?: "light" | "dark" | "auto";
744
- /** The row's identity, handed to the container's selection contract. In the element this is the `conversation-id` attribute (host `id` is the fallback). */
798
+ /** The row's identity: the `conversation-id` attribute (host `id` is the fallback). Inside `<kai-conversations>` it is handed to the container's selection contract (`kai-conversation-select`); standalone it is the `id` in this element's own `kai-select` detail. */
745
799
  conversationId?: string;
746
- /** Selected state. Reflected as `aria-current` on the row body and a `data-active` styling hook on the row; the container drives it from its `activeId`. */
800
+ /** Selected state. Reflected as `aria-current` on the row body and a `data-active` styling hook on the row; inside a container the container drives it from its `activeId`, standalone you set it yourself. */
747
801
  active?: boolean;
748
802
  /** Dense single-line row padding. */
749
803
  compact?: boolean;
@@ -755,7 +809,7 @@ export interface KaiConversationsElement extends HTMLElement {
755
809
  /** The list's section headers (`{ id, name, sortOrder, createdAt }`), rendered in array order. A group carries no conversations of its own; it is matched against `conversations` by id, so the two props are complementary rather than alternatives. Omit for an ungrouped list. Set as a JS property. */
756
810
  groups: { id: string; userId?: string; teamId?: string; name: string; sortOrder: number; createdAt: string }[];
757
811
  /** Every conversation the list renders, flat. Each one is filed under the group whose `id` equals its `groupId`; one with no `groupId`, or with a `groupId` matching no entry in `groups`, falls into a trailing "Ungrouped" section, so nothing you pass in is ever dropped. There is no recency bucketing. Set as a JS property. Omit to supply them as `<kai-conversation>` light-DOM children instead, or for the empty state. A search query that matches nothing shows a visible "No conversations match your search" state, distinct from the zero-conversations empty state. Slotted `<kai-conversation-item>` children switch the list into item mode instead: your own rows win and this array is not rendered. */
758
- conversations: { id: string; title: string; groupId?: string; scope?: { type: "document" | "collection"; documentId?: string; filters?: { tags?: string[]; authors?: string[]; contentType?: "transcript" | "markdown"; dateRange?: { from: string; to: string } } }; messageCount: number; lastMessageAt?: string; updatedAt: string; trailing?: string }[];
812
+ conversations: { id: string; title: string; groupId?: string; scope?: { type: "document" | "collection"; documentId?: string; filters?: { tags?: string[]; authors?: string[]; contentType?: "transcript" | "markdown"; dateRange?: { from: string; to: string } } }; messageCount: number; lastMessageAt?: string; updatedAt: string; trailing?: string; lastReadAt?: string }[];
759
813
  /** The id of the currently-open conversation, highlighted in the list. */
760
814
  activeId?: string;
761
815
  /** Controlled collapsed state. Set as a JS property (`el.collapsed = true`) to drive the rail from your app, updating it in response to `kai-collapse-toggle`. Omit for uncontrolled (the element manages it). Collapsed shrinks the rail to a floating reopen button. */
@@ -816,6 +870,8 @@ export interface KaiDockElement extends HTMLElement {
816
870
  unread?: boolean;
817
871
  /** Disable the launcher; `show()` and `toggle()` are gated on it. */
818
872
  disabled?: boolean;
873
+ /** Suppress the dock's own built-in mobile close X. Set this when your slotted panel content supplies its own close affordance (e.g. a `<kai-chat slot="header-end">` close button), otherwise the two stack. TRADEOFF: the mobile panel reserves a padding band above its content so the built-in X never paints over slotted content; that band stays reserved unless you set this true, so only set it once your own control is actually in place. Attribute: `hide-close`. */
874
+ hideClose?: boolean;
819
875
  /** Where focus lands on open: `content` (default, the first element you slotted), `panel`, or `none`. Attribute: `focus-on-open`. */
820
876
  focusOnOpen?: "content" | "panel" | "none";
821
877
  /** Open it programmatically (no-op while disabled). */
@@ -941,7 +997,7 @@ export interface KaiFormElement extends HTMLElement {
941
997
  /** Color mode (`auto` follows prefers-color-scheme). */
942
998
  theme?: "light" | "dark" | "auto";
943
999
  /** The form definition: a JSON Schema (`type:'object'`) + `x-kai-*` UI hints (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { type:'object', properties:{…} }`. Import the `FormDefinition` type from `@kitn.ai/ui` for the full shape. It IS self-referential (`FormField.properties` is another `FormField` map), and the generated `element-types.d.ts` inlines every named type, so the shipped declaration bottoms out in a `Record<string, unknown>` placeholder one level down rather than carrying the recursion. That is why `FormDefinition` is a `type` alias: an interface gets no implicit index signature, so it would not be assignable to that placeholder. */
944
- data?: { type: "object"; title?: string; description?: string; required?: string[]; properties: Record<string, { type: "string" | "number" | "integer" | "boolean" | "array" | "object"; title?: string; description?: string; default?: unknown; enum?: unknown[]; format?: "email" | "uri" | "url" | "date" | "date-time" | "time"; minimum?: number; maximum?: number; minLength?: number; maxLength?: number; pattern?: string; minItems?: number; maxItems?: number; items?: Record<string, unknown> | { enum: unknown[] }; properties?: Record<string, Record<string, unknown>>; required?: string[]; readOnly?: boolean; "x-kai-widget"?: "textarea" | "slider" | "rating" | "radio" | "select" | "checkbox" | "password" | "switch"; "x-kai-placeholder"?: string; "x-kai-step"?: number }>; "x-kai-order"?: string[]; "x-kai-inlineMax"?: number; "x-kai-submitLabel"?: string; "x-kai-dismissible"?: boolean; "x-kai-actions"?: { id: string; label: string; variant?: "default" | "ghost" | "outline" }[] };
1000
+ data?: { type: "object"; title?: string; description?: string; required?: string[]; properties: Record<string, { type: "string" | "number" | "integer" | "boolean" | "array" | "object"; title?: string; description?: string; default?: unknown; enum?: unknown[]; format?: "email" | "uri" | "url" | "date" | "date-time" | "time"; minimum?: number; maximum?: number; minLength?: number; maxLength?: number; pattern?: string; minItems?: number; maxItems?: number; items?: Record<string, unknown> | { enum: unknown[] }; properties?: Record<string, Record<string, unknown>>; required?: string[]; readOnly?: boolean; "x-kai-widget"?: "textarea" | "slider" | "rating" | "radio" | "select" | "checkbox" | "password" | "switch"; "x-kai-placeholder"?: string; "x-kai-step"?: number; "x-kai-format"?: "tel" | "ssn" | "credit-card" | "custom"; "x-kai-mask"?: string; "x-kai-mask-guide"?: string }>; "x-kai-order"?: string[]; "x-kai-inlineMax"?: number; "x-kai-submitLabel"?: string; "x-kai-dismissible"?: boolean; "x-kai-actions"?: { id: string; label: string; variant?: "default" | "ghost" | "outline" }[] };
945
1001
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
946
1002
  cardId?: string;
947
1003
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
@@ -1018,7 +1074,7 @@ export interface KaiInputElement extends HTMLElement {
1018
1074
  theme?: "light" | "dark" | "auto";
1019
1075
  /** Native input type: `text` (default) · `email` · `url` · `search` · `tel` · `password` · `number`. Single-line only. */
1020
1076
  type?: string;
1021
- /** Controlled value. Settable and reflected to the `value` attribute. `el.value = 'hi'` drives it (no event); typing updates it and fires `kai-input`. Read `el.value` for live state. */
1077
+ /** Controlled value, and always the CANONICAL one when a mask is active: digits for `tel` / `ssn` / `credit-card`, the formatted text for `custom`. Settable and reflected to the `value` attribute. `el.value = '5551234567'` drives it (no event) and is re-fitted to the mask on the way in, so the field shows `555-123-4567`. Read `el.value` for live state; the formatted text rides along on every `kai-input` / `kai-change` detail as `formattedValue`. */
1022
1078
  value?: string;
1023
1079
  /** Placeholder shown when empty. */
1024
1080
  placeholder?: string;
@@ -1044,11 +1100,25 @@ export interface KaiInputElement extends HTMLElement {
1044
1100
  autocomplete?: string;
1045
1101
  /** Virtual-keyboard hint forwarded to the inner input (e.g. `numeric`, `email`). */
1046
1102
  inputmode?: string;
1103
+ /** Mask pattern: `#` a digit, `@` a letter or digit, `*` an obscurable letter or digit, and every other character a positional literal (`@@@-####` → `CHG-4821`). The literal `default` is the opt-in sentinel: it resolves to the default format of `semantic` (`tel` → `###-###-####`). A bare `semantic` never starts masking on its own, so an opt-in token is what turns tier 2 on. */
1104
+ format?: string;
1105
+ /** Placeholder guide shown at unfilled positions, aligned position for position with `format`: `mm/dd/yyyy` against `##/##/####`. Spaces are a valid guide character, so a guide of blanks and separators is how a phone field shows its shape without showing letters. Without a guide the field shows only up to the last typed character. A guide is a visual aid, never an accessible name: keep the `hint` text as well. */
1106
+ guide?: string;
1107
+ /** Semantic field type: `tel` · `ssn` · `credit-card` · `custom`. On its own it sets `inputmode` / `autocomplete` / `spellcheck` / `autocorrect` / `autocapitalize` and decides the canonical value; it never starts masking by itself. */
1108
+ semantic?: "credit-card" | "custom" | "ssn" | "tel";
1109
+ /** Case folding applied to typed and pasted text: `preserve` (default) · `upper` · `lower`. Attribute: `case-mode`. */
1110
+ caseMode?: "preserve" | "upper" | "lower";
1111
+ /** What a copy or cut of a masked field puts on the clipboard: `canonical` (default) · `formatted` · `obscured` · `blocked`. Attribute: `copy-policy`. */
1112
+ copyPolicy?: "formatted" | "canonical" | "obscured" | "blocked";
1047
1113
  /** Focus the inner input (the host can't reach into the shadow root). */
1048
1114
  focus(options?: FocusOptions): void;
1049
1115
  /** Select the inner input's text. */
1050
1116
  select(): void;
1051
- /** Empty the value and fire `kai-change` with `''`. */
1117
+ /** The canonical value: digits for `tel` / `ssn` / `credit-card`, the formatted text for `custom`, and the field text when no mask is on. Identical to reading `el.value`, under the name backends use for the submitted form of a masked field. The mask engine has a third, narrower notion of raw (the fill characters with no literals at all) and that one is internal: it is not what any backend wants and it is not exposed here. */
1118
+ getRawValue(): string;
1119
+ /** The text on screen, literals and guide included. The counterpart to `formattedValue` on the `kai-input` / `kai-change` details, for a consumer that needs it outside an event. */
1120
+ getFormattedValue(): string;
1121
+ /** Empty the value and fire `kai-change` with `''`. On a masked field this resets the mask itself, not just the text on screen, so the next character starts over. */
1052
1122
  clear(): void;
1053
1123
  }
1054
1124
 
@@ -1224,6 +1294,21 @@ export interface KaiPaneElement extends HTMLElement {
1224
1294
  status?: { tone: "working" | "idle" | "done" | "error" | "blocked"; label?: string; pulse?: boolean };
1225
1295
  }
1226
1296
 
1297
+ export interface KaiPaneGridElement extends HTMLElement {
1298
+ /** Color mode (`auto` follows prefers-color-scheme). */
1299
+ theme?: "light" | "dark" | "auto";
1300
+ /** Minimum width of every pane, in px, before columns drop / the grid scrolls. Defaults to `280`. Attribute: `min-pane-width`. */
1301
+ minPaneWidth?: number;
1302
+ /** Minimum height of every pane, in px, before the grid scrolls vertically. Defaults to `200`. Attribute: `min-pane-height`. */
1303
+ minPaneHeight?: number;
1304
+ /** Column cap when the container is wide (default `3`). Attribute: `max-columns`. */
1305
+ maxColumns?: number;
1306
+ /** Gap between panes, any CSS length. Defaults to the kit gap (`var(--kai-pane-grid-gap, 0.5rem)`). Attribute: `gap`. */
1307
+ gap?: string;
1308
+ /** When set to a valid child index, render ONLY that pane full-bleed: a simple maximize hook the consumer drives (pair it with `<kai-pane>`'s `kai-maximize` event). Clear it (or point out of range) for the full tiled grid. Attribute: `maximized-index`. */
1309
+ maximizedIndex?: number | null;
1310
+ }
1311
+
1227
1312
  export interface KaiPaneGroupElement extends HTMLElement {
1228
1313
  /** Color mode (`auto` follows prefers-color-scheme). */
1229
1314
  theme?: "light" | "dark" | "auto";
@@ -1307,7 +1392,7 @@ export interface KaiPromptInputElement extends HTMLElement {
1307
1392
  submit?: "always" | "auto";
1308
1393
  /** When `false`, hides the built-in paperclip attach button even though the element otherwise supports attachments. Use this when a `+` menu in `toolbar-start` already exposes "Add files", to avoid a duplicate control. Defaults to `true`. */
1309
1394
  attach?: boolean;
1310
- /** Attachments to seed the input with (so a consumer can pre-populate staged files without an upload). Set as a JS property; the element then manages its own attachment state from there (add via the paperclip, remove per chip). */
1395
+ /** Attachments to seed the input with (so a consumer can pre-populate staged files without an upload). Set as a JS property; the element then manages its own attachment state from there (add via the paperclip, remove per chip). Each item's `url` must be a `data:` URI or an https URL, never `URL.createObjectURL`: a `blob:` URL previews perfectly and is meaningless outside this tab, so `toOpenAIMessages`/`toAnthropicMessages` refuse it. (The built-in paperclip already stages files as `data:` URIs.) */
1311
1396
  attachments?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
1312
1397
  /** Rich entity triggers. Each `{ char, kind, items }` opens a caret-anchored menu that inserts an atomic pill. Convention: `/` → skills, `@` → agents (plugins are the grouping/provenance of those items). Set as a JS property. */
1313
1398
  triggers?: { char: string; kind: string; items?: { id: string; label: string; icon?: string; description?: string; group?: string; kind?: string; promptText?: string; data?: Record<string, unknown> }[] }[];
@@ -1323,6 +1408,23 @@ export interface KaiPromptInputElement extends HTMLElement {
1323
1408
  send(): void;
1324
1409
  }
1325
1410
 
1411
+ export interface KaiRadioGroupElement extends HTMLElement {
1412
+ /** Color mode (`auto` follows prefers-color-scheme). */
1413
+ theme?: "light" | "dark" | "auto";
1414
+ /** The choices, top to bottom. Set as a JS PROPERTY (array), never an attribute. */
1415
+ options: { value: string; label: string; description?: undefined | string; disabled?: undefined | boolean }[];
1416
+ /** Controlled selected `value`. Settable and reflected to the `value` attribute. `el.value = 'degraded'` drives it; choosing a row updates it and fires `kai-change`. Read `el.value` for live state. */
1417
+ value?: string;
1418
+ /** Shared form-control name for every radio in the group. Defaults to a generated id, so the group is exclusive and keyboard-navigable even when nothing is submitted. */
1419
+ name?: string;
1420
+ /** Disable every row. Individual rows carry their own `disabled`. */
1421
+ disabled?: boolean;
1422
+ /** Accessible name for the group. */
1423
+ label?: string;
1424
+ /** Focus the group's tab stop. That is the selected radio, or the first row when nothing is selected yet. */
1425
+ focus(options?: FocusOptions): void;
1426
+ }
1427
+
1326
1428
  export interface KaiReasoningElement extends HTMLElement {
1327
1429
  /** Color mode (`auto` follows prefers-color-scheme). */
1328
1430
  theme?: "light" | "dark" | "auto";
@@ -1468,6 +1570,10 @@ export interface KaiScrollButtonElement extends HTMLElement {
1468
1570
  variant?: "outline" | "ghost" | "default";
1469
1571
  /** Button size token. Defaults to `'icon'` (square). */
1470
1572
  size?: "sm" | "md" | "lg" | "icon" | "icon-sm";
1573
+ /** The button's accessible name. It is announced whether or not the label is visible, so the text is always localisable. Defaults to `'Scroll to bottom'`. */
1574
+ label?: string;
1575
+ /** Also render `label` visibly beside the icon. Defaults to `false`, which is the icon-only button. When the text is visible it IS the accessible name, so nothing gets announced twice. */
1576
+ showLabel?: boolean;
1471
1577
  }
1472
1578
 
1473
1579
  export interface KaiSearchElement extends HTMLElement {
@@ -1502,6 +1608,31 @@ export interface KaiSegmentedElement extends HTMLElement {
1502
1608
  size?: "sm" | "md";
1503
1609
  }
1504
1610
 
1611
+ export interface KaiSelectElement extends HTMLElement {
1612
+ /** Color mode (`auto` follows prefers-color-scheme). */
1613
+ theme?: "light" | "dark" | "auto";
1614
+ /** The choices, in display order. Set as a JS PROPERTY (array), never an attribute. Rendered in full: the kit never truncates, re-orders or de-duplicates them. */
1615
+ options: { value: string; label?: undefined | string; disabled?: undefined | boolean }[];
1616
+ /** Controlled selected value. Settable and reflected to the `value` attribute. `el.value = 'high'` drives it; choosing an option updates it and fires `kai-change`. Read `el.value` for live state; for a `multiple` select read `el.values` instead. */
1617
+ value?: string;
1618
+ /** Text for a leading, disabled, empty option: the "nothing chosen yet" row. Omitted means no such row at all; there is no default wording, because inventing one would put words in your UI. */
1619
+ placeholder?: string;
1620
+ /** Allow more than one selection. Turns the control into the platform's list box, so the kit's chevron is not drawn. */
1621
+ multiple?: boolean;
1622
+ /** Force the invalid (destructive-border) state. */
1623
+ invalid?: boolean;
1624
+ /** Disable interaction. */
1625
+ disabled?: boolean;
1626
+ /** Set the native `required` attribute, and nothing more. Whether an empty select is an error is the application's rule, not the kit's. */
1627
+ required?: boolean;
1628
+ /** Accessible label for the control. */
1629
+ label?: string;
1630
+ /** Form-control name, for a native form submit. */
1631
+ name?: string;
1632
+ /** Focus the inner select (the host element can't reach it). */
1633
+ focus(options?: FocusOptions): void;
1634
+ }
1635
+
1505
1636
  export interface KaiSeparatorElement extends HTMLElement {
1506
1637
  /** Color mode (`auto` follows prefers-color-scheme). */
1507
1638
  theme?: "light" | "dark" | "auto";
@@ -1547,6 +1678,29 @@ export interface KaiSkillsElement extends HTMLElement {
1547
1678
  skills: { id: string; name: string }[];
1548
1679
  }
1549
1680
 
1681
+ export interface KaiSliderElement extends HTMLElement {
1682
+ /** Color mode (`auto` follows prefers-color-scheme). */
1683
+ theme?: "light" | "dark" | "auto";
1684
+ /** Lowest selectable value. Required: a range with no bounds is a guess, and the guess belongs to whoever knows what the number means. */
1685
+ min?: number;
1686
+ /** Highest selectable value. Required, for the same reason as `min`. */
1687
+ max?: number;
1688
+ /** Granularity. Omitted means the native default of 1; `any` means continuous. */
1689
+ step?: number | "any";
1690
+ /** Controlled value. Settable and reflected to the `value` attribute. `el.value = 40` drives it; dragging updates it and fires `kai-input` per step, `kai-change` on release. Read `el.value` for live state. */
1691
+ value?: number;
1692
+ /** Disable interaction. */
1693
+ disabled?: boolean;
1694
+ /** Accessible label for the slider. */
1695
+ label?: string;
1696
+ /** Form-control name, for a native form submit. */
1697
+ name?: string;
1698
+ /** Show the current value beside the track. Off by default. Two ways in, because one of them is not a scalar. As a bare ATTRIBUTE (`<kai-slider value-label>`) it renders the raw number. As a JS PROPERTY it also accepts a formatter function (`el.valueLabel = (v) => v + '%'`), for a slider that is not counting bare numbers. A function cannot survive an attribute, so that half is property-only. The readout is hidden from assistive tech: the slider already reports the same number, and an exposed copy would be announced twice. */
1699
+ valueLabel?: boolean | ((value: number) => string);
1700
+ /** Focus the inner range input (the host element can't reach it). */
1701
+ focus(options?: FocusOptions): void;
1702
+ }
1703
+
1550
1704
  export interface KaiSourceElement extends HTMLElement {
1551
1705
  /** Color mode (`auto` follows prefers-color-scheme). */
1552
1706
  theme?: "light" | "dark" | "auto";
@@ -1732,7 +1886,7 @@ export interface KaiThreadElement extends HTMLElement {
1732
1886
  export interface KaiToastRegionElement extends HTMLElement {
1733
1887
  /** Color mode (`auto` follows prefers-color-scheme). */
1734
1888
  theme?: "light" | "dark" | "auto";
1735
- /** The toasts to render. Newest is shown on top. Set as a JS property (array); pass a new array reference to update. Omit for an empty region, which is the normal resting state and how the imperative `toast()` API starts. */
1889
+ /** The toasts to render. Newest is shown on top. Set as a JS property (array); pass a new array reference to update. Omit for an empty region, which is the normal resting state and how the imperative `toast()` API starts. Note the handover: the first `toast()` call ADOPTS a region you placed in markup (no second region mounts) and binds the imperative store to this property, replacing any array you set. Drive a region as data OR via `toast()`, not both at once. */
1736
1890
  toasts: { id: string; message: string; variant?: "neutral" | "success" | "warning" | "error" | "info"; appearance?: "pill" | "card"; inverse?: boolean; description?: string; action?: { label: string; onAction: () => void | false }; duration?: number; dismissible?: boolean; target?: HTMLElement }[];
1737
1891
  /** Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … */
1738
1892
  position?: "top-center" | "top-right" | "top-left" | "bottom-center" | "bottom-right" | "bottom-left";
@@ -1868,6 +2022,8 @@ declare global {
1868
2022
  'kai-cards': KaiCardsElement;
1869
2023
  'kai-chain-of-thought': KaiChainOfThoughtElement;
1870
2024
  'kai-chat': KaiChatElement;
2025
+ 'kai-checkbox': KaiCheckboxElement;
2026
+ 'kai-checkbox-group': KaiCheckboxGroupElement;
1871
2027
  'kai-checkpoint': KaiCheckpointElement;
1872
2028
  'kai-choice': KaiChoiceElement;
1873
2029
  'kai-coachmark': KaiCoachmarkElement;
@@ -1903,11 +2059,13 @@ declare global {
1903
2059
  'kai-nav': KaiNavElement;
1904
2060
  'kai-notice': KaiNoticeElement;
1905
2061
  'kai-pane': KaiPaneElement;
2062
+ 'kai-pane-grid': KaiPaneGridElement;
1906
2063
  'kai-pane-group': KaiPaneGroupElement;
1907
2064
  'kai-popover': KaiPopoverElement;
1908
2065
  'kai-progress-bar': KaiProgressBarElement;
1909
2066
  'kai-prompt-dock': KaiPromptDockElement;
1910
2067
  'kai-prompt-input': KaiPromptInputElement;
2068
+ 'kai-radio-group': KaiRadioGroupElement;
1911
2069
  'kai-reasoning': KaiReasoningElement;
1912
2070
  'kai-remote': KaiRemoteElement;
1913
2071
  'kai-resizable': KaiResizableElement;
@@ -1919,11 +2077,13 @@ declare global {
1919
2077
  'kai-scroll-button': KaiScrollButtonElement;
1920
2078
  'kai-search': KaiSearchElement;
1921
2079
  'kai-segmented': KaiSegmentedElement;
2080
+ 'kai-select': KaiSelectElement;
1922
2081
  'kai-separator': KaiSeparatorElement;
1923
2082
  'kai-setting-item': KaiSettingItemElement;
1924
2083
  'kai-settings-group': KaiSettingsGroupElement;
1925
2084
  'kai-skeleton': KaiSkeletonElement;
1926
2085
  'kai-skills': KaiSkillsElement;
2086
+ 'kai-slider': KaiSliderElement;
1927
2087
  'kai-source': KaiSourceElement;
1928
2088
  'kai-sources': KaiSourcesElement;
1929
2089
  'kai-status': KaiStatusElement;
@@ -1969,6 +2129,8 @@ declare module 'react' {
1969
2129
  'kai-cards': KaiElementJsxProps;
1970
2130
  'kai-chain-of-thought': KaiElementJsxProps;
1971
2131
  'kai-chat': KaiElementJsxProps;
2132
+ 'kai-checkbox': KaiElementJsxProps;
2133
+ 'kai-checkbox-group': KaiElementJsxProps;
1972
2134
  'kai-checkpoint': KaiElementJsxProps;
1973
2135
  'kai-choice': KaiElementJsxProps;
1974
2136
  'kai-coachmark': KaiElementJsxProps;
@@ -2004,11 +2166,13 @@ declare module 'react' {
2004
2166
  'kai-nav': KaiElementJsxProps;
2005
2167
  'kai-notice': KaiElementJsxProps;
2006
2168
  'kai-pane': KaiElementJsxProps;
2169
+ 'kai-pane-grid': KaiElementJsxProps;
2007
2170
  'kai-pane-group': KaiElementJsxProps;
2008
2171
  'kai-popover': KaiElementJsxProps;
2009
2172
  'kai-progress-bar': KaiElementJsxProps;
2010
2173
  'kai-prompt-dock': KaiElementJsxProps;
2011
2174
  'kai-prompt-input': KaiElementJsxProps;
2175
+ 'kai-radio-group': KaiElementJsxProps;
2012
2176
  'kai-reasoning': KaiElementJsxProps;
2013
2177
  'kai-remote': KaiElementJsxProps;
2014
2178
  'kai-resizable': KaiElementJsxProps;
@@ -2020,11 +2184,13 @@ declare module 'react' {
2020
2184
  'kai-scroll-button': KaiElementJsxProps;
2021
2185
  'kai-search': KaiElementJsxProps;
2022
2186
  'kai-segmented': KaiElementJsxProps;
2187
+ 'kai-select': KaiElementJsxProps;
2023
2188
  'kai-separator': KaiElementJsxProps;
2024
2189
  'kai-setting-item': KaiElementJsxProps;
2025
2190
  'kai-settings-group': KaiElementJsxProps;
2026
2191
  'kai-skeleton': KaiElementJsxProps;
2027
2192
  'kai-skills': KaiElementJsxProps;
2193
+ 'kai-slider': KaiElementJsxProps;
2028
2194
  'kai-source': KaiElementJsxProps;
2029
2195
  'kai-sources': KaiElementJsxProps;
2030
2196
  'kai-status': KaiElementJsxProps;
@@ -2102,7 +2268,7 @@ export interface KaiArtifactElementProps {
2102
2268
  export interface KaiAttachmentsElementProps {
2103
2269
  /** Color mode (`auto` follows prefers-color-scheme). */
2104
2270
  theme?: "light" | "dark" | "auto";
2105
- /** The attachments to render. Omit (or pass an empty array) for the empty state, which shows `emptyText` if set and nothing otherwise. Set as a JS property (array). */
2271
+ /** The attachments to render. Omit (or pass an empty array) for the empty state, which shows `emptyText` if set and nothing otherwise. Set as a JS property (array). Each item's `url` must be a `data:` URI or an https URL, never `URL.createObjectURL`: a `blob:` URL previews here but the wire encoders (`toOpenAIMessages`/`toAnthropicMessages`) refuse it. */
2106
2272
  items?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
2107
2273
  /** Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. */
2108
2274
  variant?: "grid" | "inline" | "list";
@@ -2231,7 +2397,7 @@ export interface KaiCardsElementProps {
2231
2397
  /** JSON Schemas for the card types this app renders, keyed by envelope type. The companion of `types`, which says what DRAWS a card while this says what a VALID one looks like. An OBJECT, so it is a JS property only: `el.schemas = { 'pricing-table': pricingSchema }`, never an attribute. `createCardRegistry(...).validationSchemas` is exactly this shape. Without it the kit validates its own seven built-ins and leaves your own card type, the one your app actually cares about, as the only unchecked thing on screen. A schema here WINS over a built-in of the same name, matching `mergeCardTags`, where your entry is spread over ours. Typed `Record<string, object>` rather than `Record<string, JsonSchema>` deliberately: an imported `.json` schema widens `"type"` to `string`, and an authored one carries `$schema`/`title`/`description`/`additionalProperties`, so the tighter type would reject both of the normal ways to supply one. See `CardSchemaMap` in components/card-renderer.tsx. */
2232
2398
  schemas?: Record<string, object>;
2233
2399
  /** Optional CardPolicy handling child events. Property: `el.policy`. */
2234
- policy?: { onSubmit?: (cardId: string, data: unknown) => void; onAction?: (cardId: string, action: string, payload?: unknown) => void; onSendPrompt?: (text: string, opts: { mode: "compose" | "send"; context?: unknown; }) => void; onOpen?: (url: string, target: "tab" | "artifact") => void; onState?: (cardId: string, patch: unknown) => void; onDismiss?: (cardId: string) => void; onReopen?: (cardId: string) => void; onError?: (cardId: string, message: string) => void; maxSendPromptMode?: "compose" | "send" };
2400
+ policy?: { onSubmit?: ((cardId: string, data: unknown) => void); onAction?: ((cardId: string, action: string, payload?: unknown) => void); onSendPrompt?: ((text: string, opts: { mode: "compose" | "send"; context?: unknown }) => void); onOpen?: ((url: string, target: "tab" | "artifact") => void); onState?: ((cardId: string, patch: unknown) => void); onDismiss?: ((cardId: string) => void); onReopen?: ((cardId: string) => void); onError?: ((cardId: string, message: string) => void); maxSendPromptMode?: "compose" | "send" };
2235
2401
  /** Validate each envelope's `data` against the schema for its type before rendering it, using a built-in's own schema or yours from `schemas`. Default `true`; set `validate-cards="false"` (or `el.validateCards = false`) to opt out. A hard failure (wrong type, a missing required field) renders a diagnostic naming the field instead of the card; a soft failure (bounds) renders the card unchanged. Both emit a contract `error` event. On in production too: a model emitting a bad shape is a production failure mode, so stripping the check there would hide it from exactly the person who needs to see it. */
2236
2402
  validateCards?: boolean;
2237
2403
  }
@@ -2270,6 +2436,10 @@ export interface KaiChatElementProps {
2270
2436
  codeTheme?: string;
2271
2437
  /** Enable Shiki syntax highlighting in code blocks. Turn off to render plain `<pre>` blocks (lighter, no highlighter load). Default true. */
2272
2438
  codeHighlight?: boolean;
2439
+ /** How `reasoning` parts render across the thread. `'full'` (default) is the current collapsible-disclosure behavior; `'compact'` shows only a shimmer loader while a reasoning part streams and nothing once it settles (no expandable detail); `'off'` renders reasoning parts not at all. Forwarded to every `MessageBody` as `reasoningMode`. */
2440
+ reasoning?: "full" | "compact" | "off";
2441
+ /** Seeds the reasoning disclosure open AND keeps it tracking the stream (open while streaming, closes when it settles): the pre-Task-19f `full` behavior. Default false/absent: the panel starts closed (just the "Thinking" shimmer chip) and only opens on click, the current default (owner ruling, 2026-08-26). Meaningless when `reasoning` is `'compact'` or `'off'`. Forwarded to every `MessageBody` as `reasoningDefaultOpen`. */
2442
+ reasoningOpen?: boolean;
2273
2443
  /** Optional header title shown on the left of the header. */
2274
2444
  chatTitle?: string;
2275
2445
  /** Optional model list. When set (>1 model) a ModelSwitcher is shown in the header and a `kai-model-change` event fires on selection. */
@@ -2296,6 +2466,8 @@ export interface KaiChatElementProps {
2296
2466
  composerActions?: boolean;
2297
2467
  /** INJECT: footer row below the composer (disclaimers, token meter, …). */
2298
2468
  footer?: boolean;
2469
+ /** When `false`, hides the built-in paperclip attach button. Defaults to `true` (undeclared keeps today's behavior: attach visible), matching `DefaultPromptInput`'s own default: only an explicit `false` hides it. */
2470
+ attach?: boolean;
2299
2471
  /** Show a web-search (Globe) button in the input toolbar; calls `onWebSearch`. */
2300
2472
  webSearch?: boolean;
2301
2473
  /** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
@@ -2314,6 +2486,48 @@ export interface KaiChatElementProps {
2314
2486
  cardTypes?: Record<string, string>;
2315
2487
  /** JSON Schemas for the card types this app renders, keyed by envelope type. The companion of `cardTypes`, which says what DRAWS a card while this says what a VALID one looks like. An OBJECT, so it is a JS property only: `el.cardSchemas = { 'pricing-table': pricingSchema }`, never an attribute. `createCardRegistry(...).validationSchemas` is exactly this shape. Without it the kit validates its own seven built-ins and leaves your own card type, the one your app actually cares about, as the only unchecked thing on screen. A schema here WINS over a built-in of the same name. Typed `Record<string, object>` rather than `Record<string, JsonSchema>` deliberately: an imported `.json` schema widens `"type"` to `string`, and an authored one carries `$schema`/`title`/`description`/`additionalProperties`, so the tighter type would reject both of the normal ways to supply one. */
2316
2488
  cardSchemas?: Record<string, object>;
2489
+ /** Turns on the prior-conversations list (a list-toggle button in the header, plus a second list view sharing the panel, C-1). Attribute- settable like every other boolean flag on this element: `<kai-chat conversations>`. Requires `store`. A row select, "new conversation," or the visitor's mount-time auto-restore all deliver their messages the same way: listen for `kai-conversation-load` and set `el.messages` from `event.detail.messages` (a fresh array): this element does not update `messages` for you. Set with no `store`, the underlying `ChatThread` decides loudly (one console.error) and stays visually off; this facade always supplies its own internal load handler (the `kai-conversation-load` dispatch below), so the second ChatThread guard, missing `onConversationLoad`, never trips here, even for a consumer who never listens for the event. Default false. */
2490
+ conversations?: boolean;
2491
+ /** The adapter this thread persists conversations through: an object of three functions (`list`/`load`/`save`; `ConversationStore`, exported from `@kitn.ai/ui`'s `primitives/conversation-store`). A JS PROPERTY ONLY: `el.store = myAdapter`. It can never be an attribute, since a function-bearing object has no HTML string form, the same reasoning that keeps `messages`/`cardSchemas` property-only (the kai- contract: array/object props are JS properties, never attributes). Two built-ins ship: `localStorageStore(name, userId?)` and `fetchStore(url, userId?)`. */
2492
+ store?: { list: () => Promise<{ id: string; title: string; groupId?: string; scope?: { type: "document" | "collection"; documentId?: string; filters?: { tags?: string[]; authors?: string[]; contentType?: "transcript" | "markdown"; dateRange?: { from: string; to: string } } }; messageCount: number; lastMessageAt?: string; updatedAt: string; trailing?: string; lastReadAt?: string }[]>; load: (id: string) => Promise<{ id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" }[]>; save: (id: string, messages: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" }[]) => Promise<void>; markRead?: ((id: string) => Promise<void>) };
2493
+ /** Turns on the widget home screen (Intercom-pattern): the panel boots into a `home` view, with a greeting, most-recent-conversation card, a "new conversation" CTA, and host-defined links, plus a Home/Messages tab bar for switching back to the thread. An OBJECT, so it is a JS property only: `el.home = { greeting: { title: 'Hey' }, links: [...] }`, never an attribute (the kai- contract: array/object props are JS properties). A `links` entry with no `href` fires `kai-home-link` with that entry when tapped, rather than navigating; one WITH `href` opens it directly (only when the URL passes the kit's own scheme allowlist). Omit for the no-home widget (chat view only, unchanged). */
2494
+ home?: { greeting?: { title?: string; subtitle?: string }; recentConversation?: boolean; newConversation?: { label?: string }; links?: { label: string; href?: string; description?: string; icon?: string }[] };
2495
+ }
2496
+
2497
+ export interface KaiCheckboxElementProps {
2498
+ /** Color mode (`auto` follows prefers-color-scheme). */
2499
+ theme?: "light" | "dark" | "auto";
2500
+ /** Controlled checked state. Settable and reflected to the `checked` attribute. `el.checked = true` (or `<kai-checkbox checked>`) drives it; ticking the box updates it and fires `kai-change`. Read `el.checked` for live state. */
2501
+ checked?: boolean;
2502
+ /** Initial checked state on mount (uncontrolled seed). Bare attribute (`<kai-checkbox default-checked>`) turns it on. */
2503
+ defaultChecked?: boolean;
2504
+ /** The mixed state, for a parent box whose children are partly ticked. Visual plus an accessibility hint: the box still reports `checked === false`. */
2505
+ indeterminate?: boolean;
2506
+ /** Disable interaction. */
2507
+ disabled?: boolean;
2508
+ /** Set the native `required` attribute, and nothing more. Whether an unticked box is an error is the application's rule, not the kit's. */
2509
+ required?: boolean;
2510
+ /** Accessible label. The visible text beside the box is the consumer's to render. */
2511
+ label?: string;
2512
+ /** Form-control name (paired with `value`). */
2513
+ name?: string;
2514
+ /** Submitted value when checked (paired with `name`). Defaults to `'on'`. */
2515
+ value?: string;
2516
+ }
2517
+
2518
+ export interface KaiCheckboxGroupElementProps {
2519
+ /** Color mode (`auto` follows prefers-color-scheme). */
2520
+ theme?: "light" | "dark" | "auto";
2521
+ /** The choices, top to bottom. Set as a JS PROPERTY (array), never an attribute. Rendered in full: the kit never truncates, re-orders or de-duplicates them. */
2522
+ options: { value: string; label: string; description?: undefined | string; disabled?: undefined | boolean }[];
2523
+ /** The FIRST selected value. Settable and reflected to the `value` attribute, so `:host([value])` and `el.value` see live state, and a seed can be written in markup. Writing it makes that the whole selection; to read or drive the rest, use `el.values`. */
2524
+ value?: string;
2525
+ /** The shared form-control name every box carries, so `FormData.getAll(name)` reads the whole selection back under one key. NO DEFAULT, unlike `<kai-radio-group>`. A radio set needs a shared `name` for the browser to make it exclusive and arrow-navigable, so one is generated when none is given; checkboxes are independent controls and behave correctly with no name at all. Generating one here would submit the selection under a random key, which is worse than submitting nothing. The element is NOT form-associated (no `ElementInternals`, no `setFormValue()`), the same known gap `<kai-input>` records: the boxes live in a shadow root, so a surrounding `<form>` collects nothing from them whether or not `name` is set. Read `el.values`. The name still lands on every inner input, so it is right the day form association arrives. */
2526
+ name?: string;
2527
+ /** Disable every row. Individual rows carry their own `disabled`. */
2528
+ disabled?: boolean;
2529
+ /** Accessible name for the group. */
2530
+ label?: string;
2317
2531
  }
2318
2532
 
2319
2533
  export interface KaiCheckpointElementProps {
@@ -2466,9 +2680,9 @@ export interface KaiContextElementProps {
2466
2680
  export interface KaiConversationItemElementProps {
2467
2681
  /** Color mode (`auto` follows prefers-color-scheme). */
2468
2682
  theme?: "light" | "dark" | "auto";
2469
- /** The row's identity, handed to the container's selection contract. In the element this is the `conversation-id` attribute (host `id` is the fallback). */
2683
+ /** The row's identity: the `conversation-id` attribute (host `id` is the fallback). Inside `<kai-conversations>` it is handed to the container's selection contract (`kai-conversation-select`); standalone it is the `id` in this element's own `kai-select` detail. */
2470
2684
  conversationId?: string;
2471
- /** Selected state. Reflected as `aria-current` on the row body and a `data-active` styling hook on the row; the container drives it from its `activeId`. */
2685
+ /** Selected state. Reflected as `aria-current` on the row body and a `data-active` styling hook on the row; inside a container the container drives it from its `activeId`, standalone you set it yourself. */
2472
2686
  active?: boolean;
2473
2687
  /** Dense single-line row padding. */
2474
2688
  compact?: boolean;
@@ -2480,7 +2694,7 @@ export interface KaiConversationsElementProps {
2480
2694
  /** The list's section headers (`{ id, name, sortOrder, createdAt }`), rendered in array order. A group carries no conversations of its own; it is matched against `conversations` by id, so the two props are complementary rather than alternatives. Omit for an ungrouped list. Set as a JS property. */
2481
2695
  groups?: { id: string; userId?: string; teamId?: string; name: string; sortOrder: number; createdAt: string }[];
2482
2696
  /** Every conversation the list renders, flat. Each one is filed under the group whose `id` equals its `groupId`; one with no `groupId`, or with a `groupId` matching no entry in `groups`, falls into a trailing "Ungrouped" section, so nothing you pass in is ever dropped. There is no recency bucketing. Set as a JS property. Omit to supply them as `<kai-conversation>` light-DOM children instead, or for the empty state. A search query that matches nothing shows a visible "No conversations match your search" state, distinct from the zero-conversations empty state. Slotted `<kai-conversation-item>` children switch the list into item mode instead: your own rows win and this array is not rendered. */
2483
- conversations?: { id: string; title: string; groupId?: string; scope?: { type: "document" | "collection"; documentId?: string; filters?: { tags?: string[]; authors?: string[]; contentType?: "transcript" | "markdown"; dateRange?: { from: string; to: string } } }; messageCount: number; lastMessageAt?: string; updatedAt: string; trailing?: string }[];
2697
+ conversations?: { id: string; title: string; groupId?: string; scope?: { type: "document" | "collection"; documentId?: string; filters?: { tags?: string[]; authors?: string[]; contentType?: "transcript" | "markdown"; dateRange?: { from: string; to: string } } }; messageCount: number; lastMessageAt?: string; updatedAt: string; trailing?: string; lastReadAt?: string }[];
2484
2698
  /** The id of the currently-open conversation, highlighted in the list. */
2485
2699
  activeId?: string;
2486
2700
  /** Controlled collapsed state. Set as a JS property (`el.collapsed = true`) to drive the rail from your app, updating it in response to `kai-collapse-toggle`. Omit for uncontrolled (the element manages it). Collapsed shrinks the rail to a floating reopen button. */
@@ -2521,6 +2735,8 @@ export interface KaiDockElementProps {
2521
2735
  unread?: boolean;
2522
2736
  /** Disable the launcher; `show()` and `toggle()` are gated on it. */
2523
2737
  disabled?: boolean;
2738
+ /** Suppress the dock's own built-in mobile close X. Set this when your slotted panel content supplies its own close affordance (e.g. a `<kai-chat slot="header-end">` close button), otherwise the two stack. TRADEOFF: the mobile panel reserves a padding band above its content so the built-in X never paints over slotted content; that band stays reserved unless you set this true, so only set it once your own control is actually in place. Attribute: `hide-close`. */
2739
+ hideClose?: boolean;
2524
2740
  /** Where focus lands on open: `content` (default, the first element you slotted), `panel`, or `none`. Attribute: `focus-on-open`. */
2525
2741
  focusOnOpen?: "content" | "panel" | "none";
2526
2742
  }
@@ -2626,7 +2842,7 @@ export interface KaiFormElementProps {
2626
2842
  /** Color mode (`auto` follows prefers-color-scheme). */
2627
2843
  theme?: "light" | "dark" | "auto";
2628
2844
  /** The form definition: a JSON Schema (`type:'object'`) + `x-kai-*` UI hints (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { type:'object', properties:{…} }`. Import the `FormDefinition` type from `@kitn.ai/ui` for the full shape. It IS self-referential (`FormField.properties` is another `FormField` map), and the generated `element-types.d.ts` inlines every named type, so the shipped declaration bottoms out in a `Record<string, unknown>` placeholder one level down rather than carrying the recursion. That is why `FormDefinition` is a `type` alias: an interface gets no implicit index signature, so it would not be assignable to that placeholder. */
2629
- data?: { type: "object"; title?: string; description?: string; required?: string[]; properties: Record<string, { type: "string" | "number" | "integer" | "boolean" | "array" | "object"; title?: string; description?: string; default?: unknown; enum?: unknown[]; format?: "email" | "uri" | "url" | "date" | "date-time" | "time"; minimum?: number; maximum?: number; minLength?: number; maxLength?: number; pattern?: string; minItems?: number; maxItems?: number; items?: Record<string, unknown> | { enum: unknown[] }; properties?: Record<string, Record<string, unknown>>; required?: string[]; readOnly?: boolean; "x-kai-widget"?: "textarea" | "slider" | "rating" | "radio" | "select" | "checkbox" | "password" | "switch"; "x-kai-placeholder"?: string; "x-kai-step"?: number }>; "x-kai-order"?: string[]; "x-kai-inlineMax"?: number; "x-kai-submitLabel"?: string; "x-kai-dismissible"?: boolean; "x-kai-actions"?: { id: string; label: string; variant?: "default" | "ghost" | "outline" }[] };
2845
+ data?: { type: "object"; title?: string; description?: string; required?: string[]; properties: Record<string, { type: "string" | "number" | "integer" | "boolean" | "array" | "object"; title?: string; description?: string; default?: unknown; enum?: unknown[]; format?: "email" | "uri" | "url" | "date" | "date-time" | "time"; minimum?: number; maximum?: number; minLength?: number; maxLength?: number; pattern?: string; minItems?: number; maxItems?: number; items?: Record<string, unknown> | { enum: unknown[] }; properties?: Record<string, Record<string, unknown>>; required?: string[]; readOnly?: boolean; "x-kai-widget"?: "textarea" | "slider" | "rating" | "radio" | "select" | "checkbox" | "password" | "switch"; "x-kai-placeholder"?: string; "x-kai-step"?: number; "x-kai-format"?: "tel" | "ssn" | "credit-card" | "custom"; "x-kai-mask"?: string; "x-kai-mask-guide"?: string }>; "x-kai-order"?: string[]; "x-kai-inlineMax"?: number; "x-kai-submitLabel"?: string; "x-kai-dismissible"?: boolean; "x-kai-actions"?: { id: string; label: string; variant?: "default" | "ghost" | "outline" }[] };
2630
2846
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
2631
2847
  cardId?: string;
2632
2848
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
@@ -2685,7 +2901,7 @@ export interface KaiInputElementProps {
2685
2901
  theme?: "light" | "dark" | "auto";
2686
2902
  /** Native input type: `text` (default) · `email` · `url` · `search` · `tel` · `password` · `number`. Single-line only. */
2687
2903
  type?: string;
2688
- /** Controlled value. Settable and reflected to the `value` attribute. `el.value = 'hi'` drives it (no event); typing updates it and fires `kai-input`. Read `el.value` for live state. */
2904
+ /** Controlled value, and always the CANONICAL one when a mask is active: digits for `tel` / `ssn` / `credit-card`, the formatted text for `custom`. Settable and reflected to the `value` attribute. `el.value = '5551234567'` drives it (no event) and is re-fitted to the mask on the way in, so the field shows `555-123-4567`. Read `el.value` for live state; the formatted text rides along on every `kai-input` / `kai-change` detail as `formattedValue`. */
2689
2905
  value?: string;
2690
2906
  /** Placeholder shown when empty. */
2691
2907
  placeholder?: string;
@@ -2711,6 +2927,16 @@ export interface KaiInputElementProps {
2711
2927
  autocomplete?: string;
2712
2928
  /** Virtual-keyboard hint forwarded to the inner input (e.g. `numeric`, `email`). */
2713
2929
  inputmode?: string;
2930
+ /** Mask pattern: `#` a digit, `@` a letter or digit, `*` an obscurable letter or digit, and every other character a positional literal (`@@@-####` → `CHG-4821`). The literal `default` is the opt-in sentinel: it resolves to the default format of `semantic` (`tel` → `###-###-####`). A bare `semantic` never starts masking on its own, so an opt-in token is what turns tier 2 on. */
2931
+ format?: string;
2932
+ /** Placeholder guide shown at unfilled positions, aligned position for position with `format`: `mm/dd/yyyy` against `##/##/####`. Spaces are a valid guide character, so a guide of blanks and separators is how a phone field shows its shape without showing letters. Without a guide the field shows only up to the last typed character. A guide is a visual aid, never an accessible name: keep the `hint` text as well. */
2933
+ guide?: string;
2934
+ /** Semantic field type: `tel` · `ssn` · `credit-card` · `custom`. On its own it sets `inputmode` / `autocomplete` / `spellcheck` / `autocorrect` / `autocapitalize` and decides the canonical value; it never starts masking by itself. */
2935
+ semantic?: "credit-card" | "custom" | "ssn" | "tel";
2936
+ /** Case folding applied to typed and pasted text: `preserve` (default) · `upper` · `lower`. Attribute: `case-mode`. */
2937
+ caseMode?: "preserve" | "upper" | "lower";
2938
+ /** What a copy or cut of a masked field puts on the clipboard: `canonical` (default) · `formatted` · `obscured` · `blocked`. Attribute: `copy-policy`. */
2939
+ copyPolicy?: "formatted" | "canonical" | "obscured" | "blocked";
2714
2940
  }
2715
2941
 
2716
2942
  export interface KaiKbdElementProps {
@@ -2869,6 +3095,21 @@ export interface KaiPaneElementProps {
2869
3095
  status?: { tone: "working" | "idle" | "done" | "error" | "blocked"; label?: string; pulse?: boolean };
2870
3096
  }
2871
3097
 
3098
+ export interface KaiPaneGridElementProps {
3099
+ /** Color mode (`auto` follows prefers-color-scheme). */
3100
+ theme?: "light" | "dark" | "auto";
3101
+ /** Minimum width of every pane, in px, before columns drop / the grid scrolls. Defaults to `280`. Attribute: `min-pane-width`. */
3102
+ minPaneWidth?: number;
3103
+ /** Minimum height of every pane, in px, before the grid scrolls vertically. Defaults to `200`. Attribute: `min-pane-height`. */
3104
+ minPaneHeight?: number;
3105
+ /** Column cap when the container is wide (default `3`). Attribute: `max-columns`. */
3106
+ maxColumns?: number;
3107
+ /** Gap between panes, any CSS length. Defaults to the kit gap (`var(--kai-pane-grid-gap, 0.5rem)`). Attribute: `gap`. */
3108
+ gap?: string;
3109
+ /** When set to a valid child index, render ONLY that pane full-bleed: a simple maximize hook the consumer drives (pair it with `<kai-pane>`'s `kai-maximize` event). Clear it (or point out of range) for the full tiled grid. Attribute: `maximized-index`. */
3110
+ maximizedIndex?: number | null;
3111
+ }
3112
+
2872
3113
  export interface KaiPaneGroupElementProps {
2873
3114
  /** Color mode (`auto` follows prefers-color-scheme). */
2874
3115
  theme?: "light" | "dark" | "auto";
@@ -2942,7 +3183,7 @@ export interface KaiPromptInputElementProps {
2942
3183
  submit?: "always" | "auto";
2943
3184
  /** When `false`, hides the built-in paperclip attach button even though the element otherwise supports attachments. Use this when a `+` menu in `toolbar-start` already exposes "Add files", to avoid a duplicate control. Defaults to `true`. */
2944
3185
  attach?: boolean;
2945
- /** Attachments to seed the input with (so a consumer can pre-populate staged files without an upload). Set as a JS property; the element then manages its own attachment state from there (add via the paperclip, remove per chip). */
3186
+ /** Attachments to seed the input with (so a consumer can pre-populate staged files without an upload). Set as a JS property; the element then manages its own attachment state from there (add via the paperclip, remove per chip). Each item's `url` must be a `data:` URI or an https URL, never `URL.createObjectURL`: a `blob:` URL previews perfectly and is meaningless outside this tab, so `toOpenAIMessages`/`toAnthropicMessages` refuse it. (The built-in paperclip already stages files as `data:` URIs.) */
2946
3187
  attachments?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
2947
3188
  /** Rich entity triggers. Each `{ char, kind, items }` opens a caret-anchored menu that inserts an atomic pill. Convention: `/` → skills, `@` → agents (plugins are the grouping/provenance of those items). Set as a JS property. */
2948
3189
  triggers?: { char: string; kind: string; items?: { id: string; label: string; icon?: string; description?: string; group?: string; kind?: string; promptText?: string; data?: Record<string, unknown> }[] }[];
@@ -2950,6 +3191,21 @@ export interface KaiPromptInputElementProps {
2950
3191
  kindIcons?: Record<string, string>;
2951
3192
  }
2952
3193
 
3194
+ export interface KaiRadioGroupElementProps {
3195
+ /** Color mode (`auto` follows prefers-color-scheme). */
3196
+ theme?: "light" | "dark" | "auto";
3197
+ /** The choices, top to bottom. Set as a JS PROPERTY (array), never an attribute. */
3198
+ options: { value: string; label: string; description?: undefined | string; disabled?: undefined | boolean }[];
3199
+ /** Controlled selected `value`. Settable and reflected to the `value` attribute. `el.value = 'degraded'` drives it; choosing a row updates it and fires `kai-change`. Read `el.value` for live state. */
3200
+ value?: string;
3201
+ /** Shared form-control name for every radio in the group. Defaults to a generated id, so the group is exclusive and keyboard-navigable even when nothing is submitted. */
3202
+ name?: string;
3203
+ /** Disable every row. Individual rows carry their own `disabled`. */
3204
+ disabled?: boolean;
3205
+ /** Accessible name for the group. */
3206
+ label?: string;
3207
+ }
3208
+
2953
3209
  export interface KaiReasoningElementProps {
2954
3210
  /** Color mode (`auto` follows prefers-color-scheme). */
2955
3211
  theme?: "light" | "dark" | "auto";
@@ -3071,6 +3327,10 @@ export interface KaiScrollButtonElementProps {
3071
3327
  variant?: "outline" | "ghost" | "default";
3072
3328
  /** Button size token. Defaults to `'icon'` (square). */
3073
3329
  size?: "sm" | "md" | "lg" | "icon" | "icon-sm";
3330
+ /** The button's accessible name. It is announced whether or not the label is visible, so the text is always localisable. Defaults to `'Scroll to bottom'`. */
3331
+ label?: string;
3332
+ /** Also render `label` visibly beside the icon. Defaults to `false`, which is the icon-only button. When the text is visible it IS the accessible name, so nothing gets announced twice. */
3333
+ showLabel?: boolean;
3074
3334
  }
3075
3335
 
3076
3336
  export interface KaiSearchElementProps {
@@ -3101,6 +3361,29 @@ export interface KaiSegmentedElementProps {
3101
3361
  size?: "sm" | "md";
3102
3362
  }
3103
3363
 
3364
+ export interface KaiSelectElementProps {
3365
+ /** Color mode (`auto` follows prefers-color-scheme). */
3366
+ theme?: "light" | "dark" | "auto";
3367
+ /** The choices, in display order. Set as a JS PROPERTY (array), never an attribute. Rendered in full: the kit never truncates, re-orders or de-duplicates them. */
3368
+ options: { value: string; label?: undefined | string; disabled?: undefined | boolean }[];
3369
+ /** Controlled selected value. Settable and reflected to the `value` attribute. `el.value = 'high'` drives it; choosing an option updates it and fires `kai-change`. Read `el.value` for live state; for a `multiple` select read `el.values` instead. */
3370
+ value?: string;
3371
+ /** Text for a leading, disabled, empty option: the "nothing chosen yet" row. Omitted means no such row at all; there is no default wording, because inventing one would put words in your UI. */
3372
+ placeholder?: string;
3373
+ /** Allow more than one selection. Turns the control into the platform's list box, so the kit's chevron is not drawn. */
3374
+ multiple?: boolean;
3375
+ /** Force the invalid (destructive-border) state. */
3376
+ invalid?: boolean;
3377
+ /** Disable interaction. */
3378
+ disabled?: boolean;
3379
+ /** Set the native `required` attribute, and nothing more. Whether an empty select is an error is the application's rule, not the kit's. */
3380
+ required?: boolean;
3381
+ /** Accessible label for the control. */
3382
+ label?: string;
3383
+ /** Form-control name, for a native form submit. */
3384
+ name?: string;
3385
+ }
3386
+
3104
3387
  export interface KaiSeparatorElementProps {
3105
3388
  /** Color mode (`auto` follows prefers-color-scheme). */
3106
3389
  theme?: "light" | "dark" | "auto";
@@ -3146,6 +3429,27 @@ export interface KaiSkillsElementProps {
3146
3429
  skills?: { id: string; name: string }[];
3147
3430
  }
3148
3431
 
3432
+ export interface KaiSliderElementProps {
3433
+ /** Color mode (`auto` follows prefers-color-scheme). */
3434
+ theme?: "light" | "dark" | "auto";
3435
+ /** Lowest selectable value. Required: a range with no bounds is a guess, and the guess belongs to whoever knows what the number means. */
3436
+ min?: number;
3437
+ /** Highest selectable value. Required, for the same reason as `min`. */
3438
+ max?: number;
3439
+ /** Granularity. Omitted means the native default of 1; `any` means continuous. */
3440
+ step?: number | "any";
3441
+ /** Controlled value. Settable and reflected to the `value` attribute. `el.value = 40` drives it; dragging updates it and fires `kai-input` per step, `kai-change` on release. Read `el.value` for live state. */
3442
+ value?: number;
3443
+ /** Disable interaction. */
3444
+ disabled?: boolean;
3445
+ /** Accessible label for the slider. */
3446
+ label?: string;
3447
+ /** Form-control name, for a native form submit. */
3448
+ name?: string;
3449
+ /** Show the current value beside the track. Off by default. Two ways in, because one of them is not a scalar. As a bare ATTRIBUTE (`<kai-slider value-label>`) it renders the raw number. As a JS PROPERTY it also accepts a formatter function (`el.valueLabel = (v) => v + '%'`), for a slider that is not counting bare numbers. A function cannot survive an attribute, so that half is property-only. The readout is hidden from assistive tech: the slider already reports the same number, and an exposed copy would be announced twice. */
3450
+ valueLabel?: boolean | ((value: number) => string);
3451
+ }
3452
+
3149
3453
  export interface KaiSourceElementProps {
3150
3454
  /** Color mode (`auto` follows prefers-color-scheme). */
3151
3455
  theme?: "light" | "dark" | "auto";
@@ -3309,7 +3613,7 @@ export interface KaiThreadElementProps {
3309
3613
  export interface KaiToastRegionElementProps {
3310
3614
  /** Color mode (`auto` follows prefers-color-scheme). */
3311
3615
  theme?: "light" | "dark" | "auto";
3312
- /** The toasts to render. Newest is shown on top. Set as a JS property (array); pass a new array reference to update. Omit for an empty region, which is the normal resting state and how the imperative `toast()` API starts. */
3616
+ /** The toasts to render. Newest is shown on top. Set as a JS property (array); pass a new array reference to update. Omit for an empty region, which is the normal resting state and how the imperative `toast()` API starts. Note the handover: the first `toast()` call ADOPTS a region you placed in markup (no second region mounts) and binds the imperative store to this property, replacing any array you set. Drive a region as data OR via `toast()`, not both at once. */
3313
3617
  toasts?: { id: string; message: string; variant?: "neutral" | "success" | "warning" | "error" | "info"; appearance?: "pill" | "card"; inverse?: boolean; description?: string; action?: { label: string; onAction: () => void | false }; duration?: number; dismissible?: boolean; target?: HTMLElement }[];
3314
3618
  /** Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … */
3315
3619
  position?: "top-center" | "top-right" | "top-left" | "bottom-center" | "bottom-right" | "bottom-left";
@@ -3466,6 +3770,10 @@ export interface KaiChatElementEvents {
3466
3770
  onKaiAttachmentsChange?: (event: CustomEvent<{ attachments: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[] }>) => void;
3467
3771
  /** One or more picked files were refused because `accept` excluded them. The element renders NO message of its own: it reports the facts (name, media type, whether the kit could have sent it) and what the user should see is the application's call. Only ever fires when `accept` is set. */
3468
3772
  onKaiAttachmentsRejected?: (event: CustomEvent<{ rejected: { filename: string; mediaType: string; reason: "filtered" | "unsupported" }[] }>) => void;
3773
+ /** A conversation's history loaded: a row tap in the list, "new conversation," or the visitor's own mount-time auto-restore of their most recent thread (only fires when `conversations` is on and a `store` is set). `detail.id` is that conversation's id, `undefined` for the "new conversation" case (no id exists until the first message mints one, C-6). Set `el.messages = event.detail.messages` (already a fresh array) to actually render it, since this element does not do that for you; `messages` stays your own state like everywhere else on this element. */
3774
+ onKaiConversationLoad?: (event: CustomEvent<{ id: string | undefined; messages: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: undefined | { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: undefined | string; index?: undefined | number; streamId?: undefined | string; signature?: undefined | string; raw?: undefined | { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: undefined | "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: undefined | Record<string, unknown>; rawInput?: undefined | string; output?: undefined | Record<string, unknown>; toolCallId?: undefined | string; errorText?: undefined | string; raw?: undefined | { source: string; payload: unknown } }; raw?: undefined | { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: undefined | string; resolution?: undefined | { kind: "action"; action: string; payload?: unknown; at?: undefined | string } | { kind: "submit"; data: unknown; at?: undefined | string } | { kind: "dismissed"; at?: undefined | string } | { kind: "expired"; reason?: undefined | string; at?: undefined | string } }; raw?: undefined | { source: string; payload: unknown } } | { type: "source"; source: { id?: undefined | string; url?: undefined | string; title?: undefined | string; snippet?: undefined | string; index?: undefined | number }; raw?: undefined | { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }; raw?: undefined | { source: string; payload: unknown } })[]; actions?: undefined | ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: undefined | string; tooltip?: undefined | string })[]; avatar?: undefined | { src?: undefined | string; fallback?: undefined | string; alt?: undefined | string }; feedback?: undefined | "like" | "dislike" }[] }>) => void;
3775
+ /** A `home.links` entry with no `href` was activated (tapped/clicked/Enter). Meaningful only when `home` is set. */
3776
+ onKaiHomeLink?: (event: CustomEvent<{ entry: { label: string; href?: undefined | string; description?: undefined | string; icon?: undefined | string } }>) => void;
3469
3777
  /** An action button on a message was clicked. `action` is the built-in name or custom id. `state` is present only for the toggleable feedback votes: `'on'` when a like/dislike is set, `'off'` when re-tapped to clear. */
3470
3778
  onKaiMessageAction?: (event: CustomEvent<{ messageId: string; action: string; state?: undefined | "on" | "off" }>) => void;
3471
3779
  /** The header model switcher changed. */
@@ -3482,6 +3790,16 @@ export interface KaiChatElementEvents {
3482
3790
  onKaiWebSearch?: (event: CustomEvent<Record<string, never>>) => void;
3483
3791
  }
3484
3792
 
3793
+ export interface KaiCheckboxElementEvents {
3794
+ /** The box was ticked or unticked. */
3795
+ onKaiChange?: (event: CustomEvent<{ checked: boolean }>) => void;
3796
+ }
3797
+
3798
+ export interface KaiCheckboxGroupElementEvents {
3799
+ /** A row was ticked or unticked. `values` is the whole selection after the change, which is what a multi-select control needs; `value` is the first of them (empty when nothing is selected). Both are always present, so neither shape silently loses the other. This is `<kai-select>`'s detail, deliberately. */
3800
+ onKaiChange?: (event: CustomEvent<{ value: string; values: string[] }>) => void;
3801
+ }
3802
+
3485
3803
  export interface KaiCheckpointElementEvents {
3486
3804
  /** The checkpoint was clicked. */
3487
3805
  onKaiSelect?: (event: CustomEvent) => void;
@@ -3530,7 +3848,7 @@ export interface KaiComposerElementEvents {
3530
3848
  onKaiEntityRemove?: (event: CustomEvent<{ entity: { kind: string; id: string; label: string; icon?: undefined | string; promptText?: undefined | string; data?: undefined | Record<string, unknown> } }>) => void;
3531
3849
  /** The composer gained focus. `focus`/`blur` are NOT composed natively, so they don't escape the shadow root; these re-expose them on the host. (For `keydown`/`paste`/`focusin`/`focusout`, listen NATIVELY on `<kai-composer>`: they're composed and already cross the shadow boundary.) */
3532
3850
  onKaiFocus?: (event: CustomEvent<{ originalEvent: FocusEvent }>) => void;
3533
- /** The user submitted the composer (Enter or programmatic submit). */
3851
+ /** The user submitted the composer (Enter or programmatic submit). Note the detail carries no `attachments`; `<kai-composer>` is the bare editing surface: no send button, toolbar, or attachments. For a drop-in composer row with all three, reach for `<kai-prompt-input>`, which is built on this. */
3534
3852
  onKaiSubmit?: (event: CustomEvent<{ doc: ({ type: "text"; text: string } | { type: "entity"; entity: { kind: string; id: string; label: string; icon?: undefined | string; promptText?: undefined | string; data?: undefined | Record<string, unknown> } })[]; text: string; entities: { kind: string; id: string; label: string; icon?: undefined | string; promptText?: undefined | string; data?: undefined | Record<string, unknown> }[] }>) => void;
3535
3853
  /** A trigger character was detected at the caret (e.g. `/` or `@`). */
3536
3854
  onKaiTrigger?: (event: CustomEvent<{ char: string; query: string; rect: DOMRect }>) => void;
@@ -3550,7 +3868,8 @@ export interface KaiContextElementEvents {
3550
3868
  }
3551
3869
 
3552
3870
  export interface KaiConversationItemElementEvents {
3553
-
3871
+ /** STANDALONE activation only: the row was activated (click, Enter or Space on its body) while the item is NOT a direct child of `<kai-conversations>`. `id` is the row's identity: the `conversation-id` attribute, else the host `id`. Inside a container this never fires: activation surfaces once, as `kai-conversation-select` on the container. */
3872
+ onKaiSelect?: (event: CustomEvent<{ id: string }>) => void;
3554
3873
  }
3555
3874
 
3556
3875
  export interface KaiConversationsElementEvents {
@@ -3634,10 +3953,12 @@ export interface KaiImageElementEvents {
3634
3953
  }
3635
3954
 
3636
3955
  export interface KaiInputElementEvents {
3637
- /** The value was committed (blur). */
3638
- onKaiChange?: (event: CustomEvent<{ value: string }>) => void;
3639
- /** The value changed per keystroke. */
3640
- onKaiInput?: (event: CustomEvent<{ value: string }>) => void;
3956
+ /** The value was committed (blur). Same detail shape as `kai-input`. */
3957
+ onKaiChange?: (event: CustomEvent<{ value: string; formattedValue: string }>) => void;
3958
+ /** The value changed per keystroke. `value` is the canonical value (what a backend wants); `formattedValue` is the text on screen. With no mask the two are equal. */
3959
+ onKaiInput?: (event: CustomEvent<{ value: string; formattedValue: string }>) => void;
3960
+ /** A mask refused, or partly refused, some content. The reasons are `full` (no free position left), `wrong-class` (a letter into a digit position), `over-capacity` (a paste longer than the mask holds; what fits was kept), and `format-change-clipped` (the `format` changed under a value that no longer fits). `data` is the content that was refused. The first three are USER-INPUT errors, and are the ones worth announcing in a polite live region. `format-change-clipped` is not one: it follows the app changing its own configuration, so it reports and nothing more. None of the four touches validity, so `invalid` and `error` stay the consumer decision. */
3961
+ onKaiInputRejected?: (event: CustomEvent<{ reason: "full" | "wrong-class" | "over-capacity" | "format-change-clipped"; data: string }>) => void;
3641
3962
  }
3642
3963
 
3643
3964
  export interface KaiKbdElementEvents {
@@ -3700,6 +4021,10 @@ export interface KaiPaneElementEvents {
3700
4021
  onKaiSplit?: (event: CustomEvent) => void;
3701
4022
  }
3702
4023
 
4024
+ export interface KaiPaneGridElementEvents {
4025
+
4026
+ }
4027
+
3703
4028
  export interface KaiPaneGroupElementEvents {
3704
4029
  /** A tab was selected (click, Enter/Space, or arrow-key move). `detail.id` is the tab's id. */
3705
4030
  onKaiTabChange?: (event: CustomEvent<{ id: string }>) => void;
@@ -3727,7 +4052,7 @@ export interface KaiPromptInputElementEvents {
3727
4052
  onKaiAttachmentsChange?: (event: CustomEvent<{ attachments: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[] }>) => void;
3728
4053
  /** The Stop button was clicked while `stoppable` and `loading` are both true. */
3729
4054
  onKaiStop?: (event: CustomEvent<Record<string, never>>) => void;
3730
- /** The user submitted the prompt (Enter or send button). `value` is the flattened text (back-compat); `doc` is the structured document and `entities` the inserted pills (skills/agents) for downstream expansion. */
4055
+ /** The user submitted the prompt (Enter or send button). `value` is the flattened text (back-compat); `doc` is the structured document and `entities` the inserted pills (skills/agents) for downstream expansion. `<kai-prompt-input>` is the batteries-included composer row (send button, toolbar, attachment staging) built on `<kai-composer>`, the bare editor. */
3731
4056
  onKaiSubmit?: (event: CustomEvent<{ value: string; doc: ({ type: "text"; text: string } | { type: "entity"; entity: { kind: string; id: string; label: string; icon?: undefined | string; promptText?: undefined | string; data?: undefined | Record<string, unknown> } })[]; entities: { kind: string; id: string; label: string; icon?: undefined | string; promptText?: undefined | string; data?: undefined | Record<string, unknown> }[]; attachments: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[] }>) => void;
3732
4057
  /** A suggestion was clicked while `suggestion-mode="fill"`. */
3733
4058
  onKaiSuggestionClick?: (event: CustomEvent<{ value: string }>) => void;
@@ -3741,6 +4066,11 @@ export interface KaiPromptInputElementEvents {
3741
4066
  onKaiWebSearch?: (event: CustomEvent<Record<string, never>>) => void;
3742
4067
  }
3743
4068
 
4069
+ export interface KaiRadioGroupElementEvents {
4070
+ /** A row was chosen. */
4071
+ onKaiChange?: (event: CustomEvent<{ value: string }>) => void;
4072
+ }
4073
+
3744
4074
  export interface KaiReasoningElementEvents {
3745
4075
  /** The reasoning block expanded or collapsed (via the trigger, streaming auto-open, or a method). */
3746
4076
  onKaiOpenChange?: (event: CustomEvent<{ open: boolean }>) => void;
@@ -3807,6 +4137,11 @@ export interface KaiSegmentedElementEvents {
3807
4137
  onKaiChange?: (event: CustomEvent<{ value: string }>) => void;
3808
4138
  }
3809
4139
 
4140
+ export interface KaiSelectElementEvents {
4141
+ /** A choice was made. `value` is the first selected option (empty when nothing is selected); `values` is every selected option, which is what a `multiple` select needs. Both are always present, so neither shape silently loses the other. */
4142
+ onKaiChange?: (event: CustomEvent<{ value: string; values: string[] }>) => void;
4143
+ }
4144
+
3810
4145
  export interface KaiSeparatorElementEvents {
3811
4146
 
3812
4147
  }
@@ -3827,6 +4162,13 @@ export interface KaiSkillsElementEvents {
3827
4162
 
3828
4163
  }
3829
4164
 
4165
+ export interface KaiSliderElementEvents {
4166
+ /** The value was committed: pointer released, or a key press finished. */
4167
+ onKaiChange?: (event: CustomEvent<{ value: number }>) => void;
4168
+ /** The thumb moved. Fires per step during a drag or a key press. */
4169
+ onKaiInput?: (event: CustomEvent<{ value: number }>) => void;
4170
+ }
4171
+
3830
4172
  export interface KaiSourceElementEvents {
3831
4173
 
3832
4174
  }
@@ -3958,6 +4300,10 @@ declare module 'vue' {
3958
4300
  KaiChainOfThought: KaiVueElement<KaiChainOfThoughtElementProps, KaiChainOfThoughtElementEvents>;
3959
4301
  'kai-chat': KaiVueElement<KaiChatElementProps, KaiChatElementEvents>;
3960
4302
  KaiChat: KaiVueElement<KaiChatElementProps, KaiChatElementEvents>;
4303
+ 'kai-checkbox': KaiVueElement<KaiCheckboxElementProps, KaiCheckboxElementEvents>;
4304
+ KaiCheckbox: KaiVueElement<KaiCheckboxElementProps, KaiCheckboxElementEvents>;
4305
+ 'kai-checkbox-group': KaiVueElement<KaiCheckboxGroupElementProps, KaiCheckboxGroupElementEvents>;
4306
+ KaiCheckboxGroup: KaiVueElement<KaiCheckboxGroupElementProps, KaiCheckboxGroupElementEvents>;
3961
4307
  'kai-checkpoint': KaiVueElement<KaiCheckpointElementProps, KaiCheckpointElementEvents>;
3962
4308
  KaiCheckpoint: KaiVueElement<KaiCheckpointElementProps, KaiCheckpointElementEvents>;
3963
4309
  'kai-choice': KaiVueElement<KaiChoiceElementProps, KaiChoiceElementEvents>;
@@ -4028,6 +4374,8 @@ declare module 'vue' {
4028
4374
  KaiNotice: KaiVueElement<KaiNoticeElementProps, KaiNoticeElementEvents>;
4029
4375
  'kai-pane': KaiVueElement<KaiPaneElementProps, KaiPaneElementEvents>;
4030
4376
  KaiPane: KaiVueElement<KaiPaneElementProps, KaiPaneElementEvents>;
4377
+ 'kai-pane-grid': KaiVueElement<KaiPaneGridElementProps, KaiPaneGridElementEvents>;
4378
+ KaiPaneGrid: KaiVueElement<KaiPaneGridElementProps, KaiPaneGridElementEvents>;
4031
4379
  'kai-pane-group': KaiVueElement<KaiPaneGroupElementProps, KaiPaneGroupElementEvents>;
4032
4380
  KaiPaneGroup: KaiVueElement<KaiPaneGroupElementProps, KaiPaneGroupElementEvents>;
4033
4381
  'kai-popover': KaiVueElement<KaiPopoverElementProps, KaiPopoverElementEvents>;
@@ -4038,6 +4386,8 @@ declare module 'vue' {
4038
4386
  KaiPromptDock: KaiVueElement<KaiPromptDockElementProps, KaiPromptDockElementEvents>;
4039
4387
  'kai-prompt-input': KaiVueElement<KaiPromptInputElementProps, KaiPromptInputElementEvents>;
4040
4388
  KaiPromptInput: KaiVueElement<KaiPromptInputElementProps, KaiPromptInputElementEvents>;
4389
+ 'kai-radio-group': KaiVueElement<KaiRadioGroupElementProps, KaiRadioGroupElementEvents>;
4390
+ KaiRadioGroup: KaiVueElement<KaiRadioGroupElementProps, KaiRadioGroupElementEvents>;
4041
4391
  'kai-reasoning': KaiVueElement<KaiReasoningElementProps, KaiReasoningElementEvents>;
4042
4392
  KaiReasoning: KaiVueElement<KaiReasoningElementProps, KaiReasoningElementEvents>;
4043
4393
  'kai-remote': KaiVueElement<KaiRemoteElementProps, KaiRemoteElementEvents>;
@@ -4060,6 +4410,8 @@ declare module 'vue' {
4060
4410
  KaiSearch: KaiVueElement<KaiSearchElementProps, KaiSearchElementEvents>;
4061
4411
  'kai-segmented': KaiVueElement<KaiSegmentedElementProps, KaiSegmentedElementEvents>;
4062
4412
  KaiSegmented: KaiVueElement<KaiSegmentedElementProps, KaiSegmentedElementEvents>;
4413
+ 'kai-select': KaiVueElement<KaiSelectElementProps, KaiSelectElementEvents>;
4414
+ KaiSelect: KaiVueElement<KaiSelectElementProps, KaiSelectElementEvents>;
4063
4415
  'kai-separator': KaiVueElement<KaiSeparatorElementProps, KaiSeparatorElementEvents>;
4064
4416
  KaiSeparator: KaiVueElement<KaiSeparatorElementProps, KaiSeparatorElementEvents>;
4065
4417
  'kai-setting-item': KaiVueElement<KaiSettingItemElementProps, KaiSettingItemElementEvents>;
@@ -4070,6 +4422,8 @@ declare module 'vue' {
4070
4422
  KaiSkeleton: KaiVueElement<KaiSkeletonElementProps, KaiSkeletonElementEvents>;
4071
4423
  'kai-skills': KaiVueElement<KaiSkillsElementProps, KaiSkillsElementEvents>;
4072
4424
  KaiSkills: KaiVueElement<KaiSkillsElementProps, KaiSkillsElementEvents>;
4425
+ 'kai-slider': KaiVueElement<KaiSliderElementProps, KaiSliderElementEvents>;
4426
+ KaiSlider: KaiVueElement<KaiSliderElementProps, KaiSliderElementEvents>;
4073
4427
  'kai-source': KaiVueElement<KaiSourceElementProps, KaiSourceElementEvents>;
4074
4428
  KaiSource: KaiVueElement<KaiSourceElementProps, KaiSourceElementEvents>;
4075
4429
  'kai-sources': KaiVueElement<KaiSourcesElementProps, KaiSourcesElementEvents>;