@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
@@ -198,7 +198,7 @@ export interface KaiArtifactElement extends HTMLElement {
198
198
  export interface KaiAttachmentsElement extends HTMLElement {
199
199
  /** Color mode (`auto` follows prefers-color-scheme). */
200
200
  theme?: "light" | "dark" | "auto";
201
- /** 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). */
201
+ /** 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. */
202
202
  items: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
203
203
  /** Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. */
204
204
  variant?: "grid" | "inline" | "list";
@@ -333,7 +333,7 @@ export interface KaiCardsElement extends HTMLElement {
333
333
  /** 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. */
334
334
  schemas?: Record<string, object>;
335
335
  /** Optional CardPolicy handling child events. Property: `el.policy`. */
336
- 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" };
336
+ 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" };
337
337
  /** 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. */
338
338
  validateCards?: boolean;
339
339
  /** 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. */
@@ -384,6 +384,10 @@ export interface KaiChatElement extends HTMLElement {
384
384
  codeTheme?: string;
385
385
  /** Enable Shiki syntax highlighting in code blocks. Turn off to render plain `<pre>` blocks (lighter, no highlighter load). Default true. */
386
386
  codeHighlight?: boolean;
387
+ /** 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`. */
388
+ reasoning?: "full" | "compact" | "off";
389
+ /** 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`. */
390
+ reasoningOpen?: boolean;
387
391
  /** Optional header title shown on the left of the header. */
388
392
  chatTitle?: string;
389
393
  /** Optional model list. When set (>1 model) a ModelSwitcher is shown in the header and a `kai-model-change` event fires on selection. */
@@ -410,6 +414,8 @@ export interface KaiChatElement extends HTMLElement {
410
414
  composerActions?: boolean;
411
415
  /** INJECT: footer row below the composer (disclaimers, token meter, …). */
412
416
  footer?: boolean;
417
+ /** 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. */
418
+ attach?: boolean;
413
419
  /** Show a web-search (Globe) button in the input toolbar; calls `onWebSearch`. */
414
420
  webSearch?: boolean;
415
421
  /** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
@@ -428,6 +434,12 @@ export interface KaiChatElement extends HTMLElement {
428
434
  cardTypes?: Record<string, string>;
429
435
  /** 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. */
430
436
  cardSchemas?: Record<string, object>;
437
+ /** 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. */
438
+ conversations?: boolean;
439
+ /** 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?)`. */
440
+ 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>) };
441
+ /** 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). */
442
+ home?: { greeting?: { title?: string; subtitle?: string }; recentConversation?: boolean; newConversation?: { label?: string }; links?: { label: string; href?: string; description?: string; icon?: string }[] };
431
443
  /** 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. */
432
444
  focus(options?: FocusOptions): void;
433
445
  /** 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. */
@@ -440,6 +452,48 @@ export interface KaiChatElement extends HTMLElement {
440
452
  scrollToBottom(behavior?: "auto" | "instant" | "smooth"): void;
441
453
  }
442
454
 
455
+ export interface KaiCheckboxElement extends HTMLElement {
456
+ /** Color mode (`auto` follows prefers-color-scheme). */
457
+ theme?: "light" | "dark" | "auto";
458
+ /** 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. */
459
+ checked?: boolean;
460
+ /** Initial checked state on mount (uncontrolled seed). Bare attribute (`<kai-checkbox default-checked>`) turns it on. */
461
+ defaultChecked?: boolean;
462
+ /** The mixed state, for a parent box whose children are partly ticked. Visual plus an accessibility hint: the box still reports `checked === false`. */
463
+ indeterminate?: boolean;
464
+ /** Disable interaction. */
465
+ disabled?: boolean;
466
+ /** Set the native `required` attribute, and nothing more. Whether an unticked box is an error is the application's rule, not the kit's. */
467
+ required?: boolean;
468
+ /** Accessible label. The visible text beside the box is the consumer's to render. */
469
+ label?: string;
470
+ /** Form-control name (paired with `value`). */
471
+ name?: string;
472
+ /** Submitted value when checked (paired with `name`). Defaults to `'on'`. */
473
+ value?: string;
474
+ /** Flip the box and fire `kai-change` (no-op while disabled). */
475
+ toggle(): void;
476
+ /** Focus the inner input (the host element can't reach it). */
477
+ focus(options?: FocusOptions): void;
478
+ }
479
+
480
+ export interface KaiCheckboxGroupElement extends HTMLElement {
481
+ /** Color mode (`auto` follows prefers-color-scheme). */
482
+ theme?: "light" | "dark" | "auto";
483
+ /** 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. */
484
+ options: { value: string; label: string; description?: undefined | string; disabled?: undefined | boolean }[];
485
+ /** 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`. */
486
+ value?: string;
487
+ /** 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. */
488
+ name?: string;
489
+ /** Disable every row. Individual rows carry their own `disabled`. */
490
+ disabled?: boolean;
491
+ /** Accessible name for the group. */
492
+ label?: string;
493
+ /** 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. */
494
+ focus(options?: FocusOptions): void;
495
+ }
496
+
443
497
  export interface KaiCheckpointElement extends HTMLElement {
444
498
  /** Color mode (`auto` follows prefers-color-scheme). */
445
499
  theme?: "light" | "dark" | "auto";
@@ -634,9 +688,9 @@ export interface KaiContextElement extends HTMLElement {
634
688
  export interface KaiConversationItemElement extends HTMLElement {
635
689
  /** Color mode (`auto` follows prefers-color-scheme). */
636
690
  theme?: "light" | "dark" | "auto";
637
- /** 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). */
691
+ /** 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. */
638
692
  conversationId?: string;
639
- /** 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`. */
693
+ /** 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. */
640
694
  active?: boolean;
641
695
  /** Dense single-line row padding. */
642
696
  compact?: boolean;
@@ -648,7 +702,7 @@ export interface KaiConversationsElement extends HTMLElement {
648
702
  /** 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. */
649
703
  groups: { id: string; userId?: string; teamId?: string; name: string; sortOrder: number; createdAt: string }[];
650
704
  /** 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. */
651
- 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 }[];
705
+ 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 }[];
652
706
  /** The id of the currently-open conversation, highlighted in the list. */
653
707
  activeId?: string;
654
708
  /** 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. */
@@ -709,6 +763,8 @@ export interface KaiDockElement extends HTMLElement {
709
763
  unread?: boolean;
710
764
  /** Disable the launcher; `show()` and `toggle()` are gated on it. */
711
765
  disabled?: boolean;
766
+ /** 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`. */
767
+ hideClose?: boolean;
712
768
  /** Where focus lands on open: `content` (default, the first element you slotted), `panel`, or `none`. Attribute: `focus-on-open`. */
713
769
  focusOnOpen?: "content" | "panel" | "none";
714
770
  /** Open it programmatically (no-op while disabled). */
@@ -834,7 +890,7 @@ export interface KaiFormElement extends HTMLElement {
834
890
  /** Color mode (`auto` follows prefers-color-scheme). */
835
891
  theme?: "light" | "dark" | "auto";
836
892
  /** 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. */
837
- 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" }[] };
893
+ 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" }[] };
838
894
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
839
895
  cardId?: string;
840
896
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
@@ -911,7 +967,7 @@ export interface KaiInputElement extends HTMLElement {
911
967
  theme?: "light" | "dark" | "auto";
912
968
  /** Native input type: `text` (default) · `email` · `url` · `search` · `tel` · `password` · `number`. Single-line only. */
913
969
  type?: string;
914
- /** 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. */
970
+ /** 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`. */
915
971
  value?: string;
916
972
  /** Placeholder shown when empty. */
917
973
  placeholder?: string;
@@ -937,11 +993,25 @@ export interface KaiInputElement extends HTMLElement {
937
993
  autocomplete?: string;
938
994
  /** Virtual-keyboard hint forwarded to the inner input (e.g. `numeric`, `email`). */
939
995
  inputmode?: string;
996
+ /** 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. */
997
+ format?: string;
998
+ /** 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. */
999
+ guide?: string;
1000
+ /** 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. */
1001
+ semantic?: "credit-card" | "custom" | "ssn" | "tel";
1002
+ /** Case folding applied to typed and pasted text: `preserve` (default) · `upper` · `lower`. Attribute: `case-mode`. */
1003
+ caseMode?: "preserve" | "upper" | "lower";
1004
+ /** What a copy or cut of a masked field puts on the clipboard: `canonical` (default) · `formatted` · `obscured` · `blocked`. Attribute: `copy-policy`. */
1005
+ copyPolicy?: "formatted" | "canonical" | "obscured" | "blocked";
940
1006
  /** Focus the inner input (the host can't reach into the shadow root). */
941
1007
  focus(options?: FocusOptions): void;
942
1008
  /** Select the inner input's text. */
943
1009
  select(): void;
944
- /** Empty the value and fire `kai-change` with `''`. */
1010
+ /** 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. */
1011
+ getRawValue(): string;
1012
+ /** 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. */
1013
+ getFormattedValue(): string;
1014
+ /** 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. */
945
1015
  clear(): void;
946
1016
  }
947
1017
 
@@ -1117,6 +1187,21 @@ export interface KaiPaneElement extends HTMLElement {
1117
1187
  status?: { tone: "working" | "idle" | "done" | "error" | "blocked"; label?: string; pulse?: boolean };
1118
1188
  }
1119
1189
 
1190
+ export interface KaiPaneGridElement extends HTMLElement {
1191
+ /** Color mode (`auto` follows prefers-color-scheme). */
1192
+ theme?: "light" | "dark" | "auto";
1193
+ /** Minimum width of every pane, in px, before columns drop / the grid scrolls. Defaults to `280`. Attribute: `min-pane-width`. */
1194
+ minPaneWidth?: number;
1195
+ /** Minimum height of every pane, in px, before the grid scrolls vertically. Defaults to `200`. Attribute: `min-pane-height`. */
1196
+ minPaneHeight?: number;
1197
+ /** Column cap when the container is wide (default `3`). Attribute: `max-columns`. */
1198
+ maxColumns?: number;
1199
+ /** Gap between panes, any CSS length. Defaults to the kit gap (`var(--kai-pane-grid-gap, 0.5rem)`). Attribute: `gap`. */
1200
+ gap?: string;
1201
+ /** 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`. */
1202
+ maximizedIndex?: number | null;
1203
+ }
1204
+
1120
1205
  export interface KaiPaneGroupElement extends HTMLElement {
1121
1206
  /** Color mode (`auto` follows prefers-color-scheme). */
1122
1207
  theme?: "light" | "dark" | "auto";
@@ -1200,7 +1285,7 @@ export interface KaiPromptInputElement extends HTMLElement {
1200
1285
  submit?: "always" | "auto";
1201
1286
  /** 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`. */
1202
1287
  attach?: boolean;
1203
- /** 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). */
1288
+ /** 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.) */
1204
1289
  attachments?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
1205
1290
  /** 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. */
1206
1291
  triggers?: { char: string; kind: string; items?: { id: string; label: string; icon?: string; description?: string; group?: string; kind?: string; promptText?: string; data?: Record<string, unknown> }[] }[];
@@ -1216,6 +1301,23 @@ export interface KaiPromptInputElement extends HTMLElement {
1216
1301
  send(): void;
1217
1302
  }
1218
1303
 
1304
+ export interface KaiRadioGroupElement extends HTMLElement {
1305
+ /** Color mode (`auto` follows prefers-color-scheme). */
1306
+ theme?: "light" | "dark" | "auto";
1307
+ /** The choices, top to bottom. Set as a JS PROPERTY (array), never an attribute. */
1308
+ options: { value: string; label: string; description?: undefined | string; disabled?: undefined | boolean }[];
1309
+ /** 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. */
1310
+ value?: string;
1311
+ /** 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. */
1312
+ name?: string;
1313
+ /** Disable every row. Individual rows carry their own `disabled`. */
1314
+ disabled?: boolean;
1315
+ /** Accessible name for the group. */
1316
+ label?: string;
1317
+ /** Focus the group's tab stop. That is the selected radio, or the first row when nothing is selected yet. */
1318
+ focus(options?: FocusOptions): void;
1319
+ }
1320
+
1219
1321
  export interface KaiReasoningElement extends HTMLElement {
1220
1322
  /** Color mode (`auto` follows prefers-color-scheme). */
1221
1323
  theme?: "light" | "dark" | "auto";
@@ -1361,6 +1463,10 @@ export interface KaiScrollButtonElement extends HTMLElement {
1361
1463
  variant?: "outline" | "ghost" | "default";
1362
1464
  /** Button size token. Defaults to `'icon'` (square). */
1363
1465
  size?: "sm" | "md" | "lg" | "icon" | "icon-sm";
1466
+ /** 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'`. */
1467
+ label?: string;
1468
+ /** 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. */
1469
+ showLabel?: boolean;
1364
1470
  }
1365
1471
 
1366
1472
  export interface KaiSearchElement extends HTMLElement {
@@ -1395,6 +1501,31 @@ export interface KaiSegmentedElement extends HTMLElement {
1395
1501
  size?: "sm" | "md";
1396
1502
  }
1397
1503
 
1504
+ export interface KaiSelectElement extends HTMLElement {
1505
+ /** Color mode (`auto` follows prefers-color-scheme). */
1506
+ theme?: "light" | "dark" | "auto";
1507
+ /** 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. */
1508
+ options: { value: string; label?: undefined | string; disabled?: undefined | boolean }[];
1509
+ /** 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. */
1510
+ value?: string;
1511
+ /** 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. */
1512
+ placeholder?: string;
1513
+ /** Allow more than one selection. Turns the control into the platform's list box, so the kit's chevron is not drawn. */
1514
+ multiple?: boolean;
1515
+ /** Force the invalid (destructive-border) state. */
1516
+ invalid?: boolean;
1517
+ /** Disable interaction. */
1518
+ disabled?: boolean;
1519
+ /** Set the native `required` attribute, and nothing more. Whether an empty select is an error is the application's rule, not the kit's. */
1520
+ required?: boolean;
1521
+ /** Accessible label for the control. */
1522
+ label?: string;
1523
+ /** Form-control name, for a native form submit. */
1524
+ name?: string;
1525
+ /** Focus the inner select (the host element can't reach it). */
1526
+ focus(options?: FocusOptions): void;
1527
+ }
1528
+
1398
1529
  export interface KaiSeparatorElement extends HTMLElement {
1399
1530
  /** Color mode (`auto` follows prefers-color-scheme). */
1400
1531
  theme?: "light" | "dark" | "auto";
@@ -1440,6 +1571,29 @@ export interface KaiSkillsElement extends HTMLElement {
1440
1571
  skills: { id: string; name: string }[];
1441
1572
  }
1442
1573
 
1574
+ export interface KaiSliderElement extends HTMLElement {
1575
+ /** Color mode (`auto` follows prefers-color-scheme). */
1576
+ theme?: "light" | "dark" | "auto";
1577
+ /** Lowest selectable value. Required: a range with no bounds is a guess, and the guess belongs to whoever knows what the number means. */
1578
+ min?: number;
1579
+ /** Highest selectable value. Required, for the same reason as `min`. */
1580
+ max?: number;
1581
+ /** Granularity. Omitted means the native default of 1; `any` means continuous. */
1582
+ step?: number | "any";
1583
+ /** 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. */
1584
+ value?: number;
1585
+ /** Disable interaction. */
1586
+ disabled?: boolean;
1587
+ /** Accessible label for the slider. */
1588
+ label?: string;
1589
+ /** Form-control name, for a native form submit. */
1590
+ name?: string;
1591
+ /** 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. */
1592
+ valueLabel?: boolean | ((value: number) => string);
1593
+ /** Focus the inner range input (the host element can't reach it). */
1594
+ focus(options?: FocusOptions): void;
1595
+ }
1596
+
1443
1597
  export interface KaiSourceElement extends HTMLElement {
1444
1598
  /** Color mode (`auto` follows prefers-color-scheme). */
1445
1599
  theme?: "light" | "dark" | "auto";
@@ -1625,7 +1779,7 @@ export interface KaiThreadElement extends HTMLElement {
1625
1779
  export interface KaiToastRegionElement extends HTMLElement {
1626
1780
  /** Color mode (`auto` follows prefers-color-scheme). */
1627
1781
  theme?: "light" | "dark" | "auto";
1628
- /** 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. */
1782
+ /** 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. */
1629
1783
  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 }[];
1630
1784
  /** Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … */
1631
1785
  position?: "top-center" | "top-right" | "top-left" | "bottom-center" | "bottom-right" | "bottom-left";
@@ -1761,6 +1915,8 @@ declare global {
1761
1915
  'kai-cards': KaiCardsElement;
1762
1916
  'kai-chain-of-thought': KaiChainOfThoughtElement;
1763
1917
  'kai-chat': KaiChatElement;
1918
+ 'kai-checkbox': KaiCheckboxElement;
1919
+ 'kai-checkbox-group': KaiCheckboxGroupElement;
1764
1920
  'kai-checkpoint': KaiCheckpointElement;
1765
1921
  'kai-choice': KaiChoiceElement;
1766
1922
  'kai-coachmark': KaiCoachmarkElement;
@@ -1796,11 +1952,13 @@ declare global {
1796
1952
  'kai-nav': KaiNavElement;
1797
1953
  'kai-notice': KaiNoticeElement;
1798
1954
  'kai-pane': KaiPaneElement;
1955
+ 'kai-pane-grid': KaiPaneGridElement;
1799
1956
  'kai-pane-group': KaiPaneGroupElement;
1800
1957
  'kai-popover': KaiPopoverElement;
1801
1958
  'kai-progress-bar': KaiProgressBarElement;
1802
1959
  'kai-prompt-dock': KaiPromptDockElement;
1803
1960
  'kai-prompt-input': KaiPromptInputElement;
1961
+ 'kai-radio-group': KaiRadioGroupElement;
1804
1962
  'kai-reasoning': KaiReasoningElement;
1805
1963
  'kai-remote': KaiRemoteElement;
1806
1964
  'kai-resizable': KaiResizableElement;
@@ -1812,11 +1970,13 @@ declare global {
1812
1970
  'kai-scroll-button': KaiScrollButtonElement;
1813
1971
  'kai-search': KaiSearchElement;
1814
1972
  'kai-segmented': KaiSegmentedElement;
1973
+ 'kai-select': KaiSelectElement;
1815
1974
  'kai-separator': KaiSeparatorElement;
1816
1975
  'kai-setting-item': KaiSettingItemElement;
1817
1976
  'kai-settings-group': KaiSettingsGroupElement;
1818
1977
  'kai-skeleton': KaiSkeletonElement;
1819
1978
  'kai-skills': KaiSkillsElement;
1979
+ 'kai-slider': KaiSliderElement;
1820
1980
  'kai-source': KaiSourceElement;
1821
1981
  'kai-sources': KaiSourcesElement;
1822
1982
  'kai-status': KaiStatusElement;
@@ -1862,6 +2022,8 @@ declare module 'react' {
1862
2022
  'kai-cards': KaiElementJsxProps;
1863
2023
  'kai-chain-of-thought': KaiElementJsxProps;
1864
2024
  'kai-chat': KaiElementJsxProps;
2025
+ 'kai-checkbox': KaiElementJsxProps;
2026
+ 'kai-checkbox-group': KaiElementJsxProps;
1865
2027
  'kai-checkpoint': KaiElementJsxProps;
1866
2028
  'kai-choice': KaiElementJsxProps;
1867
2029
  'kai-coachmark': KaiElementJsxProps;
@@ -1897,11 +2059,13 @@ declare module 'react' {
1897
2059
  'kai-nav': KaiElementJsxProps;
1898
2060
  'kai-notice': KaiElementJsxProps;
1899
2061
  'kai-pane': KaiElementJsxProps;
2062
+ 'kai-pane-grid': KaiElementJsxProps;
1900
2063
  'kai-pane-group': KaiElementJsxProps;
1901
2064
  'kai-popover': KaiElementJsxProps;
1902
2065
  'kai-progress-bar': KaiElementJsxProps;
1903
2066
  'kai-prompt-dock': KaiElementJsxProps;
1904
2067
  'kai-prompt-input': KaiElementJsxProps;
2068
+ 'kai-radio-group': KaiElementJsxProps;
1905
2069
  'kai-reasoning': KaiElementJsxProps;
1906
2070
  'kai-remote': KaiElementJsxProps;
1907
2071
  'kai-resizable': KaiElementJsxProps;
@@ -1913,11 +2077,13 @@ declare module 'react' {
1913
2077
  'kai-scroll-button': KaiElementJsxProps;
1914
2078
  'kai-search': KaiElementJsxProps;
1915
2079
  'kai-segmented': KaiElementJsxProps;
2080
+ 'kai-select': KaiElementJsxProps;
1916
2081
  'kai-separator': KaiElementJsxProps;
1917
2082
  'kai-setting-item': KaiElementJsxProps;
1918
2083
  'kai-settings-group': KaiElementJsxProps;
1919
2084
  'kai-skeleton': KaiElementJsxProps;
1920
2085
  'kai-skills': KaiElementJsxProps;
2086
+ 'kai-slider': KaiElementJsxProps;
1921
2087
  'kai-source': KaiElementJsxProps;
1922
2088
  'kai-sources': KaiElementJsxProps;
1923
2089
  'kai-status': KaiElementJsxProps;
@@ -1995,7 +2161,7 @@ export interface KaiArtifactElementProps {
1995
2161
  export interface KaiAttachmentsElementProps {
1996
2162
  /** Color mode (`auto` follows prefers-color-scheme). */
1997
2163
  theme?: "light" | "dark" | "auto";
1998
- /** 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). */
2164
+ /** 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. */
1999
2165
  items?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
2000
2166
  /** Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. */
2001
2167
  variant?: "grid" | "inline" | "list";
@@ -2124,7 +2290,7 @@ export interface KaiCardsElementProps {
2124
2290
  /** 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. */
2125
2291
  schemas?: Record<string, object>;
2126
2292
  /** Optional CardPolicy handling child events. Property: `el.policy`. */
2127
- 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" };
2293
+ 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" };
2128
2294
  /** 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. */
2129
2295
  validateCards?: boolean;
2130
2296
  }
@@ -2163,6 +2329,10 @@ export interface KaiChatElementProps {
2163
2329
  codeTheme?: string;
2164
2330
  /** Enable Shiki syntax highlighting in code blocks. Turn off to render plain `<pre>` blocks (lighter, no highlighter load). Default true. */
2165
2331
  codeHighlight?: boolean;
2332
+ /** 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`. */
2333
+ reasoning?: "full" | "compact" | "off";
2334
+ /** 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`. */
2335
+ reasoningOpen?: boolean;
2166
2336
  /** Optional header title shown on the left of the header. */
2167
2337
  chatTitle?: string;
2168
2338
  /** Optional model list. When set (>1 model) a ModelSwitcher is shown in the header and a `kai-model-change` event fires on selection. */
@@ -2189,6 +2359,8 @@ export interface KaiChatElementProps {
2189
2359
  composerActions?: boolean;
2190
2360
  /** INJECT: footer row below the composer (disclaimers, token meter, …). */
2191
2361
  footer?: boolean;
2362
+ /** 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. */
2363
+ attach?: boolean;
2192
2364
  /** Show a web-search (Globe) button in the input toolbar; calls `onWebSearch`. */
2193
2365
  webSearch?: boolean;
2194
2366
  /** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
@@ -2207,6 +2379,48 @@ export interface KaiChatElementProps {
2207
2379
  cardTypes?: Record<string, string>;
2208
2380
  /** 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. */
2209
2381
  cardSchemas?: Record<string, object>;
2382
+ /** 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. */
2383
+ conversations?: boolean;
2384
+ /** 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?)`. */
2385
+ 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>) };
2386
+ /** 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). */
2387
+ home?: { greeting?: { title?: string; subtitle?: string }; recentConversation?: boolean; newConversation?: { label?: string }; links?: { label: string; href?: string; description?: string; icon?: string }[] };
2388
+ }
2389
+
2390
+ export interface KaiCheckboxElementProps {
2391
+ /** Color mode (`auto` follows prefers-color-scheme). */
2392
+ theme?: "light" | "dark" | "auto";
2393
+ /** 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. */
2394
+ checked?: boolean;
2395
+ /** Initial checked state on mount (uncontrolled seed). Bare attribute (`<kai-checkbox default-checked>`) turns it on. */
2396
+ defaultChecked?: boolean;
2397
+ /** The mixed state, for a parent box whose children are partly ticked. Visual plus an accessibility hint: the box still reports `checked === false`. */
2398
+ indeterminate?: boolean;
2399
+ /** Disable interaction. */
2400
+ disabled?: boolean;
2401
+ /** Set the native `required` attribute, and nothing more. Whether an unticked box is an error is the application's rule, not the kit's. */
2402
+ required?: boolean;
2403
+ /** Accessible label. The visible text beside the box is the consumer's to render. */
2404
+ label?: string;
2405
+ /** Form-control name (paired with `value`). */
2406
+ name?: string;
2407
+ /** Submitted value when checked (paired with `name`). Defaults to `'on'`. */
2408
+ value?: string;
2409
+ }
2410
+
2411
+ export interface KaiCheckboxGroupElementProps {
2412
+ /** Color mode (`auto` follows prefers-color-scheme). */
2413
+ theme?: "light" | "dark" | "auto";
2414
+ /** 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. */
2415
+ options: { value: string; label: string; description?: undefined | string; disabled?: undefined | boolean }[];
2416
+ /** 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`. */
2417
+ value?: string;
2418
+ /** 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. */
2419
+ name?: string;
2420
+ /** Disable every row. Individual rows carry their own `disabled`. */
2421
+ disabled?: boolean;
2422
+ /** Accessible name for the group. */
2423
+ label?: string;
2210
2424
  }
2211
2425
 
2212
2426
  export interface KaiCheckpointElementProps {
@@ -2359,9 +2573,9 @@ export interface KaiContextElementProps {
2359
2573
  export interface KaiConversationItemElementProps {
2360
2574
  /** Color mode (`auto` follows prefers-color-scheme). */
2361
2575
  theme?: "light" | "dark" | "auto";
2362
- /** 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). */
2576
+ /** 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. */
2363
2577
  conversationId?: string;
2364
- /** 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`. */
2578
+ /** 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. */
2365
2579
  active?: boolean;
2366
2580
  /** Dense single-line row padding. */
2367
2581
  compact?: boolean;
@@ -2373,7 +2587,7 @@ export interface KaiConversationsElementProps {
2373
2587
  /** 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. */
2374
2588
  groups?: { id: string; userId?: string; teamId?: string; name: string; sortOrder: number; createdAt: string }[];
2375
2589
  /** 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. */
2376
- 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 }[];
2590
+ 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 }[];
2377
2591
  /** The id of the currently-open conversation, highlighted in the list. */
2378
2592
  activeId?: string;
2379
2593
  /** 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. */
@@ -2414,6 +2628,8 @@ export interface KaiDockElementProps {
2414
2628
  unread?: boolean;
2415
2629
  /** Disable the launcher; `show()` and `toggle()` are gated on it. */
2416
2630
  disabled?: boolean;
2631
+ /** 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`. */
2632
+ hideClose?: boolean;
2417
2633
  /** Where focus lands on open: `content` (default, the first element you slotted), `panel`, or `none`. Attribute: `focus-on-open`. */
2418
2634
  focusOnOpen?: "content" | "panel" | "none";
2419
2635
  }
@@ -2519,7 +2735,7 @@ export interface KaiFormElementProps {
2519
2735
  /** Color mode (`auto` follows prefers-color-scheme). */
2520
2736
  theme?: "light" | "dark" | "auto";
2521
2737
  /** 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. */
2522
- 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" }[] };
2738
+ 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" }[] };
2523
2739
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
2524
2740
  cardId?: string;
2525
2741
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
@@ -2578,7 +2794,7 @@ export interface KaiInputElementProps {
2578
2794
  theme?: "light" | "dark" | "auto";
2579
2795
  /** Native input type: `text` (default) · `email` · `url` · `search` · `tel` · `password` · `number`. Single-line only. */
2580
2796
  type?: string;
2581
- /** 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. */
2797
+ /** 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`. */
2582
2798
  value?: string;
2583
2799
  /** Placeholder shown when empty. */
2584
2800
  placeholder?: string;
@@ -2604,6 +2820,16 @@ export interface KaiInputElementProps {
2604
2820
  autocomplete?: string;
2605
2821
  /** Virtual-keyboard hint forwarded to the inner input (e.g. `numeric`, `email`). */
2606
2822
  inputmode?: string;
2823
+ /** 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. */
2824
+ format?: string;
2825
+ /** 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. */
2826
+ guide?: string;
2827
+ /** 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. */
2828
+ semantic?: "credit-card" | "custom" | "ssn" | "tel";
2829
+ /** Case folding applied to typed and pasted text: `preserve` (default) · `upper` · `lower`. Attribute: `case-mode`. */
2830
+ caseMode?: "preserve" | "upper" | "lower";
2831
+ /** What a copy or cut of a masked field puts on the clipboard: `canonical` (default) · `formatted` · `obscured` · `blocked`. Attribute: `copy-policy`. */
2832
+ copyPolicy?: "formatted" | "canonical" | "obscured" | "blocked";
2607
2833
  }
2608
2834
 
2609
2835
  export interface KaiKbdElementProps {
@@ -2762,6 +2988,21 @@ export interface KaiPaneElementProps {
2762
2988
  status?: { tone: "working" | "idle" | "done" | "error" | "blocked"; label?: string; pulse?: boolean };
2763
2989
  }
2764
2990
 
2991
+ export interface KaiPaneGridElementProps {
2992
+ /** Color mode (`auto` follows prefers-color-scheme). */
2993
+ theme?: "light" | "dark" | "auto";
2994
+ /** Minimum width of every pane, in px, before columns drop / the grid scrolls. Defaults to `280`. Attribute: `min-pane-width`. */
2995
+ minPaneWidth?: number;
2996
+ /** Minimum height of every pane, in px, before the grid scrolls vertically. Defaults to `200`. Attribute: `min-pane-height`. */
2997
+ minPaneHeight?: number;
2998
+ /** Column cap when the container is wide (default `3`). Attribute: `max-columns`. */
2999
+ maxColumns?: number;
3000
+ /** Gap between panes, any CSS length. Defaults to the kit gap (`var(--kai-pane-grid-gap, 0.5rem)`). Attribute: `gap`. */
3001
+ gap?: string;
3002
+ /** 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`. */
3003
+ maximizedIndex?: number | null;
3004
+ }
3005
+
2765
3006
  export interface KaiPaneGroupElementProps {
2766
3007
  /** Color mode (`auto` follows prefers-color-scheme). */
2767
3008
  theme?: "light" | "dark" | "auto";
@@ -2835,7 +3076,7 @@ export interface KaiPromptInputElementProps {
2835
3076
  submit?: "always" | "auto";
2836
3077
  /** 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`. */
2837
3078
  attach?: boolean;
2838
- /** 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). */
3079
+ /** 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.) */
2839
3080
  attachments?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
2840
3081
  /** 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. */
2841
3082
  triggers?: { char: string; kind: string; items?: { id: string; label: string; icon?: string; description?: string; group?: string; kind?: string; promptText?: string; data?: Record<string, unknown> }[] }[];
@@ -2843,6 +3084,21 @@ export interface KaiPromptInputElementProps {
2843
3084
  kindIcons?: Record<string, string>;
2844
3085
  }
2845
3086
 
3087
+ export interface KaiRadioGroupElementProps {
3088
+ /** Color mode (`auto` follows prefers-color-scheme). */
3089
+ theme?: "light" | "dark" | "auto";
3090
+ /** The choices, top to bottom. Set as a JS PROPERTY (array), never an attribute. */
3091
+ options: { value: string; label: string; description?: undefined | string; disabled?: undefined | boolean }[];
3092
+ /** 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. */
3093
+ value?: string;
3094
+ /** 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. */
3095
+ name?: string;
3096
+ /** Disable every row. Individual rows carry their own `disabled`. */
3097
+ disabled?: boolean;
3098
+ /** Accessible name for the group. */
3099
+ label?: string;
3100
+ }
3101
+
2846
3102
  export interface KaiReasoningElementProps {
2847
3103
  /** Color mode (`auto` follows prefers-color-scheme). */
2848
3104
  theme?: "light" | "dark" | "auto";
@@ -2964,6 +3220,10 @@ export interface KaiScrollButtonElementProps {
2964
3220
  variant?: "outline" | "ghost" | "default";
2965
3221
  /** Button size token. Defaults to `'icon'` (square). */
2966
3222
  size?: "sm" | "md" | "lg" | "icon" | "icon-sm";
3223
+ /** 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'`. */
3224
+ label?: string;
3225
+ /** 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. */
3226
+ showLabel?: boolean;
2967
3227
  }
2968
3228
 
2969
3229
  export interface KaiSearchElementProps {
@@ -2994,6 +3254,29 @@ export interface KaiSegmentedElementProps {
2994
3254
  size?: "sm" | "md";
2995
3255
  }
2996
3256
 
3257
+ export interface KaiSelectElementProps {
3258
+ /** Color mode (`auto` follows prefers-color-scheme). */
3259
+ theme?: "light" | "dark" | "auto";
3260
+ /** 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. */
3261
+ options: { value: string; label?: undefined | string; disabled?: undefined | boolean }[];
3262
+ /** 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. */
3263
+ value?: string;
3264
+ /** 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. */
3265
+ placeholder?: string;
3266
+ /** Allow more than one selection. Turns the control into the platform's list box, so the kit's chevron is not drawn. */
3267
+ multiple?: boolean;
3268
+ /** Force the invalid (destructive-border) state. */
3269
+ invalid?: boolean;
3270
+ /** Disable interaction. */
3271
+ disabled?: boolean;
3272
+ /** Set the native `required` attribute, and nothing more. Whether an empty select is an error is the application's rule, not the kit's. */
3273
+ required?: boolean;
3274
+ /** Accessible label for the control. */
3275
+ label?: string;
3276
+ /** Form-control name, for a native form submit. */
3277
+ name?: string;
3278
+ }
3279
+
2997
3280
  export interface KaiSeparatorElementProps {
2998
3281
  /** Color mode (`auto` follows prefers-color-scheme). */
2999
3282
  theme?: "light" | "dark" | "auto";
@@ -3039,6 +3322,27 @@ export interface KaiSkillsElementProps {
3039
3322
  skills?: { id: string; name: string }[];
3040
3323
  }
3041
3324
 
3325
+ export interface KaiSliderElementProps {
3326
+ /** Color mode (`auto` follows prefers-color-scheme). */
3327
+ theme?: "light" | "dark" | "auto";
3328
+ /** Lowest selectable value. Required: a range with no bounds is a guess, and the guess belongs to whoever knows what the number means. */
3329
+ min?: number;
3330
+ /** Highest selectable value. Required, for the same reason as `min`. */
3331
+ max?: number;
3332
+ /** Granularity. Omitted means the native default of 1; `any` means continuous. */
3333
+ step?: number | "any";
3334
+ /** 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. */
3335
+ value?: number;
3336
+ /** Disable interaction. */
3337
+ disabled?: boolean;
3338
+ /** Accessible label for the slider. */
3339
+ label?: string;
3340
+ /** Form-control name, for a native form submit. */
3341
+ name?: string;
3342
+ /** 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. */
3343
+ valueLabel?: boolean | ((value: number) => string);
3344
+ }
3345
+
3042
3346
  export interface KaiSourceElementProps {
3043
3347
  /** Color mode (`auto` follows prefers-color-scheme). */
3044
3348
  theme?: "light" | "dark" | "auto";
@@ -3202,7 +3506,7 @@ export interface KaiThreadElementProps {
3202
3506
  export interface KaiToastRegionElementProps {
3203
3507
  /** Color mode (`auto` follows prefers-color-scheme). */
3204
3508
  theme?: "light" | "dark" | "auto";
3205
- /** 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. */
3509
+ /** 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. */
3206
3510
  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 }[];
3207
3511
  /** Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … */
3208
3512
  position?: "top-center" | "top-right" | "top-left" | "bottom-center" | "bottom-right" | "bottom-left";
@@ -3359,6 +3663,10 @@ export interface KaiChatElementEvents {
3359
3663
  onKaiAttachmentsChange?: (event: CustomEvent<{ attachments: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[] }>) => void;
3360
3664
  /** 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. */
3361
3665
  onKaiAttachmentsRejected?: (event: CustomEvent<{ rejected: { filename: string; mediaType: string; reason: "filtered" | "unsupported" }[] }>) => void;
3666
+ /** 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. */
3667
+ 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;
3668
+ /** A `home.links` entry with no `href` was activated (tapped/clicked/Enter). Meaningful only when `home` is set. */
3669
+ onKaiHomeLink?: (event: CustomEvent<{ entry: { label: string; href?: undefined | string; description?: undefined | string; icon?: undefined | string } }>) => void;
3362
3670
  /** 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. */
3363
3671
  onKaiMessageAction?: (event: CustomEvent<{ messageId: string; action: string; state?: undefined | "on" | "off" }>) => void;
3364
3672
  /** The header model switcher changed. */
@@ -3375,6 +3683,16 @@ export interface KaiChatElementEvents {
3375
3683
  onKaiWebSearch?: (event: CustomEvent<Record<string, never>>) => void;
3376
3684
  }
3377
3685
 
3686
+ export interface KaiCheckboxElementEvents {
3687
+ /** The box was ticked or unticked. */
3688
+ onKaiChange?: (event: CustomEvent<{ checked: boolean }>) => void;
3689
+ }
3690
+
3691
+ export interface KaiCheckboxGroupElementEvents {
3692
+ /** 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. */
3693
+ onKaiChange?: (event: CustomEvent<{ value: string; values: string[] }>) => void;
3694
+ }
3695
+
3378
3696
  export interface KaiCheckpointElementEvents {
3379
3697
  /** The checkpoint was clicked. */
3380
3698
  onKaiSelect?: (event: CustomEvent) => void;
@@ -3423,7 +3741,7 @@ export interface KaiComposerElementEvents {
3423
3741
  onKaiEntityRemove?: (event: CustomEvent<{ entity: { kind: string; id: string; label: string; icon?: undefined | string; promptText?: undefined | string; data?: undefined | Record<string, unknown> } }>) => void;
3424
3742
  /** 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.) */
3425
3743
  onKaiFocus?: (event: CustomEvent<{ originalEvent: FocusEvent }>) => void;
3426
- /** The user submitted the composer (Enter or programmatic submit). */
3744
+ /** 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. */
3427
3745
  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;
3428
3746
  /** A trigger character was detected at the caret (e.g. `/` or `@`). */
3429
3747
  onKaiTrigger?: (event: CustomEvent<{ char: string; query: string; rect: DOMRect }>) => void;
@@ -3443,7 +3761,8 @@ export interface KaiContextElementEvents {
3443
3761
  }
3444
3762
 
3445
3763
  export interface KaiConversationItemElementEvents {
3446
-
3764
+ /** 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. */
3765
+ onKaiSelect?: (event: CustomEvent<{ id: string }>) => void;
3447
3766
  }
3448
3767
 
3449
3768
  export interface KaiConversationsElementEvents {
@@ -3527,10 +3846,12 @@ export interface KaiImageElementEvents {
3527
3846
  }
3528
3847
 
3529
3848
  export interface KaiInputElementEvents {
3530
- /** The value was committed (blur). */
3531
- onKaiChange?: (event: CustomEvent<{ value: string }>) => void;
3532
- /** The value changed per keystroke. */
3533
- onKaiInput?: (event: CustomEvent<{ value: string }>) => void;
3849
+ /** The value was committed (blur). Same detail shape as `kai-input`. */
3850
+ onKaiChange?: (event: CustomEvent<{ value: string; formattedValue: string }>) => void;
3851
+ /** 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. */
3852
+ onKaiInput?: (event: CustomEvent<{ value: string; formattedValue: string }>) => void;
3853
+ /** 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. */
3854
+ onKaiInputRejected?: (event: CustomEvent<{ reason: "full" | "wrong-class" | "over-capacity" | "format-change-clipped"; data: string }>) => void;
3534
3855
  }
3535
3856
 
3536
3857
  export interface KaiKbdElementEvents {
@@ -3593,6 +3914,10 @@ export interface KaiPaneElementEvents {
3593
3914
  onKaiSplit?: (event: CustomEvent) => void;
3594
3915
  }
3595
3916
 
3917
+ export interface KaiPaneGridElementEvents {
3918
+
3919
+ }
3920
+
3596
3921
  export interface KaiPaneGroupElementEvents {
3597
3922
  /** A tab was selected (click, Enter/Space, or arrow-key move). `detail.id` is the tab's id. */
3598
3923
  onKaiTabChange?: (event: CustomEvent<{ id: string }>) => void;
@@ -3620,7 +3945,7 @@ export interface KaiPromptInputElementEvents {
3620
3945
  onKaiAttachmentsChange?: (event: CustomEvent<{ attachments: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[] }>) => void;
3621
3946
  /** The Stop button was clicked while `stoppable` and `loading` are both true. */
3622
3947
  onKaiStop?: (event: CustomEvent<Record<string, never>>) => void;
3623
- /** 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. */
3948
+ /** 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. */
3624
3949
  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;
3625
3950
  /** A suggestion was clicked while `suggestion-mode="fill"`. */
3626
3951
  onKaiSuggestionClick?: (event: CustomEvent<{ value: string }>) => void;
@@ -3634,6 +3959,11 @@ export interface KaiPromptInputElementEvents {
3634
3959
  onKaiWebSearch?: (event: CustomEvent<Record<string, never>>) => void;
3635
3960
  }
3636
3961
 
3962
+ export interface KaiRadioGroupElementEvents {
3963
+ /** A row was chosen. */
3964
+ onKaiChange?: (event: CustomEvent<{ value: string }>) => void;
3965
+ }
3966
+
3637
3967
  export interface KaiReasoningElementEvents {
3638
3968
  /** The reasoning block expanded or collapsed (via the trigger, streaming auto-open, or a method). */
3639
3969
  onKaiOpenChange?: (event: CustomEvent<{ open: boolean }>) => void;
@@ -3700,6 +4030,11 @@ export interface KaiSegmentedElementEvents {
3700
4030
  onKaiChange?: (event: CustomEvent<{ value: string }>) => void;
3701
4031
  }
3702
4032
 
4033
+ export interface KaiSelectElementEvents {
4034
+ /** 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. */
4035
+ onKaiChange?: (event: CustomEvent<{ value: string; values: string[] }>) => void;
4036
+ }
4037
+
3703
4038
  export interface KaiSeparatorElementEvents {
3704
4039
 
3705
4040
  }
@@ -3720,6 +4055,13 @@ export interface KaiSkillsElementEvents {
3720
4055
 
3721
4056
  }
3722
4057
 
4058
+ export interface KaiSliderElementEvents {
4059
+ /** The value was committed: pointer released, or a key press finished. */
4060
+ onKaiChange?: (event: CustomEvent<{ value: number }>) => void;
4061
+ /** The thumb moved. Fires per step during a drag or a key press. */
4062
+ onKaiInput?: (event: CustomEvent<{ value: number }>) => void;
4063
+ }
4064
+
3723
4065
  export interface KaiSourceElementEvents {
3724
4066
 
3725
4067
  }
@@ -3851,6 +4193,10 @@ declare module 'vue' {
3851
4193
  KaiChainOfThought: KaiVueElement<KaiChainOfThoughtElementProps, KaiChainOfThoughtElementEvents>;
3852
4194
  'kai-chat': KaiVueElement<KaiChatElementProps, KaiChatElementEvents>;
3853
4195
  KaiChat: KaiVueElement<KaiChatElementProps, KaiChatElementEvents>;
4196
+ 'kai-checkbox': KaiVueElement<KaiCheckboxElementProps, KaiCheckboxElementEvents>;
4197
+ KaiCheckbox: KaiVueElement<KaiCheckboxElementProps, KaiCheckboxElementEvents>;
4198
+ 'kai-checkbox-group': KaiVueElement<KaiCheckboxGroupElementProps, KaiCheckboxGroupElementEvents>;
4199
+ KaiCheckboxGroup: KaiVueElement<KaiCheckboxGroupElementProps, KaiCheckboxGroupElementEvents>;
3854
4200
  'kai-checkpoint': KaiVueElement<KaiCheckpointElementProps, KaiCheckpointElementEvents>;
3855
4201
  KaiCheckpoint: KaiVueElement<KaiCheckpointElementProps, KaiCheckpointElementEvents>;
3856
4202
  'kai-choice': KaiVueElement<KaiChoiceElementProps, KaiChoiceElementEvents>;
@@ -3921,6 +4267,8 @@ declare module 'vue' {
3921
4267
  KaiNotice: KaiVueElement<KaiNoticeElementProps, KaiNoticeElementEvents>;
3922
4268
  'kai-pane': KaiVueElement<KaiPaneElementProps, KaiPaneElementEvents>;
3923
4269
  KaiPane: KaiVueElement<KaiPaneElementProps, KaiPaneElementEvents>;
4270
+ 'kai-pane-grid': KaiVueElement<KaiPaneGridElementProps, KaiPaneGridElementEvents>;
4271
+ KaiPaneGrid: KaiVueElement<KaiPaneGridElementProps, KaiPaneGridElementEvents>;
3924
4272
  'kai-pane-group': KaiVueElement<KaiPaneGroupElementProps, KaiPaneGroupElementEvents>;
3925
4273
  KaiPaneGroup: KaiVueElement<KaiPaneGroupElementProps, KaiPaneGroupElementEvents>;
3926
4274
  'kai-popover': KaiVueElement<KaiPopoverElementProps, KaiPopoverElementEvents>;
@@ -3931,6 +4279,8 @@ declare module 'vue' {
3931
4279
  KaiPromptDock: KaiVueElement<KaiPromptDockElementProps, KaiPromptDockElementEvents>;
3932
4280
  'kai-prompt-input': KaiVueElement<KaiPromptInputElementProps, KaiPromptInputElementEvents>;
3933
4281
  KaiPromptInput: KaiVueElement<KaiPromptInputElementProps, KaiPromptInputElementEvents>;
4282
+ 'kai-radio-group': KaiVueElement<KaiRadioGroupElementProps, KaiRadioGroupElementEvents>;
4283
+ KaiRadioGroup: KaiVueElement<KaiRadioGroupElementProps, KaiRadioGroupElementEvents>;
3934
4284
  'kai-reasoning': KaiVueElement<KaiReasoningElementProps, KaiReasoningElementEvents>;
3935
4285
  KaiReasoning: KaiVueElement<KaiReasoningElementProps, KaiReasoningElementEvents>;
3936
4286
  'kai-remote': KaiVueElement<KaiRemoteElementProps, KaiRemoteElementEvents>;
@@ -3953,6 +4303,8 @@ declare module 'vue' {
3953
4303
  KaiSearch: KaiVueElement<KaiSearchElementProps, KaiSearchElementEvents>;
3954
4304
  'kai-segmented': KaiVueElement<KaiSegmentedElementProps, KaiSegmentedElementEvents>;
3955
4305
  KaiSegmented: KaiVueElement<KaiSegmentedElementProps, KaiSegmentedElementEvents>;
4306
+ 'kai-select': KaiVueElement<KaiSelectElementProps, KaiSelectElementEvents>;
4307
+ KaiSelect: KaiVueElement<KaiSelectElementProps, KaiSelectElementEvents>;
3956
4308
  'kai-separator': KaiVueElement<KaiSeparatorElementProps, KaiSeparatorElementEvents>;
3957
4309
  KaiSeparator: KaiVueElement<KaiSeparatorElementProps, KaiSeparatorElementEvents>;
3958
4310
  'kai-setting-item': KaiVueElement<KaiSettingItemElementProps, KaiSettingItemElementEvents>;
@@ -3963,6 +4315,8 @@ declare module 'vue' {
3963
4315
  KaiSkeleton: KaiVueElement<KaiSkeletonElementProps, KaiSkeletonElementEvents>;
3964
4316
  'kai-skills': KaiVueElement<KaiSkillsElementProps, KaiSkillsElementEvents>;
3965
4317
  KaiSkills: KaiVueElement<KaiSkillsElementProps, KaiSkillsElementEvents>;
4318
+ 'kai-slider': KaiVueElement<KaiSliderElementProps, KaiSliderElementEvents>;
4319
+ KaiSlider: KaiVueElement<KaiSliderElementProps, KaiSliderElementEvents>;
3966
4320
  'kai-source': KaiVueElement<KaiSourceElementProps, KaiSourceElementEvents>;
3967
4321
  KaiSource: KaiVueElement<KaiSourceElementProps, KaiSourceElementEvents>;
3968
4322
  'kai-sources': KaiVueElement<KaiSourcesElementProps, KaiSourcesElementEvents>;