@kitn.ai/ui 0.20.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (510) hide show
  1. package/README.md +44 -45
  2. package/dist/components/artifact-card.d.ts +51 -0
  3. package/dist/components/artifact.d.ts +1 -1
  4. package/dist/components/attachments.d.ts +2 -2
  5. package/dist/components/audio-visualizer/index.d.ts +3 -3
  6. package/dist/components/audio-visualizer/labs/lab-choreography.d.ts +59 -0
  7. package/dist/components/audio-visualizer/labs/lab-shaders.d.ts +22 -0
  8. package/dist/components/audio-visualizer/labs/lab-visualizer.d.ts +18 -0
  9. package/dist/components/audio-visualizer/variant-aurora.d.ts +2 -2
  10. package/dist/components/audio-visualizer/variant-bar.d.ts +2 -2
  11. package/dist/components/audio-visualizer/variant-custom.d.ts +2 -2
  12. package/dist/components/audio-visualizer/variant-grid.d.ts +1 -1
  13. package/dist/components/audio-visualizer/variant-radial.d.ts +1 -1
  14. package/dist/components/audio-visualizer/variant-wave.d.ts +1 -1
  15. package/dist/components/card-fallback.d.ts +12 -2
  16. package/dist/components/card-renderer.d.ts +63 -2
  17. package/dist/components/chat-container.d.ts +3 -0
  18. package/dist/components/chat-scope-picker.d.ts +2 -2
  19. package/dist/components/chat-thread.d.ts +77 -18
  20. package/dist/components/choice-card.d.ts +3 -28
  21. package/dist/components/composer-dom.d.ts +1 -1
  22. package/dist/components/composer-highlight.d.ts +1 -1
  23. package/dist/components/composer-history.d.ts +1 -1
  24. package/dist/components/composer.d.ts +1 -1
  25. package/dist/components/confirm-card.d.ts +3 -18
  26. package/dist/components/context.d.ts +8 -4
  27. package/dist/components/conversation-item.d.ts +1 -1
  28. package/dist/components/conversation-list.d.ts +4 -3
  29. package/dist/components/embed.d.ts +3 -3
  30. package/dist/components/file-tree.d.ts +11 -22
  31. package/dist/components/form-widgets.d.ts +1 -1
  32. package/dist/components/form.d.ts +3 -44
  33. package/dist/components/link-preview.d.ts +3 -3
  34. package/dist/components/loader.d.ts +30 -48
  35. package/dist/components/message-skills.d.ts +4 -0
  36. package/dist/components/message.d.ts +84 -21
  37. package/dist/components/model-switcher.d.ts +2 -2
  38. package/dist/components/prompt-input.d.ts +2 -2
  39. package/dist/components/response-compare-types.d.ts +11 -2
  40. package/dist/components/response-compare.d.ts +3 -3
  41. package/dist/components/source.d.ts +12 -3
  42. package/dist/components/tasks-card.d.ts +3 -31
  43. package/dist/components/thread.d.ts +17 -5
  44. package/dist/components/toast.d.ts +2 -2
  45. package/dist/components/tool-classify.d.ts +8 -0
  46. package/dist/components/tool-types.d.ts +29 -1
  47. package/dist/components/tool.d.ts +3 -3
  48. package/dist/components/use-card-resolution.d.ts +1 -1
  49. package/dist/create-tween-COnPwAQN.js +338 -0
  50. package/dist/create-tween-DmTUaXWE.js +341 -0
  51. package/dist/create-tween-qs-jjvNA.js +7 -0
  52. package/dist/custom-elements.json +1726 -463
  53. package/dist/elements/agent-card.js +1 -1
  54. package/dist/elements/artifact.js +1 -1
  55. package/dist/elements/attachments.js +1 -1
  56. package/dist/elements/audio-visualizer.js +1 -1
  57. package/dist/elements/avatar.js +1 -1
  58. package/dist/elements/badge.js +1 -1
  59. package/dist/elements/button.js +1 -1
  60. package/dist/elements/card.js +1 -1
  61. package/dist/elements/cards.js +1 -1
  62. package/dist/elements/chain-of-thought.js +1 -1
  63. package/dist/elements/chat-scope-picker.js +1 -1
  64. package/dist/elements/chat-types.d.ts +70 -10
  65. package/dist/elements/chat-workspace.js +1 -1
  66. package/dist/elements/chat.js +1 -1
  67. package/dist/elements/checkpoint.js +1 -1
  68. package/dist/elements/choice.js +1 -1
  69. package/dist/elements/chunks/{Icon-Tj58ES_d.js → Icon-CHG5z5L7.js} +1 -1
  70. package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-BAtCEEgg.js} +1 -1
  71. package/dist/elements/chunks/arrow-left-CFPTeL1Y.js +1 -0
  72. package/dist/elements/chunks/artifact-fBZOE4SO.js +1 -0
  73. package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-DpYMe5VQ.js} +1 -1
  74. package/dist/elements/chunks/audio-visualizer-DFwzAcuL.js +1 -0
  75. package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-D6zodm6E.js} +1 -1
  76. package/dist/elements/chunks/{button-ZWNif3--.js → button-C42vrGvO.js} +1 -1
  77. package/dist/elements/chunks/card-renderer-DMZsSuvU.js +1 -0
  78. package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
  79. package/dist/elements/chunks/chat-thread-CNNEhol-.js +1 -0
  80. package/dist/elements/chunks/check-7Q3K-iXE.js +1 -0
  81. package/dist/elements/chunks/chevron-down-CpNYLn7E.js +1 -0
  82. package/dist/elements/chunks/chevron-right-DrH85Fha.js +1 -0
  83. package/dist/elements/chunks/choice-card-YoWjpsFV.js +1 -0
  84. package/dist/elements/chunks/circle-C9KRjKYv.js +1 -0
  85. package/dist/elements/chunks/circle-check-Dmi7Y4zw.js +1 -0
  86. package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-QIxXda6k.js} +1 -1
  87. package/dist/elements/chunks/code-block-CqeugReY.js +1 -0
  88. package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-sTv-_5lF.js} +1 -1
  89. package/dist/elements/chunks/{composer-BguS0QL_.js → composer-ldQyeVEB.js} +2 -2
  90. package/dist/elements/chunks/confirm-card-C0RNOwfn.js +1 -0
  91. package/dist/elements/chunks/context-5qgypNHC.js +1 -0
  92. package/dist/elements/chunks/conversation-list-DLzOacXz.js +1 -0
  93. package/dist/elements/chunks/create-tween-V4TXZ93X.js +7 -0
  94. package/dist/elements/chunks/default-input-DeeRSDHc.js +1 -0
  95. package/dist/elements/chunks/define-lE-QvQFn.js +1 -0
  96. package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DZSa9jog.js} +1 -1
  97. package/dist/elements/chunks/{download-Dsbg3ste.js → download-B2ZmnGqW.js} +1 -1
  98. package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-R3B7cSRE.js} +1 -1
  99. package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BmxceY4l.js} +1 -1
  100. package/dist/elements/chunks/embed-BVrtTZPo.js +1 -0
  101. package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-BbdR7YO7.js} +1 -1
  102. package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-D7dijo1A.js} +1 -1
  103. package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-0upJ5jeb.js} +1 -1
  104. package/dist/elements/chunks/{folder-glP5rha4.js → folder-D8z58t7B.js} +1 -1
  105. package/dist/elements/chunks/form-p5BKJNw5.js +1 -0
  106. package/dist/elements/chunks/hover-card-CN9s2md5.js +1 -0
  107. package/dist/elements/chunks/{icon-BLb8beoi.js → icon-CNiKP37E.js} +1 -1
  108. package/dist/elements/chunks/{info-CIsv92JK.js → info-BSnuJiHI.js} +1 -1
  109. package/dist/elements/chunks/{input-Dds1kFyt.js → input-BauDmzxX.js} +1 -1
  110. package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-BlHJaP9b.js} +1 -1
  111. package/dist/elements/chunks/{link-i4r_k7z4.js → link-DEi5iUWC.js} +1 -1
  112. package/dist/elements/chunks/link-preview-BaHyM6W4.js +1 -0
  113. package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-CmJG_Fkq.js} +1 -1
  114. package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-D2jaMpCN.js} +1 -1
  115. package/dist/elements/chunks/message-C5UfXZ2Z.js +1 -0
  116. package/dist/elements/chunks/message-CXWLmmLn.js +1 -0
  117. package/dist/elements/chunks/message-square-hXylwsy0.js +1 -0
  118. package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-C9ytZFMU.js} +1 -1
  119. package/dist/elements/chunks/model-switcher-CivGfD7-.js +1 -0
  120. package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-DJL_sbOm.js} +1 -1
  121. package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CFaB8naM.js} +1 -1
  122. package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-Br5SksfR.js} +1 -1
  123. package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-CMIvtAhU.js} +1 -1
  124. package/dist/elements/chunks/reasoning-DpZcNzLq.js +1 -0
  125. package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-Q8i3T9a_.js} +1 -1
  126. package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-B5IHCBFV.js} +1 -1
  127. package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-eLeydBje.js} +1 -1
  128. package/dist/elements/chunks/scroll-button-DWbz0yA1.js +1 -0
  129. package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-Xic78AD_.js} +1 -1
  130. package/dist/elements/chunks/{settings-B8lmec4D.js → settings-B8H51pAx.js} +1 -1
  131. package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-CsG9eYLe.js} +1 -1
  132. package/dist/elements/chunks/{share-C3lTiYGt.js → share-CNTx0z__.js} +1 -1
  133. package/dist/elements/chunks/skeleton-CLlHTcMI.js +1 -0
  134. package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
  135. package/dist/elements/chunks/source-BoVcU5ok.js +1 -0
  136. package/dist/elements/chunks/{star-CSJtGxgv.js → star-CxQVTVhI.js} +1 -1
  137. package/dist/elements/chunks/{store-CHyW95TP.js → store-CLtS5CUG.js} +1 -1
  138. package/dist/elements/chunks/tasks-card-CzlkLmUt.js +1 -0
  139. package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D5NhpIQ1.js} +1 -1
  140. package/dist/elements/chunks/textarea-x5Uxsmtm.js +1 -0
  141. package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-UobcJnx9.js} +1 -1
  142. package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-BOfs9eXq.js} +1 -1
  143. package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-Be6Dh_dt.js} +1 -1
  144. package/dist/elements/chunks/tooltip-RxMxyCLF.js +1 -0
  145. package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C4yxH2XN.js} +1 -1
  146. package/dist/elements/chunks/use-card-resolution-jva9ehH6.js +1 -0
  147. package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Dx4VRcjS.js} +2 -2
  148. package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-Y1ZTWgEa.js} +1 -1
  149. package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-B9gml9Cg.js} +2 -2
  150. package/dist/elements/chunks/{video-B7vmenoC.js → video-CrPHsytT.js} +1 -1
  151. package/dist/elements/chunks/x-_-OPHept.js +1 -0
  152. package/dist/elements/coachmark.js +1 -1
  153. package/dist/elements/code-block.js +1 -1
  154. package/dist/elements/command.js +1 -1
  155. package/dist/elements/compare.js +1 -1
  156. package/dist/elements/composer.js +1 -1
  157. package/dist/elements/confirm-card.js +1 -1
  158. package/dist/elements/context-meter.js +1 -1
  159. package/dist/elements/conversation-list.js +1 -1
  160. package/dist/elements/default-input.d.ts +34 -7
  161. package/dist/elements/define.d.ts +1 -0
  162. package/dist/elements/dialog.js +1 -1
  163. package/dist/elements/disclosure.d.ts +1 -1
  164. package/dist/elements/editable-label.js +1 -1
  165. package/dist/elements/element-data-types.d.ts +107 -0
  166. package/dist/elements/embed.js +1 -1
  167. package/dist/elements/empty.js +1 -1
  168. package/dist/elements/feedback-bar.js +1 -1
  169. package/dist/elements/file-tree.js +1 -1
  170. package/dist/elements/file-upload.js +1 -1
  171. package/dist/elements/form.js +1 -1
  172. package/dist/elements/hover-card.js +1 -1
  173. package/dist/elements/icon.js +1 -1
  174. package/dist/elements/image.js +1 -1
  175. package/dist/elements/input.js +1 -1
  176. package/dist/elements/kbd.js +1 -1
  177. package/dist/elements/link-preview.js +1 -1
  178. package/dist/elements/loader.js +1 -1
  179. package/dist/elements/markdown.js +1 -1
  180. package/dist/elements/menu.js +1 -1
  181. package/dist/elements/message-skills.js +1 -1
  182. package/dist/elements/message.js +1 -1
  183. package/dist/elements/model-switcher.js +1 -1
  184. package/dist/elements/nav.js +1 -1
  185. package/dist/elements/notice.js +1 -1
  186. package/dist/elements/pane-group.js +1 -1
  187. package/dist/elements/pane.js +1 -1
  188. package/dist/elements/popover.js +1 -1
  189. package/dist/elements/progress-bar.js +1 -1
  190. package/dist/elements/prompt-dock.js +1 -1
  191. package/dist/elements/prompt-input.js +1 -1
  192. package/dist/elements/prompt-suggestions.js +1 -1
  193. package/dist/elements/reasoning.js +1 -1
  194. package/dist/elements/register.d.ts +8 -6
  195. package/dist/elements/remote.js +1 -1
  196. package/dist/elements/resizable.js +1 -1
  197. package/dist/elements/response-stream.js +1 -1
  198. package/dist/elements/screen.js +1 -1
  199. package/dist/elements/scroll-area.js +1 -1
  200. package/dist/elements/scroll-button.js +1 -1
  201. package/dist/elements/search.js +1 -1
  202. package/dist/elements/segmented.js +1 -1
  203. package/dist/elements/separator.js +1 -1
  204. package/dist/elements/setting-item.js +1 -1
  205. package/dist/elements/settings-group.js +1 -1
  206. package/dist/elements/skeleton.js +1 -1
  207. package/dist/elements/slot-text.d.ts +51 -0
  208. package/dist/elements/slots.d.ts +21 -0
  209. package/dist/elements/source.js +1 -1
  210. package/dist/elements/status.js +1 -1
  211. package/dist/elements/switch.js +1 -1
  212. package/dist/elements/tabs.js +1 -1
  213. package/dist/elements/tasks.js +1 -1
  214. package/dist/elements/text-shimmer.js +1 -1
  215. package/dist/elements/thinking-bar.js +1 -1
  216. package/dist/elements/thread.js +1 -1
  217. package/dist/elements/toast.js +1 -1
  218. package/dist/elements/tool.js +1 -1
  219. package/dist/elements/tooltip.js +1 -1
  220. package/dist/elements/validate-messages.d.ts +25 -0
  221. package/dist/elements/voice-input.js +1 -1
  222. package/dist/elements/voice-output.js +1 -1
  223. package/dist/elements.d.ts +2734 -217
  224. package/dist/index.d.ts +163 -125
  225. package/dist/index.js +6572 -5990
  226. package/dist/index.server.d.ts +5 -0
  227. package/dist/index.server.js +5403 -4860
  228. package/dist/kai-provider.es.d.ts +5 -0
  229. package/dist/kai-provider.es.js +79 -77
  230. package/dist/kai.es.d.ts +5 -0
  231. package/dist/kai.es.js +1 -1
  232. package/dist/llms/llms-full.txt +774 -151
  233. package/dist/llms/llms.txt +57 -5
  234. package/dist/mcp.es.js +4756 -782
  235. package/dist/primitives/card-component-types.d.ts +11 -0
  236. package/dist/primitives/card-data-types.d.ts +196 -0
  237. package/dist/primitives/card-host.d.ts +1 -1
  238. package/dist/primitives/card-recovery.d.ts +3 -3
  239. package/dist/primitives/card-registry.d.ts +4 -14
  240. package/dist/primitives/card-resolution.d.ts +1 -1
  241. package/dist/primitives/card-routing.d.ts +1 -1
  242. package/dist/primitives/card-tags.d.ts +11 -0
  243. package/dist/primitives/card-validate-cards.d.ts +57 -0
  244. package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
  245. package/dist/primitives/card-validate-schemas.d.ts +17 -0
  246. package/dist/primitives/card-validate.d.ts +24 -0
  247. package/dist/primitives/create-kai-chat.d.ts +3 -3
  248. package/dist/primitives/embed-providers.d.ts +1 -1
  249. package/dist/primitives/link-preview.d.ts +1 -1
  250. package/dist/primitives/message-feedback.d.ts +2 -2
  251. package/dist/primitives/toast-store.d.ts +1 -1
  252. package/dist/react/index.d.ts +730 -302
  253. package/dist/react/use-kai-chat.d.ts +8 -4
  254. package/dist/react.d.ts +5 -0
  255. package/dist/react.js +418 -317
  256. package/dist/register-impl-AxIlqjo7.js +145 -0
  257. package/dist/remote/host-embed.d.ts +1 -1
  258. package/dist/remote/index.d.ts +2 -2
  259. package/dist/remote/provider-runtime.d.ts +2 -2
  260. package/dist/remote/provider.d.ts +2 -2
  261. package/dist/remote/validate.d.ts +1 -1
  262. package/dist/remote/wire.d.ts +1 -1
  263. package/dist/schemas/artifact.schema.json +92 -0
  264. package/dist/schemas/embed.schema.json +0 -2
  265. package/dist/schemas/from-tool-call.d.ts +48 -0
  266. package/dist/schemas/index.d.ts +48 -0
  267. package/dist/schemas/link.schema.json +0 -2
  268. package/dist/schemas/provider-subsets.d.ts +136 -0
  269. package/dist/schemas/registry.d.ts +140 -0
  270. package/dist/schemas/tool-defs.d.ts +121 -0
  271. package/dist/schemas.d.ts +5 -0
  272. package/dist/schemas.js +717 -0
  273. package/dist/solid-BGYSJyHG.js +22960 -0
  274. package/dist/solid-Dt2nEwfl.js +20194 -0
  275. package/dist/solid.d.ts +66 -0
  276. package/dist/solid.js +261 -0
  277. package/dist/solid.server.d.ts +5 -0
  278. package/dist/solid.server.js +261 -0
  279. package/dist/state/index.d.ts +14 -4
  280. package/dist/state/messages.d.ts +8 -3
  281. package/dist/state/mock.d.ts +46 -0
  282. package/dist/state/parts.d.ts +85 -0
  283. package/dist/state/stream.d.ts +15 -9
  284. package/dist/state.d.ts +5 -0
  285. package/dist/state.js +253 -84
  286. package/dist/ui/action-icons.d.ts +1 -1
  287. package/dist/ui/collapsible.d.ts +11 -6
  288. package/dist/ui/dropdown.d.ts +47 -42
  289. package/dist/ui/pane-group.d.ts +1 -1
  290. package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-B44X69AG.js} +1 -1
  291. package/dist/variant-aurora-BRhV8Flk.js +391 -0
  292. package/dist/variant-aurora-Ds5ou9BH.js +381 -0
  293. package/dist/variant-custom-B20FhtYj.js +170 -0
  294. package/dist/{variant-custom-Dif-IV4u.js → variant-custom-BvA1Schl.js} +1 -1
  295. package/dist/variant-custom-D6Rtvneb.js +200 -0
  296. package/dist/{variant-wave-DbfNdvSi.js → variant-wave-BUQtkryq.js} +1 -1
  297. package/dist/variant-wave-BVYnqy9L.js +218 -0
  298. package/dist/variant-wave-DXC6_LcH.js +208 -0
  299. package/dist/wire/chunk.d.ts +171 -0
  300. package/dist/wire/consume.d.ts +26 -0
  301. package/dist/wire/encode.d.ts +221 -0
  302. package/dist/wire/files.d.ts +116 -0
  303. package/dist/wire/formats/anthropic.d.ts +2 -0
  304. package/dist/wire/formats/openai.d.ts +5 -0
  305. package/dist/wire/index.d.ts +16 -0
  306. package/dist/wire/media-types.d.ts +106 -0
  307. package/dist/wire/read.d.ts +26 -0
  308. package/dist/wire/sink-helpers.d.ts +21 -0
  309. package/dist/wire/sse.d.ts +33 -0
  310. package/dist/wire.d.ts +5 -53
  311. package/dist/wire.js +1126 -0
  312. package/frameworks/react/index.tsx +191 -158
  313. package/frameworks/react/runtime.tsx +21 -1
  314. package/frameworks/react/use-kai-chat.tsx +11 -1
  315. package/llms-full.txt +774 -151
  316. package/llms.txt +57 -5
  317. package/package.json +62 -8
  318. package/src/agent-tooling/archetypes.ts +127 -0
  319. package/src/agent-tooling/integrations/anthropic.ts +436 -0
  320. package/src/agent-tooling/integrations/cloudflare.ts +141 -52
  321. package/src/agent-tooling/integrations/langgraph.ts +80 -13
  322. package/src/agent-tooling/integrations/mastra.ts +161 -12
  323. package/src/agent-tooling/integrations/mock.ts +47 -8
  324. package/src/agent-tooling/integrations/ollama.ts +68 -24
  325. package/src/agent-tooling/integrations/openai.ts +89 -0
  326. package/src/agent-tooling/integrations/openrouter.ts +58 -8
  327. package/src/agent-tooling/integrations/pi.ts +104 -23
  328. package/src/agent-tooling/integrations/pydantic-ai.ts +61 -5
  329. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +120 -13
  330. package/src/agent-tooling/mcp/manifest.ts +206 -25
  331. package/src/agent-tooling/mcp/tools/debug.ts +13 -7
  332. package/src/agent-tooling/mcp/tools/reference.ts +285 -6
  333. package/src/agent-tooling/mcp/tools/scaffold.ts +4152 -661
  334. package/src/agent-tooling/registry.ts +74 -0
  335. package/src/agent-tooling/types.ts +361 -1
  336. package/src/components/artifact-card.tsx +120 -0
  337. package/src/components/audio-visualizer/index.tsx +9 -0
  338. package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
  339. package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
  340. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
  341. package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
  342. package/src/components/card-fallback.tsx +42 -10
  343. package/src/components/card-renderer.tsx +146 -9
  344. package/src/components/chat-container.tsx +4 -0
  345. package/src/components/chat-thread.tsx +128 -52
  346. package/src/components/choice-card.tsx +17 -29
  347. package/src/components/composer.tsx +12 -2
  348. package/src/components/confirm-card.tsx +24 -20
  349. package/src/components/context.tsx +30 -16
  350. package/src/components/conversation-item.tsx +6 -0
  351. package/src/components/conversation-list.tsx +18 -2
  352. package/src/components/file-tree.tsx +11 -22
  353. package/src/components/file-upload.tsx +14 -8
  354. package/src/components/form.tsx +14 -47
  355. package/src/components/loader.tsx +39 -12
  356. package/src/components/message-skills.tsx +4 -0
  357. package/src/components/message.tsx +294 -46
  358. package/src/components/model-switcher.tsx +38 -8
  359. package/src/components/reasoning.tsx +92 -5
  360. package/src/components/response-compare-types.ts +25 -0
  361. package/src/components/response-compare.tsx +4 -9
  362. package/src/components/source.tsx +50 -24
  363. package/src/components/tasks-card.tsx +10 -33
  364. package/src/components/thread.tsx +84 -42
  365. package/src/components/toast.tsx +17 -6
  366. package/src/components/tool-classify.ts +26 -0
  367. package/src/components/tool-types.ts +31 -1
  368. package/src/elements/agent-card.tsx +11 -11
  369. package/src/elements/artifact.tsx +15 -7
  370. package/src/elements/attachments.tsx +6 -4
  371. package/src/elements/avatar.tsx +1 -1
  372. package/src/elements/button.tsx +29 -6
  373. package/src/elements/card.tsx +2 -2
  374. package/src/elements/cards.tsx +109 -14
  375. package/src/elements/chain-of-thought.tsx +14 -10
  376. package/src/elements/chat-scope-picker.tsx +9 -7
  377. package/src/elements/chat-types.ts +59 -6
  378. package/src/elements/chat-workspace.tsx +50 -9
  379. package/src/elements/chat.tsx +90 -3
  380. package/src/elements/choice.tsx +8 -8
  381. package/src/elements/coachmark.tsx +1 -1
  382. package/src/elements/command.tsx +8 -23
  383. package/src/elements/compare.tsx +7 -6
  384. package/src/elements/compiled.css +1 -1
  385. package/src/elements/composer.tsx +8 -8
  386. package/src/elements/confirm-card.tsx +9 -9
  387. package/src/elements/context-meter.tsx +4 -18
  388. package/src/elements/conversation-list.tsx +14 -9
  389. package/src/elements/default-input.tsx +131 -8
  390. package/src/elements/define.tsx +178 -43
  391. package/src/elements/editable-label.tsx +17 -2
  392. package/src/elements/element-data-types.ts +112 -0
  393. package/src/elements/element-meta.json +1624 -306
  394. package/src/elements/element-types.d.ts +2645 -214
  395. package/src/elements/feedback-bar.tsx +1 -1
  396. package/src/elements/file-tree.tsx +1 -1
  397. package/src/elements/file-upload.tsx +1 -1
  398. package/src/elements/form.tsx +14 -6
  399. package/src/elements/input.tsx +1 -1
  400. package/src/elements/kbd.tsx +1 -1
  401. package/src/elements/menu.tsx +40 -32
  402. package/src/elements/message-skills.tsx +7 -9
  403. package/src/elements/message.tsx +200 -32
  404. package/src/elements/model-switcher.tsx +5 -3
  405. package/src/elements/pane.tsx +1 -1
  406. package/src/elements/prompt-dock.tsx +8 -8
  407. package/src/elements/prompt-input.tsx +10 -10
  408. package/src/elements/prompt-suggestions.tsx +7 -4
  409. package/src/elements/reasoning.tsx +1 -1
  410. package/src/elements/register.ts +9 -0
  411. package/src/elements/resizable.globals.d.ts +36 -0
  412. package/src/elements/resizable.tsx +24 -13
  413. package/src/elements/response-stream.tsx +1 -1
  414. package/src/elements/scroll-button.tsx +1 -1
  415. package/src/elements/search.tsx +6 -1
  416. package/src/elements/segmented.tsx +5 -11
  417. package/src/elements/separator.tsx +1 -1
  418. package/src/elements/slot-text.ts +73 -0
  419. package/src/elements/slots.ts +67 -24
  420. package/src/elements/source.tsx +21 -22
  421. package/src/elements/switch.tsx +3 -3
  422. package/src/elements/tasks.tsx +5 -4
  423. package/src/elements/thread.tsx +36 -4
  424. package/src/elements/toast.tsx +3 -2
  425. package/src/elements/tool.tsx +1 -1
  426. package/src/elements/validate-messages.ts +76 -0
  427. package/src/elements/voice-input.tsx +7 -7
  428. package/src/elements/voice-output.tsx +4 -4
  429. package/src/index.ts +145 -6
  430. package/src/primitives/card-component-types.ts +63 -0
  431. package/src/primitives/card-data-types.ts +312 -0
  432. package/src/primitives/card-recovery.ts +13 -2
  433. package/src/primitives/card-registry.tsx +34 -23
  434. package/src/primitives/card-schemas/artifact.schema.json +92 -0
  435. package/src/primitives/card-schemas/embed.schema.json +0 -2
  436. package/src/primitives/card-schemas/link.schema.json +0 -2
  437. package/src/primitives/card-tags.ts +61 -0
  438. package/src/primitives/card-validate-cards.ts +255 -0
  439. package/src/primitives/card-validate-generator.testlib.ts +38 -0
  440. package/src/primitives/card-validate-schemas.ts +58 -0
  441. package/src/primitives/card-validate.ts +48 -20
  442. package/src/primitives/create-tween.ts +54 -2
  443. package/src/primitives/message-feedback.ts +3 -2
  444. package/src/primitives/use-audio-analysis.ts +20 -1
  445. package/src/primitives/use-sequencer.ts +20 -1
  446. package/src/schemas/from-tool-call.ts +151 -0
  447. package/src/schemas/index.ts +340 -0
  448. package/src/schemas/provider-subsets.ts +538 -0
  449. package/src/schemas/registry.ts +355 -0
  450. package/src/schemas/tool-defs.ts +492 -0
  451. package/src/solid.ts +150 -0
  452. package/src/state/index.ts +27 -1
  453. package/src/state/messages.ts +21 -4
  454. package/src/state/mock.ts +211 -0
  455. package/src/state/parts.ts +295 -0
  456. package/src/state/stream.ts +56 -58
  457. package/src/ui/collapsible.tsx +17 -5
  458. package/src/ui/dropdown.tsx +46 -12
  459. package/src/ui/overlay.tsx +10 -4
  460. package/src/wire/chunk.ts +201 -0
  461. package/src/wire/consume.ts +440 -0
  462. package/src/wire/encode.ts +689 -0
  463. package/src/wire/files.ts +342 -0
  464. package/src/wire/formats/anthropic.ts +267 -0
  465. package/src/wire/formats/openai.ts +187 -0
  466. package/src/wire/index.ts +73 -0
  467. package/src/wire/media-types.ts +344 -0
  468. package/src/wire/read.ts +152 -0
  469. package/src/wire/sink-helpers.ts +50 -0
  470. package/src/wire/sse.ts +140 -0
  471. package/dist/create-tween-hvpmSuwW.js +0 -7
  472. package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
  473. package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
  474. package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
  475. package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
  476. package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
  477. package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
  478. package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
  479. package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
  480. package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
  481. package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
  482. package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
  483. package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
  484. package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
  485. package/dist/elements/chunks/default-input-Cqqilg9t.js +0 -1
  486. package/dist/elements/chunks/define-D1din7Ld.js +0 -1
  487. package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
  488. package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
  489. package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
  490. package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
  491. package/dist/elements/chunks/model-switcher-B94YAKsM.js +0 -1
  492. package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
  493. package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
  494. package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
  495. package/dist/elements/chunks/slots--V19HwUb.js +0 -1
  496. package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
  497. package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
  498. package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
  499. package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
  500. package/dist/host-embed.d.ts +0 -33
  501. package/dist/kai-provider.d.ts +0 -2
  502. package/dist/origin.d.ts +0 -5
  503. package/dist/provider-runtime.d.ts +0 -27
  504. package/dist/register-impl-DQyejdfb.js +0 -145
  505. package/dist/validate.d.ts +0 -4
  506. package/dist/version.d.ts +0 -4
  507. package/src/components/component-meta.json +0 -4550
  508. package/src/elements/card-media.jpg +0 -0
  509. package/src/elements/resizable.d.ts +0 -27
  510. package/src/elements/video-poster.jpg +0 -0
package/llms-full.txt CHANGED
@@ -19,7 +19,7 @@ This is the single most common mistake. Arrays and objects (`messages`, `models`
19
19
 
20
20
  ```js
21
21
  const chat = document.querySelector('kai-chat');
22
- chat.messages = [{ id: '1', role: 'assistant', content: 'Hi!' }]; // ✅ property
22
+ chat.messages = [{ id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hi!' }] }]; // ✅ property
23
23
  ```
24
24
  ```html
25
25
  <kai-chat messages="[...]"></kai-chat> <!-- ❌ never works -->
@@ -49,18 +49,70 @@ All 80 elements are also exported individually. Use them for custom layouts or f
49
49
 
50
50
  ## ChatMessage schema (required for `<kai-chat>`)
51
51
 
52
+ A message's content is an **ordered `parts` array**. There is no `content` string: it was removed in 0.20.0. Text, reasoning, tool calls, generative-UI cards, citations and file attachments all live in `parts`, in the order the model produced them, so a post-tool answer renders below its tool panel instead of being glued onto the pre-tool text.
53
+
52
54
  ```ts
53
55
  interface ChatMessage {
54
56
  id: string;
55
57
  role: 'user' | 'assistant';
56
- content: string;
57
- reasoning?: { text: string; label?: string };
58
- tools?: ToolPart[];
59
- attachments?: AttachmentData[];
58
+ /** The ONLY content channel. Ordered. */
59
+ parts: MessagePart[];
60
+ /** Action buttons under the message. Chrome, not content. */
60
61
  actions?: ('copy' | 'like' | 'dislike' | 'regenerate' | 'edit')[];
62
+ avatar?: { src?: string; fallback?: string; alt?: string };
63
+ feedback?: 'like' | 'dislike';
64
+ }
65
+
66
+ /** Six variants, one per kind of content. Every variant may also carry `raw`
67
+ * (`{ source, payload }`), the untranslated provider block the part was
68
+ * normalized from, for echoing a turn back to the model verbatim. */
69
+ type MessagePart =
70
+ | { type: 'text'; text: string; raw?: RawOrigin }
71
+ | { type: 'reasoning'; text: string; label?: string; index?: number; signature?: string; raw?: RawOrigin }
72
+ | { type: 'tool'; tool: ToolPart; raw?: RawOrigin }
73
+ | { type: 'card'; envelope: CardEnvelope; raw?: RawOrigin }
74
+ | { type: 'source'; source: MessageSource; raw?: RawOrigin }
75
+ | { type: 'file'; attachment: AttachmentData; raw?: RawOrigin };
76
+
77
+ interface ToolPart {
78
+ type: string;
79
+ /** Rendering classification. Derived from `type` when you omit it; an explicit
80
+ * value you set is preserved across later patches. */
81
+ kind?: 'command' | 'file-change' | 'search' | 'fetch' | 'mcp' | 'image' | 'generic';
82
+ state: 'input-streaming' | 'input-available' | 'output-available' | 'output-error';
83
+ input?: Record<string, unknown>;
84
+ /** Raw accumulated argument fragments, for character-level streaming. */
85
+ rawInput?: string;
86
+ output?: Record<string, unknown>;
87
+ toolCallId?: string;
88
+ errorText?: string;
89
+ raw?: RawOrigin;
90
+ }
91
+
92
+ /** A citation. Exported as `MessageSource` (the bare `Source` name belongs to
93
+ * the citation-chip component). */
94
+ interface MessageSource { id?: string; url?: string; title?: string; snippet?: string; index?: number }
95
+ ```
96
+
97
+ The simplest possible message, and one with reasoning + a tool call before its answer:
98
+
99
+ ```js
100
+ { id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hi!' }] }
101
+
102
+ {
103
+ id: '2',
104
+ role: 'assistant',
105
+ parts: [
106
+ { type: 'reasoning', text: 'I should search for current pricing.' },
107
+ { type: 'tool', tool: { type: 'search', state: 'output-available', toolCallId: 'tc_1',
108
+ input: { query: 'current pricing' }, output: { results: ['A', 'B'] } } },
109
+ { type: 'text', text: 'Here is what I found.' },
110
+ ],
61
111
  }
62
112
  ```
63
113
 
114
+ Types are importable: `import type { ChatMessage, MessagePart, MessageSource } from '@kitn.ai/ui'` (also from `'@kitn.ai/ui/react'` and `'@kitn.ai/ui/state'`).
115
+
64
116
  ## Framework wiring
65
117
 
66
118
  **Plain HTML / CDN**
@@ -121,33 +173,38 @@ Composable: combine `<kai-message>`, `<kai-prompt-input>`, `<kai-reasoning>`,
121
173
  ### 3 — Handle `submit` and stream
122
174
  ```js
123
175
  import '@kitn.ai/ui/elements';
176
+ // The streaming fold. It is 5 lines if you would rather inline it: see the
177
+ // Streaming recipe below.
178
+ import { appendTextPart } from '@kitn.ai/ui/state';
179
+
124
180
  const chat = document.querySelector('kai-chat');
125
181
  chat.messages = [];
126
182
 
127
183
  chat.addEventListener('kai-submit', async (e) => {
128
184
  const userText = e.detail.value;
129
185
 
130
- // Append the user message (new array see streaming note)
131
- const history = [...chat.messages, { id: crypto.randomUUID(), role: 'user', content: userText }];
186
+ // Append the user message (new array, see the streaming note)
187
+ const history = [...chat.messages, { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: userText }] }];
132
188
  chat.messages = history;
133
189
  chat.loading = true;
134
190
 
135
191
  // Add an empty assistant placeholder to stream into
136
192
  const aid = crypto.randomUUID();
137
- chat.messages = [...history, { id: aid, role: 'assistant', content: '' }];
193
+ chat.messages = [...history, { id: aid, role: 'assistant', parts: [] }];
138
194
 
139
- let answer = '';
140
195
  for await (const token of streamFromYourAPI(history)) {
141
- answer += token;
142
- chat.messages = chat.messages.map((m) => (m.id === aid ? { ...m, content: answer } : m));
196
+ // Fold each delta onto the message's TRAILING text part. Do NOT replace
197
+ // `parts` wholesale: that drops reasoning/tool/card parts already on it.
198
+ chat.messages = chat.messages.map((m) =>
199
+ m.id === aid ? { ...m, parts: appendTextPart(m.parts, token) } : m);
143
200
  }
144
201
  chat.loading = false;
145
202
  });
146
203
  ```
147
204
 
148
205
  ### 4 — Wire optional features
149
- - Reasoning: add `reasoning: { text: '…' }` to an assistant message.
150
- - Tool calls: add `tools: [{ type: 'search', state: 'output-available', input: {…}, output: {…} }]`.
206
+ - Reasoning: push `{ type: 'reasoning', text: '…' }` onto the message's `parts`.
207
+ - Tool calls: push `{ type: 'tool', tool: { type: 'search', state: 'output-available', input: {…}, output: {…} } }`.
151
208
  - Model switcher: `chat.models = [{ id: 'gpt-4o', name: 'GPT-4o' }]; chat.currentModel = 'gpt-4o';` — listen for `modelchange`.
152
209
  - Token meter: `chat.context = { usedTokens: 1200, maxTokens: 128000 };`.
153
210
  - History sidebar: add `<kai-conversations>`; listen for `select` and `newchat`.
@@ -159,17 +216,35 @@ Override `--kai-color-*` tokens on `:root` (they pierce Shadow DOM).
159
216
 
160
217
  ## Streaming recipe (critical)
161
218
 
162
- To update messages while streaming, **reassign a NEW array containing a NEW message object** on every chunk. Mutating an existing message object in place will NOT trigger a re-render:
219
+ Two rules, and both bite:
163
220
 
164
- ```js
165
- // re-renders
166
- chat.messages = chat.messages.map((m) => (m.id === id ? { ...m, content: next } : m));
221
+ 1. **Reassign a NEW array containing a NEW message object on every chunk.** Mutating an existing message object in place will NOT trigger a re-render.
222
+ 2. **Fold the delta onto the message's TRAILING text part.** Replacing `parts` with a fresh single-text array re-renders fine but silently deletes any reasoning / tool / card parts the turn already produced.
167
223
 
168
- // ❌ does NOT re-render
169
- chat.messages[i].content = next;
224
+ ```js
225
+ // The fold. `@kitn.ai/ui/state` exports exactly this as `appendTextPart`.
226
+ const appendText = (parts, delta) => {
227
+ const last = parts[parts.length - 1];
228
+ return last?.type === 'text'
229
+ ? [...parts.slice(0, -1), { ...last, text: last.text + delta }]
230
+ : [...parts, { type: 'text', text: delta }];
231
+ };
232
+
233
+ // ✅ re-renders, and keeps every part already on the message
234
+ chat.messages = chat.messages.map((m) =>
235
+ m.id === id ? { ...m, parts: appendText(m.parts, delta) } : m);
236
+
237
+ // ❌ does NOT re-render (same array, same object)
238
+ chat.messages[i].parts = appendText(chat.messages[i].parts, delta);
239
+
240
+ // ❌ re-renders, but drops the message's reasoning/tool/card parts
241
+ chat.messages = chat.messages.map((m) =>
242
+ m.id === id ? { ...m, parts: [{ type: 'text', text: answer }] } : m);
170
243
  ```
171
244
 
172
- The same rule applies to every array/object property (`models`, `context`, `suggestions`, …): replace, don't mutate.
245
+ Opening a new text part when the last part is not text is what stops a post-tool answer being glued onto the pre-tool text.
246
+
247
+ The same reassign rule applies to every array/object property (`models`, `context`, `suggestions`, …): replace, don't mutate.
173
248
 
174
249
  ---
175
250
 
@@ -183,16 +258,17 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
183
258
 
184
259
  | Property | Attribute | Type | Description |
185
260
  |---|---|---|---|
186
- | `name` | `name` | `undefined \| string` | The agent's name the primary label. Attribute: `name`. |
261
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
262
+ | `name` | `name` | `undefined \| string` | The agent's name, the primary label. Attribute: `name`. |
187
263
  | `active` | `active` | `undefined \| false \| true` | Selected / focused state: highlighted border + surface. Attribute: `active`. |
188
- | `needsAttention` | `needs-attention` | `undefined \| false \| true` | Raise a prominent "Needs you" pill plus a glowing amber edge the attention-routing signal that pulls focus to this agent. Attribute: `needs-attention`. |
189
- | `status` | — | `undefined \| { tone: "working" \| "idle" \| "done" \| "error" \| "blocked"; label?: undefined \| string; pulse?: undefined \| false \| true }` | Run status a JS PROPERTY (object), not an attribute. Shape: `{ tone, label?, pulse? }`, where `tone` is one of `working` \| `idle` \| `done` \| `error` \| `blocked` (maps to the kit's tool hues), `label` is an optional short string beside the dot, and `pulse` animates the dot. Set it with `el.status = { tone: 'working', label: 'Working', pulse: true }`. |
264
+ | `needsAttention` | `needs-attention` | `undefined \| false \| true` | Raise a prominent "Needs you" pill plus a glowing amber edge. This is the attention-routing signal that pulls focus to this agent. Attribute: `needs-attention`. |
265
+ | `status` | — | `undefined \| { tone: "working" \| "idle" \| "done" \| "error" \| "blocked"; label?: undefined \| string; pulse?: undefined \| false \| true }` | Run status. A JS PROPERTY (object), not an attribute. Shape: `{ tone, label?, pulse? }`, where `tone` is one of `working` \| `idle` \| `done` \| `error` \| `blocked` (maps to the kit's tool hues), `label` is an optional short string beside the dot, and `pulse` animates the dot. Set it with `el.status = { tone: 'working', label: 'Working', pulse: true }`. |
190
266
 
191
267
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
192
268
 
193
269
  | Event | `detail` type | Description |
194
270
  |---|---|---|
195
- | `kai-activate` | `CustomEvent` | The card was activated clicked, or Enter / Space while focused. Promote this agent back to focus. |
271
+ | `kai-activate` | `CustomEvent` | The card was activated by a click, or by Enter / Space while focused. Promote this agent back to focus. |
196
272
  | `kai-menu` | `CustomEvent` | The trailing "..." kebab was clicked. The consumer opens its own menu; the card only surfaces the affordance (the click does not also activate the card). |
197
273
 
198
274
  **Styleable parts** (restyle from outside via `kai-agent-card::part(name)`):
@@ -210,11 +286,12 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
210
286
 
211
287
  | Property | Attribute | Type | Description |
212
288
  |---|---|---|---|
289
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
213
290
  | `src` | `src` | `undefined \| string` | URL the preview iframe frames. Consumer-controlled. |
214
- | `files` | — | `{ path: string; url?: undefined \| string; code?: undefined \| string; language?: undefined \| string; type?: undefined \| "html" \| "pdf" \| "image" \| "other"; additions?: undefined \| number; deletions?: undefined \| number; status?: undefined \| "added" \| "modified" \| "deleted" \| "renamed" \| "untracked" }[]` | Files for the Code tab tree + each file's preview `url`. Set as a JS property (array). |
291
+ | `files` | — | `undefined \| { path: string; url?: undefined \| string; code?: undefined \| string; language?: undefined \| string; type?: undefined \| "html" \| "pdf" \| "image" \| "other"; additions?: undefined \| number; deletions?: undefined \| number; status?: undefined \| "added" \| "modified" \| "deleted" \| "renamed" \| "untracked" }[]` | Files for the Code tab tree + each file's preview `url`. Omit for a preview-only artifact (the Code tab then has nothing to show; pair it with `no-tabs` to hide the toggle). Set as a JS property (array). |
215
292
  | `tab` | `tab` | `undefined \| "preview" \| "code"` | Controlled active tab: `preview` or `code`. When set, the artifact follows it (re-asserted on change). Leave unset for an uncontrolled tab (see `defaultTab`). |
216
293
  | `defaultTab` | `default-tab` | `undefined \| "preview" \| "code"` | Uncontrolled INITIAL tab (used only when `tab` is unset). Default `preview`. Seeds the starting tab; the user can then switch freely without the consumer re-asserting a controlled `tab`. |
217
- | `activeFile` | `active-file` | `undefined \| string` | Selected file path syncs the tree highlight, Code source, and preview. |
294
+ | `activeFile` | `active-file` | `undefined \| string` | Selected file path. Syncs the tree highlight, Code source, and preview. |
218
295
  | `sandbox` | `sandbox` | `undefined \| string` | iframe `sandbox` override. Secure default `allow-scripts allow-forms` (NOT `allow-same-origin`). |
219
296
  | `iframeTitle` | `iframe-title` | `undefined \| string` | Accessible title for the preview iframe. |
220
297
  | `maximized` | `maximized` | `undefined \| false \| true` | Reflects the artifact's own maximized view-state (usually driven by the protocol). |
@@ -235,9 +312,24 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
235
312
  |---|---|---|
236
313
  | `kai-file-select` | `CustomEvent<{ path: string }>` | Fired when a file is selected. `detail.path`. |
237
314
  | `kai-maximize-change` | `CustomEvent<{ maximized: false \| true }>` | Artifact's own maximize button toggled (consumer-observable; non-bubbling). |
315
+ | `kai-maximize-intent` | `CustomEvent<{ requested: false \| true }>` | The maximize PROTOCOL intent, raised as a raw bubbling + composed CustomEvent (not through `dispatch`) so an enclosing `<kai-resizable>` can catch it and maximize the containing panel. Declared here so it is typed and reaches the generated API. Listen for it to drive maximize from your own chrome, or re-emit it to trigger one. |
238
316
  | `kai-navigate` | `CustomEvent<{ url: string }>` | Fired when the preview navigates. `detail.url` = the new location. |
239
317
  | `kai-tab-change` | `CustomEvent<{ tab: "preview" \| "code" }>` | Fired when the Preview\|Code tab changes. `detail.tab`. |
240
318
 
319
+ **Methods** (call on the element instance: `document.querySelector('kai-artifact').back()`):
320
+
321
+ | Method | Signature | Description |
322
+ |---|---|---|
323
+ | `back` | `(): void` | Go back in the artifact's own history stack (no-op when there's no prior entry). |
324
+ | `forward` | `(): void` | Go forward in the history stack (no-op when there's no forward entry). |
325
+ | `reload` | `(): void` | Force-reload the current preview url (also re-renders an inline PDF). |
326
+ | `home` | `(): void` | Navigate to the `src` home url (no-op when there's no `src`). |
327
+ | `navigate` | `(url: string): void` | Push + load a url in the preview, the path-field submit path (fires kai-navigate). |
328
+ | `selectFile` | `(path: string): void` | Select a file by path: highlights the tree, shows its source, navigates the preview (fires kai-file-select + kai-navigate). Named selectFile to avoid the `activeFile` prop. |
329
+ | `openExternal` | `(): void` | Open the current url in a new browser tab (no-op when there's no concrete url). Named openExternal, NOT openInTab, which is a prop (toolbar button visibility). |
330
+ | `maximize` | `(): void` | Enter the maximized view-state (fires kai-maximize-change{maximized:true}). Named maximize, NOT maximized, which is a prop. |
331
+ | `restore` | `(): void` | Exit the maximized view-state (fires kai-maximize-change{maximized:false}). |
332
+
241
333
  ---
242
334
 
243
335
  ### `kai-attachments` / `Attachments`
@@ -246,7 +338,8 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
246
338
 
247
339
  | Property | Attribute | Type | Description |
248
340
  |---|---|---|---|
249
- | `items` | | `{ id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]` | The attachments to render. Set as a JS property (array). |
341
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
342
+ | `items` | — | `undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]` | 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). |
250
343
  | `variant` | `variant` | `undefined \| "grid" \| "inline" \| "list"` | Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. |
251
344
  | `hoverCard` | `hover-card` | `undefined \| false \| true` | Wrap each item in a hover card that previews its details. |
252
345
  | `removable` | `removable` | `undefined \| false \| true` | Show a remove button per item; clicking it fires a `kai-remove` event. |
@@ -263,7 +356,7 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
263
356
 
264
357
  | Part | Description |
265
358
  |---|---|
266
- | `::part(preview)` | The image shown in an attachment’s hover-card preview. Bounded by default (max ~320×256, aspect preserved) so a large image never blows up the card raise or lower the cap from outside. — `kai-attachments::part(preview) { max-width: 32rem; max-height: 24rem }` |
359
+ | `::part(preview)` | The image shown in an attachment’s hover-card preview. Bounded by default (max ~320×256, aspect preserved) so a large image never blows up the card. Raise or lower the cap from outside. — `kai-attachments::part(preview) { max-width: 32rem; max-height: 24rem }` |
267
360
 
268
361
  ---
269
362
 
@@ -273,6 +366,7 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
273
366
 
274
367
  | Property | Attribute | Type | Description |
275
368
  |---|---|---|---|
369
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
276
370
  | `variant` | `variant` | `undefined \| string` | Look to render: `bar` (default), `grid`, `radial`, `wave`, `aurora`, `custom`. `aura` is accepted as a LiveKit-markup alias for `aurora`. Attribute: `variant`. |
277
371
  | `state` | `state` | `undefined \| string` | `idle` (default), `connecting`, `listening`, `thinking`, `speaking`, `disconnected` (connection down: the dead, flat look). LiveKit's room-lifecycle state names are accepted as aliases. Attribute: `state`. |
278
372
  | `size` | `size` | `undefined \| string` | `icon` \| `sm` \| `md` (default) \| `lg` \| `xl`. Attribute: `size`. |
@@ -309,9 +403,10 @@ _No events._
309
403
 
310
404
  | Property | Attribute | Type | Description |
311
405
  |---|---|---|---|
406
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
312
407
  | `src` | `src` | `undefined \| string` | Image URL/data-URI. When absent, the `fallback` initials show instead. |
313
408
  | `alt` | `alt` | `undefined \| string` | Alt text for the image. Defaults to `fallback`. |
314
- | `fallback` | `fallback` | `undefined \| string` | Short text shown when there's no image usually initials (e.g. "JD", "AI"). |
409
+ | `fallback` | `fallback` | `undefined \| string` | Short text shown when there's no image, usually initials (e.g. "JD", "AI"). |
315
410
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg"` | Size token: `sm` \| `md` (default) \| `lg`. |
316
411
 
317
412
  _No events._
@@ -324,10 +419,17 @@ _No events._
324
419
 
325
420
  | Property | Attribute | Type | Description |
326
421
  |---|---|---|---|
422
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
327
423
  | `variant` | `variant` | `undefined \| "default" \| "count" \| "citation"` | `default` (muted pill) · `count` (compact number badge) · `citation` (filled primary, for inline citation markers). Defaults to `default`. |
328
424
 
329
425
  _No events._
330
426
 
427
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
428
+
429
+ | Slot | Mode | Description |
430
+ |---|---|---|
431
+ | _(default)_ | inject | The badge's label: text, or a small inline icon plus text. |
432
+
331
433
  **Styleable parts** (restyle from outside via `kai-badge::part(name)`):
332
434
 
333
435
  | Part | Description |
@@ -342,13 +444,14 @@ _No events._
342
444
 
343
445
  | Property | Attribute | Type | Description |
344
446
  |---|---|---|---|
345
- | `variant` | `variant` | `undefined \| "default" \| "subtle" \| "ghost" \| "outline" \| "destructive"` | Visual style. `default` (filled), `subtle` (muted text, hover tint — the toolbar icon look), `ghost` (transparent, hover fill), `outline`, or `destructive`. Defaults to `default`. |
447
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
448
+ | `variant` | `variant` | `undefined \| "default" \| "subtle" \| "ghost" \| "outline" \| "destructive"` | Visual style. `default` (filled), `subtle` (muted text, hover tint, the toolbar icon look), `ghost` (transparent, hover fill), `outline`, or `destructive`. Defaults to `default`. |
346
449
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | Size token. `icon` / `icon-sm` are square (for icon-only buttons); `sm` / `md` / `lg` size text buttons. Defaults to `md`. |
347
450
  | `icon` | `icon` | `undefined \| string` | Leading icon: a named icon (e.g. `"mic"`, `"plus"`), an image URL/data-URI, or plain text. Renders before any slotted label. |
348
451
  | `iconTrailing` | `icon-trailing` | `undefined \| string` | Trailing icon, after the label (e.g. `"chevron-down"` for a menu affordance). |
349
- | `label` | `label` | `undefined \| string` | Accessible name. REQUIRED for icon-only buttons (no visible text); ignored when you slot visible text, which already names the button. |
452
+ | `label` | `label` | `undefined \| string` | Accessible name. REQUIRED for icon-only buttons (no visible text); ignored when you slot visible text, which already names the button. An `aria-label` on top of visible text REPLACES that name rather than adding to it, so a button reading "Save" that answers to "Submit" is unusable by speech input (WCAG 2.5.3, Label in Name). The visible text wins. An `icon` / `icon-sm` size hides the slot, which makes the button icon-only whatever you slotted, so `label` is what names it there. |
350
453
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the button (non-interactive, dimmed). |
351
- | `full` | `full` | `undefined \| false \| true` | Stretch the button to the full width of its container (a block button) e.g. a card CTA or a stacked action. Attribute: `full`. |
454
+ | `full` | `full` | `undefined \| false \| true` | Stretch the button to the full width of its container (a block button), e.g. a card CTA or a stacked action. Attribute: `full`. |
352
455
  | `align` | `align` | `undefined \| "start" \| "center" \| "end"` | Justify the button's content: `start`, `center` (default), or `end`. Combine with `full` for a full-width, left-aligned button. |
353
456
  | `type` | `type` | `undefined \| "button" \| "submit" \| "reset"` | Native button `type`. Defaults to `button` (so it never submits a form). |
354
457
 
@@ -358,10 +461,19 @@ _No events._
358
461
  |---|---|---|
359
462
  | `kai-click` | `CustomEvent` | The button was activated (pointer or keyboard). Carries no detail. The native `click` also bubbles (composed) for consumers who prefer it. |
360
463
 
464
+ **Methods** (call on the element instance: `document.querySelector('kai-button').focus(…)`):
465
+
466
+ | Method | Signature | Description |
467
+ |---|---|---|
468
+ | `focus` | `(options?: FocusOptions): void` | Focus the inner `<button>` (host.focus() would focus the wrapper). |
469
+ | `blur` | `(): void` | Blur the inner `<button>`. |
470
+ | `click` | `(): void` | Programmatically activate the button. Runs the same path as a user click and fires kai-click. Forwarding to the inner button means `disabled` is respected automatically. |
471
+
361
472
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
362
473
 
363
474
  | Slot | Mode | Description |
364
475
  |---|---|---|
476
+ | _(default)_ | inject | The button's label. Omit it for an icon-only button (pair with `aria-label`). |
365
477
  | `icon` | replace | A custom leading icon (any inline SVG, inherits `currentColor`). Wins over the `icon` prop. |
366
478
 
367
479
  **Styleable parts** (restyle from outside via `kai-button::part(name)`):
@@ -378,8 +490,9 @@ _No events._
378
490
 
379
491
  | Property | Attribute | Type | Description |
380
492
  |---|---|---|---|
493
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
381
494
  | `appearance` | `appearance` | `undefined \| "outlined" \| "filled" \| "plain" \| "accent"` | Surface treatment: `outlined` (default) \| `filled` \| `plain` \| `accent`. Attribute: `appearance`. |
382
- | `orientation` | `orientation` | `undefined \| "vertical" \| "horizontal" \| "responsive"` | `vertical` (default, media on top) \| `horizontal` (media at the start) \| `responsive` (horizontal when the card's container is wide enough, else vertical a container query on the card's own width). Attribute: `orientation`. |
495
+ | `orientation` | `orientation` | `undefined \| "vertical" \| "horizontal" \| "responsive"` | `vertical` (default, media on top) \| `horizontal` (media at the start) \| `responsive` (horizontal when the card's container is wide enough, else vertical, via a container query on the card's own width). Attribute: `orientation`. |
383
496
  | `collapse` | `collapse` | `undefined \| string` | The card width below which a `responsive` card collapses to vertical and the footer actions stack. A CSS length; default `28rem`. Attribute: `collapse`. |
384
497
  | `dense` | `dense` | `undefined \| false \| true` | Tighter spacing for dense lists. Attribute: `dense`. |
385
498
  | `dismissible` | `dismissible` | `undefined \| false \| true` | Show a close (×) that hides the card and emits `kai-dismiss`. Attribute: `dismissible`. Off by default. |
@@ -399,6 +512,7 @@ _No events._
399
512
 
400
513
  | Slot | Mode | Description |
401
514
  |---|---|---|
515
+ | _(default)_ | inject | The card body, below the header/media regions. |
402
516
  | `media` | inject | Full-bleed media (image/video/illustration) at the top (vertical) or start (horizontal). Clipped to the card corners. |
403
517
  | `header` | inject | Header content, e.g. a title. Rendered above the body. |
404
518
  | `header-actions` | inject | An actions cluster pinned to the end of the header row. |
@@ -424,15 +538,26 @@ _No events._
424
538
 
425
539
  | Property | Attribute | Type | Description |
426
540
  |---|---|---|---|
541
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
427
542
  | `cards` | — | `undefined \| { 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 } }[]` | The stream of card envelopes to render. Set as a JS PROPERTY: `el.cards = [...]`. |
428
543
  | `types` | — | `undefined \| Record<string, string>` | Optional type→tag overrides/additions (merged over the built-ins). Property: `el.types`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. |
544
+ | `schemas` | — | `undefined \| Record<string, object>` | 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. |
429
545
  | `policy` | — | `undefined \| { onSubmit?: undefined \| (cardId: string, data: unknown) => void; onAction?: undefined \| (cardId: string, action: string, payload?: unknown) => void; onSendPrompt?: undefined \| (text: string, opts: { mode: "compose" \| "send"; context?: unknown; }) => void; onOpen?: undefined \| (url: string, target: "tab" \| "artifact") => void; onState?: undefined \| (cardId: string, patch: unknown) => void; onDismiss?: undefined \| (cardId: string) => void; onReopen?: undefined \| (cardId: string) => void; onError?: undefined \| (cardId: string, message: string) => void; maxSendPromptMode?: undefined \| "compose" \| "send" }` | Optional CardPolicy handling child events. Property: `el.policy`. |
546
+ | `validateCards` | `validate-cards` | `undefined \| false \| true` | 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. |
430
547
 
431
548
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
432
549
 
433
550
  | Event | `detail` type | Description |
434
551
  |---|---|---|
435
- | `kai-card-resolved` | `CustomEvent<{ cardId: string; resolution: { 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 } }>` | A child card transitioned to a resolved/deferred state (an action was chosen, a form/tasks submission landed, or it was dismissed) — re-emitted off the host as a non-bubbling convenience event so a consumer can observe resolution centrally without diffing the cards array. `detail` = `{ cardId, resolution }`. (A `reopen` un-resolves a card and has no `CardResolution`, so it does NOT fire this observe reopen via the underlying bubbling `kai-card` event.) |
552
+ | `kai-card-resolved` | `CustomEvent<{ cardId: string; resolution: { 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 } }>` | A child card transitioned to a resolved/deferred state (an action was chosen, a form/tasks submission landed, or it was dismissed). Re-emitted off the host as a non-bubbling convenience event so a consumer can observe resolution centrally without diffing the cards array. `detail` = `{ cardId, resolution }`. (A `reopen` un-resolves a card and has no `CardResolution`, so it does NOT fire this; observe reopen via the underlying bubbling `kai-card` event.) |
553
+
554
+ **Methods** (call on the element instance: `document.querySelector('kai-cards').resolve(…)`):
555
+
556
+ | Method | Signature | Description |
557
+ |---|---|---|
558
+ | `resolve` | `(cardId: string, resolution: CardResolution): void` | 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. |
559
+ | `dismiss` | `(cardId: string): void` | Collapse a card to its re-openable stub from the host side. Convenience for `resolve(cardId, { kind: 'dismissed' })`. |
560
+ | `getCard` | `(cardId: string): HTMLElement \| null` | Return the live child element node for a card id (or null) so consumers can call that card's own methods (focus/expand/…) without a shadow-DOM query. |
436
561
 
437
562
  ---
438
563
 
@@ -442,16 +567,31 @@ _No events._
442
567
 
443
568
  | Property | Attribute | Type | Description |
444
569
  |---|---|---|---|
445
- | `steps` | | `{ label: string; content?: undefined \| string; id?: undefined \| string }[]` | The reasoning steps. Set as a JS property. Compound sub-parts collapse to this one data model (Route 1). Each `{ label, content?, id? }`. |
446
- | `type` | `type` | `undefined \| "single" \| "multiple"` | Open mode: `'multiple'` (default any number of steps open at once) or `'single'` (at most one open; opening a step closes the others). |
570
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
571
+ | `steps` | | `undefined \| { label: string; content?: undefined \| string; id?: undefined \| string }[]` | The reasoning steps. Set as a JS property. Compound sub-parts collapse to this one data model (Route 1). Each `{ label, content?, id? }`. Omit to supply the steps as `<kai-step>` light-DOM children instead; when both are present the property's steps come first. |
572
+ | `type` | `type` | `undefined \| "single" \| "multiple"` | Open mode: `'multiple'` (default, any number of steps open at once) or `'single'` (at most one open; opening a step closes the others). |
447
573
  | `value` | — | `undefined \| string \| string[]` | Controlled open step key(s). When set, it WINS over user interaction (the consumer owns the open set). String in `single` mode, string[] in `multiple` mode. Set as a JS property. |
448
- | `defaultValue` | — | `undefined \| string \| string[]` | Uncontrolled INITIAL open step key(s) seeds which steps render expanded. Ignored once `value` is provided. Set as a JS property. |
574
+ | `defaultValue` | — | `undefined \| string \| string[]` | Uncontrolled INITIAL open step key(s), seeding which steps render expanded. Ignored once `value` is provided. Set as a JS property. |
449
575
 
450
576
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
451
577
 
452
578
  | Event | `detail` type | Description |
453
579
  |---|---|---|
454
- | `kai-value-change` | `CustomEvent<{ value: string \| string[] }>` | The open set changed by user click OR an expand()/collapse()/toggle() call. `value` is a string in `single` mode, a string[] in `multiple` mode. (Maps Radix Accordion's onValueChange.) |
580
+ | `kai-value-change` | `CustomEvent<{ value: string \| string[] }>` | The open set changed, by user click OR an expand()/collapse()/toggle() call. `value` is a string in `single` mode, a string[] in `multiple` mode. (Maps Radix Accordion's onValueChange.) |
581
+
582
+ **Methods** (call on the element instance: `document.querySelector('kai-chain-of-thought').expand(…)`):
583
+
584
+ | Method | Signature | Description |
585
+ |---|---|---|
586
+ | `expand` | `(index?: number): void` | Open one step's detail by index, or ALL steps when called with no arg. In `single` mode opening one step closes the others (expand-all keeps the last). |
587
+ | `collapse` | `(index?: number): void` | Close one step's detail by index, or ALL steps when called with no arg. |
588
+ | `toggle` | `(index?: number): void` | Flip one step's open state by index. |
589
+
590
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
591
+
592
+ | Child element | Attributes | Text content | Notes |
593
+ |---|---|---|---|
594
+ | `<kai-step>` | `label`, `step-id` | yes | Parse a single light-DOM `<kai-step>` element into a `Step` descriptor. Attribute mapping: - `label` → Step.label (the always-visible heading) - `step-id` → Step.id (optional stable open-set key) - textContent → Step.content (optional expandable detail) |
455
595
 
456
596
  ---
457
597
 
@@ -461,14 +601,15 @@ _No events._
461
601
 
462
602
  | Property | Attribute | Type | Description |
463
603
  |---|---|---|---|
464
- | `messages` | | `{ id: string; role: "user" \| "assistant"; content: string; reasoning?: undefined \| { text: string; label?: undefined \| string }; tools?: undefined \| { type: string; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string }[]; attachments?: undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]; 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" }[]` | The full message thread to render, newest last. Each entry carries its role, content, and optional reasoning/tools/attachments/actions. Set as a JS property (`el.messages = [...]`). |
604
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
605
+ | `search` | `search` | `undefined \| false \| true` | Show a Search (Globe) button in the input toolbar; fires a `search` event. |
465
606
  | `value` | — | `undefined \| string \| ({ type: "text"; text: string } \| { type: "entity"; entity: { kind: string; id: string; label: string; icon?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> } })[]` | Value of the input. A **string** is controlled (the host owns the text and updates it on `kai-value-change`). A **ComposerDoc** is a one-time seed that pre-populates pills; the user then edits freely. Leave unset for uncontrolled. |
466
607
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown in the empty input. |
467
608
  | `loading` | `loading` | `undefined \| false \| true` | When true, shows the loading/streaming state and disables submit (use while awaiting the assistant's reply). |
468
609
  | `suggestions` | — | `undefined \| string[]` | Starter prompts shown above the input when the thread is empty. Clicking one follows `suggestionMode`. Set as a JS property. |
469
610
  | `suggestionMode` | `suggestion-mode` | `undefined \| "submit" \| "fill"` | What clicking a suggestion does: `'submit'` (default) sends it immediately as if typed and submitted; `'fill'` just places it in the input. |
470
611
  | `persistSuggestions` | `persist-suggestions` | `undefined \| false \| true` | Keep suggestions visible after the conversation starts. By default suggestions are conversation starters and hide once `messages` is non-empty; set this to keep them always shown. Default false. |
471
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. |
612
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. |
472
613
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name for syntax-highlighted code blocks (e.g. `'github-dark-dimmed'`). |
473
614
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Enable Shiki syntax highlighting in code blocks. Turn off to render plain `<pre>` blocks (lighter, no highlighter load). Default true. |
474
615
  | `chatTitle` | `chat-title` | `undefined \| string` | Optional header title shown on the left of the header. |
@@ -476,25 +617,29 @@ _No events._
476
617
  | `currentModel` | `current-model` | `undefined \| string` | The currently selected model id (pairs with `models`). |
477
618
  | `context` | — | `undefined \| { usedTokens: number; maxTokens: number; inputTokens?: undefined \| number; outputTokens?: undefined \| number; estimatedCost?: undefined \| number }` | Optional context-window token usage. When set, a Context token meter is shown in the header. |
478
619
  | `scrollButton` | `scroll-button` | `undefined \| false \| true` | Show the scroll-to-bottom button inside the scroll area. Default true. |
479
- | `headerStart` | `header-start` | `undefined \| false \| true` | Whether the host has `slot="header-start"` content (left of the title) set by the `<kai-chat>` facade so a custom control forces the header open. |
620
+ | `headerStart` | `header-start` | `undefined \| false \| true` | Whether the host has `slot="header-start"` content (left of the title). Set by the `<kai-chat>` facade so a custom control forces the header open. |
480
621
  | `headerEnd` | `header-end` | `undefined \| false \| true` | Whether the host has `slot="header-end"` content (right of the controls). |
481
- | `headerFull` | `header-full` | `undefined \| false \| true` | REPLACE full custom header in place of the built-in title/model/context bar. |
482
- | `sidebar` | `sidebar` | `undefined \| false \| true` | INJECT left sidebar column (e.g. a conversation list / your own nav). |
483
- | `empty` | `empty` | `undefined \| false \| true` | REPLACE custom zero-state rendered in the message area while the thread is empty (replaces the empty message list only; the composer and its suggestions still render). |
484
- | `composer` | `composer` | `undefined \| false \| true` | REPLACE full custom composer in place of the built-in prompt input. The projected content wires its own submit (the data-flow boundary). |
485
- | `composerActions` | `composer-actions` | `undefined \| false \| true` | INJECT accessory row just above the composer (e.g. extra actions). |
486
- | `footer` | `footer` | `undefined \| false \| true` | INJECT footer row below the composer (disclaimers, token meter, …). |
487
- | `search` | `search` | `undefined \| false \| true` | Show a Search (Globe) button in the input toolbar; fires a `search` event. |
622
+ | `headerFull` | `header-full` | `undefined \| false \| true` | REPLACE: full custom header in place of the built-in title/model/context bar. |
623
+ | `sidebar` | `sidebar` | `undefined \| false \| true` | INJECT: left sidebar column (e.g. a conversation list / your own nav). |
624
+ | `empty` | `empty` | `undefined \| false \| true` | REPLACE: custom zero-state rendered in the message area while the thread is empty (replaces the empty message list only; the composer and its suggestions still render). |
625
+ | `composer` | `composer` | `undefined \| false \| true` | REPLACE: full custom composer in place of the built-in prompt input. The projected content wires its own submit (the data-flow boundary). |
626
+ | `composerActions` | `composer-actions` | `undefined \| false \| true` | INJECT: accessory row just above the composer (e.g. extra actions). |
627
+ | `footer` | `footer` | `undefined \| false \| true` | INJECT: footer row below the composer (disclaimers, token meter, …). |
488
628
  | `voice` | `voice` | `undefined \| false \| true` | Show a Voice (Mic) button in the input toolbar; fires a `voice` event. |
489
- | `triggers` | — | `undefined \| { char: string; kind: string; items?: undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; group?: undefined \| string; kind?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> }[] }[]` | Rich entity triggers each `{ char, kind, items }` opens a caret-anchored menu that inserts an atomic pill (`/` skills, `@` agents/plugins). Set as a JS property; forwarded to the input. |
629
+ | `triggers` | — | `undefined \| { char: string; kind: string; items?: undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; group?: undefined \| string; kind?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> }[] }[]` | Rich entity triggers. Each `{ char, kind, items }` opens a caret-anchored menu that inserts an atomic pill (`/` skills, `@` agents/plugins). Set as a JS property; forwarded to the input. |
490
630
  | `kindIcons` | — | `undefined \| Record<string, string>` | Default icon per entity kind (kind → image src) for pills/menu items. |
491
631
  | `actionsReveal` | `actions-reveal` | `undefined \| "always" \| "hover"` | Whether each message's action bar is always visible (`'always'`, default) or only revealed on hover of that message row (`'hover'`). |
632
+ | `accept` | `accept` | `undefined \| string` | Which attachment media types the user may stage, in HTML `accept` syntax: `<kai-chat accept="image/*,application/pdf">`. A plain string, so unlike `messages` it DOES work as an attribute. Omitted means no filter. MEDIA TYPES ONLY -- exact (`image/png`) or subtype wildcard (`text/*`). HTML allows a file extension here and this does not: `accept=".py"` THROWS with the entry named, rather than silently resolving to a picker that accepts nothing. It can only NARROW what the kit can already encode: `accept="image/*"` resolves to the four image formats both APIs take, not to every image type the OS offers. Pass the SAME string to `toOpenAIMessages(msgs, { accept })` and the picker and the wire cannot disagree -- both resolve it through `resolveMediaPolicy` against one declaration. That declaration is readable as `encodableMediaTypes()` from `@kitn.ai/ui/wire`, if you would rather build your own picker than use this prop. |
633
+ | `messages` | — | `undefined \| { 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" }[]` | The full message thread to render, newest last. Each entry carries its role, ordered `parts`, and optional actions/avatar/feedback. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). Omit for an empty thread. Re-declared here (rather than inherited from `ChatThreadProps`) because the ELEMENT registers a `[]` default and renders the empty state without it, while the SolidJS `<ChatThread>` component still requires it. The facade hands it a validated array either way. Matches `<kai-thread>`. |
634
+ | `cardTypes` | — | `undefined \| Record<string, string>` | Optional card type -> custom-element tag overrides/additions for `card` parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. |
635
+ | `cardSchemas` | — | `undefined \| Record<string, object>` | 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. |
492
636
 
493
637
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
494
638
 
495
639
  | Event | `detail` type | Description |
496
640
  |---|---|---|
497
641
  | `kai-attachments-change` | `CustomEvent<{ attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | The staged attachments changed (file added or removed). Carries the full current list so a consumer can react in real time. |
642
+ | `kai-attachments-rejected` | `CustomEvent<{ rejected: { filename: string; mediaType: string; reason: "filtered" \| "unsupported" }[] }>` | 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. |
498
643
  | `kai-message-action` | `CustomEvent<{ messageId: string; action: string; state?: undefined \| "on" \| "off" }>` | 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. |
499
644
  | `kai-model-change` | `CustomEvent<{ modelId: string }>` | The header model switcher changed. |
500
645
  | `kai-search` | `CustomEvent<Record<string, never>>` | The Search button was clicked. |
@@ -503,6 +648,16 @@ _No events._
503
648
  | `kai-value-change` | `CustomEvent<{ value: string }>` | Fired on every input change. |
504
649
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Mic / voice button was clicked. |
505
650
 
651
+ **Methods** (call on the element instance: `document.querySelector('kai-chat').focus(…)`):
652
+
653
+ | Method | Signature | Description |
654
+ |---|---|---|
655
+ | `focus` | `(options?: FocusOptions): void` | 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. |
656
+ | `blur` | `(): void` | 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. |
657
+ | `clear` | `(): void` | Empty the COMPOSER: drops the draft text and every staged attachment, then fires `kai-value-change` with `''`. It does NOT touch the thread. `messages` is the consumer's own state, so clearing history stays the consumer's call. |
658
+ | `send` | `(): void` | Submit whatever the composer currently holds, on the same path as Enter or the send button: fires `kai-submit` with that value plus the staged attachments, then drops the attachments. It takes no argument, so to send text the user never typed, set `el.value` first. There is no empty-check, so an empty composer still fires. The draft is cleared afterwards only when `value` is uncontrolled; a controlled host owns its value and clears it itself. Named `send`, not `submit`, to match the shared vocabulary. |
659
+ | `scrollToBottom` | `(behavior?: ScrollBehavior): void` | Scroll the message viewport to the newest message. Defaults to `'smooth'`; pass `'instant'` to jump without animating. |
660
+
506
661
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
507
662
 
508
663
  | Slot | Mode | Description |
@@ -511,7 +666,7 @@ _No events._
511
666
  | `header-end` | inject | Trailing header controls. |
512
667
  | `header` | replace | Full custom header; replaces the built-in title/model/context bar. |
513
668
  | `sidebar` | inject | Left column (your nav / conversation list). Fixed width; use compose-your-own for resizable. |
514
- | `empty` | replace | Custom zero-state rendered in the message area while the thread is empty. Replaces the empty message list only the composer and any suggestions still render. |
669
+ | `empty` | replace | Custom zero-state rendered in the message area while the thread is empty. Replaces the empty message list only; the composer and any suggestions still render. |
515
670
  | `composer` | replace | Full custom composer; you own submit + loading, drive the thread via messages. |
516
671
  | `composer-actions` | inject | Accessory row above the composer. |
517
672
  | `footer` | inject | Row below the composer (disclaimers, token meter). |
@@ -533,9 +688,10 @@ _No events._
533
688
 
534
689
  | Property | Attribute | Type | Description |
535
690
  |---|---|---|---|
691
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
536
692
  | `label` | `label` | `undefined \| string` | Optional text beside the icon. |
537
693
  | `tooltip` | `tooltip` | `undefined \| string` | Tooltip on hover. |
538
- | `variant` | `variant` | `undefined \| "default" \| "ghost" \| "outline"` | Visual button style. |
694
+ | `variant` | `variant` | `undefined \| "ghost" \| "default" \| "outline"` | Visual button style. |
539
695
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | Button size (use an `icon*` size for an icon-only checkpoint). |
540
696
 
541
697
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -552,11 +708,12 @@ _No events._
552
708
 
553
709
  | Property | Attribute | Type | Description |
554
710
  |---|---|---|---|
555
- | `data` | | `undefined \| Record<string, unknown>` | The choice definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { prompt, options:[…], allowOther?, submitLabel? }`. Import `ChoiceCardData` from `@kitn.ai/ui` for the full shape. |
711
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
712
+ | `data` | — | `undefined \| { prompt?: undefined \| string; options: { id: string; label: string; description?: undefined \| string; media?: undefined \| { image?: undefined \| string; imageAlt?: undefined \| string; icon?: undefined \| string }; meta?: undefined \| string; recommended?: undefined \| false \| true; disabled?: undefined \| false \| true; payload?: unknown }[]; allowOther?: undefined \| false \| true \| { label?: undefined \| string; placeholder?: undefined \| string }; submitLabel?: undefined \| string; dismissible?: undefined \| false \| true }` | The choice definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { prompt, options:[…], allowOther?, submitLabel? }`. Import `ChoiceCardData` from `@kitn.ai/ui` for the full shape. |
556
713
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
557
714
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
558
715
  | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'action', action:'…' }`. |
559
- | `value` | `value` | `undefined \| string` | Controlled selection the selected option id. When set, the consumer owns the current pick (RadioGroup `value`). Attribute: `value`. |
716
+ | `value` | `value` | `undefined \| string` | Controlled selection: the selected option id. When set, the consumer owns the current pick (RadioGroup `value`). Attribute: `value`. |
560
717
  | `defaultValue` | `default-value` | `undefined \| string` | Option id to pre-select on mount (uncontrolled seed). Attribute: `default-value`. |
561
718
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the whole radiogroup + Submit (e.g. while the agent is busy). Attribute: `disabled`. |
562
719
 
@@ -566,6 +723,16 @@ _No events._
566
723
  |---|---|---|
567
724
  | `kai-value-change` | `CustomEvent<{ value: string }>` | The selection changed BEFORE submit (a row click or the `select()` method). Distinct from the terminal `action` verb on the `kai-card` contract event. |
568
725
 
726
+ **Methods** (call on the element instance: `document.querySelector('kai-choice').focus(…)`):
727
+
728
+ | Method | Signature | Description |
729
+ |---|---|---|
730
+ | `focus` | `(options?: FocusOptions): void` | Focus the radiogroup roving tab stop (or the Other input when selected). |
731
+ | `select` | `(optionId: string): void` | Select an option by id locally: no emit, fires kai-value-change (same as a row click). Lets a consumer pre-highlight or drive selection externally. |
732
+ | `send` | `(): void` | Submit the current selection: emits the `action` verb on kai-card and resolves the card (single-shot). Named `send`, not `submit`, per the shared vocabulary. |
733
+ | `dismiss` | `(): void` | Trigger the dismiss path: emits `dismiss` on kai-card and optimistically collapses the card to its re-openable stub. |
734
+ | `reopen` | `(): void` | Re-open a dismissed card from its stub: emits `reopen` on kai-card. |
735
+
569
736
  ---
570
737
 
571
738
  ### `kai-coachmark` / `Coachmark`
@@ -574,12 +741,13 @@ _No events._
574
741
 
575
742
  | Property | Attribute | Type | Description |
576
743
  |---|---|---|---|
744
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
577
745
  | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute; the element still self-manages). Set `el.open = true`, or `<kai-coachmark open>`; listen for `kai-open-change`. |
578
746
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
579
747
  | `headline` | `headline` | `undefined \| string` | The bold title. Named `headline` because `title` collides with the global `HTMLElement.title` attribute (it throws at registration). |
580
748
  | `badge` | `badge` | `undefined \| string` | A small badge pill beside the headline (e.g. "New"). |
581
749
  | `placement` | `placement` | `undefined \| string` | Floating placement relative to the anchor (default `bottom`). |
582
- | `tone` | `tone` | `undefined \| "error" \| "primary" \| "info" \| "success" \| "warning"` | Color tone: `primary` (default, theme accent), `info` (blue), `success` (green), `warning` (amber), or `error` (red) reusing the kit's tool hues. |
750
+ | `tone` | `tone` | `undefined \| "primary" \| "info" \| "success" \| "warning" \| "error"` | Color tone: `primary` (default, theme accent), `info` (blue), `success` (green), `warning` (amber), or `error` (red), reusing the kit's tool hues. |
583
751
  | `arrow` | `arrow` | `undefined \| false \| true` | Render the arrow that points at the anchor (default `true`). Set `arrow="false"` for a plain bubble with no pointer. |
584
752
 
585
753
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -589,10 +757,19 @@ _No events._
589
757
  | `kai-dismiss` | `CustomEvent<Record<string, never>>` | The × dismiss button was pressed. The consumer records that this hint was seen so it won't show again. |
590
758
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The coachmark opened or closed (a method, the ×, or a driven `open`). |
591
759
 
760
+ **Methods** (call on the element instance: `document.querySelector('kai-coachmark').show()`):
761
+
762
+ | Method | Signature | Description |
763
+ |---|---|---|
764
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
765
+ | `hide` | `(): void` | Close it programmatically. |
766
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
767
+
592
768
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
593
769
 
594
770
  | Slot | Mode | Description |
595
771
  |---|---|---|
772
+ | _(default)_ | inject | The ANCHOR the coachmark points at: the element it attaches to and positions against. The bubble body is the `content` slot. |
596
773
  | `content` | replace | The bubble body text shown under the headline. |
597
774
 
598
775
  **Styleable parts** (restyle from outside via `kai-coachmark::part(name)`):
@@ -613,11 +790,12 @@ _No events._
613
790
 
614
791
  | Property | Attribute | Type | Description |
615
792
  |---|---|---|---|
793
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
616
794
  | `code` | `code` | `string` | The source code to render. |
617
795
  | `language` | `language` | `undefined \| string` | Language grammar (e.g. `js`, `python`). Defaults to `tsx`. |
618
796
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name. |
619
797
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting (renders plain text, no Shiki). |
620
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Code text sizing. |
798
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Code text sizing. |
621
799
 
622
800
  _No events._
623
801
 
@@ -629,7 +807,8 @@ _No events._
629
807
 
630
808
  | Property | Attribute | Type | Description |
631
809
  |---|---|---|---|
632
- | `items` | | `undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; shortcut?: undefined \| string; group?: undefined \| string }[]` | Flat list of items. Set as a JS property — not an HTML attribute. |
810
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
811
+ | `items` | — | `undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; shortcut?: undefined \| string; group?: undefined \| string }[]` | Flat list of items. Set as a JS property, not an HTML attribute. |
633
812
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text for the search input. |
634
813
  | `emptyLabel` | `empty-label` | `undefined \| string` | Label shown when no items match the current query. |
635
814
 
@@ -637,10 +816,18 @@ _No events._
637
816
 
638
817
  | Event | `detail` type | Description |
639
818
  |---|---|---|
640
- | `kai-active-change` | `CustomEvent<{ id: undefined \| string }>` | Fired when the highlighted/active item changes via Arrow keys or when filtering re-clamps the active row. `id` is the newly active item's id, or `undefined` when no item is active (e.g. the filtered list is empty). Lets a host preview the active item without committing a selection. |
819
+ | `kai-active-change` | `CustomEvent<{ id: string \| undefined }>` | Fired when the highlighted/active item changes, via Arrow keys or when filtering re-clamps the active row. `id` is the newly active item's id, or `undefined` when no item is active (e.g. the filtered list is empty). Lets a host preview the active item without committing a selection. |
641
820
  | `kai-query-change` | `CustomEvent<{ value: string }>` | Fired on every keystroke in the search input. |
642
821
  | `kai-select` | `CustomEvent<{ id: string }>` | Fired when the user selects an item (click or Enter). |
643
822
 
823
+ **Methods** (call on the element instance: `document.querySelector('kai-command').focus(…)`):
824
+
825
+ | Method | Signature | Description |
826
+ |---|---|---|
827
+ | `focus` | `(options?: FocusOptions): void` | Focus the search combobox input inside the shadow root so the palette is type-ready on demand (Shadow-DOM autofocus is unreliable, so hosts call this after opening the palette). |
828
+ | `blur` | `(): void` | Blur the focused search input. |
829
+ | `clear` | `(): void` | Reset the search query to empty, re-showing all items, and fire `kai-query-change` with `''`. Mirrors the Escape-key behavior. |
830
+
644
831
  **Styleable parts** (restyle from outside via `kai-command::part(name)`):
645
832
 
646
833
  | Part | Description |
@@ -655,11 +842,12 @@ _No events._
655
842
 
656
843
  | Property | Attribute | Type | Description |
657
844
  |---|---|---|---|
845
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
658
846
  | `data` | — | `undefined \| Record<string, unknown>` | The compare definition (prompt + the two candidates). Set as a JS PROPERTY: `el.data = { prompt, candidates: [A, B], collapse? }`. Import `ResponseCompareData` from `@kitn.ai/ui` for the full shape. |
659
847
  | `compareId` | `compare-id` | `undefined \| string` | Stable id correlating every emitted event. Attribute: `compare-id`. |
660
848
  | `selection` | — | `undefined \| Record<string, unknown>` | Re-hydrate / control the user's pick. Set as a JS PROPERTY: `el.selection = { chosenId, rejectedIds }`. Renders the collapsed winner. |
661
- | `layout` | `layout` | `undefined \| "auto" \| "columns" \| "tabs"` | Layout: `'auto'` (default columns when wide, tabs when narrow, by CONTAINER width) \| `'columns'` (side-by-side) \| `'tabs'` (pills to switch). Attribute: `layout`. |
662
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Prose/text size for the rendered candidates. Attribute: `prose-size`. |
849
+ | `layout` | `layout` | `undefined \| "auto" \| "columns" \| "tabs"` | Layout: `'auto'` (default, columns when wide, tabs when narrow, by CONTAINER width) \| `'columns'` (side-by-side) \| `'tabs'` (pills to switch). Attribute: `layout`. |
850
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Prose/text size for the rendered candidates. Attribute: `prose-size`. |
663
851
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme for code blocks in the candidates. Attribute: `code-theme`. |
664
852
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Whether code blocks are syntax-highlighted. Attribute: `code-highlight`. |
665
853
 
@@ -671,6 +859,13 @@ _No events._
671
859
  | `kai-error` | `CustomEvent<{ compareId: string; message: string }>` | The definition was unusable. |
672
860
  | `kai-ready` | `CustomEvent<{ compareId: string }>` | Both candidates have settled and the pick is live. |
673
861
 
862
+ **Methods** (call on the element instance: `document.querySelector('kai-compare').select(…)`):
863
+
864
+ | Method | Signature | Description |
865
+ |---|---|---|
866
+ | `select` | `(candidateId: string): void` | Programmatically commit a pick by candidate id, on the same path as the "Pick this" button: fires kai-compare-select and optimistically collapses (single-shot; inert while streaming or already resolved). `select` does NOT collide with the `selection` prop (distinct identifier). |
867
+ | `focus` | `(options?: FocusOptions): void` | Focus the current roving tab stop (the focused candidate's "Pick this" radio) so a consumer can move keyboard focus into the radiogroup. |
868
+
674
869
  ---
675
870
 
676
871
  ### `kai-composer` / `Composer`
@@ -679,14 +874,15 @@ _No events._
679
874
 
680
875
  | Property | Attribute | Type | Description |
681
876
  |---|---|---|---|
682
- | `value` | | `undefined \| string \| ({ type: "text"; text: string } \| { type: "entity"; entity: { kind: string; id: string; label: string; icon?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> } })[]` | Controlled value — string or a full ComposerDoc (set as JS property). |
877
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
878
+ | `value` | — | `undefined \| string \| ({ type: "text"; text: string } \| { type: "entity"; entity: { kind: string; id: string; label: string; icon?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> } })[]` | Controlled value: a string or a full ComposerDoc (set as JS property). |
683
879
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown when the composer is empty. |
684
880
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the composer entirely (non-interactive). |
685
881
  | `loading` | `loading` | `undefined \| false \| true` | Show a loading/streaming state and block submit. |
686
- | `maxHeight` | `max-height` | `undefined \| string \| number` | Maximum height in px before the content scrolls. Default 240. |
882
+ | `maxHeight` | `max-height` | `undefined \| number \| string` | Maximum height in px before the content scrolls. Default 240. |
687
883
  | `submitOnEnter` | `submit-on-enter` | `undefined \| false \| true` | Whether pressing Enter (without Shift) submits. Default true. |
688
- | `triggers` | — | `undefined \| { char: string; kind: string; items?: undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; group?: undefined \| string; kind?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> }[] }[]` | Trigger definitions set as a JS property. |
689
- | `highlights` | — | `undefined \| (string \| { pattern: string; flags?: undefined \| string; class?: undefined \| string })[]` | Keyword highlight rules set as a JS property. |
884
+ | `triggers` | — | `undefined \| { char: string; kind: string; items?: undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; group?: undefined \| string; kind?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> }[] }[]` | Trigger definitions. Set as a JS property. |
885
+ | `highlights` | — | `undefined \| (string \| { pattern: string; flags?: undefined \| string; class?: undefined \| string })[]` | Keyword highlight rules. Set as a JS property. |
690
886
  | `kindIcons` | — | `undefined \| Record<string, string>` | Default icon per entity kind (kind → image URL/data-URI) for items without their own `icon`. Overrides the built-in agent/plugin glyphs. JS property. |
691
887
 
692
888
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -696,12 +892,22 @@ _No events._
696
892
  | `kai-blur` | `CustomEvent<{ originalEvent: FocusEvent }>` | The composer lost focus. |
697
893
  | `kai-entity-add` | `CustomEvent<{ entity: { kind: string; id: string; label: string; icon?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> } }>` | An entity pill was inserted into the composer. |
698
894
  | `kai-entity-remove` | `CustomEvent<{ entity: { kind: string; id: string; label: string; icon?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> } }>` | An entity pill was deleted from the composer. |
699
- | `kai-focus` | `CustomEvent<{ originalEvent: FocusEvent }>` | 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.) |
895
+ | `kai-focus` | `CustomEvent<{ originalEvent: FocusEvent }>` | 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.) |
700
896
  | `kai-submit` | `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> }[] }>` | The user submitted the composer (Enter or programmatic submit). |
701
897
  | `kai-trigger` | `CustomEvent<{ char: string; query: string; rect: DOMRect }>` | A trigger character was detected at the caret (e.g. `/` or `@`). |
702
898
  | `kai-trigger-close` | `CustomEvent<Record<string, never>>` | The active trigger was dismissed (Escape, space, or outside click). |
703
899
  | `kai-value-change` | `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> }[] }>` | The content changed (fires on every input event). |
704
900
 
901
+ **Methods** (call on the element instance: `document.querySelector('kai-composer').focus(…)`):
902
+
903
+ | Method | Signature | Description |
904
+ |---|---|---|
905
+ | `focus` | `(options?: FocusOptions): void` | Focus the editable element. `focus`/`blur` are NOT composed natively, so a host-level focus() can't reach the editable inside the shadow root. This is the only way to focus the composer programmatically. |
906
+ | `blur` | `(): void` | Blur the editable element. |
907
+ | `clear` | `(): void` | Empty the composer to a blank doc (resets the internal value + history baseline; fires kai-value-change). |
908
+ | `send` | `(): void` | Submit the current content programmatically, on the same path as Enter (fires kai-submit). Named `send`, not `submit`, to match the shared vocabulary and avoid any submit collision. |
909
+ | `insertEntity` | `(entity: EntityRef): void` | Insert an atomic entity pill (skill/agent/plugin) at the caret without typing a trigger (fires kai-entity-add). |
910
+
705
911
  ---
706
912
 
707
913
  ### `kai-confirm` / `Confirm`
@@ -710,14 +916,24 @@ _No events._
710
916
 
711
917
  | Property | Attribute | Type | Description |
712
918
  |---|---|---|---|
713
- | `data` | | `undefined \| Record<string, unknown>` | The confirm definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { body, tone, actions:[…] }`. Import `ConfirmCardData` from `@kitn.ai/ui` for the full shape. |
919
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
920
+ | `data` | — | `undefined \| { heading?: undefined \| string; body?: undefined \| string; tone?: undefined \| "default" \| "warning" \| "danger"; actions: { id: string; label: string; style?: undefined \| "primary" \| "default" \| "destructive"; payload?: unknown; default?: undefined \| false \| true }[]; dismissible?: undefined \| false \| true }` | The confirm definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { body, tone, actions:[…] }`. Import `ConfirmCardData` from `@kitn.ai/ui` for the full shape. |
714
921
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
715
922
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
716
- | `autofocus` | `autofocus` | `undefined \| false \| true` | Focus the default action on mount (off by default no focus-stealing). Attribute: `autofocus`. |
923
+ | `autofocus` | `autofocus` | `undefined \| false \| true` | Focus the default action on mount (off by default, so nothing steals focus). Attribute: `autofocus`. |
717
924
  | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'action', action:'…' }`. |
718
925
 
719
926
  _No events._
720
927
 
928
+ **Methods** (call on the element instance: `document.querySelector('kai-confirm').focus(…)`):
929
+
930
+ | Method | Signature | Description |
931
+ |---|---|---|
932
+ | `focus` | `(options?: FocusOptions): void` | Focus the default action button (or the first action if none is default). The same target `autofocus` focuses on mount, but on demand. |
933
+ | `confirm` | `(actionId?: string): void` | Activate an action by id: emits the `action` verb on kai-card and resolves the card (single-shot). With no id, invokes the default action. |
934
+ | `dismiss` | `(): void` | Trigger the dismiss path: emits `dismiss` on kai-card and optimistically collapses the card to its re-openable stub. |
935
+ | `reopen` | `(): void` | Re-open a dismissed card from its stub: emits `reopen` on kai-card. |
936
+
721
937
  ---
722
938
 
723
939
  ### `kai-context` / `Context`
@@ -726,6 +942,7 @@ _No events._
726
942
 
727
943
  | Property | Attribute | Type | Description |
728
944
  |---|---|---|---|
945
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
729
946
  | `context` | — | `undefined \| { usedTokens: number; maxTokens: number; inputTokens?: undefined \| number; outputTokens?: undefined \| number; reasoningTokens?: undefined \| number; cacheTokens?: undefined \| number; estimatedCost?: undefined \| number }` | Token-usage data. Set as a JS property. |
730
947
  | `warnThreshold` | `warn-threshold` | `undefined \| number` | Fraction (0–1) above which the meter turns yellow. Defaults to `0.7` (70%). |
731
948
  | `dangerThreshold` | `danger-threshold` | `undefined \| number` | Fraction (0–1) above which the meter turns red. Defaults to `0.9` (90%). |
@@ -736,6 +953,13 @@ _No events._
736
953
  |---|---|---|
737
954
  | `kai-threshold-change` | `CustomEvent<{ level: "ok" \| "warn" \| "danger" }>` | Fires when the computed severity level changes (ok → warn → danger or back). `detail.level` is `'ok'`, `'warn'`, or `'danger'`. |
738
955
 
956
+ **Styleable parts** (restyle from outside via `kai-context::part(name)`):
957
+
958
+ | Part | Description |
959
+ |---|---|
960
+ | `::part(track)` | The usage meter track inside the hover-card breakdown. Carries `role="progressbar"` and is in the DOM only while the card is open. Restyle its height, radius, or background from outside. — `kai-context::part(track) { height: 0.5rem }` |
961
+ | `::part(fill)` | The used-tokens portion of that meter. Its width follows `usedTokens / maxTokens`; its default color is the severity hue picked by `warnThreshold` / `dangerThreshold`, so recoloring it from outside replaces that signal. — `kai-context::part(fill) { background: var(--color-tool-blue) }` |
962
+
739
963
  ---
740
964
 
741
965
  ### `kai-conversations` / `Conversations`
@@ -744,8 +968,9 @@ _No events._
744
968
 
745
969
  | Property | Attribute | Type | Description |
746
970
  |---|---|---|---|
747
- | `groups` | | `{ id: string; userId?: undefined \| string; teamId?: undefined \| string; name: string; sortOrder: number; createdAt: string }[]` | Pre-bucketed conversation groups (e.g. "Today", "Yesterday"), each with its own conversations. Use this when you want to control the grouping/headers yourself; otherwise pass a flat `conversations` array. Set as a JS property. |
748
- | `conversations` | — | `{ id: string; title: string; groupId?: undefined \| string; scope: { type: "document" \| "collection"; documentId?: undefined \| string; filters?: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }; messageCount: number; lastMessageAt: string; updatedAt: string; trailing?: undefined \| string }[]` | A flat list of conversation summaries; the component buckets them by recency for you. Ignored when `groups` is provided. Set as a JS property. |
971
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
972
+ | `groups` | — | `undefined \| { id: string; userId?: undefined \| string; teamId?: undefined \| string; name: string; sortOrder: number; createdAt: string }[]` | 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. |
973
+ | `conversations` | — | `undefined \| { id: string; title: string; groupId?: undefined \| string; scope: { type: "document" \| "collection"; documentId?: undefined \| string; filters?: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }; messageCount: number; lastMessageAt: string; updatedAt: string; trailing?: undefined \| string }[]` | 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. |
749
974
  | `activeId` | `active-id` | `undefined \| string` | The id of the currently-open conversation, highlighted in the list. |
750
975
  | `collapsed` | `collapsed` | `undefined \| false \| true` | 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. |
751
976
  | `defaultCollapsed` | `default-collapsed` | `undefined \| false \| true` | Initial collapsed state when uncontrolled (default false). Use the `default-collapsed` attribute to start collapsed in plain HTML. |
@@ -760,13 +985,30 @@ _No events._
760
985
  | `kai-search` | `CustomEvent<{ query: string }>` | The built-in search box query changed (typing, or a programmatic `clear()` which fires it with `''`). Lets a consumer mirror or server-side the filter. |
761
986
  | `kai-toggle-sidebar` | `CustomEvent<Record<string, never>>` | The sidebar toggle was clicked. |
762
987
 
988
+ **Methods** (call on the element instance: `document.querySelector('kai-conversations').focus(…)`):
989
+
990
+ | Method | Signature | Description |
991
+ |---|---|---|
992
+ | `focus` | `(options?: FocusOptions): void` | Focus the built-in search input inside the shadow root. |
993
+ | `clear` | `(): void` | Clear the internal search query (resets the list filter) and fire kai-search with an empty string. |
994
+ | `select` | `(id: string): void` | Programmatically select a conversation by id. The mirror of the kai-conversation-select event (a convenience over driving `activeId`). |
995
+ | `collapse` | `(): void` | Collapse the rail to its floating reopen button (fires `kai-collapse-toggle`). |
996
+ | `expand` | `(): void` | Expand the rail back to the full list (fires `kai-collapse-toggle`). |
997
+ | `toggle` | `(): void` | Toggle the rail collapsed/expanded (fires `kai-collapse-toggle`). |
998
+
763
999
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
764
1000
 
765
1001
  | Slot | Mode | Description |
766
1002
  |---|---|---|
767
1003
  | `header` | replace | Full custom title bar; replaces the built-in toggle / "Chats" / New-chat row. |
768
1004
  | `empty` | replace | Custom zero-state shown when there are no conversations; replaces the built-in "No conversations yet". |
769
- | `footer` | inject | A row below the list account, settings, or usage. |
1005
+ | `footer` | inject | A row below the list: account, settings, or usage. |
1006
+
1007
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
1008
+
1009
+ | Child element | Attributes | Text content | Notes |
1010
+ |---|---|---|---|
1011
+ | `<kai-conversation>` | `group-id`, `id` | yes | Parse a single light-DOM `<kai-conversation>` element into a `ConversationSummary`. Attribute mapping: - `id` → ConversationSummary.id - `group-id` → ConversationSummary.groupId (optional) - textContent → ConversationSummary.title Required fields not expressible as HTML attributes (`scope`, `messageCount`, `lastMessageAt`, `updatedAt`) receive safe defaults so the rendered list item is fully functional with just `id` + title text. |
770
1012
 
771
1013
  **Styleable parts** (restyle from outside via `kai-conversations::part(name)`):
772
1014
 
@@ -782,6 +1024,7 @@ _No events._
782
1024
 
783
1025
  | Property | Attribute | Type | Description |
784
1026
  |---|---|---|---|
1027
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
785
1028
  | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute; the element still self-manages on Escape/backdrop). Set `el.open = true`, or `<kai-dialog open>`; listen for `kai-open-change`. |
786
1029
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
787
1030
 
@@ -791,10 +1034,20 @@ _No events._
791
1034
  |---|---|---|
792
1035
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The dialog opened or closed (Escape, backdrop click, a driven `open`, or a method). |
793
1036
 
1037
+ **Methods** (call on the element instance: `document.querySelector('kai-dialog').show()`):
1038
+
1039
+ | Method | Signature | Description |
1040
+ |---|---|---|
1041
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1042
+ | `hide` | `(): void` | Close it programmatically. |
1043
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1044
+ | `focus` | `(options?: FocusOptions): void` | Move focus to the dialog panel (no-op while closed). |
1045
+
794
1046
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
795
1047
 
796
1048
  | Slot | Mode | Description |
797
1049
  |---|---|---|
1050
+ | _(default)_ | inject | The dialog body, between the `header` and `footer` slots. |
798
1051
  | `header` | inject | Optional title region at the top of the panel. |
799
1052
  | `footer` | inject | Optional actions region at the bottom of the panel. |
800
1053
 
@@ -816,7 +1069,8 @@ _No events._
816
1069
 
817
1070
  | Property | Attribute | Type | Description |
818
1071
  |---|---|---|---|
819
- | `value` | `value` | `undefined \| string` | The label text — settable and reflected to the `value` attribute. Read `el.value` for live state. |
1072
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1073
+ | `value` | `value` | `undefined \| string` | The label text. Settable and reflected to the `value` attribute. Read `el.value` for live state. |
820
1074
  | `editing` | `editing` | `undefined \| false \| true` | Controlled edit state. `el.editing = true` opens the field; reflected to the `editing` attribute. |
821
1075
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder shown while editing / when the value is empty. |
822
1076
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable entering edit mode. |
@@ -828,6 +1082,14 @@ _No events._
828
1082
  | `kai-cancel` | `CustomEvent<Record<string, never>>` | Edit was cancelled (Esc); the text is restored. |
829
1083
  | `kai-rename` | `CustomEvent<{ value: string }>` | Committed a changed value (Enter / blur). |
830
1084
 
1085
+ **Methods** (call on the element instance: `document.querySelector('kai-editable-label').edit()`):
1086
+
1087
+ | Method | Signature | Description |
1088
+ |---|---|---|
1089
+ | `edit` | `(): void` | Switch the label into its editing field, which autofocuses and selects the current text. Same entry point as a user double-click, and a no-op while `disabled`. Commit with `commit()` or by blurring, abandon with `cancel()` or Escape. |
1090
+ | `commit` | `(): void` | Close the field and keep what was typed, exactly as blurring it does. `kai-rename` fires only when the text actually changed, so committing an untouched field is silent. A no-op while the field is closed. |
1091
+ | `cancel` | `(): void` | Abandon the edit, exactly as Escape does: the original text is restored, the field closes and `kai-cancel` fires. `kai-rename` never fires, even if the field was edited. Also works when `editing` was set programmatically and the field has not rendered yet. |
1092
+
831
1093
  **Styleable parts** (restyle from outside via `kai-editable-label::part(name)`):
832
1094
 
833
1095
  | Part | Description |
@@ -843,6 +1105,7 @@ _No events._
843
1105
 
844
1106
  | Property | Attribute | Type | Description |
845
1107
  |---|---|---|---|
1108
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
846
1109
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted event. Set as an attribute or property. |
847
1110
  | `data` | — | `undefined \| { provider: "youtube" \| "vimeo" \| "generic"; id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; poster?: undefined \| string; start?: undefined \| number; aspectRatio?: undefined \| "16:9" \| "4:3" \| "1:1" \| "9:16" }` | The embed payload (provider + id/url + options). Set as a JS **property** (object). |
848
1111
 
@@ -856,11 +1119,19 @@ _No events._
856
1119
 
857
1120
  | Property | Attribute | Type | Description |
858
1121
  |---|---|---|---|
1122
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
859
1123
  | `emptyTitle` | `empty-title` | `undefined \| string` | Title text. Attribute: `empty-title` (`title` is a global HTML attribute). |
860
1124
  | `description` | `description` | `undefined \| string` | Description text. |
861
1125
 
862
1126
  _No events._
863
1127
 
1128
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1129
+
1130
+ | Slot | Mode | Description |
1131
+ |---|---|---|
1132
+ | _(default)_ | inject | The empty-state body below the title/description, usually the call to action. |
1133
+ | `media` | replace | The leading illustration or icon above the title (any inline SVG or <img>). Replaces the built-in media box. |
1134
+
864
1135
  ---
865
1136
 
866
1137
  ### `kai-feedback-bar` / `FeedbackBar`
@@ -869,7 +1140,8 @@ _No events._
869
1140
 
870
1141
  | Property | Attribute | Type | Description |
871
1142
  |---|---|---|---|
872
- | `barTitle` | `bar-title` | `undefined \| string` | The banner label (e.g. "Was this helpful?"). Attribute: `bar-title` (`title` is avoided — it's a global HTML attribute). |
1143
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1144
+ | `barTitle` | `bar-title` | `undefined \| string` | The banner label (e.g. "Was this helpful?"). Attribute: `bar-title` (`title` is avoided because it is a global HTML attribute). |
873
1145
  | `collectDetail` | `collect-detail` | `undefined \| false \| true` | When set, a not-helpful vote opens an optional detail form before the thank-you confirmation. Attribute: `collect-detail`. |
874
1146
  | `categories` | — | `undefined \| string[]` | Optional category chips for the detail form. Set as a JS property (array). |
875
1147
  | `detailTitle` | `detail-title` | `undefined \| string` | Heading for the detail form. Attribute: `detail-title`. |
@@ -893,8 +1165,9 @@ _No events._
893
1165
 
894
1166
  | Property | Attribute | Type | Description |
895
1167
  |---|---|---|---|
1168
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
896
1169
  | `files` | — | `{ path: string; url?: undefined \| string; code?: undefined \| string; language?: undefined \| string; type?: undefined \| "html" \| "pdf" \| "image" \| "other"; additions?: undefined \| number; deletions?: undefined \| number; status?: undefined \| "added" \| "modified" \| "deleted" \| "renamed" \| "untracked" }[]` | The files to render. Set as a JS property (array of `{ path, url?, code?, language?, type?, additions?, deletions?, status? }`). |
897
- | `activeFile` | `active-file` | `undefined \| string` | Selected file path highlighted in the tree. |
1170
+ | `activeFile` | `active-file` | `undefined \| string` | Selected file path, highlighted in the tree. |
898
1171
  | `defaultExpanded` | — | `undefined \| string[]` | Folder paths expanded initially. Omit to start with all folders open. |
899
1172
  | `summary` | `summary` | `undefined \| false \| true` | Show a changed-files summary header (file count + summed `+/-` + Collapse-all). Attribute: `summary`. Off by default. |
900
1173
 
@@ -921,9 +1194,10 @@ _No events._
921
1194
 
922
1195
  | Property | Attribute | Type | Description |
923
1196
  |---|---|---|---|
1197
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
924
1198
  | `multiple` | `multiple` | `undefined \| false \| true` | Allow selecting multiple files (default true). |
925
1199
  | `accept` | `accept` | `undefined \| string` | `accept` attribute for the file picker (e.g. `image/*`). |
926
- | `disabled` | `disabled` | `undefined \| false \| true` | Disable the dropzone no clicking, no drag-and-drop. |
1200
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the dropzone: no clicking, no drag-and-drop. |
927
1201
  | `label` | `label` | `undefined \| string` | Default dropzone label (overridable via the default slot). |
928
1202
 
929
1203
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -932,6 +1206,12 @@ _No events._
932
1206
  |---|---|---|
933
1207
  | `kai-files-added` | `CustomEvent<{ files: File[] }>` | Files were picked or dropped. |
934
1208
 
1209
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1210
+
1211
+ | Slot | Mode | Description |
1212
+ |---|---|---|
1213
+ | _(default)_ | inject | Custom dropzone content, replacing the default label (the `label` prop is the fallback). |
1214
+
935
1215
  ---
936
1216
 
937
1217
  ### `kai-form` / `Form`
@@ -940,7 +1220,8 @@ _No events._
940
1220
 
941
1221
  | Property | Attribute | Type | Description |
942
1222
  |---|---|---|---|
943
- | `data` | | `undefined \| Record<string, unknown>` | 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, so the element types it loosely). |
1223
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1224
+ | `data` | — | `undefined \| { type: "object"; title?: undefined \| string; description?: undefined \| string; required?: undefined \| string[]; properties: Record<string, { type: "string" \| "number" \| "integer" \| "boolean" \| "array" \| "object"; title?: undefined \| string; description?: undefined \| string; default?: unknown; enum?: undefined \| unknown[]; format?: undefined \| "email" \| "uri" \| "url" \| "date" \| "date-time" \| "time"; minimum?: undefined \| number; maximum?: undefined \| number; minLength?: undefined \| number; maxLength?: undefined \| number; pattern?: undefined \| string; minItems?: undefined \| number; maxItems?: undefined \| number; items?: undefined \| Record<string, unknown> \| { enum: unknown[] }; properties?: undefined \| Record<string, Record<string, unknown>>; required?: undefined \| string[]; readOnly?: undefined \| false \| true; "x-kai-widget"?: undefined \| "textarea" \| "slider" \| "rating" \| "radio" \| "select" \| "checkbox" \| "password" \| "switch"; "x-kai-placeholder"?: undefined \| string; "x-kai-step"?: undefined \| number }>; "x-kai-order"?: undefined \| string[]; "x-kai-inlineMax"?: undefined \| number; "x-kai-submitLabel"?: undefined \| string; "x-kai-dismissible"?: undefined \| false \| true; "x-kai-actions"?: undefined \| { id: string; label: string; variant?: undefined \| "default" \| "ghost" \| "outline" }[] }` | The form definition: a JSON Schema (`type:'object'`) + `x-kai-*` UI hints (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { type:'object', properties:{…} }`. Import the `FormDefinition` type from `@kitn.ai/ui` for the full shape. It IS self-referential (`FormField.properties` is another `FormField` map), and the generated `element-types.d.ts` inlines every named type, so the shipped declaration bottoms out in a `Record<string, unknown>` placeholder one level down rather than carrying the recursion. That is why `FormDefinition` is a `type` alias: an interface gets no implicit index signature, so it would not be assignable to that placeholder. |
944
1225
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
945
1226
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
946
1227
  | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'submit', data:{…} }`. |
@@ -952,7 +1233,18 @@ _No events._
952
1233
 
953
1234
  | Event | `detail` type | Description |
954
1235
  |---|---|---|
955
- | `kai-values-change` | `CustomEvent<{ values: Record<string, unknown>; valid: false \| true }>` | The form's values changed on input current coerced values + validity. |
1236
+ | `kai-values-change` | `CustomEvent<{ values: Record<string, unknown>; valid: false \| true }>` | The form's values changed on input. Carries the current coerced values + validity. |
1237
+
1238
+ **Methods** (call on the element instance: `document.querySelector('kai-form').focus(…)`):
1239
+
1240
+ | Method | Signature | Description |
1241
+ |---|---|---|
1242
+ | `focus` | `(options?: FocusOptions): void` | Focus the first control, or the first INVALID control after a failed validation. |
1243
+ | `send` | `(): void` | Validate + submit programmatically: focus the first invalid field on failure, else emit the `submit` CardEvent and resolve. Named `send`, not `submit`. |
1244
+ | `validate` | `(): void` | Run client-side validation now and return `{ valid, errors? }` WITHOUT submitting. |
1245
+ | `reset` | `(): void` | Re-seed the form from each field's `default` and clear errors. |
1246
+ | `dismiss` | `(): void` | Trigger the dismiss path (emit `dismiss` + collapse to the re-openable stub). |
1247
+ | `reopen` | `(): void` | Re-open a dismissed card from its stub (emit `reopen`). |
956
1248
 
957
1249
  ---
958
1250
 
@@ -962,6 +1254,7 @@ _No events._
962
1254
 
963
1255
  | Property | Attribute | Type | Description |
964
1256
  |---|---|---|---|
1257
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
965
1258
  | `openDelay` | `open-delay` | `undefined \| number` | Delay (ms) before the card opens on hover. Defaults to 0 (focus opens it immediately too). |
966
1259
  | `closeDelay` | `close-delay` | `undefined \| number` | Delay (ms) before it closes after the pointer leaves. Defaults to 300. |
967
1260
  | `placement` | `placement` | `undefined \| string` | Preferred placement: `'top' \| 'bottom' \| 'left' \| 'right'` (+ optional `-start`/`-end`). Defaults to `'bottom'`; flips to stay in view. |
@@ -975,10 +1268,19 @@ _No events._
975
1268
  |---|---|---|
976
1269
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The card opened or closed (by hover/focus, outside-click, or a method). |
977
1270
 
1271
+ **Methods** (call on the element instance: `document.querySelector('kai-hover-card').show()`):
1272
+
1273
+ | Method | Signature | Description |
1274
+ |---|---|---|
1275
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1276
+ | `hide` | `(): void` | Close it programmatically. |
1277
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1278
+
978
1279
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
979
1280
 
980
1281
  | Slot | Mode | Description |
981
1282
  |---|---|---|
1283
+ | _(default)_ | inject | The TRIGGER the card hovers off. The card body is the `card` slot. |
982
1284
  | `card` | inject | The rich content shown in the floating hover card. |
983
1285
 
984
1286
  ---
@@ -989,6 +1291,7 @@ _No events._
989
1291
 
990
1292
  | Property | Attribute | Type | Description |
991
1293
  |---|---|---|---|
1294
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
992
1295
  | `name` | `name` | `undefined \| string` | A curated icon name (e.g. `"mic"`, `"globe"`), an image URL/data-URI, or plain text. |
993
1296
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg"` | Size token: `sm` \| `md` (default) \| `lg`. |
994
1297
 
@@ -1008,6 +1311,7 @@ _No events._
1008
1311
 
1009
1312
  | Property | Attribute | Type | Description |
1010
1313
  |---|---|---|---|
1314
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1011
1315
  | `base64` | `base64` | `undefined \| string` | Base64-encoded image data (pair with `media-type`). |
1012
1316
  | `bytes` | — | `undefined \| Uint8Array<ArrayBufferLike>` | Raw image bytes (set as a JS property). |
1013
1317
  | `alt` | `alt` | `undefined \| string` | Alt text. |
@@ -1023,8 +1327,9 @@ _No events._
1023
1327
 
1024
1328
  | Property | Attribute | Type | Description |
1025
1329
  |---|---|---|---|
1330
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1026
1331
  | `type` | `type` | `undefined \| string` | Native input type: `text` (default) · `email` · `url` · `search` · `tel` · `password` · `number`. Single-line only. |
1027
- | `value` | `value` | `undefined \| string` | 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. |
1332
+ | `value` | `value` | `undefined \| string` | 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. |
1028
1333
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder shown when empty. |
1029
1334
  | `label` | `label` | `undefined \| string` | Field label, linked to the input. |
1030
1335
  | `hint` | `hint` | `undefined \| string` | Helper text below the control. |
@@ -1045,6 +1350,14 @@ _No events._
1045
1350
  | `kai-change` | `CustomEvent<{ value: string }>` | The value was committed (blur). |
1046
1351
  | `kai-input` | `CustomEvent<{ value: string }>` | The value changed per keystroke. |
1047
1352
 
1353
+ **Methods** (call on the element instance: `document.querySelector('kai-input').focus(…)`):
1354
+
1355
+ | Method | Signature | Description |
1356
+ |---|---|---|
1357
+ | `focus` | `(options?: FocusOptions): void` | Focus the inner input (the host can't reach into the shadow root). |
1358
+ | `select` | `(): void` | Select the inner input's text. |
1359
+ | `clear` | `(): void` | Empty the value and fire `kai-change` with `''`. |
1360
+
1048
1361
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1049
1362
 
1050
1363
  | Slot | Mode | Description |
@@ -1069,12 +1382,19 @@ _No events._
1069
1382
 
1070
1383
  | Property | Attribute | Type | Description |
1071
1384
  |---|---|---|---|
1072
- | `keys` | `keys` | `undefined \| string` | Shortcut spec — tokens joined by `+` (e.g. `Mod+Shift+K`). Omit it to show default-slot content instead. Display only; the element does not bind keys. |
1073
- | `platform` | `platform` | `undefined \| "other" \| "auto" \| "mac"` | `mac` uses ⌘/⌥, `other` uses Ctrl. `auto` (default) sniffs the OS. |
1385
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1386
+ | `keys` | `keys` | `undefined \| string` | Shortcut spec: tokens joined by `+` (e.g. `Mod+Shift+K`). Omit it to show default-slot content instead. Display only; the element does not bind keys. |
1387
+ | `platform` | `platform` | `undefined \| "auto" \| "mac" \| "other"` | `mac` uses ⌘/⌥, `other` uses Ctrl. `auto` (default) sniffs the OS. |
1074
1388
  | `size` | `size` | `undefined \| "sm" \| "md"` | Cap size: `sm` or `md`. Defaults to `md`. |
1075
1389
 
1076
1390
  _No events._
1077
1391
 
1392
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1393
+
1394
+ | Slot | Mode | Description |
1395
+ |---|---|---|
1396
+ | _(default)_ | inject | Literal key text, when you are not using the `keys` prop to render key caps. |
1397
+
1078
1398
  **Styleable parts** (restyle from outside via `kai-kbd::part(name)`):
1079
1399
 
1080
1400
  | Part | Description |
@@ -1090,6 +1410,7 @@ _No events._
1090
1410
 
1091
1411
  | Property | Attribute | Type | Description |
1092
1412
  |---|---|---|---|
1413
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1093
1414
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted event. Set as an attribute or property. |
1094
1415
  | `data` | — | `undefined \| { url: string; title?: undefined \| string; description?: undefined \| string; image?: undefined \| string; imageAlt?: undefined \| string; favicon?: undefined \| string; domain?: undefined \| string; siteName?: undefined \| string }` | The link payload (OG metadata). Set as a JS **property** (object). |
1095
1416
 
@@ -1103,6 +1424,7 @@ _No events._
1103
1424
 
1104
1425
  | Property | Attribute | Type | Description |
1105
1426
  |---|---|---|---|
1427
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1106
1428
  | `variant` | `variant` | `undefined \| "circular" \| "classic" \| "pulse" \| "pulse-dot" \| "dots" \| "typing" \| "wave" \| "bars" \| "terminal" \| "text-blink" \| "text-shimmer" \| "loading-dots"` | The animation style: `'circular' \| 'classic' \| 'pulse' \| 'pulse-dot' \| 'dots' \| 'typing' \| 'wave' \| 'bars' \| 'terminal' \| 'text-blink' \| 'text-shimmer' \| 'loading-dots'`. Defaults to `'circular'`. |
1107
1429
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg"` | Loader size: `'sm' \| 'md' \| 'lg'`. Defaults to `'md'`. |
1108
1430
  | `text` | `text` | `undefined \| string` | Label for the text-based variants. |
@@ -1117,8 +1439,9 @@ _No events._
1117
1439
 
1118
1440
  | Property | Attribute | Type | Description |
1119
1441
  |---|---|---|---|
1442
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1120
1443
  | `content` | `content` | `string` | The markdown source to render. |
1121
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Text/markdown sizing. |
1444
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing. |
1122
1445
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme for fenced code blocks. |
1123
1446
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting (no Shiki loads). |
1124
1447
 
@@ -1132,22 +1455,31 @@ _No events._
1132
1455
 
1133
1456
  | Property | Attribute | Type | Description |
1134
1457
  |---|---|---|---|
1135
- | `items` | | `undefined \| { id?: undefined \| string; label?: undefined \| string; icon?: undefined \| string; shortcut?: undefined \| string; checked?: undefined \| false \| true; radioGroup?: undefined \| string; disabled?: undefined \| false \| true; separator?: undefined \| false \| true; heading?: undefined \| false \| true; items?: undefined \| Record<string, unknown>[] }[]` | Tree of menu items. Set as a JS property — not an HTML attribute. |
1458
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1459
+ | `items` | — | `undefined \| { id?: undefined \| string; label?: undefined \| string; icon?: undefined \| string; shortcut?: undefined \| string; checked?: undefined \| false \| true; radioGroup?: undefined \| string; disabled?: undefined \| false \| true; separator?: undefined \| false \| true; heading?: undefined \| false \| true; items?: undefined \| Record<string, unknown>[] }[]` | Tree of menu items. Set as a JS property, not an HTML attribute. |
1136
1460
  | `placement` | `placement` | `undefined \| string` | Optional placement hint (unused by the underlying Dropdown which always positions bottom-start, kept for future extension). |
1137
- | `triggerIcon` | `trigger-icon` | `undefined \| string` | Built-in trigger: leading icon (a named icon like `"plus"`, an image URL/data-URI, or text). Use this instead of slotting `slot="trigger"` for the common case a slotted trigger overrides it. |
1138
- | `triggerLabel` | `trigger-label` | `undefined \| string` | Built-in trigger: a text label (e.g. `"High"`). |
1461
+ | `triggerIcon` | `trigger-icon` | `undefined \| string` | Built-in trigger: leading icon (a named icon like `"plus"`, an image URL/data-URI, or text). Use this instead of slotting `slot="trigger"` for the common case; a slotted trigger overrides it. |
1462
+ | `triggerLabel` | `trigger-label` | `undefined \| string` | Built-in trigger: a text label (e.g. `"High"`). This is the trigger's VISIBLE text, so it is also its accessible name, and `label` does not override it: an accessible name that does not contain the visible text is unreachable by speech input, which is what WCAG 2.5.3 (Label in Name) exists for. A slotted `slot="trigger"` replaces this built-in trigger entirely and is named differently; see `label`. |
1139
1463
  | `triggerIconTrailing` | `trigger-icon-trailing` | `undefined \| string` | Built-in trigger: a trailing icon (e.g. `"chevron-down"` for a select look). |
1140
- | `label` | `label` | `undefined \| string` | Accessible name for an icon-only trigger (no visible label). |
1464
+ | `label` | `label` | `undefined \| string` | Accessible name for a trigger with no visible label. Ignored when `triggerLabel` is set, which is already the visible name. It DOES name a slotted `slot="trigger"`, and that is a difference in what the two slots MEAN, not a limitation. `<kai-button>`'s slot IS the button's label, so text slotted there is the name and `label` steps aside. This slot is VISUAL content, a `+` or an `<svg>`, with the name supplied separately: decoration beside a name, never a second name competing with one. So `label` names the trigger here by design. Slotting a real WORD rather than a glyph makes that word a visible label, and an accessible name has to contain the visible text. Then either drop `label` or make it contain the word you slotted. |
1141
1465
  | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute, the menu still self-manages on click/keyboard). Set `el.open = true`, or `<kai-menu open>`; listen for `kai-open-change`. |
1142
1466
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1143
- | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger click/keyboard and `show()` no longer open the menu. |
1467
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger: click/keyboard and `show()` no longer open the menu. |
1144
1468
 
1145
1469
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1146
1470
 
1147
1471
  | Event | `detail` type | Description |
1148
1472
  |---|---|---|
1149
1473
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The menu opened or closed (by click, keyboard, Escape, outside-click, or a method). |
1150
- | `kai-select` | `CustomEvent<{ id: string; checked?: undefined \| false \| true; radioGroup?: undefined \| string }>` | Fired when the user selects a leaf item. - Plain items: `{ id }`. - Checkbox items: `{ id, checked }` where `checked` is the NEW state. - Radio items: `{ id, radioGroup }` the consumer marks `id` as the selected one in `radioGroup` and clears the others. |
1474
+ | `kai-select` | `CustomEvent<{ id: string; checked?: undefined \| false \| true; radioGroup?: undefined \| string }>` | Fired when the user selects a leaf item. - Plain items: `{ id }`. - Checkbox items: `{ id, checked }` where `checked` is the NEW state. - Radio items: `{ id, radioGroup }`, where the consumer marks `id` as the selected one in `radioGroup` and clears the others. |
1475
+
1476
+ **Methods** (call on the element instance: `document.querySelector('kai-menu').show()`):
1477
+
1478
+ | Method | Signature | Description |
1479
+ |---|---|---|
1480
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1481
+ | `hide` | `(): void` | Close it programmatically. |
1482
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1151
1483
 
1152
1484
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1153
1485
 
@@ -1169,17 +1501,19 @@ _No events._
1169
1501
 
1170
1502
  | Property | Attribute | Type | Description |
1171
1503
  |---|---|---|---|
1172
- | `message` | | `undefined \| { id: string; role: "user" \| "assistant"; content: string; reasoning?: undefined \| { text: string; label?: undefined \| string }; tools?: undefined \| { type: string; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string }[]; attachments?: undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]; 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" }` | The full message object. Set as a JS property. |
1173
- | `role` | `role` | `undefined \| "user" \| "assistant"` | Convenience for simple cases when not passing a `message` object. |
1174
- | `content` | `content` | `undefined \| string` | Convenience content (used when `message` is not set). |
1504
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1505
+ | `message` | | `undefined \| { 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" }` | The full message object. Set as a JS property. |
1506
+ | `role` | `role` | `undefined \| "user" \| "assistant"` | Who is speaking: `'user'` or `'assistant'`. Convenience for simple cases when not passing a `message` object. This is the SEMANTIC role of the message, not an ARIA role. The name collides with the global ARIA `role` attribute, which is why the facade lifts it off the host (see `liftRoleOffHost`). Neither speaker is a valid ARIA role, so a `role="user"` left on `<kai-message>` is a CRITICAL axe `aria-roles` violation. The accessible role lives on the row inside the shadow root instead: `role="article"` plus an `aria-label` naming the speaker, matching the SolidJS `<Message>` component. |
1175
1507
  | `markdown` | `markdown` | `undefined \| false \| true` | Force markdown on/off. Defaults to on for assistant, off for user. |
1176
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Text/markdown sizing for the message body. |
1508
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing for the message body. |
1177
1509
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name used for fenced code blocks in the content. |
1178
1510
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting for code blocks (no Shiki loads). |
1179
1511
  | `actionsReveal` | `actions-reveal` | `undefined \| "always" \| "hover"` | Whether the action bar is always visible (`'always'`, default) or only revealed on hover of the message row (`'hover'`). |
1180
1512
  | `avatarSrc` | `avatar-src` | `undefined \| string` | Convenience avatar image URL (used when `message.avatar` is not set). |
1181
1513
  | `avatarFallback` | `avatar-fallback` | `undefined \| string` | Convenience avatar fallback text (used when `message.avatar` is not set). |
1182
1514
  | `avatar` | `avatar` | `undefined \| string` | Avatar rail mode. `'none'` omits the avatar rail entirely so the body spans the full row (predictable layout when you never show avatars). Any other value keeps the default behaviour: the built-in avatar when one resolves, or your `slot="avatar"` content when projected (which REPLACES the built-in). |
1515
+ | `cardTypes` | — | `undefined \| Record<string, string>` | Optional card type -> custom-element tag overrides/additions for `card` parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. |
1516
+ | `cardSchemas` | — | `undefined \| Record<string, object>` | 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. |
1183
1517
 
1184
1518
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1185
1519
 
@@ -1187,14 +1521,26 @@ _No events._
1187
1521
  |---|---|---|
1188
1522
  | `kai-message-action` | `CustomEvent<{ messageId: string; action: string; state?: undefined \| "on" \| "off" }>` | An action button 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. |
1189
1523
 
1524
+ **Methods** (call on the element instance: `document.querySelector('kai-message').copy()`):
1525
+
1526
+ | Method | Signature | Description |
1527
+ |---|---|---|
1528
+ | `copy` | `(): void` | Copy the message content to the clipboard and show the copied check. |
1529
+
1190
1530
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1191
1531
 
1192
1532
  | Slot | Mode | Description |
1193
1533
  |---|---|---|
1194
- | `before-body` | inject | A per-message header at the TOP of the body, above reasoning/tools/content a model-name label, a role + timestamp line. |
1195
- | `after-body` | inject | A row at the BOTTOM of the body, below the action bar a citation/sources row, a token-cost/latency line. |
1534
+ | `before-body` | inject | A per-message header at the TOP of the body, above reasoning/tools/content: a model-name label, a role + timestamp line. |
1535
+ | `after-body` | inject | A row at the BOTTOM of the body, below the action bar: a citation/sources row, a token-cost/latency line. |
1196
1536
  | `avatar` | replace | Replaces the built-in avatar rail with your own node. Use `avatar="none"` to omit the rail and let the body span the full row. |
1197
1537
 
1538
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
1539
+
1540
+ | Child element | Attributes | Text content | Notes |
1541
+ |---|---|---|---|
1542
+ | `<kai-action>` | `action`, `icon`, `id`, `label`, `tooltip` | yes | |
1543
+
1198
1544
  **Styleable parts** (restyle from outside via `kai-message::part(name)`):
1199
1545
 
1200
1546
  | Part | Description |
@@ -1203,6 +1549,7 @@ _No events._
1203
1549
  | `::part(bubble)` | The content bubble wrapper. Restyle its background, radius, or padding; for a user message this is the rounded chat bubble. — `kai-message::part(bubble) { background: var(--color-primary); color: var(--color-primary-foreground) }` |
1204
1550
  | `::part(content)` | The rendered message text/markdown region (same node as `bubble`). Target it to tune typography from outside. — `kai-message::part(content) { font-size: 0.9375rem }` |
1205
1551
  | `::part(actions)` | The action-bar row (copy / like / regenerate …). Restyle its spacing or hide it entirely from outside. — `kai-message::part(actions) { gap: 0.25rem }` |
1552
+ | `::part(citations)` | The citation row rendered from the message’s `source` parts: a wrapped row of chips below the bubble, never inside it. Restyle its spacing or hide it entirely from outside. — `kai-message::part(citations) { gap: 0.5rem }` |
1206
1553
  | `::part(avatar)` | Replaces the built-in avatar rail with your own node. Use `avatar="none"` to omit the rail and let the body span the full row. |
1207
1554
 
1208
1555
  ---
@@ -1213,11 +1560,12 @@ _No events._
1213
1560
 
1214
1561
  | Property | Attribute | Type | Description |
1215
1562
  |---|---|---|---|
1216
- | `models` | | `{ id: string; name: string; provider?: undefined \| string; description?: undefined \| string; group?: undefined \| string }[]` | The selectable models. Set as a JS property (array). |
1563
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1564
+ | `models` | — | `undefined \| { id: string; name: string; provider?: undefined \| string; description?: undefined \| string; group?: undefined \| string }[]` | The selectable models. Set as a JS property (array). Omit to supply them as `<kai-model>` light-DOM children instead; when both are present the property's models come first. |
1217
1565
  | `currentModel` | `current-model` | `undefined \| string` | The currently-selected model id. Defaults to the first model. |
1218
1566
  | `open` | `open` | `undefined \| false \| true` | Drive/observe the dropdown's open state (Shoelace-style: settable + reflected to the `open` attribute, the dropdown still self-manages on click/keyboard). Set `el.open = true`, or `<kai-model-switcher open>`; listen for `kai-open-change`. |
1219
1567
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1220
- | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger click/keyboard and `show()` no longer open the dropdown. |
1568
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger: click/keyboard and `show()` no longer open the dropdown. |
1221
1569
 
1222
1570
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1223
1571
 
@@ -1226,6 +1574,20 @@ _No events._
1226
1574
  | `kai-model-change` | `CustomEvent<{ modelId: string }>` | A model was selected. |
1227
1575
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The model dropdown opened or closed (by click, keyboard, Escape, outside-click, or a method). |
1228
1576
 
1577
+ **Methods** (call on the element instance: `document.querySelector('kai-model-switcher').show()`):
1578
+
1579
+ | Method | Signature | Description |
1580
+ |---|---|---|
1581
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1582
+ | `hide` | `(): void` | Close it programmatically. |
1583
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1584
+
1585
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
1586
+
1587
+ | Child element | Attributes | Text content | Notes |
1588
+ |---|---|---|---|
1589
+ | `<kai-model>` | `description`, `group`, `id`, `provider` | yes | Parse a single light-DOM `<kai-model>` element into a `ModelOption` descriptor. Attribute mapping: - `id` → ModelOption.id - textContent → ModelOption.name - `provider` → ModelOption.provider (optional) - `description` → ModelOption.description (optional subtitle) - `group` → ModelOption.group (optional collapsible section) |
1590
+
1229
1591
  ---
1230
1592
 
1231
1593
  ### `kai-nav` / `Nav`
@@ -1234,7 +1596,8 @@ _No events._
1234
1596
 
1235
1597
  | Property | Attribute | Type | Description |
1236
1598
  |---|---|---|---|
1237
- | `items` | | `undefined \| { id: string; label?: undefined \| string; icon?: undefined \| string; badge?: undefined \| string; trailing?: undefined \| string; disabled?: undefined \| false \| true; children?: undefined \| Record<string, unknown>[]; status?: undefined \| { tone: "error" \| "primary" \| "info" \| "success" \| "warning" \| "neutral"; label?: undefined \| string; pulse?: undefined \| false \| true }; meta?: undefined \| string; action?: undefined \| { icon: string; label: string }; closable?: undefined \| false \| true }[]` | The nav items. Set as a JS property (array, not an attribute). Each item may carry `children` (a collapsible group), a `status` dot, and trailing `meta` text. |
1599
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1600
+ | `items` | — | `undefined \| { id: string; label?: undefined \| string; icon?: undefined \| string; badge?: undefined \| string; trailing?: undefined \| string; disabled?: undefined \| false \| true; children?: undefined \| Record<string, unknown>[]; status?: undefined \| { tone: "primary" \| "info" \| "success" \| "warning" \| "error" \| "neutral"; label?: undefined \| string; pulse?: undefined \| false \| true }; meta?: undefined \| string; action?: undefined \| { icon: string; label: string }; closable?: undefined \| false \| true }[]` | The nav items. Set as a JS property (array, not an attribute). Each item may carry `children` (a collapsible group), a `status` dot, and trailing `meta` text. |
1238
1601
  | `value` | `value` | `undefined \| string` | Active item id (controlled). |
1239
1602
  | `defaultValue` | `default-value` | `undefined \| string` | Initial active id when uncontrolled. |
1240
1603
  | `defaultCollapsed` | — | `undefined \| string[]` | Ids of group items collapsed on first render (groups default to expanded). Set as a JS property (array). |
@@ -1247,6 +1610,12 @@ _No events._
1247
1610
  | `kai-nav-item-close` | `CustomEvent<{ value: string }>` | A `closable` row's trailing close button was activated (not a select). `value` is the item id. |
1248
1611
  | `kai-nav-select` | `CustomEvent<{ id: string }>` | A nav item was activated. |
1249
1612
 
1613
+ **Methods** (call on the element instance: `document.querySelector('kai-nav').select(…)`):
1614
+
1615
+ | Method | Signature | Description |
1616
+ |---|---|---|
1617
+ | `select` | `(id: string): void` | Activate an item by id (fires kai-nav-select). |
1618
+
1250
1619
  **Styleable parts** (restyle from outside via `kai-nav::part(name)`):
1251
1620
 
1252
1621
  | Part | Description |
@@ -1267,7 +1636,8 @@ _No events._
1267
1636
 
1268
1637
  | Property | Attribute | Type | Description |
1269
1638
  |---|---|---|---|
1270
- | `severity` | `severity` | `undefined \| "error" \| "info" \| "success" \| "warning" \| "neutral"` | `neutral` (default) · `info` · `warning` · `error` · `success`. Drives the leading icon's color and the a11y role (`alert` for errors, else `status`). |
1639
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1640
+ | `severity` | `severity` | `undefined \| "neutral" \| "info" \| "warning" \| "error" \| "success"` | `neutral` (default) · `info` · `warning` · `error` · `success`. Drives the leading icon's color and the a11y role (`alert` for errors, else `status`). |
1271
1641
  | `icon` | `icon` | `undefined \| string` | Leading icon: omit for the severity default, `"none"` to hide it, or a named icon to override. |
1272
1642
  | `dismissible` | `dismissible` | `undefined \| false \| true` | Show a dismiss (×) that hides the notice and emits `kai-dismiss`. |
1273
1643
 
@@ -1281,8 +1651,9 @@ _No events._
1281
1651
 
1282
1652
  | Slot | Mode | Description |
1283
1653
  |---|---|---|
1284
- | `action` | inject | A trailing action beside the message a link or button. |
1285
- | `icon` | replace | A custom leading icon (any inline SVG, inherits `currentColor`). Overrides the severity default and the `icon` prop — the same escape hatch as `kai-button`. |
1654
+ | _(default)_ | inject | The notice message. `icon` and `action` are the named seams around it. |
1655
+ | `action` | inject | A trailing action beside the message: a link or button. |
1656
+ | `icon` | replace | A custom leading icon (any inline SVG, inherits `currentColor`). Overrides the severity default and the `icon` prop, the same escape hatch as `kai-button`. |
1286
1657
 
1287
1658
  ---
1288
1659
 
@@ -1292,6 +1663,7 @@ _No events._
1292
1663
 
1293
1664
  | Property | Attribute | Type | Description |
1294
1665
  |---|---|---|---|
1666
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1295
1667
  | `headline` | `headline` | `undefined \| string` | The pane title (the agent / window name). Named `headline` because `title` collides with the global `HTMLElement.title` attribute (it throws at registration). Attribute: `headline`. |
1296
1668
  | `subtitle` | `subtitle` | `undefined \| string` | A role / label shown under the title (e.g. "Reviewer", "claude-sonnet"). Attribute: `subtitle`. |
1297
1669
  | `maximized` | `maximized` | `undefined \| false \| true` | Show the restore glyph instead of maximize, and signal the maximized view-state. Drive it yourself in response to `kai-maximize`. Attribute: `maximized`. |
@@ -1306,13 +1678,14 @@ _No events._
1306
1678
  |---|---|---|
1307
1679
  | `kai-close` | `CustomEvent` | The close (×) control was clicked. |
1308
1680
  | `kai-dock` | `CustomEvent` | The dock control was clicked (only present when `show-dock`). |
1309
- | `kai-maximize` | `CustomEvent<{ maximized: false \| true }>` | The maximize/restore control was clicked. `detail.maximized` is the intended NEXT state drive the `maximized` prop yourself from it. |
1681
+ | `kai-maximize` | `CustomEvent<{ maximized: false \| true }>` | The maximize/restore control was clicked. `detail.maximized` is the intended NEXT state. Drive the `maximized` prop yourself from it. |
1310
1682
  | `kai-split` | `CustomEvent` | The split control was clicked (only present when `show-split`). |
1311
1683
 
1312
1684
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1313
1685
 
1314
1686
  | Slot | Mode | Description |
1315
1687
  |---|---|---|
1688
+ | _(default)_ | inject | The pane body, below the header row. |
1316
1689
  | `leading` | inject | A glyph or avatar at the start of the pane header. |
1317
1690
  | `actions` | inject | Extra header controls, before the built-in window controls. |
1318
1691
  | `footer` | inject | A pinned row below the body (e.g. a composer). |
@@ -1334,6 +1707,7 @@ _No events._
1334
1707
 
1335
1708
  | Property | Attribute | Type | Description |
1336
1709
  |---|---|---|---|
1710
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1337
1711
  | `tabs` | — | `undefined \| { id: string; name: string; status?: undefined \| { tone: "working" \| "idle" \| "done" \| "error" \| "blocked"; label?: undefined \| string; pulse?: undefined \| false \| true }; needsAttention?: undefined \| false \| true; number?: undefined \| number }[]` | The tabs to render. An array of `{ id, name, status?, needsAttention?, number? }` set as a JS PROPERTY (not an HTML attribute). |
1338
1712
  | `active` | `active` | `undefined \| string` | The active tab id (controlled, and reflected to the `active` ATTRIBUTE so `::part`/`[active]` selectors and the per-tab named slot follow it). Set it as the `active` attribute or drive it from `kai-tab-change`; omit for uncontrolled (the first tab). |
1339
1713
  | `focused` | `focused` | `undefined \| false \| true` | Highlight the frame as the ACTIVE group in a multi-group layout. Attribute: `focused`. |
@@ -1346,6 +1720,19 @@ _No events._
1346
1720
  | `kai-tab-close` | `CustomEvent<{ id: string }>` | A tab's close (×) was clicked. Drop the tab from `tabs` yourself. |
1347
1721
  | `kai-tab-menu` | `CustomEvent<{ id: string }>` | A tab's "…" overflow was clicked. Open your own menu from `detail.id`. |
1348
1722
 
1723
+ **Methods** (call on the element instance: `document.querySelector('kai-pane-group').select(…)`):
1724
+
1725
+ | Method | Signature | Description |
1726
+ |---|---|---|
1727
+ | `select` | `(id: string): void` | Select a tab by id (fires `kai-tab-change`). Ignores unknown ids. |
1728
+ | `focus` | `(): void` | Focus the active tab in the strip. |
1729
+
1730
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1731
+
1732
+ | Slot | Mode | Description |
1733
+ |---|---|---|
1734
+ | _(default)_ | inject | Content shown for every tab. Use it INSTEAD of the per-tab `slot="<tab id>"` seams when you swap the content yourself. |
1735
+
1349
1736
  **Styleable parts** (restyle from outside via `kai-pane-group::part(name)`):
1350
1737
 
1351
1738
  | Part | Description |
@@ -1364,7 +1751,8 @@ _No events._
1364
1751
 
1365
1752
  | Property | Attribute | Type | Description |
1366
1753
  |---|---|---|---|
1367
- | `placement` | `placement` | `undefined \| "top" \| "right" \| "bottom" \| "left" \| "top-start" \| "top-end" \| "right-start" \| "right-end" \| "bottom-start" \| "bottom-end" \| "left-start" \| "left-end"` | Floating placement relative to the trigger (floating-ui placement). |
1754
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1755
+ | `placement` | `placement` | `undefined \| "top" \| "right" \| "bottom" \| "left" \| "bottom-end" \| "bottom-start" \| "left-end" \| "left-start" \| "right-end" \| "right-start" \| "top-end" \| "top-start"` | Floating placement relative to the trigger (floating-ui placement). |
1368
1756
  | `gutter` | `gutter` | `undefined \| number` | Gap in px between the trigger and the panel. |
1369
1757
  | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute, the element still self-manages on click). Set `el.open = true`, or `<kai-popover open>`; listen for `kai-open-change`. |
1370
1758
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
@@ -1376,6 +1764,21 @@ _No events._
1376
1764
  |---|---|---|
1377
1765
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The popover opened or closed (click, Escape, outside-click, or a method). |
1378
1766
 
1767
+ **Methods** (call on the element instance: `document.querySelector('kai-popover').show()`):
1768
+
1769
+ | Method | Signature | Description |
1770
+ |---|---|---|
1771
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1772
+ | `hide` | `(): void` | Close it programmatically. |
1773
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1774
+
1775
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1776
+
1777
+ | Slot | Mode | Description |
1778
+ |---|---|---|
1779
+ | _(default)_ | inject | The popover panel body. The control that opens it is the `trigger` slot. |
1780
+ | `trigger` | replace | The control that opens the popover (a button, an avatar, …). The panel anchors to it. |
1781
+
1379
1782
  ---
1380
1783
 
1381
1784
  ### `kai-progress-bar` / `ProgressBar`
@@ -1384,6 +1787,7 @@ _No events._
1384
1787
 
1385
1788
  | Property | Attribute | Type | Description |
1386
1789
  |---|---|---|---|
1790
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1387
1791
  | `value` | `value` | `undefined \| number` | Current progress value (0..max). Attribute: `value`. |
1388
1792
  | `max` | `max` | `undefined \| number` | The value `value` runs to (default 100). Attribute: `max`. |
1389
1793
  | `label` | `label` | `undefined \| string` | Optional caption above the track. Attribute: `label`. |
@@ -1406,8 +1810,9 @@ _No events._
1406
1810
 
1407
1811
  | Property | Attribute | Type | Description |
1408
1812
  |---|---|---|---|
1409
- | `frame` | `frame` | `undefined \| "none" \| "inset" \| "edge"` | How the tray frames the input — the SPATIAL inset axis: `inset` (default, the classic recessed frame on every side) \| `edge` (top/bottom inset only; the input sits flush left/right so the lips span the full width) \| `none` (no inset; the lips attach directly as a plain stack). Attribute: `frame`. |
1410
- | `appearance` | `appearance` | `undefined \| "outlined" \| "filled" \| "plain" \| "soft"` | How the tray surface looks the VISUAL axis, orthogonal to `frame`: `soft` (default, sunken surface + border + radius) \| `outlined` (transparent + border + radius) \| `filled` (sunken, no border, + radius) \| `plain` (bare). Attribute: `appearance`. |
1813
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1814
+ | `frame` | `frame` | `undefined \| "inset" \| "edge" \| "none"` | How the tray frames the input, the SPATIAL inset axis: `inset` (default, the classic recessed frame on every side) \| `edge` (top/bottom inset only; the input sits flush left/right so the lips span the full width) \| `none` (no inset; the lips attach directly as a plain stack). Attribute: `frame`. |
1815
+ | `appearance` | `appearance` | `undefined \| "soft" \| "outlined" \| "filled" \| "plain"` | How the tray surface looks, the VISUAL axis orthogonal to `frame`: `soft` (default, sunken surface + border + radius) \| `outlined` (transparent + border + radius) \| `filled` (sunken, no border, + radius) \| `plain` (bare). Attribute: `appearance`. |
1411
1816
 
1412
1817
  _No events._
1413
1818
 
@@ -1415,6 +1820,7 @@ _No events._
1415
1820
 
1416
1821
  | Slot | Mode | Description |
1417
1822
  |---|---|---|
1823
+ | _(default)_ | inject | The input the dock wraps, typically a `<kai-prompt-input>`. The `top`/`bottom` slots are the lips around it. |
1418
1824
  | `top` | inject | The top lip: a notice or banner above the input. Rendered only when filled. |
1419
1825
  | `bottom` | inject | The bottom lip: a mode or controls row below the input. Rendered only when filled. |
1420
1826
 
@@ -1434,6 +1840,7 @@ _No events._
1434
1840
 
1435
1841
  | Property | Attribute | Type | Description |
1436
1842
  |---|---|---|---|
1843
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1437
1844
  | `value` | — | `undefined \| string \| ({ type: "text"; text: string } \| { type: "entity"; entity: { kind: string; id: string; label: string; icon?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> } })[]` | Value of the input, as a JS property. A **string** is the controlled text mirror (the host owns it and updates on `kai-value-change`). A **ComposerDoc** (array of text/entity segments) is a one-time **seed** that pre-populates pills (skills/agents/plugins); the user then edits freely. Leave unset for uncontrolled behavior. `kai-submit`/`kai-value-change` always emit `value` as the flattened string (back-compat) plus the structured `doc` + `entities`. |
1438
1845
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown in the empty input. |
1439
1846
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the input and submit button entirely (non-interactive). |
@@ -1443,17 +1850,17 @@ _No events._
1443
1850
  | `search` | `search` | `undefined \| false \| true` | Show a Search (Globe) button in the left toolbar; clicking it fires a `search` event. |
1444
1851
  | `voice` | `voice` | `undefined \| false \| true` | Show a Voice (Mic) button in the left toolbar; clicking it fires a `voice` event. |
1445
1852
  | `stoppable` | `stoppable` | `undefined \| false \| true` | When set and `loading` is true, the send button is replaced by a Stop button (square icon, "Stop" aria-label). Clicking it fires `kai-stop`. |
1446
- | `submit` | `submit` | `undefined \| "always" \| "auto"` | Send-button visibility. `'always'` (default) always shows it; `'auto'` shows it only when there's text/attachments (an empty composer hides it Enter still submits). To hide it entirely (Enter-only), it's pure CSS: `::part(send){display:none}` no prop needed. Restyle via `::part(send)`. The Stop button (`stoppable` + `loading`) is unaffected. |
1853
+ | `submit` | `submit` | `undefined \| "always" \| "auto"` | Send-button visibility. `'always'` (default) always shows it; `'auto'` shows it only when there's text/attachments (an empty composer hides it, though Enter still submits). To hide it entirely (Enter-only), it's pure CSS: `::part(send){display:none}`, no prop needed. Restyle via `::part(send)`. The Stop button (`stoppable` + `loading`) is unaffected. |
1447
1854
  | `attach` | `attach` | `undefined \| false \| true` | 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`. |
1448
1855
  | `attachments` | — | `undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]` | 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). |
1449
- | `triggers` | — | `undefined \| { char: string; kind: string; items?: undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; group?: undefined \| string; kind?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> }[] }[]` | 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. |
1856
+ | `triggers` | — | `undefined \| { char: string; kind: string; items?: undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; group?: undefined \| string; kind?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> }[] }[]` | 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. |
1450
1857
  | `kindIcons` | — | `undefined \| Record<string, string>` | Default icon per entity kind (kind → image URL/data-URI) for pills/menu items without their own `icon`. Overrides the built-in agent/plugin glyphs. JS property. |
1451
1858
 
1452
1859
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1453
1860
 
1454
1861
  | Event | `detail` type | Description |
1455
1862
  |---|---|---|
1456
- | `kai-attachments-change` | `CustomEvent<{ attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | The staged attachments changed a file was added (via the paperclip) or removed (per-chip ×). Carries the full current list so a consumer can react in real time (validate, show upload progress, toggle the send button). |
1863
+ | `kai-attachments-change` | `CustomEvent<{ attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | The staged attachments changed: a file was added (via the paperclip) or removed (per-chip ×). Carries the full current list so a consumer can react in real time (validate, show upload progress, toggle the send button). |
1457
1864
  | `kai-search` | `CustomEvent<Record<string, never>>` | The Search (Globe) toolbar button was clicked. |
1458
1865
  | `kai-stop` | `CustomEvent<Record<string, never>>` | The Stop button was clicked while `stoppable` and `loading` are both true. |
1459
1866
  | `kai-submit` | `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 }[] }>` | 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. |
@@ -1462,19 +1869,34 @@ _No events._
1462
1869
  | `kai-value-change` | `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> }[] }>` | The input changed (fires on every edit). Carries the flattened `value` plus the structured `doc` + `entities`. |
1463
1870
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Voice (Mic) toolbar button was clicked. |
1464
1871
 
1872
+ **Methods** (call on the element instance: `document.querySelector('kai-prompt-input').focus(…)`):
1873
+
1874
+ | Method | Signature | Description |
1875
+ |---|---|---|
1876
+ | `focus` | `(options?: FocusOptions): void` | Focus the text editor inside the shadow root (not the hidden file input). |
1877
+ | `blur` | `(): void` | Blur the focused input control. |
1878
+ | `clear` | `(): void` | Clear the text and any staged attachments (fires kai-value-change / kai-attachments-change so a controlled consumer can react). |
1879
+ | `send` | `(): void` | Send the current value programmatically, on the same path as Enter / the send button (fires kai-submit, then clears staged attachments). Named `send`, not `submit`, to avoid colliding with the `submit` prop. |
1880
+
1465
1881
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1466
1882
 
1467
1883
  | Slot | Mode | Description |
1468
1884
  |---|---|---|
1469
- | `input-top` | inject | Inside the card, above the textarea (e.g. an inline status strip). For content above/below the whole card, use your own layout that is light DOM you control. |
1470
- | `toolbar-start` | inject | Leading controls in the input toolbar where a + menu goes. |
1885
+ | `input-top` | inject | Inside the card, above the textarea (e.g. an inline status strip). For content above/below the whole card, use your own layout; that is light DOM you control. |
1886
+ | `toolbar-start` | inject | Leading controls in the input toolbar, where a + menu goes. |
1471
1887
  | `toolbar-end` | inject | Trailing controls in the toolbar, before the Send button. |
1472
1888
 
1889
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
1890
+
1891
+ | Child element | Attributes | Text content | Notes |
1892
+ |---|---|---|---|
1893
+ | `<kai-action>` | `action`, `icon`, `id`, `label`, `tooltip` | yes | |
1894
+
1473
1895
  **Styleable parts** (restyle from outside via `kai-prompt-input::part(name)`):
1474
1896
 
1475
1897
  | Part | Description |
1476
1898
  |---|---|
1477
- | `::part(send)` | The send button. Restyle from outside, or hide it entirely (Enter-only) hiding is pure CSS, which is why there is no `submit="never"`. — `kai-prompt-input::part(send) { display: none } /* Enter-only; or restyle: background, border-radius, … */` |
1899
+ | `::part(send)` | The send button. Restyle from outside, or hide it entirely (Enter-only). Hiding is pure CSS, which is why there is no `submit="never"`. — `kai-prompt-input::part(send) { display: none } /* Enter-only; or restyle: background, border-radius, … */` |
1478
1900
 
1479
1901
  ---
1480
1902
 
@@ -1484,13 +1906,14 @@ _No events._
1484
1906
 
1485
1907
  | Property | Attribute | Type | Description |
1486
1908
  |---|---|---|---|
1909
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1487
1910
  | `text` | `text` | `string` | The reasoning text to display. |
1488
1911
  | `label` | `label` | `undefined \| string` | Trigger label. |
1489
1912
  | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute; the element still self-manages on trigger click + while streaming). Set `el.open = true`; listen for `kai-open-change`. |
1490
1913
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1491
1914
  | `streaming` | `streaming` | `undefined \| false \| true` | While true, auto-expands (and re-collapses when it flips false). |
1492
1915
  | `markdown` | `markdown` | `undefined \| false \| true` | Render `text` as markdown. |
1493
- | `disabled` | `disabled` | `undefined \| false \| true` | Gate the disclosure trigger programmatic `show()/hide()/toggle()` still work, but the trigger click no longer toggles. |
1916
+ | `disabled` | `disabled` | `undefined \| false \| true` | Gate the disclosure trigger: programmatic `show()/hide()/toggle()` still work, but the trigger click no longer toggles. |
1494
1917
 
1495
1918
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1496
1919
 
@@ -1498,6 +1921,14 @@ _No events._
1498
1921
  |---|---|---|
1499
1922
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The reasoning block expanded or collapsed (via the trigger, streaming auto-open, or a method). |
1500
1923
 
1924
+ **Methods** (call on the element instance: `document.querySelector('kai-reasoning').show()`):
1925
+
1926
+ | Method | Signature | Description |
1927
+ |---|---|---|
1928
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1929
+ | `hide` | `(): void` | Close it programmatically. |
1930
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1931
+
1501
1932
  ---
1502
1933
 
1503
1934
  ### `kai-remote` / `Remote`
@@ -1506,6 +1937,7 @@ _No events._
1506
1937
 
1507
1938
  | Property | Attribute | Type | Description |
1508
1939
  |---|---|---|---|
1940
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1509
1941
  | `src` | `src` | `undefined \| string` | The remote card URL. Attribute: `src`. |
1510
1942
  | `providerOrigin` | `provider-origin` | `undefined \| string` | Exact provider origin (https: or http://localhost for dev). Attribute: `provider-origin`. |
1511
1943
  | `envelope` | — | `undefined \| Record<string, unknown>` | The card envelope to render. JS property only. |
@@ -1521,16 +1953,31 @@ _No events._
1521
1953
 
1522
1954
  | Property | Attribute | Type | Description |
1523
1955
  |---|---|---|---|
1524
- | `orientation` | `orientation` | `undefined \| "vertical" \| "horizontal"` | Layout axis: `horizontal` (row, default) or `vertical` (column). |
1525
- | `maximizedIndex` | | `undefined \| null \| number` | Which item index is maximized (null = none). Declarative source of truth. |
1526
- | `handle` | `handle` | `undefined \| "none" \| "line" \| "grip"` | Divider affordance drawn inside each draggable handle's 8px grab zone: - `line` (default) a 1px hairline, transparent at rest, tinting on hover/drag. - `grip` — a dotted grip handle. - `none` — no visible divider, just the invisible hit-area. The full grab zone and keyboard/ARIA behavior are identical for all three. |
1956
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1957
+ | `orientation` | `orientation` | `undefined \| "horizontal" \| "vertical"` | Layout axis: `horizontal` (row, default) or `vertical` (column). |
1958
+ | `maximizedIndex` | | `undefined \| number \| null` | Which item index is maximized (null = none). Declarative source of truth. |
1959
+ | `handle` | `handle` | `undefined \| "line" \| "grip" \| "none"` | Divider affordance drawn inside each draggable handle's 8px grab zone: - `line` (default): a 1px hairline, transparent at rest, tinting on hover/drag. - `grip`: a dotted grip handle. - `none`: no visible divider, just the invisible hit-area. The full grab zone and keyboard/ARIA behavior are identical for all three. |
1527
1960
 
1528
1961
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1529
1962
 
1530
1963
  | Event | `detail` type | Description |
1531
1964
  |---|---|---|
1532
1965
  | `kai-change` | `CustomEvent<{ sizes: number[] }>` | Fired on drag-end / keyboard resize / visibility change. `detail.sizes` = panel sizes in percent. |
1533
- | `kai-maximize-change` | `CustomEvent<{ maximized: false \| true; index: null \| number }>` | Observe layout maximize state. |
1966
+ | `kai-maximize-change` | `CustomEvent<{ maximized: false \| true; index: number \| null }>` | Observe layout maximize state. |
1967
+ | `kai-maximize-state` | `CustomEvent<{ maximized: false \| true }>` | Authoritative maximize state, dispatched as a raw composed CustomEvent (not through `dispatch`) onto the affected `<kai-resizable-item>` and, on restore, onto the group host. A nested element (e.g. `<kai-artifact>`) listens for it to reconcile its own toggle. |
1968
+
1969
+ **Methods** (call on the element instance: `document.querySelector('kai-resizable').maximize(…)`):
1970
+
1971
+ | Method | Signature | Description |
1972
+ |---|---|---|
1973
+ | `maximize` | `(index: number): void` | Imperatively maximize the item at `index` (thin wrapper over `maximizedIndex`). |
1974
+ | `restore` | `(): void` | Imperatively restore from the maximized layout. |
1975
+
1976
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1977
+
1978
+ | Slot | Mode | Description |
1979
+ |---|---|---|
1980
+ | _(default)_ | inject | The `<kai-resizable-item>` panels, in order. Dividers are inserted between them. |
1534
1981
 
1535
1982
  ---
1536
1983
 
@@ -1540,12 +1987,13 @@ _No events._
1540
1987
 
1541
1988
  | Property | Attribute | Type | Description |
1542
1989
  |---|---|---|---|
1990
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1543
1991
  | `size` | `size` | `undefined \| string` | Initial main-axis size: `"280px"` (fixed) or `"25%"`/`25` (percent). Omitted → flexible. |
1544
1992
  | `min` | `min` | `undefined \| string` | Minimum size during resize (px or %). |
1545
1993
  | `max` | `max` | `undefined \| string` | Maximum size during resize (px or %). |
1546
1994
  | `locked` | `locked` | `undefined \| false \| true` | Fix this panel's size; adjacent dividers become non-draggable. |
1547
1995
  | `hidden` | `hidden` | `undefined \| false \| true` | Hide this panel; its divider is dropped and the rest reflow. |
1548
- | `collapsed` | `collapsed` | `undefined \| false \| true` | Collapse this panel same layout effect as `hidden` (divider dropped, the rest reflow), but it WORKS as a bare boolean from framework JSX. A plain `<kai-resizable-item collapsed>` in React/Solid/Vue/Svelte collapses the panel at the first render; `hidden` does not, because a JSX boolean sets neither the `hidden` attribute nor the IDL property on a custom element, so the parent never sees it. The facade reflects `collapsed` to a `collapsed` attribute the parent reads. Prefer this over `hidden` for declarative collapse. |
1996
+ | `collapsed` | `collapsed` | `undefined \| false \| true` | Collapse this panel. Same layout effect as `hidden` (divider dropped, the rest reflow), but it WORKS as a bare boolean from framework JSX. A plain `<kai-resizable-item collapsed>` in React/Solid/Vue/Svelte collapses the panel at the first render; `hidden` does not, because a JSX boolean sets neither the `hidden` attribute nor the IDL property on a custom element, so the parent never sees it. The facade reflects `collapsed` to a `collapsed` attribute the parent reads. Prefer this over `hidden` for declarative collapse. |
1549
1997
 
1550
1998
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1551
1999
 
@@ -1553,6 +2001,13 @@ _No events._
1553
2001
  |---|---|---|
1554
2002
  | `kai-change` | `CustomEvent<unknown>` | |
1555
2003
  | `kai-maximize-change` | `CustomEvent<unknown>` | |
2004
+ | `kai-maximize-state` | `CustomEvent<unknown>` | |
2005
+
2006
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2007
+
2008
+ | Slot | Mode | Description |
2009
+ |---|---|---|
2010
+ | _(default)_ | inject | This panel's content. |
1556
2011
 
1557
2012
  ---
1558
2013
 
@@ -1562,7 +2017,8 @@ _No events._
1562
2017
 
1563
2018
  | Property | Attribute | Type | Description |
1564
2019
  |---|---|---|---|
1565
- | `text` | | `undefined \| string \| AsyncIterable<string>` | Text to stream. A string, or an `AsyncIterable<string>` (set as a JS property — async iterables can't be HTML attributes). |
2020
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2021
+ | `text` | — | `undefined \| string \| AsyncIterable<string>` | Text to stream. A string, or an `AsyncIterable<string>` (set as a JS property, since async iterables can't be HTML attributes). |
1566
2022
  | `mode` | `mode` | `undefined \| "typewriter" \| "fade"` | Reveal animation. |
1567
2023
  | `speed` | `speed` | `undefined \| number` | Characters/segments per tick. |
1568
2024
  | `as` | `as` | `undefined \| string` | Element tag to render as. |
@@ -1581,19 +2037,28 @@ _No events._
1581
2037
 
1582
2038
  | Property | Attribute | Type | Description |
1583
2039
  |---|---|---|---|
1584
- | `availableAuthors` | — | `string[]` | Authors to offer as scope filters. Set as a JS property. |
1585
- | `availableTags` | — | `string[]` | Tags to offer as scope filters. Set as a JS property. |
2040
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2041
+ | `availableAuthors` | — | `undefined \| string[]` | Authors to offer as scope filters. Omit to drop the Authors section (for a tag-only picker). Set as a JS property. |
2042
+ | `availableTags` | — | `undefined \| string[]` | Tags to offer as scope filters. Omit to drop the Tags section (for an author-only picker). Set as a JS property. |
1586
2043
  | `currentLabel` | `current-label` | `undefined \| string` | The label shown on the trigger for the active scope. |
1587
2044
  | `open` | `open` | `undefined \| false \| true` | Drive/observe the dropdown's open state (Shoelace-style: settable + reflected to the `open` attribute, the dropdown still self-manages on click/keyboard). Set `el.open = true`, or `<kai-scope-picker open>`; listen for `kai-open-change`. |
1588
2045
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1589
- | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger click/keyboard and `show()` no longer open the dropdown. |
2046
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger: click/keyboard and `show()` no longer open the dropdown. |
1590
2047
 
1591
2048
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1592
2049
 
1593
2050
  | Event | `detail` type | Description |
1594
2051
  |---|---|---|
1595
2052
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The scope dropdown opened or closed (by click, keyboard, Escape, outside-click, or a method). |
1596
- | `kai-scope-change` | `CustomEvent<{ filters: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }>` | A scope was chosen (`undefined` filters = "All Content"). |
2053
+ | `kai-scope-change` | `CustomEvent<{ filters: { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } \| undefined }>` | A scope was chosen (`undefined` filters = "All Content"). |
2054
+
2055
+ **Methods** (call on the element instance: `document.querySelector('kai-scope-picker').show()`):
2056
+
2057
+ | Method | Signature | Description |
2058
+ |---|---|---|
2059
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
2060
+ | `hide` | `(): void` | Close it programmatically. |
2061
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1597
2062
 
1598
2063
  ---
1599
2064
 
@@ -1603,6 +2068,7 @@ _No events._
1603
2068
 
1604
2069
  | Property | Attribute | Type | Description |
1605
2070
  |---|---|---|---|
2071
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1606
2072
  | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute; the element still self-manages). Set `el.open = true`, or `<kai-screen open>`; listen for `kai-open-change`. |
1607
2073
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1608
2074
  | `headline` | `headline` | `undefined \| string` | Header title text. A projected `title` slot overrides it. (Named `headline` because `title` collides with the global `HTMLElement.title` attribute.) |
@@ -1616,10 +2082,20 @@ _No events._
1616
2082
  | `kai-back` | `CustomEvent<Record<string, never>>` | Back navigation intent: the back button or Escape. The consumer flips their own routing in response (the screen knows nothing about the trigger). |
1617
2083
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The screen opened or closed (a method, `Escape` close, or driven `open`). |
1618
2084
 
2085
+ **Methods** (call on the element instance: `document.querySelector('kai-screen').show()`):
2086
+
2087
+ | Method | Signature | Description |
2088
+ |---|---|---|
2089
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
2090
+ | `hide` | `(): void` | Close it programmatically. |
2091
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
2092
+ | `focus` | `(options?: FocusOptions): void` | Move focus to the screen surface (no-op while closed). |
2093
+
1619
2094
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1620
2095
 
1621
2096
  | Slot | Mode | Description |
1622
2097
  |---|---|---|
2098
+ | _(default)_ | inject | The screen body, below the title bar. |
1623
2099
  | `title` | replace | Rich header title; overrides the `headline` prop. |
1624
2100
  | `actions` | inject | Header trailing cluster (e.g. an avatar or overflow menu). |
1625
2101
 
@@ -1640,10 +2116,17 @@ _No events._
1640
2116
 
1641
2117
  | Property | Attribute | Type | Description |
1642
2118
  |---|---|---|---|
2119
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1643
2120
  | `orientation` | `orientation` | `undefined \| "vertical" \| "horizontal" \| "both"` | Which axis scrolls. `vertical` (default) · `horizontal` · `both`. The cross axis is clamped so content can't overflow it. |
1644
2121
 
1645
2122
  _No events._
1646
2123
 
2124
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2125
+
2126
+ | Slot | Mode | Description |
2127
+ |---|---|---|
2128
+ | _(default)_ | inject | The scrollable content. |
2129
+
1647
2130
  **Styleable parts** (restyle from outside via `kai-scroll-area::part(name)`):
1648
2131
 
1649
2132
  | Part | Description |
@@ -1658,15 +2141,16 @@ _No events._
1658
2141
 
1659
2142
  | Property | Attribute | Type | Description |
1660
2143
  |---|---|---|---|
2144
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1661
2145
  | `for` | `for` | `undefined \| string` | CSS id of the scroll container to control. When omitted the element walks up the DOM (outside its own shadow root) to find the nearest scrollable ancestor. Mirrors the `for` convention of `<label for="...">`. |
1662
- | `variant` | `variant` | `undefined \| "default" \| "ghost" \| "outline"` | Button visual variant: `'outline' \| 'ghost' \| 'default'`. Defaults to `'outline'`. |
2146
+ | `variant` | `variant` | `undefined \| "outline" \| "ghost" \| "default"` | Button visual variant: `'outline' \| 'ghost' \| 'default'`. Defaults to `'outline'`. |
1663
2147
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | Button size token. Defaults to `'icon'` (square). |
1664
2148
 
1665
2149
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1666
2150
 
1667
2151
  | Event | `detail` type | Description |
1668
2152
  |---|---|---|
1669
- | `kai-scroll` | `CustomEvent` | Emitted when the user clicks the button and `scrollToBottom()` is called. Carries no detail consumers use it to know a manual scroll occurred. |
2153
+ | `kai-scroll` | `CustomEvent` | Emitted when the user clicks the button and `scrollToBottom()` is called. Carries no detail; consumers use it to know a manual scroll occurred. |
1670
2154
 
1671
2155
  ---
1672
2156
 
@@ -1676,7 +2160,8 @@ _No events._
1676
2160
 
1677
2161
  | Property | Attribute | Type | Description |
1678
2162
  |---|---|---|---|
1679
- | `value` | `value` | `undefined \| string` | Controlled query — settable and reflected to the `value` attribute. Read `el.value` for live state. |
2163
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2164
+ | `value` | `value` | `undefined \| string` | Controlled query. Settable and reflected to the `value` attribute. Read `el.value` for live state. |
1680
2165
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder. Defaults to `Search…`. |
1681
2166
  | `icon` | `icon` | `undefined \| string` | Leading icon-NAME string (a curated name, URL, or text), resolved to a glyph the same way `kai-button`'s `icon` is. Defaults to `search`. |
1682
2167
  | `debounce` | `debounce` | `undefined \| number` | Debounce window for `kai-search`, in ms. Defaults to `200`. |
@@ -1691,6 +2176,13 @@ _No events._
1691
2176
  | `kai-search` | `CustomEvent<{ value: string }>` | The query changed (debounced live, and on clear). |
1692
2177
  | `kai-submit` | `CustomEvent<{ value: string }>` | Enter was pressed. |
1693
2178
 
2179
+ **Methods** (call on the element instance: `document.querySelector('kai-search').focus()`):
2180
+
2181
+ | Method | Signature | Description |
2182
+ |---|---|---|
2183
+ | `focus` | `(): void` | Focus the search input inside the shadow root. A native `focus()` on the host lands on the host itself and never reaches it. |
2184
+ | `clear` | `(): void` | Empty the field and fire `kai-search` with an empty value immediately, cancelling any debounced search still in flight, then return focus to the input. Same path as the trailing clear button. |
2185
+
1694
2186
  **Styleable parts** (restyle from outside via `kai-search::part(name)`):
1695
2187
 
1696
2188
  | Part | Description |
@@ -1707,8 +2199,9 @@ _No events._
1707
2199
 
1708
2200
  | Property | Attribute | Type | Description |
1709
2201
  |---|---|---|---|
2202
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1710
2203
  | `options` | — | `{ value: string; label: string; icon?: undefined \| string }[]` | The selectable segments, left to right. Set as a JS property (array). |
1711
- | `value` | `value` | `undefined \| string` | Controlled selected `value` settable and reflected to the `value` attribute. `el.value = 'preview'` drives it; choosing a segment updates it and fires `kai-change`. Read `el.value` for live state. |
2204
+ | `value` | `value` | `undefined \| string` | Controlled selected `value`. Settable and reflected to the `value` attribute. `el.value = 'preview'` drives it; choosing a segment updates it and fires `kai-change`. Read `el.value` for live state. |
1712
2205
  | `size` | `size` | `undefined \| "sm" \| "md"` | Control density: `sm` or `md`. Defaults to `md`. |
1713
2206
 
1714
2207
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -1732,7 +2225,8 @@ _No events._
1732
2225
 
1733
2226
  | Property | Attribute | Type | Description |
1734
2227
  |---|---|---|---|
1735
- | `orientation` | `orientation` | `undefined \| "vertical" \| "horizontal"` | `horizontal` (default, block + full-width) or `vertical` (a rule inside a flex/grid row — it stretches to the row height). |
2228
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2229
+ | `orientation` | `orientation` | `undefined \| "horizontal" \| "vertical"` | `horizontal` (default, block + full-width) or `vertical` (a rule inside a flex/grid row, stretching to the row height). |
1736
2230
 
1737
2231
  _No events._
1738
2232
 
@@ -1750,6 +2244,7 @@ _No events._
1750
2244
 
1751
2245
  | Property | Attribute | Type | Description |
1752
2246
  |---|---|---|---|
2247
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1753
2248
  | `label` | `label` | `undefined \| string` | Row label (primary text). Attribute: `label`. |
1754
2249
  | `description` | `description` | `undefined \| string` | Optional secondary description under the label. Attribute: `description`. |
1755
2250
 
@@ -1776,11 +2271,18 @@ _No events._
1776
2271
 
1777
2272
  | Property | Attribute | Type | Description |
1778
2273
  |---|---|---|---|
2274
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1779
2275
  | `heading` | `heading` | `undefined \| string` | Small section heading shown above the card. Attribute: `heading`. |
1780
2276
  | `description` | `description` | `undefined \| string` | Optional muted description under the heading. Attribute: `description`. |
1781
2277
 
1782
2278
  _No events._
1783
2279
 
2280
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2281
+
2282
+ | Slot | Mode | Description |
2283
+ |---|---|---|
2284
+ | _(default)_ | inject | The `<kai-setting-item>` rows in this group. |
2285
+
1784
2286
  **Styleable parts** (restyle from outside via `kai-settings-group::part(name)`):
1785
2287
 
1786
2288
  | Part | Description |
@@ -1796,6 +2298,7 @@ _No events._
1796
2298
 
1797
2299
  | Property | Attribute | Type | Description |
1798
2300
  |---|---|---|---|
2301
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1799
2302
  | `variant` | `variant` | `undefined \| "text" \| "rect" \| "circle"` | `text` (one or more lines), `rect` (a block), or `circle` (round). Defaults to `text`. |
1800
2303
  | `width` | `width` | `undefined \| string` | CSS width (e.g. `'12rem'`, `'60%'`). Defaults to full width (responsive); for `circle` it is the diameter. |
1801
2304
  | `height` | `height` | `undefined \| string` | CSS height. Defaults per variant (a text line height; circle = width). |
@@ -1817,10 +2320,17 @@ _No events._
1817
2320
 
1818
2321
  | Property | Attribute | Type | Description |
1819
2322
  |---|---|---|---|
1820
- | `skills` | | `{ id: string; name: string }[]` | The active skills to badge. Set as a JS property. |
2323
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2324
+ | `skills` | — | `undefined \| { id: string; name: string }[]` | The active skills to badge. Set as a JS property. Omit to supply them as `<kai-skill>` light-DOM children instead; when both are present the property's skills come first. Nothing renders when there are none. |
1821
2325
 
1822
2326
  _No events._
1823
2327
 
2328
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
2329
+
2330
+ | Child element | Attributes | Text content | Notes |
2331
+ |---|---|---|---|
2332
+ | `<kai-skill>` | `id` | yes | Parse a single light-DOM `<kai-skill>` element into a `Skill` descriptor. Attribute / content mapping: - `id` → Skill.id (falls back to `name` when absent) - `textContent` → Skill.name (the human-readable badge label) Example: `<kai-skill id="web-search">Web Search</kai-skill>` |
2333
+
1824
2334
  ---
1825
2335
 
1826
2336
  ### `kai-source` / `Source`
@@ -1829,9 +2339,10 @@ _No events._
1829
2339
 
1830
2340
  | Property | Attribute | Type | Description |
1831
2341
  |---|---|---|---|
2342
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1832
2343
  | `href` | `href` | `undefined \| string` | The URL this citation links to (the domain also seeds the default label/favicon). |
1833
2344
  | `label` | `label` | `undefined \| string` | Trigger label (defaults to the domain). |
1834
- | `headline` | `headline` | `undefined \| string` | Hover-card headline. Attribute: `headline` (`title` is avoided it's a global HTML attribute that reflects in a CE constructor and breaks it). |
2345
+ | `headline` | `headline` | `undefined \| string` | Hover-card headline. Attribute: `headline` (`title` is avoided because it is a global HTML attribute that reflects in a CE constructor and breaks it). |
1835
2346
  | `description` | `description` | `undefined \| string` | Hover-card body text describing the source. |
1836
2347
  | `showFavicon` | `show-favicon` | `undefined \| false \| true` | Show the source's favicon next to the trigger label. |
1837
2348
 
@@ -1845,12 +2356,19 @@ _No events._
1845
2356
 
1846
2357
  | Property | Attribute | Type | Description |
1847
2358
  |---|---|---|---|
1848
- | `sources` | | `{ href: string; title?: undefined \| string; description?: undefined \| string; label?: undefined \| string; showFavicon?: undefined \| false \| true }[]` | The sources to render. Set as a JS property. |
2359
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2360
+ | `sources` | — | `undefined \| { href: string; title?: undefined \| string; description?: undefined \| string; label?: undefined \| string; showFavicon?: undefined \| false \| true }[]` | The sources to render. Set as a JS property. Omit to supply them as `<kai-source>` light-DOM children instead; when both are present the property's sources come first. |
1849
2361
  | `showFavicon` | `show-favicon` | `undefined \| false \| true` | Show favicons on all items (per-item `showFavicon` overrides). |
1850
- | `numbered` | `numbered` | `undefined \| false \| true` | When true, each citation chip is labelled with its 1-based index in the merged (prop + declarative-children) list (`[1]`, `[2]`, …) instead of the per-item `label` or domain fallback. HTML attribute: `numbered` (boolean bare attribute or `numbered="true"`). JS property: `el.numbered = true`. |
2362
+ | `numbered` | `numbered` | `undefined \| false \| true` | When true, each citation chip is labelled with its 1-based index in the merged (prop + declarative-children) list (`[1]`, `[2]`, …) instead of the per-item `label` or domain fallback. HTML attribute: `numbered` (boolean: a bare attribute or `numbered="true"`). JS property: `el.numbered = true`. |
1851
2363
 
1852
2364
  _No events._
1853
2365
 
2366
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
2367
+
2368
+ | Child element | Attributes | Text content | Notes |
2369
+ |---|---|---|---|
2370
+ | `<kai-source>` | `description`, `headline`, `href`, `label`, `show-favicon` | — | Parse a single light-DOM `<kai-source>` element into a `KaiSourceItem` descriptor. Attribute mapping: - `href` → KaiSourceItem.href - `label` → KaiSourceItem.label - `headline` → KaiSourceItem.title (matches kai-source's prop name; "title" is a reserved HTMLElement attribute so kai-source uses "headline") - `description` → KaiSourceItem.description - `show-favicon`→ KaiSourceItem.showFavicon (bare boolean attribute) |
2371
+
1854
2372
  ---
1855
2373
 
1856
2374
  ### `kai-status` / `Status`
@@ -1859,6 +2377,7 @@ _No events._
1859
2377
 
1860
2378
  | Property | Attribute | Type | Description |
1861
2379
  |---|---|---|---|
2380
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1862
2381
  | `status` | `status` | `undefined \| "new" \| "online" \| "busy" \| "away" \| "offline"` | Presence/notification state → color. `new` (default) maps to the blue hue. |
1863
2382
  | `pulse` | `pulse` | `undefined \| false \| true` | Animated ping ring (off by default; respects prefers-reduced-motion). |
1864
2383
  | `label` | `label` | `undefined \| string` | Accessible name. Without it the dot is decorative. |
@@ -1880,10 +2399,11 @@ _No events._
1880
2399
 
1881
2400
  | Property | Attribute | Type | Description |
1882
2401
  |---|---|---|---|
1883
- | `suggestions` | | `(string \| { label: string; value?: undefined \| string; icon?: undefined \| string })[]` | The suggestions. Strings, or `{ label, value }` when the displayed text and the emitted value differ. Set as a JS property. |
1884
- | `variant` | `variant` | `undefined \| "default" \| "ghost" \| "outline"` | Chip style: `'outline'` (default), `'ghost'`, or `'default'` (filled). |
2402
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2403
+ | `suggestions` | | `undefined \| (string \| { label: string; value?: undefined \| string; icon?: undefined \| string })[]` | The suggestions. Strings, or `{ label, value }` when the displayed text and the emitted value differ. Set as a JS property. Omit to supply them as `<kai-suggestion>` light-DOM children instead; when both are present the property's suggestions come first. |
2404
+ | `variant` | `variant` | `undefined \| "outline" \| "ghost" \| "default"` | Chip style: `'outline'` (default), `'ghost'`, or `'default'` (filled). |
1885
2405
  | `size` | `size` | `undefined \| "md" \| "lg"` | Row height for `layout="list"`: `'md'` (default) or `'lg'` for taller rows. Chips are unaffected. |
1886
- | `layout` | `layout` | `undefined \| "list" \| "chips"` | Layout: `'chips'` (default) renders a wrapping row of rounded pills; `'list'` renders a vertical, full-width "Ideas for you" list each row is left-aligned with a leading `icon`, a label, and a hover background. |
2406
+ | `layout` | `layout` | `undefined \| "chips" \| "list"` | Layout: `'chips'` (default) renders a wrapping row of rounded pills; `'list'` renders a vertical, full-width "Ideas for you" list where each row is left-aligned with a leading `icon`, a label, and a hover background. |
1887
2407
  | `block` | `block` | `undefined \| false \| true` | Full-width left-aligned rows instead of pills. |
1888
2408
  | `highlight` | `highlight` | `undefined \| string` | Substring to highlight within each suggestion. |
1889
2409
 
@@ -1893,6 +2413,12 @@ _No events._
1893
2413
  |---|---|---|
1894
2414
  | `kai-select` | `CustomEvent<{ value: string }>` | A suggestion was clicked. |
1895
2415
 
2416
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
2417
+
2418
+ | Child element | Attributes | Text content | Notes |
2419
+ |---|---|---|---|
2420
+ | `<kai-suggestion>` | `icon`, `value` | yes | Parse a single `<kai-suggestion>` node into an `Item` descriptor. |
2421
+
1896
2422
  ---
1897
2423
 
1898
2424
  ### `kai-switch` / `Switch`
@@ -1901,7 +2427,8 @@ _No events._
1901
2427
 
1902
2428
  | Property | Attribute | Type | Description |
1903
2429
  |---|---|---|---|
1904
- | `checked` | `checked` | `undefined \| false \| true` | Controlled checked state — settable and reflected to the `checked` attribute. `el.checked = true` (or `<kai-switch checked>`) drives it; the toggle UI updates it and fires `kai-change`. Read `el.checked` for live state. |
2430
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2431
+ | `checked` | `checked` | `undefined \| false \| true` | Controlled checked state. Settable and reflected to the `checked` attribute. `el.checked = true` (or `<kai-switch checked>`) drives it; the toggle UI updates it and fires `kai-change`. Read `el.checked` for live state. |
1905
2432
  | `defaultChecked` | `default-checked` | `undefined \| false \| true` | Initial checked state on mount (uncontrolled seed). Bare attribute (`<kai-switch default-checked>`) turns it on. |
1906
2433
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable interaction. |
1907
2434
  | `label` | `label` | `undefined \| string` | Accessible label. |
@@ -1914,6 +2441,13 @@ _No events._
1914
2441
  |---|---|---|
1915
2442
  | `kai-change` | `CustomEvent<{ checked: false \| true }>` | The toggle changed. |
1916
2443
 
2444
+ **Methods** (call on the element instance: `document.querySelector('kai-switch').toggle()`):
2445
+
2446
+ | Method | Signature | Description |
2447
+ |---|---|---|
2448
+ | `toggle` | `(): void` | Flip the switch and fire `kai-change` (no-op while disabled). |
2449
+ | `focus` | `(options?: FocusOptions): void` | Focus the inner `role="switch"` button (the host element can't reach it). |
2450
+
1917
2451
  ---
1918
2452
 
1919
2453
  ### `kai-tabs` / `Tabs`
@@ -1922,6 +2456,7 @@ _No events._
1922
2456
 
1923
2457
  | Property | Attribute | Type | Description |
1924
2458
  |---|---|---|---|
2459
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1925
2460
  | `items` | — | `undefined \| { id: string; label?: undefined \| string; icon?: undefined \| string; disabled?: undefined \| false \| true }[]` | Tabs to render. Set as a JS property, not an HTML attribute. |
1926
2461
  | `value` | `value` | `undefined \| string` | Controlled selected id. Set as a JS property (or the `value` attribute); drive it from your app in response to `kai-tab-change`. Omit for uncontrolled. |
1927
2462
  | `defaultValue` | `default-value` | `undefined \| string` | Initial selected id when uncontrolled (use the `default-value` attribute in plain HTML). |
@@ -1935,6 +2470,13 @@ _No events._
1935
2470
  |---|---|---|
1936
2471
  | `kai-tab-change` | `CustomEvent<{ value: string }>` | A tab was selected (click, Enter/Space, or arrow-key move). `value` is the item's id. |
1937
2472
 
2473
+ **Methods** (call on the element instance: `document.querySelector('kai-tabs').select(…)`):
2474
+
2475
+ | Method | Signature | Description |
2476
+ |---|---|---|
2477
+ | `select` | `(id: string): void` | Select a tab by id (fires `kai-tab-change`). Ignores unknown/disabled ids. |
2478
+ | `focus` | `(): void` | Focus the active tab (or the first focusable tab). |
2479
+
1938
2480
  **Styleable parts** (restyle from outside via `kai-tabs::part(name)`):
1939
2481
 
1940
2482
  | Part | Description |
@@ -1950,7 +2492,8 @@ _No events._
1950
2492
 
1951
2493
  | Property | Attribute | Type | Description |
1952
2494
  |---|---|---|---|
1953
- | `data` | | `undefined \| Record<string, unknown>` | The tasks definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { tasks:[…], selectAll, confirmLabel, … }`. Import `TasksCardData` from `@kitn.ai/ui` for the full shape. |
2495
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2496
+ | `data` | — | `undefined \| { mode?: undefined \| "select" \| "progress"; heading?: undefined \| string; tasks: { id: string; label: string; description?: undefined \| string; checked?: undefined \| false \| true; disabled?: undefined \| false \| true }[]; selectAll?: undefined \| false \| true; confirmLabel?: undefined \| string; allowEmpty?: undefined \| false \| true; min?: undefined \| number; max?: undefined \| number; dismissible?: undefined \| false \| true }` | The tasks definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { tasks:[…], selectAll, confirmLabel, … }`. Import `TasksCardData` from `@kitn.ai/ui` for the full shape. |
1954
2497
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
1955
2498
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
1956
2499
  | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'submit', data:{ selected:[…] } }`. |
@@ -1963,7 +2506,18 @@ _No events._
1963
2506
 
1964
2507
  | Event | `detail` type | Description |
1965
2508
  |---|---|---|
1966
- | `kai-value-change` | `CustomEvent<{ value: string[] }>` | The selection changed on a toggle the selected ids in input order. |
2509
+ | `kai-value-change` | `CustomEvent<{ value: string[] }>` | The selection changed on a toggle. Carries the selected ids in input order. |
2510
+
2511
+ **Methods** (call on the element instance: `document.querySelector('kai-tasks').select(…)`):
2512
+
2513
+ | Method | Signature | Description |
2514
+ |---|---|---|
2515
+ | `select` | `(taskIds?: string[]): void` | Set the checked task ids (local-only, no emit), respecting disabled/max. With no arg, select all toggleable rows. |
2516
+ | `toggle` | `(taskId: string, checked?: boolean): void` | Toggle one task by id, honoring the max gate (no `checked` = flip). |
2517
+ | `send` | `(): void` | Confirm the current selection: emits the `submit` CardEvent + resolves (only when the min/max gate passes). Named `send`, not `submit`. |
2518
+ | `focus` | `(options?: FocusOptions): void` | Focus the task group (select-all checkbox if shown, else the first row). |
2519
+ | `dismiss` | `(): void` | Trigger the dismiss path (emit `dismiss` + collapse to the re-openable stub). |
2520
+ | `reopen` | `(): void` | Re-open a dismissed card from its stub (emit `reopen`). |
1967
2521
 
1968
2522
  ---
1969
2523
 
@@ -1973,6 +2527,7 @@ _No events._
1973
2527
 
1974
2528
  | Property | Attribute | Type | Description |
1975
2529
  |---|---|---|---|
2530
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1976
2531
  | `text` | `text` | `undefined \| string` | The text to shimmer. |
1977
2532
  | `as` | `as` | `undefined \| string` | Element tag to render as (default `span`). |
1978
2533
  | `duration` | `duration` | `undefined \| number` | Animation duration in seconds. |
@@ -1988,6 +2543,7 @@ _No events._
1988
2543
 
1989
2544
  | Property | Attribute | Type | Description |
1990
2545
  |---|---|---|---|
2546
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1991
2547
  | `text` | `text` | `undefined \| string` | The shimmering label, e.g. "Thinking…". |
1992
2548
  | `stoppable` | `stoppable` | `undefined \| false \| true` | When true, show a "stop" affordance that fires a `stop` event. |
1993
2549
  | `stopLabel` | `stop-label` | `undefined \| string` | Label for the stop affordance. |
@@ -2006,14 +2562,17 @@ _No events._
2006
2562
 
2007
2563
  | Property | Attribute | Type | Description |
2008
2564
  |---|---|---|---|
2009
- | `messages` | | `undefined \| { id: string; role: "user" \| "assistant"; content: string; reasoning?: undefined \| { text: string; label?: undefined \| string }; tools?: undefined \| { type: string; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string }[]; attachments?: undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]; 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" }[]` | The full message thread to render, newest last. Each entry carries its role, content, and optional reasoning/tools/attachments/actions/avatar. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). |
2010
- | `loading` | `loading` | `undefined \| false \| true` | Show a typing indicator on the pending assistant turn set while awaiting the assistant's reply. |
2011
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. |
2565
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2566
+ | `messages` | | `undefined \| { 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" }[]` | The full message thread to render, newest last. Each entry carries its role, ordered `parts`, and optional actions/avatar/feedback. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). |
2567
+ | `loading` | `loading` | `undefined \| false \| true` | Show a typing indicator on the pending assistant turn. Set it while awaiting the assistant's reply. |
2568
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. |
2012
2569
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name for syntax-highlighted code blocks (e.g. `'github-dark-dimmed'`). |
2013
2570
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Enable Shiki syntax highlighting in code blocks. Turn off to render plain `<pre>` blocks (lighter, no highlighter load). Default true. |
2014
2571
  | `actionsReveal` | `actions-reveal` | `undefined \| "always" \| "hover"` | Whether each message's action bar is always visible (`'always'`, default) or only revealed on hover of that message row (`'hover'`). |
2015
2572
  | `scrollButton` | `scroll-button` | `undefined \| false \| true` | Show the scroll-to-bottom button inside the scroll area. Default true. |
2016
2573
  | `class` | `class` | `undefined \| string` | Extra classes applied to the thread's inner root. |
2574
+ | `cardTypes` | — | `undefined \| Record<string, string>` | Optional card type -> custom-element tag overrides/additions for `card` parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. |
2575
+ | `cardSchemas` | — | `undefined \| Record<string, object>` | 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. |
2017
2576
 
2018
2577
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2019
2578
 
@@ -2021,6 +2580,12 @@ _No events._
2021
2580
  |---|---|---|
2022
2581
  | `kai-message-action` | `CustomEvent<{ messageId: string; action: string; state?: undefined \| "on" \| "off" }>` | A message's action button was clicked. `action` is the built-in name (`copy` / `like` / `dislike` / `regenerate` / `edit`) or a custom id. `state` is present only for the toggleable feedback votes: `'on'` when a like/dislike is set, `'off'` when re-tapped to clear. |
2023
2582
 
2583
+ **Methods** (call on the element instance: `document.querySelector('kai-thread').scrollToBottom(…)`):
2584
+
2585
+ | Method | Signature | Description |
2586
+ |---|---|---|
2587
+ | `scrollToBottom` | `(behavior?: ScrollBehavior): void` | Scroll the message list to the bottom (default `'smooth'`). |
2588
+
2024
2589
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2025
2590
 
2026
2591
  | Slot | Mode | Description |
@@ -2035,7 +2600,8 @@ _No events._
2035
2600
 
2036
2601
  | Property | Attribute | Type | Description |
2037
2602
  |---|---|---|---|
2038
- | `toasts` | | `{ id: string; message: string; variant?: undefined \| "error" \| "info" \| "success" \| "warning" \| "neutral"; appearance?: undefined \| "pill" \| "card"; inverse?: undefined \| false \| true; description?: undefined \| string; action?: undefined \| { label: string; onAction: () => void \| false }; duration?: undefined \| number; dismissible?: undefined \| false \| true; target?: undefined \| HTMLElement }[]` | The toasts to render. Newest is shown on top. Set as a JS property (array); pass a new array reference to update. |
2603
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2604
+ | `toasts` | — | `undefined \| { id: string; message: string; variant?: undefined \| "neutral" \| "success" \| "warning" \| "error" \| "info"; appearance?: undefined \| "pill" \| "card"; inverse?: undefined \| false \| true; description?: undefined \| string; action?: undefined \| { label: string; onAction: () => void \| false }; duration?: undefined \| number; dismissible?: undefined \| false \| true; target?: undefined \| HTMLElement }[]` | 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. |
2039
2605
  | `position` | `position` | `undefined \| "top-center" \| "top-right" \| "top-left" \| "bottom-center" \| "bottom-right" \| "bottom-left"` | Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … |
2040
2606
  | `max` | `max` | `undefined \| number` | Max simultaneously-visible toasts; the rest queue. Defaults to `3`. |
2041
2607
  | `stack` | `stack` | `undefined \| "expanded" \| "collapsed"` | Stacking: 'expanded' (default, full column) \| 'collapsed' (Sonner-style pile that expands on hover/focus). Attribute: stack. |
@@ -2048,7 +2614,7 @@ _No events._
2048
2614
  | Event | `detail` type | Description |
2049
2615
  |---|---|---|
2050
2616
  | `kai-action` | `CustomEvent<{ id: string; label: string }>` | A toast's action button was pressed. |
2051
- | `kai-dismiss` | `CustomEvent<{ id: string; reason: "action" \| "timeout" \| "close" }>` | A toast left the stack. `reason` is `'timeout' \| 'close' \| 'action'`. |
2617
+ | `kai-dismiss` | `CustomEvent<{ id: string; reason: "timeout" \| "close" \| "action" }>` | A toast left the stack. `reason` is `'timeout' \| 'close' \| 'action'`. |
2052
2618
 
2053
2619
  ---
2054
2620
 
@@ -2058,10 +2624,11 @@ _No events._
2058
2624
 
2059
2625
  | Property | Attribute | Type | Description |
2060
2626
  |---|---|---|---|
2061
- | `tool` | | `undefined \| { type: string; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string }` | The tool-call to display. Set as a JS property. |
2627
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2628
+ | `tool` | — | `undefined \| { 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 } }` | The tool-call to display. Set as a JS property. |
2062
2629
  | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute; the element still self-manages on trigger click). Set `el.open = true`, or `<kai-tool open>`; listen for `kai-open-change`. |
2063
2630
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
2064
- | `disabled` | `disabled` | `undefined \| false \| true` | Gate the disclosure trigger programmatic `show()/hide()/toggle()` still work, but the trigger click no longer toggles. |
2631
+ | `disabled` | `disabled` | `undefined \| false \| true` | Gate the disclosure trigger: programmatic `show()/hide()/toggle()` still work, but the trigger click no longer toggles. |
2065
2632
 
2066
2633
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2067
2634
 
@@ -2069,6 +2636,14 @@ _No events._
2069
2636
  |---|---|---|
2070
2637
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The panel expanded or collapsed (by trigger click or a method). |
2071
2638
 
2639
+ **Methods** (call on the element instance: `document.querySelector('kai-tool').show()`):
2640
+
2641
+ | Method | Signature | Description |
2642
+ |---|---|---|
2643
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
2644
+ | `hide` | `(): void` | Close it programmatically. |
2645
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
2646
+
2072
2647
  ---
2073
2648
 
2074
2649
  ### `kai-tooltip` / `Tooltip`
@@ -2077,6 +2652,7 @@ _No events._
2077
2652
 
2078
2653
  | Property | Attribute | Type | Description |
2079
2654
  |---|---|---|---|
2655
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2080
2656
  | `content` | `content` | `undefined \| string` | The hint text shown on hover/focus of the slotted trigger. |
2081
2657
  | `openDelay` | `open-delay` | `undefined \| number` | Delay (ms) before the tooltip appears on hover. Defaults to 600. Focus shows it immediately regardless. |
2082
2658
  | `closeDelay` | `close-delay` | `undefined \| number` | Delay (ms) before it hides after the pointer leaves. Defaults to 0 (hides immediately). |
@@ -2091,6 +2667,20 @@ _No events._
2091
2667
  |---|---|---|
2092
2668
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The tooltip opened or closed (by hover/focus, outside-click, or a method). |
2093
2669
 
2670
+ **Methods** (call on the element instance: `document.querySelector('kai-tooltip').show()`):
2671
+
2672
+ | Method | Signature | Description |
2673
+ |---|---|---|
2674
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
2675
+ | `hide` | `(): void` | Close it programmatically. |
2676
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
2677
+
2678
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2679
+
2680
+ | Slot | Mode | Description |
2681
+ |---|---|---|
2682
+ | _(default)_ | inject | The TRIGGER the tooltip describes. The tip text is the `text` prop. |
2683
+
2094
2684
  ---
2095
2685
 
2096
2686
  ### `kai-voice-input` / `VoiceInput`
@@ -2099,7 +2689,8 @@ _No events._
2099
2689
 
2100
2690
  | Property | Attribute | Type | Description |
2101
2691
  |---|---|---|---|
2102
- | `transcribe` | | `undefined \| (audio: Blob) => Promise<string>` | Transcriber the host supplies — records audio, returns the text. This is a **function-valued property** (`el.transcribe = async blob => '...'`) because a value-returning callback can't be modelled as a fire-and-forget event. |
2692
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2693
+ | `transcribe` | — | `undefined \| (audio: Blob) => Promise<string>` | Transcriber the host supplies: records audio, returns the text. This is a **function-valued property** (`el.transcribe = async blob => '...'`) because a value-returning callback can't be modelled as a fire-and-forget event. |
2103
2694
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the mic button (non-interactive). |
2104
2695
  | `recognitionLang` | `recognition-lang` | `undefined \| string` | BCP-47 language tag for the native `SpeechRecognition` path (e.g. `en-US`). Attribute: `recognition-lang` (the plain `lang` attribute is reserved by `HTMLElement` and can't be a custom-element property). No effect when `transcribe` is set or the browser lacks SpeechRecognition. |
2105
2696
  | `interim` | `interim` | `undefined \| false \| true` | Emit live partial transcripts (`kai-transcript-interim`) during native recognition. Attribute: `interim`. No-op on the transcribe/fallback paths. |
@@ -2108,10 +2699,17 @@ _No events._
2108
2699
 
2109
2700
  | Event | `detail` type | Description |
2110
2701
  |---|---|---|
2111
- | `kai-audio-captured` | `CustomEvent<{ blob: Blob }>` | Raw audio captured (before transcription) for hosts that prefer to handle transcription themselves instead of via the `transcribe` property. Also the unsupported-fallback signal: no `transcribe`, no SpeechRecognition, so only the blob is produced (no text). |
2112
- | `kai-recording-change` | `CustomEvent<{ recording: false \| true }>` | Recording started or stopped lets the host drive its own UI (waveform, push-to-talk indicator) in sync with the mic. Fires on real transitions only (manual click and programmatic start()/stop()), never on mount. |
2702
+ | `kai-audio-captured` | `CustomEvent<{ blob: Blob }>` | Raw audio captured (before transcription), for hosts that prefer to handle transcription themselves instead of via the `transcribe` property. Also the unsupported-fallback signal: no `transcribe`, no SpeechRecognition, so only the blob is produced (no text). |
2703
+ | `kai-recording-change` | `CustomEvent<{ recording: false \| true }>` | Recording started or stopped. Lets the host drive its own UI (waveform, push-to-talk indicator) in sync with the mic. Fires on real transitions only (manual click and programmatic start()/stop()), never on mount. |
2113
2704
  | `kai-transcript-interim` | `CustomEvent<{ text: string }>` | Live partial transcript during native recognition (only when `interim` is set). Fires repeatedly before the final `kai-transcription`. |
2114
- | `kai-transcription` | `CustomEvent<{ text: string }>` | Final transcript the `transcribe` property resolved, OR native `SpeechRecognition` produced final text (no `transcribe` set). |
2705
+ | `kai-transcription` | `CustomEvent<{ text: string }>` | Final transcript: the `transcribe` property resolved, OR native `SpeechRecognition` produced final text (no `transcribe` set). |
2706
+
2707
+ **Methods** (call on the element instance: `document.querySelector('kai-voice-input').start()`):
2708
+
2709
+ | Method | Signature | Description |
2710
+ |---|---|---|
2711
+ | `start` | `(): void` | Begin recording programmatically (e.g. push-to-talk bound to a global key). Runs the same getUserMedia path as clicking the mic; no-ops if already recording. |
2712
+ | `stop` | `(): void` | Stop the in-progress recording, producing the blob (→ kai-audio-captured) and running transcription. Pairs with start() for push-to-talk. |
2115
2713
 
2116
2714
  ---
2117
2715
 
@@ -2121,17 +2719,27 @@ _No events._
2121
2719
 
2122
2720
  | Property | Attribute | Type | Description |
2123
2721
  |---|---|---|---|
2722
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2124
2723
  | `text` | `text` | `undefined \| string` | The utterance to read aloud. |
2125
2724
  | `autoplay` | `autoplay` | `undefined \| false \| true` | Speak automatically when `text` is set/changed. |
2126
- | `synthesize` | — | `undefined \| (text: string) => Promise<Blob>` | TTS model seam the host supplies given text, returns an audio `Blob` to play. This is a **function-valued property** (`el.synthesize = async text => blob`); when set, the native `speechSynthesis` path is bypassed. Mirrors `<kai-voice-input>`'s `transcribe`. A value-returning callback can't be modelled as a fire-and-forget event, hence a property. |
2725
+ | `synthesize` | — | `undefined \| (text: string) => Promise<Blob>` | TTS model seam the host supplies: given text, returns an audio `Blob` to play. This is a **function-valued property** (`el.synthesize = async text => blob`); when set, the native `speechSynthesis` path is bypassed. Mirrors `<kai-voice-input>`'s `transcribe`. A value-returning callback can't be modelled as a fire-and-forget event, hence a property. |
2127
2726
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the button (non-interactive). |
2128
2727
 
2129
2728
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2130
2729
 
2131
2730
  | Event | `detail` type | Description |
2132
2731
  |---|---|---|
2133
- | `kai-speaking-change` | `CustomEvent<{ speaking: false \| true }>` | Playback started or stopped drive your own UI in sync. Fires on real transitions only (manual click and programmatic speak()/stop()), never on mount. |
2134
- | `kai-synthesized` | `CustomEvent<{ blob: Blob }>` | The model path (`synthesize`) resolved audio the raw `Blob` before playback. |
2732
+ | `kai-speaking-change` | `CustomEvent<{ speaking: false \| true }>` | Playback started or stopped. Drive your own UI in sync. Fires on real transitions only (manual click and programmatic speak()/stop()), never on mount. |
2733
+ | `kai-synthesized` | `CustomEvent<{ blob: Blob }>` | The model path (`synthesize`) resolved audio: the raw `Blob` before playback. |
2734
+
2735
+ **Methods** (call on the element instance: `document.querySelector('kai-voice-output').speak()`):
2736
+
2737
+ | Method | Signature | Description |
2738
+ |---|---|---|
2739
+ | `speak` | `(): void` | Speak the current `text` (native, or via `synthesize` if set). |
2740
+ | `pause` | `(): void` | Pause playback (resumable). |
2741
+ | `resume` | `(): void` | Resume paused playback. |
2742
+ | `stop` | `(): void` | Stop playback and reset. |
2135
2743
 
2136
2744
  **Styleable parts** (restyle from outside via `kai-voice-output::part(name)`):
2137
2745
 
@@ -2147,16 +2755,17 @@ _No events._
2147
2755
 
2148
2756
  | Property | Attribute | Type | Description |
2149
2757
  |---|---|---|---|
2150
- | `groups` | | `{ id: string; userId?: undefined \| string; teamId?: undefined \| string; name: string; sortOrder: number; createdAt: string }[]` | Pre-bucketed conversation groups for the sidebar. Set as a JS property. |
2151
- | `conversations` | — | `{ id: string; title: string; groupId?: undefined \| string; scope: { type: "document" \| "collection"; documentId?: undefined \| string; filters?: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }; messageCount: number; lastMessageAt: string; updatedAt: string; trailing?: undefined \| string }[]` | Flat conversation list (auto-bucketed if `groups` is empty). Set as a JS property. |
2758
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2759
+ | `groups` | — | `undefined \| { id: string; userId?: undefined \| string; teamId?: undefined \| string; name: string; sortOrder: number; createdAt: string }[]` | The sidebar's section headers, 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 sidebar. Set as a JS property. |
2760
+ | `conversations` | — | `undefined \| { id: string; title: string; groupId?: undefined \| string; scope: { type: "document" \| "collection"; documentId?: undefined \| string; filters?: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }; messageCount: number; lastMessageAt: string; updatedAt: string; trailing?: undefined \| string }[]` | Every conversation in the sidebar, 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 (headerless when `compact`), so nothing you pass in is ever dropped. There is no recency bucketing. Set as a JS property. Omit for an empty sidebar, or when `no-conversations` replaces the built-in list with your own `sidebar-header` content. |
2152
2761
  | `activeId` | `active-id` | `undefined \| string` | Id of the open conversation, highlighted in the sidebar. |
2153
- | `messages` | — | `{ id: string; role: "user" \| "assistant"; content: string; reasoning?: undefined \| { text: string; label?: undefined \| string }; tools?: undefined \| { type: string; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string }[]; attachments?: undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]; 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" }[]` | The active conversation's message thread, newest last. Set as a JS property. |
2762
+ | `messages` | — | `undefined \| { 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" }[]` | The active conversation's message thread, newest last. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). Omit for an empty thread. |
2154
2763
  | `value` | `value` | `undefined \| string` | |
2155
2764
  | `placeholder` | `placeholder` | `undefined \| string` | |
2156
2765
  | `loading` | `loading` | `undefined \| false \| true` | |
2157
2766
  | `suggestions` | — | `undefined \| string[]` | |
2158
2767
  | `suggestionMode` | `suggestion-mode` | `undefined \| "submit" \| "fill"` | |
2159
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | |
2768
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | |
2160
2769
  | `codeTheme` | `code-theme` | `undefined \| string` | |
2161
2770
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | |
2162
2771
  | `chatTitle` | `chat-title` | `undefined \| string` | |
@@ -2176,6 +2785,8 @@ _No events._
2176
2785
  | `collapseBelow` | `collapse-below` | `undefined \| number` | Auto-collapse the rail when the workspace's own width drops below this many px, and re-expand when it grows back above. Uncontrolled only (it never fights an app-driven `sidebarCollapsed`); omit to disable. Fires `kai-sidebar-toggle`. Attribute: `collapse-below`. |
2177
2786
  | `compact` | `compact` | `undefined \| false \| true` | Render Recents as dense single-line rows (a leading dot + title, no count). |
2178
2787
  | `noConversations` | `no-conversations` | `undefined \| false \| true` | Suppress the built-in ConversationList so the `sidebar-header` slot owns the whole rail flex region (for apps that supply their own rail nav). Default false. Attribute: `no-conversations`. |
2788
+ | `cardTypes` | — | `undefined \| Record<string, string>` | Optional card type -> custom-element tag overrides/additions for `card` parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. |
2789
+ | `cardSchemas` | — | `undefined \| Record<string, object>` | 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. |
2179
2790
 
2180
2791
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2181
2792
 
@@ -2192,6 +2803,18 @@ _No events._
2192
2803
  | `kai-value-change` | `CustomEvent<{ value: string }>` | Fired on every input change. |
2193
2804
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Mic / voice button was clicked. |
2194
2805
 
2806
+ **Methods** (call on the element instance: `document.querySelector('kai-workspace').toggleSidebar()`):
2807
+
2808
+ | Method | Signature | Description |
2809
+ |---|---|---|
2810
+ | `toggleSidebar` | `(): void` | Collapse/expand the conversation sidebar and fire `kai-sidebar-toggle`. |
2811
+ | `collapseSidebar` | `(): void` | Force the conversation sidebar collapsed (fires `kai-sidebar-toggle`). |
2812
+ | `expandSidebar` | `(): void` | Force the conversation sidebar expanded (fires `kai-sidebar-toggle`). |
2813
+ | `focus` | `(options?: FocusOptions): void` | Focus the thread's composer. |
2814
+ | `clear` | `(): void` | Clear the thread draft + staged attachments. |
2815
+ | `send` | `(): void` | Submit the current thread draft programmatically (fires `kai-submit`). |
2816
+ | `scrollToBottom` | `(behavior?: ScrollBehavior): void` | Scroll the thread to the newest message. |
2817
+
2195
2818
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2196
2819
 
2197
2820
  | Slot | Mode | Description |