@kitn.ai/ui 0.20.1 → 0.21.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 (500) 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 +68 -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-D3Y5qiij.js +338 -0
  50. package/dist/create-tween-DGKY1FDY.js +341 -0
  51. package/dist/create-tween-paho5Rh-.js +7 -0
  52. package/dist/custom-elements.json +1681 -454
  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-C-2LvGDG.js} +1 -1
  70. package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-CaYoImEk.js} +1 -1
  71. package/dist/elements/chunks/arrow-left-Bb46H7w9.js +1 -0
  72. package/dist/elements/chunks/artifact-BBIscOxJ.js +1 -0
  73. package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-BB56zoji.js} +1 -1
  74. package/dist/elements/chunks/audio-visualizer-BxOx4THz.js +1 -0
  75. package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-SxLSZ19R.js} +1 -1
  76. package/dist/elements/chunks/{button-ZWNif3--.js → button-CmkUkz6c.js} +1 -1
  77. package/dist/elements/chunks/card-renderer-Db4ffkWv.js +1 -0
  78. package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
  79. package/dist/elements/chunks/chat-thread-B3Ks-cKr.js +1 -0
  80. package/dist/elements/chunks/check-C8desc_h.js +1 -0
  81. package/dist/elements/chunks/chevron-down-DKtc2HNB.js +1 -0
  82. package/dist/elements/chunks/chevron-right-Dw4GPhwL.js +1 -0
  83. package/dist/elements/chunks/choice-card-Q9cYYDyM.js +1 -0
  84. package/dist/elements/chunks/circle-BeokBsvB.js +1 -0
  85. package/dist/elements/chunks/circle-check-wJSqt859.js +1 -0
  86. package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-D0MEfxOM.js} +1 -1
  87. package/dist/elements/chunks/code-block-C8VzAraE.js +1 -0
  88. package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-Cvj61obE.js} +1 -1
  89. package/dist/elements/chunks/{composer-BguS0QL_.js → composer-D86ztyL0.js} +2 -2
  90. package/dist/elements/chunks/confirm-card-BdyDAGLw.js +1 -0
  91. package/dist/elements/chunks/context-WHDl-xSx.js +1 -0
  92. package/dist/elements/chunks/conversation-list-CdR14_Y4.js +1 -0
  93. package/dist/elements/chunks/create-tween-C_A_B638.js +7 -0
  94. package/dist/elements/chunks/{default-input-Cqqilg9t.js → default-input-CSZqUM81.js} +1 -1
  95. package/dist/elements/chunks/define-BzPnNsXg.js +1 -0
  96. package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DVKsoc1F.js} +1 -1
  97. package/dist/elements/chunks/{download-Dsbg3ste.js → download-BjA54IMJ.js} +1 -1
  98. package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-BjVVHWXH.js} +1 -1
  99. package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BNedmqfi.js} +1 -1
  100. package/dist/elements/chunks/embed-DQsl4XEq.js +1 -0
  101. package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-Di-dw2CU.js} +1 -1
  102. package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-B3sAODCV.js} +1 -1
  103. package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-Dgto-bMn.js} +1 -1
  104. package/dist/elements/chunks/{folder-glP5rha4.js → folder-D6rMadFV.js} +1 -1
  105. package/dist/elements/chunks/form-CILxvhQ7.js +1 -0
  106. package/dist/elements/chunks/hover-card-BtiF4XiU.js +1 -0
  107. package/dist/elements/chunks/{icon-BLb8beoi.js → icon-DKkmze3y.js} +1 -1
  108. package/dist/elements/chunks/{info-CIsv92JK.js → info-CWRjZkuy.js} +1 -1
  109. package/dist/elements/chunks/{input-Dds1kFyt.js → input--QJBK6BA.js} +1 -1
  110. package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-C3zuKh8R.js} +1 -1
  111. package/dist/elements/chunks/{link-i4r_k7z4.js → link-BL5JA7Q0.js} +1 -1
  112. package/dist/elements/chunks/link-preview-CU37bbXh.js +1 -0
  113. package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-XmpyxyRH.js} +1 -1
  114. package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-3Gv5UZ_o.js} +1 -1
  115. package/dist/elements/chunks/message-CnBA2OUt.js +1 -0
  116. package/dist/elements/chunks/message-X5NZbq-W.js +1 -0
  117. package/dist/elements/chunks/message-square-CnpAm_P0.js +1 -0
  118. package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-DwyrUTcM.js} +1 -1
  119. package/dist/elements/chunks/{model-switcher-B94YAKsM.js → model-switcher-CclaJEHt.js} +1 -1
  120. package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-Cepd-KcS.js} +1 -1
  121. package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CdwKjrlu.js} +1 -1
  122. package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-DMVXBD3b.js} +1 -1
  123. package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-Di_WZE_5.js} +1 -1
  124. package/dist/elements/chunks/reasoning-D0jMch5u.js +1 -0
  125. package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-1znbL1Gv.js} +1 -1
  126. package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-DVhhgx8n.js} +1 -1
  127. package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-Bs4bFS1P.js} +1 -1
  128. package/dist/elements/chunks/scroll-button-Bet5NLc4.js +1 -0
  129. package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-C5Gh8qGC.js} +1 -1
  130. package/dist/elements/chunks/{settings-B8lmec4D.js → settings-5vmEFg0K.js} +1 -1
  131. package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-BWLB38kz.js} +1 -1
  132. package/dist/elements/chunks/{share-C3lTiYGt.js → share-BZ3G_DaB.js} +1 -1
  133. package/dist/elements/chunks/skeleton-DafPJQTp.js +1 -0
  134. package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
  135. package/dist/elements/chunks/source-D_qj_6uJ.js +1 -0
  136. package/dist/elements/chunks/{star-CSJtGxgv.js → star-Cuv7j2IE.js} +1 -1
  137. package/dist/elements/chunks/{store-CHyW95TP.js → store-C1StPHfW.js} +1 -1
  138. package/dist/elements/chunks/tasks-card-EyoBZfpA.js +1 -0
  139. package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D9gRSh34.js} +1 -1
  140. package/dist/elements/chunks/textarea-DuhtHfcm.js +1 -0
  141. package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-B9hwv43w.js} +1 -1
  142. package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-Bm3DUEDe.js} +1 -1
  143. package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-CDtW7L0z.js} +1 -1
  144. package/dist/elements/chunks/tooltip-eB226mGK.js +1 -0
  145. package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C1SNzTqK.js} +1 -1
  146. package/dist/elements/chunks/use-card-resolution-YZh2MvI5.js +1 -0
  147. package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Cj1F7D0l.js} +2 -2
  148. package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-T9gpt7QE.js} +1 -1
  149. package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-CxU9z-TE.js} +2 -2
  150. package/dist/elements/chunks/{video-B7vmenoC.js → video-CK4R7Rus.js} +1 -1
  151. package/dist/elements/chunks/x-D1qx0aLd.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 +7 -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 +2 -2
  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/slots.d.ts +16 -0
  208. package/dist/elements/source.js +1 -1
  209. package/dist/elements/status.js +1 -1
  210. package/dist/elements/switch.js +1 -1
  211. package/dist/elements/tabs.js +1 -1
  212. package/dist/elements/tasks.js +1 -1
  213. package/dist/elements/text-shimmer.js +1 -1
  214. package/dist/elements/thinking-bar.js +1 -1
  215. package/dist/elements/thread.js +1 -1
  216. package/dist/elements/toast.js +1 -1
  217. package/dist/elements/tool.js +1 -1
  218. package/dist/elements/tooltip.js +1 -1
  219. package/dist/elements/validate-messages.d.ts +25 -0
  220. package/dist/elements/voice-input.js +1 -1
  221. package/dist/elements/voice-output.js +1 -1
  222. package/dist/elements.d.ts +2725 -214
  223. package/dist/index.d.ts +163 -125
  224. package/dist/index.js +6336 -5802
  225. package/dist/index.server.d.ts +5 -0
  226. package/dist/index.server.js +5446 -4937
  227. package/dist/kai-provider.es.d.ts +5 -0
  228. package/dist/kai-provider.es.js +79 -77
  229. package/dist/kai.es.d.ts +5 -0
  230. package/dist/kai.es.js +1 -1
  231. package/dist/llms/llms-full.txt +762 -148
  232. package/dist/llms/llms.txt +57 -5
  233. package/dist/mcp.es.js +4010 -770
  234. package/dist/primitives/card-component-types.d.ts +11 -0
  235. package/dist/primitives/card-data-types.d.ts +196 -0
  236. package/dist/primitives/card-host.d.ts +1 -1
  237. package/dist/primitives/card-recovery.d.ts +3 -3
  238. package/dist/primitives/card-registry.d.ts +4 -14
  239. package/dist/primitives/card-resolution.d.ts +1 -1
  240. package/dist/primitives/card-routing.d.ts +1 -1
  241. package/dist/primitives/card-tags.d.ts +11 -0
  242. package/dist/primitives/card-validate-cards.d.ts +57 -0
  243. package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
  244. package/dist/primitives/card-validate-schemas.d.ts +17 -0
  245. package/dist/primitives/card-validate.d.ts +24 -0
  246. package/dist/primitives/create-kai-chat.d.ts +3 -3
  247. package/dist/primitives/embed-providers.d.ts +1 -1
  248. package/dist/primitives/link-preview.d.ts +1 -1
  249. package/dist/primitives/message-feedback.d.ts +2 -2
  250. package/dist/primitives/toast-store.d.ts +1 -1
  251. package/dist/react/index.d.ts +717 -299
  252. package/dist/react/use-kai-chat.d.ts +8 -4
  253. package/dist/react.d.ts +5 -0
  254. package/dist/react.js +417 -316
  255. package/dist/register-impl-bzrZ44ft.js +145 -0
  256. package/dist/remote/host-embed.d.ts +1 -1
  257. package/dist/remote/index.d.ts +2 -2
  258. package/dist/remote/provider-runtime.d.ts +2 -2
  259. package/dist/remote/provider.d.ts +2 -2
  260. package/dist/remote/validate.d.ts +1 -1
  261. package/dist/remote/wire.d.ts +1 -1
  262. package/dist/schemas/artifact.schema.json +92 -0
  263. package/dist/schemas/embed.schema.json +0 -2
  264. package/dist/schemas/from-tool-call.d.ts +48 -0
  265. package/dist/schemas/index.d.ts +48 -0
  266. package/dist/schemas/link.schema.json +0 -2
  267. package/dist/schemas/provider-subsets.d.ts +136 -0
  268. package/dist/schemas/registry.d.ts +140 -0
  269. package/dist/schemas/tool-defs.d.ts +121 -0
  270. package/dist/schemas.d.ts +5 -0
  271. package/dist/schemas.js +717 -0
  272. package/dist/solid-BCJUQqwS.js +22750 -0
  273. package/dist/solid-BdENBcEq.js +20108 -0
  274. package/dist/solid.d.ts +66 -0
  275. package/dist/solid.js +261 -0
  276. package/dist/solid.server.d.ts +5 -0
  277. package/dist/solid.server.js +261 -0
  278. package/dist/state/index.d.ts +14 -4
  279. package/dist/state/messages.d.ts +8 -3
  280. package/dist/state/mock.d.ts +46 -0
  281. package/dist/state/parts.d.ts +85 -0
  282. package/dist/state/stream.d.ts +15 -9
  283. package/dist/state.d.ts +5 -0
  284. package/dist/state.js +253 -84
  285. package/dist/ui/action-icons.d.ts +1 -1
  286. package/dist/ui/collapsible.d.ts +11 -6
  287. package/dist/ui/dropdown.d.ts +47 -42
  288. package/dist/ui/pane-group.d.ts +1 -1
  289. package/dist/variant-aurora-1wMcXRUS.js +381 -0
  290. package/dist/variant-aurora-BKHHiOgL.js +391 -0
  291. package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-DOb_o4pC.js} +1 -1
  292. package/dist/{variant-custom-Dif-IV4u.js → variant-custom-CJGB0Kbw.js} +1 -1
  293. package/dist/variant-custom-Co0CkXJF.js +200 -0
  294. package/dist/variant-custom-DWSSgmd2.js +170 -0
  295. package/dist/variant-wave-C3-45cL6.js +218 -0
  296. package/dist/{variant-wave-DbfNdvSi.js → variant-wave-CEODq-9q.js} +1 -1
  297. package/dist/variant-wave-jdXBnCUZ.js +208 -0
  298. package/dist/wire/chunk.d.ts +171 -0
  299. package/dist/wire/consume.d.ts +26 -0
  300. package/dist/wire/encode.d.ts +148 -0
  301. package/dist/wire/formats/anthropic.d.ts +2 -0
  302. package/dist/wire/formats/openai.d.ts +5 -0
  303. package/dist/wire/index.d.ts +14 -0
  304. package/dist/wire/read.d.ts +26 -0
  305. package/dist/wire/sink-helpers.d.ts +21 -0
  306. package/dist/wire/sse.d.ts +33 -0
  307. package/dist/wire.d.ts +5 -53
  308. package/dist/wire.js +898 -0
  309. package/frameworks/react/index.tsx +183 -154
  310. package/frameworks/react/runtime.tsx +21 -1
  311. package/frameworks/react/use-kai-chat.tsx +11 -1
  312. package/llms-full.txt +762 -148
  313. package/llms.txt +57 -5
  314. package/package.json +61 -8
  315. package/src/agent-tooling/archetypes.ts +96 -0
  316. package/src/agent-tooling/integrations/anthropic.ts +412 -0
  317. package/src/agent-tooling/integrations/cloudflare.ts +136 -52
  318. package/src/agent-tooling/integrations/langgraph.ts +67 -13
  319. package/src/agent-tooling/integrations/mastra.ts +120 -12
  320. package/src/agent-tooling/integrations/mock.ts +43 -8
  321. package/src/agent-tooling/integrations/ollama.ts +61 -24
  322. package/src/agent-tooling/integrations/openai.ts +87 -0
  323. package/src/agent-tooling/integrations/openrouter.ts +56 -8
  324. package/src/agent-tooling/integrations/pi.ts +79 -23
  325. package/src/agent-tooling/integrations/pydantic-ai.ts +24 -2
  326. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +84 -13
  327. package/src/agent-tooling/mcp/manifest.ts +206 -25
  328. package/src/agent-tooling/mcp/tools/debug.ts +13 -7
  329. package/src/agent-tooling/mcp/tools/reference.ts +285 -6
  330. package/src/agent-tooling/mcp/tools/scaffold.ts +3345 -675
  331. package/src/agent-tooling/registry.ts +17 -0
  332. package/src/agent-tooling/types.ts +229 -1
  333. package/src/components/artifact-card.tsx +120 -0
  334. package/src/components/audio-visualizer/index.tsx +9 -0
  335. package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
  336. package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
  337. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
  338. package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
  339. package/src/components/card-fallback.tsx +42 -10
  340. package/src/components/card-renderer.tsx +146 -9
  341. package/src/components/chat-container.tsx +4 -0
  342. package/src/components/chat-thread.tsx +118 -51
  343. package/src/components/choice-card.tsx +17 -29
  344. package/src/components/composer.tsx +12 -2
  345. package/src/components/confirm-card.tsx +24 -20
  346. package/src/components/context.tsx +12 -4
  347. package/src/components/conversation-list.tsx +18 -2
  348. package/src/components/file-tree.tsx +11 -22
  349. package/src/components/file-upload.tsx +14 -8
  350. package/src/components/form.tsx +14 -47
  351. package/src/components/loader.tsx +39 -12
  352. package/src/components/message-skills.tsx +4 -0
  353. package/src/components/message.tsx +294 -46
  354. package/src/components/reasoning.tsx +92 -5
  355. package/src/components/response-compare-types.ts +25 -0
  356. package/src/components/response-compare.tsx +4 -9
  357. package/src/components/source.tsx +50 -24
  358. package/src/components/tasks-card.tsx +10 -33
  359. package/src/components/thread.tsx +84 -42
  360. package/src/components/toast.tsx +11 -5
  361. package/src/components/tool-classify.ts +26 -0
  362. package/src/components/tool-types.ts +31 -1
  363. package/src/elements/agent-card.tsx +11 -11
  364. package/src/elements/artifact.tsx +15 -7
  365. package/src/elements/attachments.tsx +6 -4
  366. package/src/elements/avatar.tsx +1 -1
  367. package/src/elements/button.tsx +3 -3
  368. package/src/elements/card.tsx +2 -2
  369. package/src/elements/cards.tsx +109 -14
  370. package/src/elements/chain-of-thought.tsx +14 -10
  371. package/src/elements/chat-scope-picker.tsx +9 -7
  372. package/src/elements/chat-types.ts +59 -6
  373. package/src/elements/chat-workspace.tsx +50 -9
  374. package/src/elements/chat.tsx +64 -3
  375. package/src/elements/choice.tsx +8 -8
  376. package/src/elements/coachmark.tsx +1 -1
  377. package/src/elements/command.tsx +8 -23
  378. package/src/elements/compare.tsx +7 -6
  379. package/src/elements/compiled.css +1 -1
  380. package/src/elements/composer.tsx +8 -8
  381. package/src/elements/confirm-card.tsx +9 -9
  382. package/src/elements/context-meter.tsx +4 -18
  383. package/src/elements/conversation-list.tsx +14 -9
  384. package/src/elements/default-input.tsx +3 -3
  385. package/src/elements/define.tsx +178 -43
  386. package/src/elements/editable-label.tsx +17 -2
  387. package/src/elements/element-data-types.ts +112 -0
  388. package/src/elements/element-meta.json +1596 -304
  389. package/src/elements/element-types.d.ts +2636 -211
  390. package/src/elements/feedback-bar.tsx +1 -1
  391. package/src/elements/file-tree.tsx +1 -1
  392. package/src/elements/file-upload.tsx +1 -1
  393. package/src/elements/form.tsx +14 -6
  394. package/src/elements/input.tsx +1 -1
  395. package/src/elements/kbd.tsx +1 -1
  396. package/src/elements/menu.tsx +10 -29
  397. package/src/elements/message-skills.tsx +7 -9
  398. package/src/elements/message.tsx +200 -32
  399. package/src/elements/model-switcher.tsx +5 -3
  400. package/src/elements/pane.tsx +1 -1
  401. package/src/elements/prompt-dock.tsx +8 -8
  402. package/src/elements/prompt-input.tsx +10 -10
  403. package/src/elements/prompt-suggestions.tsx +7 -4
  404. package/src/elements/reasoning.tsx +1 -1
  405. package/src/elements/register.ts +9 -0
  406. package/src/elements/resizable.globals.d.ts +36 -0
  407. package/src/elements/resizable.tsx +24 -13
  408. package/src/elements/response-stream.tsx +1 -1
  409. package/src/elements/scroll-button.tsx +1 -1
  410. package/src/elements/search.tsx +6 -1
  411. package/src/elements/segmented.tsx +5 -11
  412. package/src/elements/separator.tsx +1 -1
  413. package/src/elements/slots.ts +57 -24
  414. package/src/elements/source.tsx +21 -22
  415. package/src/elements/switch.tsx +3 -3
  416. package/src/elements/tasks.tsx +5 -4
  417. package/src/elements/thread.tsx +36 -4
  418. package/src/elements/toast.tsx +3 -2
  419. package/src/elements/tool.tsx +1 -1
  420. package/src/elements/validate-messages.ts +76 -0
  421. package/src/elements/voice-input.tsx +7 -7
  422. package/src/elements/voice-output.tsx +4 -4
  423. package/src/index.ts +141 -6
  424. package/src/primitives/card-component-types.ts +63 -0
  425. package/src/primitives/card-data-types.ts +312 -0
  426. package/src/primitives/card-recovery.ts +13 -2
  427. package/src/primitives/card-registry.tsx +34 -23
  428. package/src/primitives/card-schemas/artifact.schema.json +92 -0
  429. package/src/primitives/card-schemas/embed.schema.json +0 -2
  430. package/src/primitives/card-schemas/link.schema.json +0 -2
  431. package/src/primitives/card-tags.ts +61 -0
  432. package/src/primitives/card-validate-cards.ts +255 -0
  433. package/src/primitives/card-validate-generator.testlib.ts +38 -0
  434. package/src/primitives/card-validate-schemas.ts +58 -0
  435. package/src/primitives/card-validate.ts +48 -20
  436. package/src/primitives/create-tween.ts +54 -2
  437. package/src/primitives/message-feedback.ts +3 -2
  438. package/src/primitives/use-audio-analysis.ts +20 -1
  439. package/src/primitives/use-sequencer.ts +20 -1
  440. package/src/schemas/from-tool-call.ts +151 -0
  441. package/src/schemas/index.ts +340 -0
  442. package/src/schemas/provider-subsets.ts +538 -0
  443. package/src/schemas/registry.ts +355 -0
  444. package/src/schemas/tool-defs.ts +492 -0
  445. package/src/solid.ts +150 -0
  446. package/src/state/index.ts +27 -1
  447. package/src/state/messages.ts +21 -4
  448. package/src/state/mock.ts +211 -0
  449. package/src/state/parts.ts +295 -0
  450. package/src/state/stream.ts +56 -58
  451. package/src/ui/collapsible.tsx +17 -5
  452. package/src/ui/dropdown.tsx +46 -12
  453. package/src/ui/overlay.tsx +10 -4
  454. package/src/wire/chunk.ts +201 -0
  455. package/src/wire/consume.ts +440 -0
  456. package/src/wire/encode.ts +474 -0
  457. package/src/wire/formats/anthropic.ts +267 -0
  458. package/src/wire/formats/openai.ts +187 -0
  459. package/src/wire/index.ts +52 -0
  460. package/src/wire/read.ts +152 -0
  461. package/src/wire/sink-helpers.ts +50 -0
  462. package/src/wire/sse.ts +140 -0
  463. package/dist/create-tween-hvpmSuwW.js +0 -7
  464. package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
  465. package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
  466. package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
  467. package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
  468. package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
  469. package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
  470. package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
  471. package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
  472. package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
  473. package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
  474. package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
  475. package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
  476. package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
  477. package/dist/elements/chunks/define-D1din7Ld.js +0 -1
  478. package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
  479. package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
  480. package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
  481. package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
  482. package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
  483. package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
  484. package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
  485. package/dist/elements/chunks/slots--V19HwUb.js +0 -1
  486. package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
  487. package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
  488. package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
  489. package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
  490. package/dist/host-embed.d.ts +0 -33
  491. package/dist/kai-provider.d.ts +0 -2
  492. package/dist/origin.d.ts +0 -5
  493. package/dist/provider-runtime.d.ts +0 -27
  494. package/dist/register-impl-DQyejdfb.js +0 -145
  495. package/dist/validate.d.ts +0 -4
  496. package/dist/version.d.ts +0 -4
  497. package/src/components/component-meta.json +0 -4550
  498. package/src/elements/card-media.jpg +0 -0
  499. package/src/elements/resizable.d.ts +0 -27
  500. 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
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. |
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,19 +617,21 @@ _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
+ | `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>`. |
633
+ | `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. |
634
+ | `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
635
 
493
636
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
494
637
 
@@ -503,6 +646,16 @@ _No events._
503
646
  | `kai-value-change` | `CustomEvent<{ value: string }>` | Fired on every input change. |
504
647
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Mic / voice button was clicked. |
505
648
 
649
+ **Methods** (call on the element instance: `document.querySelector('kai-chat').focus(…)`):
650
+
651
+ | Method | Signature | Description |
652
+ |---|---|---|
653
+ | `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. |
654
+ | `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. |
655
+ | `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. |
656
+ | `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. |
657
+ | `scrollToBottom` | `(behavior?: ScrollBehavior): void` | Scroll the message viewport to the newest message. Defaults to `'smooth'`; pass `'instant'` to jump without animating. |
658
+
506
659
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
507
660
 
508
661
  | Slot | Mode | Description |
@@ -511,7 +664,7 @@ _No events._
511
664
  | `header-end` | inject | Trailing header controls. |
512
665
  | `header` | replace | Full custom header; replaces the built-in title/model/context bar. |
513
666
  | `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. |
667
+ | `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
668
  | `composer` | replace | Full custom composer; you own submit + loading, drive the thread via messages. |
516
669
  | `composer-actions` | inject | Accessory row above the composer. |
517
670
  | `footer` | inject | Row below the composer (disclaimers, token meter). |
@@ -533,9 +686,10 @@ _No events._
533
686
 
534
687
  | Property | Attribute | Type | Description |
535
688
  |---|---|---|---|
689
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
536
690
  | `label` | `label` | `undefined \| string` | Optional text beside the icon. |
537
691
  | `tooltip` | `tooltip` | `undefined \| string` | Tooltip on hover. |
538
- | `variant` | `variant` | `undefined \| "default" \| "ghost" \| "outline"` | Visual button style. |
692
+ | `variant` | `variant` | `undefined \| "ghost" \| "default" \| "outline"` | Visual button style. |
539
693
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | Button size (use an `icon*` size for an icon-only checkpoint). |
540
694
 
541
695
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -552,11 +706,12 @@ _No events._
552
706
 
553
707
  | Property | Attribute | Type | Description |
554
708
  |---|---|---|---|
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. |
709
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
710
+ | `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
711
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
557
712
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
558
713
  | `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`. |
714
+ | `value` | `value` | `undefined \| string` | Controlled selection: the selected option id. When set, the consumer owns the current pick (RadioGroup `value`). Attribute: `value`. |
560
715
  | `defaultValue` | `default-value` | `undefined \| string` | Option id to pre-select on mount (uncontrolled seed). Attribute: `default-value`. |
561
716
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the whole radiogroup + Submit (e.g. while the agent is busy). Attribute: `disabled`. |
562
717
 
@@ -566,6 +721,16 @@ _No events._
566
721
  |---|---|---|
567
722
  | `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
723
 
724
+ **Methods** (call on the element instance: `document.querySelector('kai-choice').focus(…)`):
725
+
726
+ | Method | Signature | Description |
727
+ |---|---|---|
728
+ | `focus` | `(options?: FocusOptions): void` | Focus the radiogroup roving tab stop (or the Other input when selected). |
729
+ | `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. |
730
+ | `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. |
731
+ | `dismiss` | `(): void` | Trigger the dismiss path: emits `dismiss` on kai-card and optimistically collapses the card to its re-openable stub. |
732
+ | `reopen` | `(): void` | Re-open a dismissed card from its stub: emits `reopen` on kai-card. |
733
+
569
734
  ---
570
735
 
571
736
  ### `kai-coachmark` / `Coachmark`
@@ -574,12 +739,13 @@ _No events._
574
739
 
575
740
  | Property | Attribute | Type | Description |
576
741
  |---|---|---|---|
742
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
577
743
  | `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
744
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
579
745
  | `headline` | `headline` | `undefined \| string` | The bold title. Named `headline` because `title` collides with the global `HTMLElement.title` attribute (it throws at registration). |
580
746
  | `badge` | `badge` | `undefined \| string` | A small badge pill beside the headline (e.g. "New"). |
581
747
  | `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. |
748
+ | `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
749
  | `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
750
 
585
751
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -589,10 +755,19 @@ _No events._
589
755
  | `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
756
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The coachmark opened or closed (a method, the ×, or a driven `open`). |
591
757
 
758
+ **Methods** (call on the element instance: `document.querySelector('kai-coachmark').show()`):
759
+
760
+ | Method | Signature | Description |
761
+ |---|---|---|
762
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
763
+ | `hide` | `(): void` | Close it programmatically. |
764
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
765
+
592
766
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
593
767
 
594
768
  | Slot | Mode | Description |
595
769
  |---|---|---|
770
+ | _(default)_ | inject | The ANCHOR the coachmark points at: the element it attaches to and positions against. The bubble body is the `content` slot. |
596
771
  | `content` | replace | The bubble body text shown under the headline. |
597
772
 
598
773
  **Styleable parts** (restyle from outside via `kai-coachmark::part(name)`):
@@ -613,11 +788,12 @@ _No events._
613
788
 
614
789
  | Property | Attribute | Type | Description |
615
790
  |---|---|---|---|
791
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
616
792
  | `code` | `code` | `string` | The source code to render. |
617
793
  | `language` | `language` | `undefined \| string` | Language grammar (e.g. `js`, `python`). Defaults to `tsx`. |
618
794
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name. |
619
795
  | `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. |
796
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Code text sizing. |
621
797
 
622
798
  _No events._
623
799
 
@@ -629,7 +805,8 @@ _No events._
629
805
 
630
806
  | Property | Attribute | Type | Description |
631
807
  |---|---|---|---|
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. |
808
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
809
+ | `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
810
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text for the search input. |
634
811
  | `emptyLabel` | `empty-label` | `undefined \| string` | Label shown when no items match the current query. |
635
812
 
@@ -637,10 +814,18 @@ _No events._
637
814
 
638
815
  | Event | `detail` type | Description |
639
816
  |---|---|---|
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. |
817
+ | `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
818
  | `kai-query-change` | `CustomEvent<{ value: string }>` | Fired on every keystroke in the search input. |
642
819
  | `kai-select` | `CustomEvent<{ id: string }>` | Fired when the user selects an item (click or Enter). |
643
820
 
821
+ **Methods** (call on the element instance: `document.querySelector('kai-command').focus(…)`):
822
+
823
+ | Method | Signature | Description |
824
+ |---|---|---|
825
+ | `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). |
826
+ | `blur` | `(): void` | Blur the focused search input. |
827
+ | `clear` | `(): void` | Reset the search query to empty, re-showing all items, and fire `kai-query-change` with `''`. Mirrors the Escape-key behavior. |
828
+
644
829
  **Styleable parts** (restyle from outside via `kai-command::part(name)`):
645
830
 
646
831
  | Part | Description |
@@ -655,11 +840,12 @@ _No events._
655
840
 
656
841
  | Property | Attribute | Type | Description |
657
842
  |---|---|---|---|
843
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
658
844
  | `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
845
  | `compareId` | `compare-id` | `undefined \| string` | Stable id correlating every emitted event. Attribute: `compare-id`. |
660
846
  | `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`. |
847
+ | `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`. |
848
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Prose/text size for the rendered candidates. Attribute: `prose-size`. |
663
849
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme for code blocks in the candidates. Attribute: `code-theme`. |
664
850
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Whether code blocks are syntax-highlighted. Attribute: `code-highlight`. |
665
851
 
@@ -671,6 +857,13 @@ _No events._
671
857
  | `kai-error` | `CustomEvent<{ compareId: string; message: string }>` | The definition was unusable. |
672
858
  | `kai-ready` | `CustomEvent<{ compareId: string }>` | Both candidates have settled and the pick is live. |
673
859
 
860
+ **Methods** (call on the element instance: `document.querySelector('kai-compare').select(…)`):
861
+
862
+ | Method | Signature | Description |
863
+ |---|---|---|
864
+ | `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). |
865
+ | `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. |
866
+
674
867
  ---
675
868
 
676
869
  ### `kai-composer` / `Composer`
@@ -679,14 +872,15 @@ _No events._
679
872
 
680
873
  | Property | Attribute | Type | Description |
681
874
  |---|---|---|---|
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). |
875
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
876
+ | `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
877
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown when the composer is empty. |
684
878
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the composer entirely (non-interactive). |
685
879
  | `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. |
880
+ | `maxHeight` | `max-height` | `undefined \| number \| string` | Maximum height in px before the content scrolls. Default 240. |
687
881
  | `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. |
882
+ | `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. |
883
+ | `highlights` | — | `undefined \| (string \| { pattern: string; flags?: undefined \| string; class?: undefined \| string })[]` | Keyword highlight rules. Set as a JS property. |
690
884
  | `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
885
 
692
886
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -696,12 +890,22 @@ _No events._
696
890
  | `kai-blur` | `CustomEvent<{ originalEvent: FocusEvent }>` | The composer lost focus. |
697
891
  | `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
892
  | `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.) |
893
+ | `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
894
  | `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
895
  | `kai-trigger` | `CustomEvent<{ char: string; query: string; rect: DOMRect }>` | A trigger character was detected at the caret (e.g. `/` or `@`). |
702
896
  | `kai-trigger-close` | `CustomEvent<Record<string, never>>` | The active trigger was dismissed (Escape, space, or outside click). |
703
897
  | `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
898
 
899
+ **Methods** (call on the element instance: `document.querySelector('kai-composer').focus(…)`):
900
+
901
+ | Method | Signature | Description |
902
+ |---|---|---|
903
+ | `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. |
904
+ | `blur` | `(): void` | Blur the editable element. |
905
+ | `clear` | `(): void` | Empty the composer to a blank doc (resets the internal value + history baseline; fires kai-value-change). |
906
+ | `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. |
907
+ | `insertEntity` | `(entity: EntityRef): void` | Insert an atomic entity pill (skill/agent/plugin) at the caret without typing a trigger (fires kai-entity-add). |
908
+
705
909
  ---
706
910
 
707
911
  ### `kai-confirm` / `Confirm`
@@ -710,14 +914,24 @@ _No events._
710
914
 
711
915
  | Property | Attribute | Type | Description |
712
916
  |---|---|---|---|
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. |
917
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
918
+ | `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
919
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
715
920
  | `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`. |
921
+ | `autofocus` | `autofocus` | `undefined \| false \| true` | Focus the default action on mount (off by default, so nothing steals focus). Attribute: `autofocus`. |
717
922
  | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'action', action:'…' }`. |
718
923
 
719
924
  _No events._
720
925
 
926
+ **Methods** (call on the element instance: `document.querySelector('kai-confirm').focus(…)`):
927
+
928
+ | Method | Signature | Description |
929
+ |---|---|---|
930
+ | `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. |
931
+ | `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. |
932
+ | `dismiss` | `(): void` | Trigger the dismiss path: emits `dismiss` on kai-card and optimistically collapses the card to its re-openable stub. |
933
+ | `reopen` | `(): void` | Re-open a dismissed card from its stub: emits `reopen` on kai-card. |
934
+
721
935
  ---
722
936
 
723
937
  ### `kai-context` / `Context`
@@ -726,6 +940,7 @@ _No events._
726
940
 
727
941
  | Property | Attribute | Type | Description |
728
942
  |---|---|---|---|
943
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
729
944
  | `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
945
  | `warnThreshold` | `warn-threshold` | `undefined \| number` | Fraction (0–1) above which the meter turns yellow. Defaults to `0.7` (70%). |
731
946
  | `dangerThreshold` | `danger-threshold` | `undefined \| number` | Fraction (0–1) above which the meter turns red. Defaults to `0.9` (90%). |
@@ -744,8 +959,9 @@ _No events._
744
959
 
745
960
  | Property | Attribute | Type | Description |
746
961
  |---|---|---|---|
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. |
962
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
963
+ | `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. |
964
+ | `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
965
  | `activeId` | `active-id` | `undefined \| string` | The id of the currently-open conversation, highlighted in the list. |
750
966
  | `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
967
  | `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 +976,30 @@ _No events._
760
976
  | `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
977
  | `kai-toggle-sidebar` | `CustomEvent<Record<string, never>>` | The sidebar toggle was clicked. |
762
978
 
979
+ **Methods** (call on the element instance: `document.querySelector('kai-conversations').focus(…)`):
980
+
981
+ | Method | Signature | Description |
982
+ |---|---|---|
983
+ | `focus` | `(options?: FocusOptions): void` | Focus the built-in search input inside the shadow root. |
984
+ | `clear` | `(): void` | Clear the internal search query (resets the list filter) and fire kai-search with an empty string. |
985
+ | `select` | `(id: string): void` | Programmatically select a conversation by id. The mirror of the kai-conversation-select event (a convenience over driving `activeId`). |
986
+ | `collapse` | `(): void` | Collapse the rail to its floating reopen button (fires `kai-collapse-toggle`). |
987
+ | `expand` | `(): void` | Expand the rail back to the full list (fires `kai-collapse-toggle`). |
988
+ | `toggle` | `(): void` | Toggle the rail collapsed/expanded (fires `kai-collapse-toggle`). |
989
+
763
990
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
764
991
 
765
992
  | Slot | Mode | Description |
766
993
  |---|---|---|
767
994
  | `header` | replace | Full custom title bar; replaces the built-in toggle / "Chats" / New-chat row. |
768
995
  | `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. |
996
+ | `footer` | inject | A row below the list: account, settings, or usage. |
997
+
998
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
999
+
1000
+ | Child element | Attributes | Text content | Notes |
1001
+ |---|---|---|---|
1002
+ | `<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
1003
 
771
1004
  **Styleable parts** (restyle from outside via `kai-conversations::part(name)`):
772
1005
 
@@ -782,6 +1015,7 @@ _No events._
782
1015
 
783
1016
  | Property | Attribute | Type | Description |
784
1017
  |---|---|---|---|
1018
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
785
1019
  | `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
1020
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
787
1021
 
@@ -791,10 +1025,20 @@ _No events._
791
1025
  |---|---|---|
792
1026
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The dialog opened or closed (Escape, backdrop click, a driven `open`, or a method). |
793
1027
 
1028
+ **Methods** (call on the element instance: `document.querySelector('kai-dialog').show()`):
1029
+
1030
+ | Method | Signature | Description |
1031
+ |---|---|---|
1032
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1033
+ | `hide` | `(): void` | Close it programmatically. |
1034
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1035
+ | `focus` | `(options?: FocusOptions): void` | Move focus to the dialog panel (no-op while closed). |
1036
+
794
1037
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
795
1038
 
796
1039
  | Slot | Mode | Description |
797
1040
  |---|---|---|
1041
+ | _(default)_ | inject | The dialog body, between the `header` and `footer` slots. |
798
1042
  | `header` | inject | Optional title region at the top of the panel. |
799
1043
  | `footer` | inject | Optional actions region at the bottom of the panel. |
800
1044
 
@@ -816,7 +1060,8 @@ _No events._
816
1060
 
817
1061
  | Property | Attribute | Type | Description |
818
1062
  |---|---|---|---|
819
- | `value` | `value` | `undefined \| string` | The label text — settable and reflected to the `value` attribute. Read `el.value` for live state. |
1063
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1064
+ | `value` | `value` | `undefined \| string` | The label text. Settable and reflected to the `value` attribute. Read `el.value` for live state. |
820
1065
  | `editing` | `editing` | `undefined \| false \| true` | Controlled edit state. `el.editing = true` opens the field; reflected to the `editing` attribute. |
821
1066
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder shown while editing / when the value is empty. |
822
1067
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable entering edit mode. |
@@ -828,6 +1073,14 @@ _No events._
828
1073
  | `kai-cancel` | `CustomEvent<Record<string, never>>` | Edit was cancelled (Esc); the text is restored. |
829
1074
  | `kai-rename` | `CustomEvent<{ value: string }>` | Committed a changed value (Enter / blur). |
830
1075
 
1076
+ **Methods** (call on the element instance: `document.querySelector('kai-editable-label').edit()`):
1077
+
1078
+ | Method | Signature | Description |
1079
+ |---|---|---|
1080
+ | `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. |
1081
+ | `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. |
1082
+ | `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. |
1083
+
831
1084
  **Styleable parts** (restyle from outside via `kai-editable-label::part(name)`):
832
1085
 
833
1086
  | Part | Description |
@@ -843,6 +1096,7 @@ _No events._
843
1096
 
844
1097
  | Property | Attribute | Type | Description |
845
1098
  |---|---|---|---|
1099
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
846
1100
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted event. Set as an attribute or property. |
847
1101
  | `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
1102
 
@@ -856,11 +1110,19 @@ _No events._
856
1110
 
857
1111
  | Property | Attribute | Type | Description |
858
1112
  |---|---|---|---|
1113
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
859
1114
  | `emptyTitle` | `empty-title` | `undefined \| string` | Title text. Attribute: `empty-title` (`title` is a global HTML attribute). |
860
1115
  | `description` | `description` | `undefined \| string` | Description text. |
861
1116
 
862
1117
  _No events._
863
1118
 
1119
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1120
+
1121
+ | Slot | Mode | Description |
1122
+ |---|---|---|
1123
+ | _(default)_ | inject | The empty-state body below the title/description, usually the call to action. |
1124
+ | `media` | replace | The leading illustration or icon above the title (any inline SVG or <img>). Replaces the built-in media box. |
1125
+
864
1126
  ---
865
1127
 
866
1128
  ### `kai-feedback-bar` / `FeedbackBar`
@@ -869,7 +1131,8 @@ _No events._
869
1131
 
870
1132
  | Property | Attribute | Type | Description |
871
1133
  |---|---|---|---|
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). |
1134
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1135
+ | `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
1136
  | `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
1137
  | `categories` | — | `undefined \| string[]` | Optional category chips for the detail form. Set as a JS property (array). |
875
1138
  | `detailTitle` | `detail-title` | `undefined \| string` | Heading for the detail form. Attribute: `detail-title`. |
@@ -893,8 +1156,9 @@ _No events._
893
1156
 
894
1157
  | Property | Attribute | Type | Description |
895
1158
  |---|---|---|---|
1159
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
896
1160
  | `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. |
1161
+ | `activeFile` | `active-file` | `undefined \| string` | Selected file path, highlighted in the tree. |
898
1162
  | `defaultExpanded` | — | `undefined \| string[]` | Folder paths expanded initially. Omit to start with all folders open. |
899
1163
  | `summary` | `summary` | `undefined \| false \| true` | Show a changed-files summary header (file count + summed `+/-` + Collapse-all). Attribute: `summary`. Off by default. |
900
1164
 
@@ -921,9 +1185,10 @@ _No events._
921
1185
 
922
1186
  | Property | Attribute | Type | Description |
923
1187
  |---|---|---|---|
1188
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
924
1189
  | `multiple` | `multiple` | `undefined \| false \| true` | Allow selecting multiple files (default true). |
925
1190
  | `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. |
1191
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the dropzone: no clicking, no drag-and-drop. |
927
1192
  | `label` | `label` | `undefined \| string` | Default dropzone label (overridable via the default slot). |
928
1193
 
929
1194
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -932,6 +1197,12 @@ _No events._
932
1197
  |---|---|---|
933
1198
  | `kai-files-added` | `CustomEvent<{ files: File[] }>` | Files were picked or dropped. |
934
1199
 
1200
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1201
+
1202
+ | Slot | Mode | Description |
1203
+ |---|---|---|
1204
+ | _(default)_ | inject | Custom dropzone content, replacing the default label (the `label` prop is the fallback). |
1205
+
935
1206
  ---
936
1207
 
937
1208
  ### `kai-form` / `Form`
@@ -940,7 +1211,8 @@ _No events._
940
1211
 
941
1212
  | Property | Attribute | Type | Description |
942
1213
  |---|---|---|---|
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). |
1214
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1215
+ | `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
1216
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
945
1217
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
946
1218
  | `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 +1224,18 @@ _No events._
952
1224
 
953
1225
  | Event | `detail` type | Description |
954
1226
  |---|---|---|
955
- | `kai-values-change` | `CustomEvent<{ values: Record<string, unknown>; valid: false \| true }>` | The form's values changed on input current coerced values + validity. |
1227
+ | `kai-values-change` | `CustomEvent<{ values: Record<string, unknown>; valid: false \| true }>` | The form's values changed on input. Carries the current coerced values + validity. |
1228
+
1229
+ **Methods** (call on the element instance: `document.querySelector('kai-form').focus(…)`):
1230
+
1231
+ | Method | Signature | Description |
1232
+ |---|---|---|
1233
+ | `focus` | `(options?: FocusOptions): void` | Focus the first control, or the first INVALID control after a failed validation. |
1234
+ | `send` | `(): void` | Validate + submit programmatically: focus the first invalid field on failure, else emit the `submit` CardEvent and resolve. Named `send`, not `submit`. |
1235
+ | `validate` | `(): void` | Run client-side validation now and return `{ valid, errors? }` WITHOUT submitting. |
1236
+ | `reset` | `(): void` | Re-seed the form from each field's `default` and clear errors. |
1237
+ | `dismiss` | `(): void` | Trigger the dismiss path (emit `dismiss` + collapse to the re-openable stub). |
1238
+ | `reopen` | `(): void` | Re-open a dismissed card from its stub (emit `reopen`). |
956
1239
 
957
1240
  ---
958
1241
 
@@ -962,6 +1245,7 @@ _No events._
962
1245
 
963
1246
  | Property | Attribute | Type | Description |
964
1247
  |---|---|---|---|
1248
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
965
1249
  | `openDelay` | `open-delay` | `undefined \| number` | Delay (ms) before the card opens on hover. Defaults to 0 (focus opens it immediately too). |
966
1250
  | `closeDelay` | `close-delay` | `undefined \| number` | Delay (ms) before it closes after the pointer leaves. Defaults to 300. |
967
1251
  | `placement` | `placement` | `undefined \| string` | Preferred placement: `'top' \| 'bottom' \| 'left' \| 'right'` (+ optional `-start`/`-end`). Defaults to `'bottom'`; flips to stay in view. |
@@ -975,10 +1259,19 @@ _No events._
975
1259
  |---|---|---|
976
1260
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The card opened or closed (by hover/focus, outside-click, or a method). |
977
1261
 
1262
+ **Methods** (call on the element instance: `document.querySelector('kai-hover-card').show()`):
1263
+
1264
+ | Method | Signature | Description |
1265
+ |---|---|---|
1266
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1267
+ | `hide` | `(): void` | Close it programmatically. |
1268
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1269
+
978
1270
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
979
1271
 
980
1272
  | Slot | Mode | Description |
981
1273
  |---|---|---|
1274
+ | _(default)_ | inject | The TRIGGER the card hovers off. The card body is the `card` slot. |
982
1275
  | `card` | inject | The rich content shown in the floating hover card. |
983
1276
 
984
1277
  ---
@@ -989,6 +1282,7 @@ _No events._
989
1282
 
990
1283
  | Property | Attribute | Type | Description |
991
1284
  |---|---|---|---|
1285
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
992
1286
  | `name` | `name` | `undefined \| string` | A curated icon name (e.g. `"mic"`, `"globe"`), an image URL/data-URI, or plain text. |
993
1287
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg"` | Size token: `sm` \| `md` (default) \| `lg`. |
994
1288
 
@@ -1008,6 +1302,7 @@ _No events._
1008
1302
 
1009
1303
  | Property | Attribute | Type | Description |
1010
1304
  |---|---|---|---|
1305
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1011
1306
  | `base64` | `base64` | `undefined \| string` | Base64-encoded image data (pair with `media-type`). |
1012
1307
  | `bytes` | — | `undefined \| Uint8Array<ArrayBufferLike>` | Raw image bytes (set as a JS property). |
1013
1308
  | `alt` | `alt` | `undefined \| string` | Alt text. |
@@ -1023,8 +1318,9 @@ _No events._
1023
1318
 
1024
1319
  | Property | Attribute | Type | Description |
1025
1320
  |---|---|---|---|
1321
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1026
1322
  | `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. |
1323
+ | `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
1324
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder shown when empty. |
1029
1325
  | `label` | `label` | `undefined \| string` | Field label, linked to the input. |
1030
1326
  | `hint` | `hint` | `undefined \| string` | Helper text below the control. |
@@ -1045,6 +1341,14 @@ _No events._
1045
1341
  | `kai-change` | `CustomEvent<{ value: string }>` | The value was committed (blur). |
1046
1342
  | `kai-input` | `CustomEvent<{ value: string }>` | The value changed per keystroke. |
1047
1343
 
1344
+ **Methods** (call on the element instance: `document.querySelector('kai-input').focus(…)`):
1345
+
1346
+ | Method | Signature | Description |
1347
+ |---|---|---|
1348
+ | `focus` | `(options?: FocusOptions): void` | Focus the inner input (the host can't reach into the shadow root). |
1349
+ | `select` | `(): void` | Select the inner input's text. |
1350
+ | `clear` | `(): void` | Empty the value and fire `kai-change` with `''`. |
1351
+
1048
1352
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1049
1353
 
1050
1354
  | Slot | Mode | Description |
@@ -1069,12 +1373,19 @@ _No events._
1069
1373
 
1070
1374
  | Property | Attribute | Type | Description |
1071
1375
  |---|---|---|---|
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. |
1376
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1377
+ | `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. |
1378
+ | `platform` | `platform` | `undefined \| "auto" \| "mac" \| "other"` | `mac` uses ⌘/⌥, `other` uses Ctrl. `auto` (default) sniffs the OS. |
1074
1379
  | `size` | `size` | `undefined \| "sm" \| "md"` | Cap size: `sm` or `md`. Defaults to `md`. |
1075
1380
 
1076
1381
  _No events._
1077
1382
 
1383
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1384
+
1385
+ | Slot | Mode | Description |
1386
+ |---|---|---|
1387
+ | _(default)_ | inject | Literal key text, when you are not using the `keys` prop to render key caps. |
1388
+
1078
1389
  **Styleable parts** (restyle from outside via `kai-kbd::part(name)`):
1079
1390
 
1080
1391
  | Part | Description |
@@ -1090,6 +1401,7 @@ _No events._
1090
1401
 
1091
1402
  | Property | Attribute | Type | Description |
1092
1403
  |---|---|---|---|
1404
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1093
1405
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted event. Set as an attribute or property. |
1094
1406
  | `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
1407
 
@@ -1103,6 +1415,7 @@ _No events._
1103
1415
 
1104
1416
  | Property | Attribute | Type | Description |
1105
1417
  |---|---|---|---|
1418
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1106
1419
  | `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
1420
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg"` | Loader size: `'sm' \| 'md' \| 'lg'`. Defaults to `'md'`. |
1108
1421
  | `text` | `text` | `undefined \| string` | Label for the text-based variants. |
@@ -1117,8 +1430,9 @@ _No events._
1117
1430
 
1118
1431
  | Property | Attribute | Type | Description |
1119
1432
  |---|---|---|---|
1433
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1120
1434
  | `content` | `content` | `string` | The markdown source to render. |
1121
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Text/markdown sizing. |
1435
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing. |
1122
1436
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme for fenced code blocks. |
1123
1437
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting (no Shiki loads). |
1124
1438
 
@@ -1132,22 +1446,31 @@ _No events._
1132
1446
 
1133
1447
  | Property | Attribute | Type | Description |
1134
1448
  |---|---|---|---|
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. |
1449
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1450
+ | `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
1451
  | `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. |
1452
+ | `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
1453
  | `triggerLabel` | `trigger-label` | `undefined \| string` | Built-in trigger: a text label (e.g. `"High"`). |
1139
1454
  | `triggerIconTrailing` | `trigger-icon-trailing` | `undefined \| string` | Built-in trigger: a trailing icon (e.g. `"chevron-down"` for a select look). |
1140
1455
  | `label` | `label` | `undefined \| string` | Accessible name for an icon-only trigger (no visible label). |
1141
1456
  | `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
1457
  | `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. |
1458
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger: click/keyboard and `show()` no longer open the menu. |
1144
1459
 
1145
1460
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1146
1461
 
1147
1462
  | Event | `detail` type | Description |
1148
1463
  |---|---|---|
1149
1464
  | `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. |
1465
+ | `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. |
1466
+
1467
+ **Methods** (call on the element instance: `document.querySelector('kai-menu').show()`):
1468
+
1469
+ | Method | Signature | Description |
1470
+ |---|---|---|
1471
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1472
+ | `hide` | `(): void` | Close it programmatically. |
1473
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1151
1474
 
1152
1475
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1153
1476
 
@@ -1169,17 +1492,19 @@ _No events._
1169
1492
 
1170
1493
  | Property | Attribute | Type | Description |
1171
1494
  |---|---|---|---|
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). |
1495
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1496
+ | `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. |
1497
+ | `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
1498
  | `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. |
1499
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing for the message body. |
1177
1500
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name used for fenced code blocks in the content. |
1178
1501
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting for code blocks (no Shiki loads). |
1179
1502
  | `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
1503
  | `avatarSrc` | `avatar-src` | `undefined \| string` | Convenience avatar image URL (used when `message.avatar` is not set). |
1181
1504
  | `avatarFallback` | `avatar-fallback` | `undefined \| string` | Convenience avatar fallback text (used when `message.avatar` is not set). |
1182
1505
  | `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). |
1506
+ | `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. |
1507
+ | `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
1508
 
1184
1509
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1185
1510
 
@@ -1187,14 +1512,26 @@ _No events._
1187
1512
  |---|---|---|
1188
1513
  | `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
1514
 
1515
+ **Methods** (call on the element instance: `document.querySelector('kai-message').copy()`):
1516
+
1517
+ | Method | Signature | Description |
1518
+ |---|---|---|
1519
+ | `copy` | `(): void` | Copy the message content to the clipboard and show the copied check. |
1520
+
1190
1521
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1191
1522
 
1192
1523
  | Slot | Mode | Description |
1193
1524
  |---|---|---|
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. |
1525
+ | `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. |
1526
+ | `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
1527
  | `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
1528
 
1529
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
1530
+
1531
+ | Child element | Attributes | Text content | Notes |
1532
+ |---|---|---|---|
1533
+ | `<kai-action>` | `action`, `icon`, `id`, `label`, `tooltip` | yes | |
1534
+
1198
1535
  **Styleable parts** (restyle from outside via `kai-message::part(name)`):
1199
1536
 
1200
1537
  | Part | Description |
@@ -1203,6 +1540,7 @@ _No events._
1203
1540
  | `::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
1541
  | `::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
1542
  | `::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 }` |
1543
+ | `::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
1544
  | `::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
1545
 
1208
1546
  ---
@@ -1213,11 +1551,12 @@ _No events._
1213
1551
 
1214
1552
  | Property | Attribute | Type | Description |
1215
1553
  |---|---|---|---|
1216
- | `models` | | `{ id: string; name: string; provider?: undefined \| string; description?: undefined \| string; group?: undefined \| string }[]` | The selectable models. Set as a JS property (array). |
1554
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1555
+ | `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
1556
  | `currentModel` | `current-model` | `undefined \| string` | The currently-selected model id. Defaults to the first model. |
1218
1557
  | `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
1558
  | `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. |
1559
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger: click/keyboard and `show()` no longer open the dropdown. |
1221
1560
 
1222
1561
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1223
1562
 
@@ -1226,6 +1565,20 @@ _No events._
1226
1565
  | `kai-model-change` | `CustomEvent<{ modelId: string }>` | A model was selected. |
1227
1566
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The model dropdown opened or closed (by click, keyboard, Escape, outside-click, or a method). |
1228
1567
 
1568
+ **Methods** (call on the element instance: `document.querySelector('kai-model-switcher').show()`):
1569
+
1570
+ | Method | Signature | Description |
1571
+ |---|---|---|
1572
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1573
+ | `hide` | `(): void` | Close it programmatically. |
1574
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1575
+
1576
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
1577
+
1578
+ | Child element | Attributes | Text content | Notes |
1579
+ |---|---|---|---|
1580
+ | `<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) |
1581
+
1229
1582
  ---
1230
1583
 
1231
1584
  ### `kai-nav` / `Nav`
@@ -1234,7 +1587,8 @@ _No events._
1234
1587
 
1235
1588
  | Property | Attribute | Type | Description |
1236
1589
  |---|---|---|---|
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. |
1590
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1591
+ | `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
1592
  | `value` | `value` | `undefined \| string` | Active item id (controlled). |
1239
1593
  | `defaultValue` | `default-value` | `undefined \| string` | Initial active id when uncontrolled. |
1240
1594
  | `defaultCollapsed` | — | `undefined \| string[]` | Ids of group items collapsed on first render (groups default to expanded). Set as a JS property (array). |
@@ -1247,6 +1601,12 @@ _No events._
1247
1601
  | `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
1602
  | `kai-nav-select` | `CustomEvent<{ id: string }>` | A nav item was activated. |
1249
1603
 
1604
+ **Methods** (call on the element instance: `document.querySelector('kai-nav').select(…)`):
1605
+
1606
+ | Method | Signature | Description |
1607
+ |---|---|---|
1608
+ | `select` | `(id: string): void` | Activate an item by id (fires kai-nav-select). |
1609
+
1250
1610
  **Styleable parts** (restyle from outside via `kai-nav::part(name)`):
1251
1611
 
1252
1612
  | Part | Description |
@@ -1267,7 +1627,8 @@ _No events._
1267
1627
 
1268
1628
  | Property | Attribute | Type | Description |
1269
1629
  |---|---|---|---|
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`). |
1630
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1631
+ | `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
1632
  | `icon` | `icon` | `undefined \| string` | Leading icon: omit for the severity default, `"none"` to hide it, or a named icon to override. |
1272
1633
  | `dismissible` | `dismissible` | `undefined \| false \| true` | Show a dismiss (×) that hides the notice and emits `kai-dismiss`. |
1273
1634
 
@@ -1281,8 +1642,9 @@ _No events._
1281
1642
 
1282
1643
  | Slot | Mode | Description |
1283
1644
  |---|---|---|
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`. |
1645
+ | _(default)_ | inject | The notice message. `icon` and `action` are the named seams around it. |
1646
+ | `action` | inject | A trailing action beside the message: a link or button. |
1647
+ | `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
1648
 
1287
1649
  ---
1288
1650
 
@@ -1292,6 +1654,7 @@ _No events._
1292
1654
 
1293
1655
  | Property | Attribute | Type | Description |
1294
1656
  |---|---|---|---|
1657
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1295
1658
  | `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
1659
  | `subtitle` | `subtitle` | `undefined \| string` | A role / label shown under the title (e.g. "Reviewer", "claude-sonnet"). Attribute: `subtitle`. |
1297
1660
  | `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 +1669,14 @@ _No events._
1306
1669
  |---|---|---|
1307
1670
  | `kai-close` | `CustomEvent` | The close (×) control was clicked. |
1308
1671
  | `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. |
1672
+ | `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
1673
  | `kai-split` | `CustomEvent` | The split control was clicked (only present when `show-split`). |
1311
1674
 
1312
1675
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1313
1676
 
1314
1677
  | Slot | Mode | Description |
1315
1678
  |---|---|---|
1679
+ | _(default)_ | inject | The pane body, below the header row. |
1316
1680
  | `leading` | inject | A glyph or avatar at the start of the pane header. |
1317
1681
  | `actions` | inject | Extra header controls, before the built-in window controls. |
1318
1682
  | `footer` | inject | A pinned row below the body (e.g. a composer). |
@@ -1334,6 +1698,7 @@ _No events._
1334
1698
 
1335
1699
  | Property | Attribute | Type | Description |
1336
1700
  |---|---|---|---|
1701
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1337
1702
  | `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
1703
  | `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
1704
  | `focused` | `focused` | `undefined \| false \| true` | Highlight the frame as the ACTIVE group in a multi-group layout. Attribute: `focused`. |
@@ -1346,6 +1711,19 @@ _No events._
1346
1711
  | `kai-tab-close` | `CustomEvent<{ id: string }>` | A tab's close (×) was clicked. Drop the tab from `tabs` yourself. |
1347
1712
  | `kai-tab-menu` | `CustomEvent<{ id: string }>` | A tab's "…" overflow was clicked. Open your own menu from `detail.id`. |
1348
1713
 
1714
+ **Methods** (call on the element instance: `document.querySelector('kai-pane-group').select(…)`):
1715
+
1716
+ | Method | Signature | Description |
1717
+ |---|---|---|
1718
+ | `select` | `(id: string): void` | Select a tab by id (fires `kai-tab-change`). Ignores unknown ids. |
1719
+ | `focus` | `(): void` | Focus the active tab in the strip. |
1720
+
1721
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1722
+
1723
+ | Slot | Mode | Description |
1724
+ |---|---|---|
1725
+ | _(default)_ | inject | Content shown for every tab. Use it INSTEAD of the per-tab `slot="<tab id>"` seams when you swap the content yourself. |
1726
+
1349
1727
  **Styleable parts** (restyle from outside via `kai-pane-group::part(name)`):
1350
1728
 
1351
1729
  | Part | Description |
@@ -1364,7 +1742,8 @@ _No events._
1364
1742
 
1365
1743
  | Property | Attribute | Type | Description |
1366
1744
  |---|---|---|---|
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). |
1745
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1746
+ | `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
1747
  | `gutter` | `gutter` | `undefined \| number` | Gap in px between the trigger and the panel. |
1369
1748
  | `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
1749
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
@@ -1376,6 +1755,21 @@ _No events._
1376
1755
  |---|---|---|
1377
1756
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The popover opened or closed (click, Escape, outside-click, or a method). |
1378
1757
 
1758
+ **Methods** (call on the element instance: `document.querySelector('kai-popover').show()`):
1759
+
1760
+ | Method | Signature | Description |
1761
+ |---|---|---|
1762
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1763
+ | `hide` | `(): void` | Close it programmatically. |
1764
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1765
+
1766
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1767
+
1768
+ | Slot | Mode | Description |
1769
+ |---|---|---|
1770
+ | _(default)_ | inject | The popover panel body. The control that opens it is the `trigger` slot. |
1771
+ | `trigger` | replace | The control that opens the popover (a button, an avatar, …). The panel anchors to it. |
1772
+
1379
1773
  ---
1380
1774
 
1381
1775
  ### `kai-progress-bar` / `ProgressBar`
@@ -1384,6 +1778,7 @@ _No events._
1384
1778
 
1385
1779
  | Property | Attribute | Type | Description |
1386
1780
  |---|---|---|---|
1781
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1387
1782
  | `value` | `value` | `undefined \| number` | Current progress value (0..max). Attribute: `value`. |
1388
1783
  | `max` | `max` | `undefined \| number` | The value `value` runs to (default 100). Attribute: `max`. |
1389
1784
  | `label` | `label` | `undefined \| string` | Optional caption above the track. Attribute: `label`. |
@@ -1406,8 +1801,9 @@ _No events._
1406
1801
 
1407
1802
  | Property | Attribute | Type | Description |
1408
1803
  |---|---|---|---|
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`. |
1804
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1805
+ | `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`. |
1806
+ | `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
1807
 
1412
1808
  _No events._
1413
1809
 
@@ -1415,6 +1811,7 @@ _No events._
1415
1811
 
1416
1812
  | Slot | Mode | Description |
1417
1813
  |---|---|---|
1814
+ | _(default)_ | inject | The input the dock wraps, typically a `<kai-prompt-input>`. The `top`/`bottom` slots are the lips around it. |
1418
1815
  | `top` | inject | The top lip: a notice or banner above the input. Rendered only when filled. |
1419
1816
  | `bottom` | inject | The bottom lip: a mode or controls row below the input. Rendered only when filled. |
1420
1817
 
@@ -1434,6 +1831,7 @@ _No events._
1434
1831
 
1435
1832
  | Property | Attribute | Type | Description |
1436
1833
  |---|---|---|---|
1834
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1437
1835
  | `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
1836
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown in the empty input. |
1439
1837
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the input and submit button entirely (non-interactive). |
@@ -1443,17 +1841,17 @@ _No events._
1443
1841
  | `search` | `search` | `undefined \| false \| true` | Show a Search (Globe) button in the left toolbar; clicking it fires a `search` event. |
1444
1842
  | `voice` | `voice` | `undefined \| false \| true` | Show a Voice (Mic) button in the left toolbar; clicking it fires a `voice` event. |
1445
1843
  | `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. |
1844
+ | `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
1845
  | `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
1846
  | `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. |
1847
+ | `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
1848
  | `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
1849
 
1452
1850
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1453
1851
 
1454
1852
  | Event | `detail` type | Description |
1455
1853
  |---|---|---|
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). |
1854
+ | `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
1855
  | `kai-search` | `CustomEvent<Record<string, never>>` | The Search (Globe) toolbar button was clicked. |
1458
1856
  | `kai-stop` | `CustomEvent<Record<string, never>>` | The Stop button was clicked while `stoppable` and `loading` are both true. |
1459
1857
  | `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 +1860,34 @@ _No events._
1462
1860
  | `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
1861
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Voice (Mic) toolbar button was clicked. |
1464
1862
 
1863
+ **Methods** (call on the element instance: `document.querySelector('kai-prompt-input').focus(…)`):
1864
+
1865
+ | Method | Signature | Description |
1866
+ |---|---|---|
1867
+ | `focus` | `(options?: FocusOptions): void` | Focus the text editor inside the shadow root (not the hidden file input). |
1868
+ | `blur` | `(): void` | Blur the focused input control. |
1869
+ | `clear` | `(): void` | Clear the text and any staged attachments (fires kai-value-change / kai-attachments-change so a controlled consumer can react). |
1870
+ | `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. |
1871
+
1465
1872
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1466
1873
 
1467
1874
  | Slot | Mode | Description |
1468
1875
  |---|---|---|
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. |
1876
+ | `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. |
1877
+ | `toolbar-start` | inject | Leading controls in the input toolbar, where a + menu goes. |
1471
1878
  | `toolbar-end` | inject | Trailing controls in the toolbar, before the Send button. |
1472
1879
 
1880
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
1881
+
1882
+ | Child element | Attributes | Text content | Notes |
1883
+ |---|---|---|---|
1884
+ | `<kai-action>` | `action`, `icon`, `id`, `label`, `tooltip` | yes | |
1885
+
1473
1886
  **Styleable parts** (restyle from outside via `kai-prompt-input::part(name)`):
1474
1887
 
1475
1888
  | Part | Description |
1476
1889
  |---|---|
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, … */` |
1890
+ | `::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
1891
 
1479
1892
  ---
1480
1893
 
@@ -1484,13 +1897,14 @@ _No events._
1484
1897
 
1485
1898
  | Property | Attribute | Type | Description |
1486
1899
  |---|---|---|---|
1900
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1487
1901
  | `text` | `text` | `string` | The reasoning text to display. |
1488
1902
  | `label` | `label` | `undefined \| string` | Trigger label. |
1489
1903
  | `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
1904
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1491
1905
  | `streaming` | `streaming` | `undefined \| false \| true` | While true, auto-expands (and re-collapses when it flips false). |
1492
1906
  | `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. |
1907
+ | `disabled` | `disabled` | `undefined \| false \| true` | Gate the disclosure trigger: programmatic `show()/hide()/toggle()` still work, but the trigger click no longer toggles. |
1494
1908
 
1495
1909
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1496
1910
 
@@ -1498,6 +1912,14 @@ _No events._
1498
1912
  |---|---|---|
1499
1913
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The reasoning block expanded or collapsed (via the trigger, streaming auto-open, or a method). |
1500
1914
 
1915
+ **Methods** (call on the element instance: `document.querySelector('kai-reasoning').show()`):
1916
+
1917
+ | Method | Signature | Description |
1918
+ |---|---|---|
1919
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
1920
+ | `hide` | `(): void` | Close it programmatically. |
1921
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1922
+
1501
1923
  ---
1502
1924
 
1503
1925
  ### `kai-remote` / `Remote`
@@ -1506,6 +1928,7 @@ _No events._
1506
1928
 
1507
1929
  | Property | Attribute | Type | Description |
1508
1930
  |---|---|---|---|
1931
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1509
1932
  | `src` | `src` | `undefined \| string` | The remote card URL. Attribute: `src`. |
1510
1933
  | `providerOrigin` | `provider-origin` | `undefined \| string` | Exact provider origin (https: or http://localhost for dev). Attribute: `provider-origin`. |
1511
1934
  | `envelope` | — | `undefined \| Record<string, unknown>` | The card envelope to render. JS property only. |
@@ -1521,16 +1944,31 @@ _No events._
1521
1944
 
1522
1945
  | Property | Attribute | Type | Description |
1523
1946
  |---|---|---|---|
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. |
1947
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1948
+ | `orientation` | `orientation` | `undefined \| "horizontal" \| "vertical"` | Layout axis: `horizontal` (row, default) or `vertical` (column). |
1949
+ | `maximizedIndex` | | `undefined \| number \| null` | Which item index is maximized (null = none). Declarative source of truth. |
1950
+ | `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
1951
 
1528
1952
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1529
1953
 
1530
1954
  | Event | `detail` type | Description |
1531
1955
  |---|---|---|
1532
1956
  | `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. |
1957
+ | `kai-maximize-change` | `CustomEvent<{ maximized: false \| true; index: number \| null }>` | Observe layout maximize state. |
1958
+ | `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. |
1959
+
1960
+ **Methods** (call on the element instance: `document.querySelector('kai-resizable').maximize(…)`):
1961
+
1962
+ | Method | Signature | Description |
1963
+ |---|---|---|
1964
+ | `maximize` | `(index: number): void` | Imperatively maximize the item at `index` (thin wrapper over `maximizedIndex`). |
1965
+ | `restore` | `(): void` | Imperatively restore from the maximized layout. |
1966
+
1967
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1968
+
1969
+ | Slot | Mode | Description |
1970
+ |---|---|---|
1971
+ | _(default)_ | inject | The `<kai-resizable-item>` panels, in order. Dividers are inserted between them. |
1534
1972
 
1535
1973
  ---
1536
1974
 
@@ -1540,12 +1978,13 @@ _No events._
1540
1978
 
1541
1979
  | Property | Attribute | Type | Description |
1542
1980
  |---|---|---|---|
1981
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1543
1982
  | `size` | `size` | `undefined \| string` | Initial main-axis size: `"280px"` (fixed) or `"25%"`/`25` (percent). Omitted → flexible. |
1544
1983
  | `min` | `min` | `undefined \| string` | Minimum size during resize (px or %). |
1545
1984
  | `max` | `max` | `undefined \| string` | Maximum size during resize (px or %). |
1546
1985
  | `locked` | `locked` | `undefined \| false \| true` | Fix this panel's size; adjacent dividers become non-draggable. |
1547
1986
  | `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. |
1987
+ | `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
1988
 
1550
1989
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1551
1990
 
@@ -1553,6 +1992,13 @@ _No events._
1553
1992
  |---|---|---|
1554
1993
  | `kai-change` | `CustomEvent<unknown>` | |
1555
1994
  | `kai-maximize-change` | `CustomEvent<unknown>` | |
1995
+ | `kai-maximize-state` | `CustomEvent<unknown>` | |
1996
+
1997
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1998
+
1999
+ | Slot | Mode | Description |
2000
+ |---|---|---|
2001
+ | _(default)_ | inject | This panel's content. |
1556
2002
 
1557
2003
  ---
1558
2004
 
@@ -1562,7 +2008,8 @@ _No events._
1562
2008
 
1563
2009
  | Property | Attribute | Type | Description |
1564
2010
  |---|---|---|---|
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). |
2011
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2012
+ | `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
2013
  | `mode` | `mode` | `undefined \| "typewriter" \| "fade"` | Reveal animation. |
1567
2014
  | `speed` | `speed` | `undefined \| number` | Characters/segments per tick. |
1568
2015
  | `as` | `as` | `undefined \| string` | Element tag to render as. |
@@ -1581,19 +2028,28 @@ _No events._
1581
2028
 
1582
2029
  | Property | Attribute | Type | Description |
1583
2030
  |---|---|---|---|
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. |
2031
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2032
+ | `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. |
2033
+ | `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
2034
  | `currentLabel` | `current-label` | `undefined \| string` | The label shown on the trigger for the active scope. |
1587
2035
  | `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
2036
  | `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. |
2037
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger: click/keyboard and `show()` no longer open the dropdown. |
1590
2038
 
1591
2039
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1592
2040
 
1593
2041
  | Event | `detail` type | Description |
1594
2042
  |---|---|---|
1595
2043
  | `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"). |
2044
+ | `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"). |
2045
+
2046
+ **Methods** (call on the element instance: `document.querySelector('kai-scope-picker').show()`):
2047
+
2048
+ | Method | Signature | Description |
2049
+ |---|---|---|
2050
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
2051
+ | `hide` | `(): void` | Close it programmatically. |
2052
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
1597
2053
 
1598
2054
  ---
1599
2055
 
@@ -1603,6 +2059,7 @@ _No events._
1603
2059
 
1604
2060
  | Property | Attribute | Type | Description |
1605
2061
  |---|---|---|---|
2062
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1606
2063
  | `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
2064
  | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1608
2065
  | `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 +2073,20 @@ _No events._
1616
2073
  | `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
2074
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The screen opened or closed (a method, `Escape` close, or driven `open`). |
1618
2075
 
2076
+ **Methods** (call on the element instance: `document.querySelector('kai-screen').show()`):
2077
+
2078
+ | Method | Signature | Description |
2079
+ |---|---|---|
2080
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
2081
+ | `hide` | `(): void` | Close it programmatically. |
2082
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
2083
+ | `focus` | `(options?: FocusOptions): void` | Move focus to the screen surface (no-op while closed). |
2084
+
1619
2085
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1620
2086
 
1621
2087
  | Slot | Mode | Description |
1622
2088
  |---|---|---|
2089
+ | _(default)_ | inject | The screen body, below the title bar. |
1623
2090
  | `title` | replace | Rich header title; overrides the `headline` prop. |
1624
2091
  | `actions` | inject | Header trailing cluster (e.g. an avatar or overflow menu). |
1625
2092
 
@@ -1640,10 +2107,17 @@ _No events._
1640
2107
 
1641
2108
  | Property | Attribute | Type | Description |
1642
2109
  |---|---|---|---|
2110
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1643
2111
  | `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
2112
 
1645
2113
  _No events._
1646
2114
 
2115
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2116
+
2117
+ | Slot | Mode | Description |
2118
+ |---|---|---|
2119
+ | _(default)_ | inject | The scrollable content. |
2120
+
1647
2121
  **Styleable parts** (restyle from outside via `kai-scroll-area::part(name)`):
1648
2122
 
1649
2123
  | Part | Description |
@@ -1658,15 +2132,16 @@ _No events._
1658
2132
 
1659
2133
  | Property | Attribute | Type | Description |
1660
2134
  |---|---|---|---|
2135
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1661
2136
  | `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'`. |
2137
+ | `variant` | `variant` | `undefined \| "outline" \| "ghost" \| "default"` | Button visual variant: `'outline' \| 'ghost' \| 'default'`. Defaults to `'outline'`. |
1663
2138
  | `size` | `size` | `undefined \| "sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | Button size token. Defaults to `'icon'` (square). |
1664
2139
 
1665
2140
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1666
2141
 
1667
2142
  | Event | `detail` type | Description |
1668
2143
  |---|---|---|
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. |
2144
+ | `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
2145
 
1671
2146
  ---
1672
2147
 
@@ -1676,7 +2151,8 @@ _No events._
1676
2151
 
1677
2152
  | Property | Attribute | Type | Description |
1678
2153
  |---|---|---|---|
1679
- | `value` | `value` | `undefined \| string` | Controlled query — settable and reflected to the `value` attribute. Read `el.value` for live state. |
2154
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2155
+ | `value` | `value` | `undefined \| string` | Controlled query. Settable and reflected to the `value` attribute. Read `el.value` for live state. |
1680
2156
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder. Defaults to `Search…`. |
1681
2157
  | `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
2158
  | `debounce` | `debounce` | `undefined \| number` | Debounce window for `kai-search`, in ms. Defaults to `200`. |
@@ -1691,6 +2167,13 @@ _No events._
1691
2167
  | `kai-search` | `CustomEvent<{ value: string }>` | The query changed (debounced live, and on clear). |
1692
2168
  | `kai-submit` | `CustomEvent<{ value: string }>` | Enter was pressed. |
1693
2169
 
2170
+ **Methods** (call on the element instance: `document.querySelector('kai-search').focus()`):
2171
+
2172
+ | Method | Signature | Description |
2173
+ |---|---|---|
2174
+ | `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. |
2175
+ | `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. |
2176
+
1694
2177
  **Styleable parts** (restyle from outside via `kai-search::part(name)`):
1695
2178
 
1696
2179
  | Part | Description |
@@ -1707,8 +2190,9 @@ _No events._
1707
2190
 
1708
2191
  | Property | Attribute | Type | Description |
1709
2192
  |---|---|---|---|
2193
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1710
2194
  | `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. |
2195
+ | `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
2196
  | `size` | `size` | `undefined \| "sm" \| "md"` | Control density: `sm` or `md`. Defaults to `md`. |
1713
2197
 
1714
2198
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -1732,7 +2216,8 @@ _No events._
1732
2216
 
1733
2217
  | Property | Attribute | Type | Description |
1734
2218
  |---|---|---|---|
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). |
2219
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2220
+ | `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
2221
 
1737
2222
  _No events._
1738
2223
 
@@ -1750,6 +2235,7 @@ _No events._
1750
2235
 
1751
2236
  | Property | Attribute | Type | Description |
1752
2237
  |---|---|---|---|
2238
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1753
2239
  | `label` | `label` | `undefined \| string` | Row label (primary text). Attribute: `label`. |
1754
2240
  | `description` | `description` | `undefined \| string` | Optional secondary description under the label. Attribute: `description`. |
1755
2241
 
@@ -1776,11 +2262,18 @@ _No events._
1776
2262
 
1777
2263
  | Property | Attribute | Type | Description |
1778
2264
  |---|---|---|---|
2265
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1779
2266
  | `heading` | `heading` | `undefined \| string` | Small section heading shown above the card. Attribute: `heading`. |
1780
2267
  | `description` | `description` | `undefined \| string` | Optional muted description under the heading. Attribute: `description`. |
1781
2268
 
1782
2269
  _No events._
1783
2270
 
2271
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2272
+
2273
+ | Slot | Mode | Description |
2274
+ |---|---|---|
2275
+ | _(default)_ | inject | The `<kai-setting-item>` rows in this group. |
2276
+
1784
2277
  **Styleable parts** (restyle from outside via `kai-settings-group::part(name)`):
1785
2278
 
1786
2279
  | Part | Description |
@@ -1796,6 +2289,7 @@ _No events._
1796
2289
 
1797
2290
  | Property | Attribute | Type | Description |
1798
2291
  |---|---|---|---|
2292
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1799
2293
  | `variant` | `variant` | `undefined \| "text" \| "rect" \| "circle"` | `text` (one or more lines), `rect` (a block), or `circle` (round). Defaults to `text`. |
1800
2294
  | `width` | `width` | `undefined \| string` | CSS width (e.g. `'12rem'`, `'60%'`). Defaults to full width (responsive); for `circle` it is the diameter. |
1801
2295
  | `height` | `height` | `undefined \| string` | CSS height. Defaults per variant (a text line height; circle = width). |
@@ -1817,10 +2311,17 @@ _No events._
1817
2311
 
1818
2312
  | Property | Attribute | Type | Description |
1819
2313
  |---|---|---|---|
1820
- | `skills` | | `{ id: string; name: string }[]` | The active skills to badge. Set as a JS property. |
2314
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2315
+ | `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
2316
 
1822
2317
  _No events._
1823
2318
 
2319
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
2320
+
2321
+ | Child element | Attributes | Text content | Notes |
2322
+ |---|---|---|---|
2323
+ | `<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>` |
2324
+
1824
2325
  ---
1825
2326
 
1826
2327
  ### `kai-source` / `Source`
@@ -1829,9 +2330,10 @@ _No events._
1829
2330
 
1830
2331
  | Property | Attribute | Type | Description |
1831
2332
  |---|---|---|---|
2333
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1832
2334
  | `href` | `href` | `undefined \| string` | The URL this citation links to (the domain also seeds the default label/favicon). |
1833
2335
  | `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). |
2336
+ | `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
2337
  | `description` | `description` | `undefined \| string` | Hover-card body text describing the source. |
1836
2338
  | `showFavicon` | `show-favicon` | `undefined \| false \| true` | Show the source's favicon next to the trigger label. |
1837
2339
 
@@ -1845,12 +2347,19 @@ _No events._
1845
2347
 
1846
2348
  | Property | Attribute | Type | Description |
1847
2349
  |---|---|---|---|
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. |
2350
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2351
+ | `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
2352
  | `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`. |
2353
+ | `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
2354
 
1852
2355
  _No events._
1853
2356
 
2357
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
2358
+
2359
+ | Child element | Attributes | Text content | Notes |
2360
+ |---|---|---|---|
2361
+ | `<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) |
2362
+
1854
2363
  ---
1855
2364
 
1856
2365
  ### `kai-status` / `Status`
@@ -1859,6 +2368,7 @@ _No events._
1859
2368
 
1860
2369
  | Property | Attribute | Type | Description |
1861
2370
  |---|---|---|---|
2371
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1862
2372
  | `status` | `status` | `undefined \| "new" \| "online" \| "busy" \| "away" \| "offline"` | Presence/notification state → color. `new` (default) maps to the blue hue. |
1863
2373
  | `pulse` | `pulse` | `undefined \| false \| true` | Animated ping ring (off by default; respects prefers-reduced-motion). |
1864
2374
  | `label` | `label` | `undefined \| string` | Accessible name. Without it the dot is decorative. |
@@ -1880,10 +2390,11 @@ _No events._
1880
2390
 
1881
2391
  | Property | Attribute | Type | Description |
1882
2392
  |---|---|---|---|
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). |
2393
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2394
+ | `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. |
2395
+ | `variant` | `variant` | `undefined \| "outline" \| "ghost" \| "default"` | Chip style: `'outline'` (default), `'ghost'`, or `'default'` (filled). |
1885
2396
  | `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. |
2397
+ | `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
2398
  | `block` | `block` | `undefined \| false \| true` | Full-width left-aligned rows instead of pills. |
1888
2399
  | `highlight` | `highlight` | `undefined \| string` | Substring to highlight within each suggestion. |
1889
2400
 
@@ -1893,6 +2404,12 @@ _No events._
1893
2404
  |---|---|---|
1894
2405
  | `kai-select` | `CustomEvent<{ value: string }>` | A suggestion was clicked. |
1895
2406
 
2407
+ **Declarative children** (compose these in light DOM instead of setting the JS property):
2408
+
2409
+ | Child element | Attributes | Text content | Notes |
2410
+ |---|---|---|---|
2411
+ | `<kai-suggestion>` | `icon`, `value` | yes | Parse a single `<kai-suggestion>` node into an `Item` descriptor. |
2412
+
1896
2413
  ---
1897
2414
 
1898
2415
  ### `kai-switch` / `Switch`
@@ -1901,7 +2418,8 @@ _No events._
1901
2418
 
1902
2419
  | Property | Attribute | Type | Description |
1903
2420
  |---|---|---|---|
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. |
2421
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2422
+ | `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
2423
  | `defaultChecked` | `default-checked` | `undefined \| false \| true` | Initial checked state on mount (uncontrolled seed). Bare attribute (`<kai-switch default-checked>`) turns it on. |
1906
2424
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable interaction. |
1907
2425
  | `label` | `label` | `undefined \| string` | Accessible label. |
@@ -1914,6 +2432,13 @@ _No events._
1914
2432
  |---|---|---|
1915
2433
  | `kai-change` | `CustomEvent<{ checked: false \| true }>` | The toggle changed. |
1916
2434
 
2435
+ **Methods** (call on the element instance: `document.querySelector('kai-switch').toggle()`):
2436
+
2437
+ | Method | Signature | Description |
2438
+ |---|---|---|
2439
+ | `toggle` | `(): void` | Flip the switch and fire `kai-change` (no-op while disabled). |
2440
+ | `focus` | `(options?: FocusOptions): void` | Focus the inner `role="switch"` button (the host element can't reach it). |
2441
+
1917
2442
  ---
1918
2443
 
1919
2444
  ### `kai-tabs` / `Tabs`
@@ -1922,6 +2447,7 @@ _No events._
1922
2447
 
1923
2448
  | Property | Attribute | Type | Description |
1924
2449
  |---|---|---|---|
2450
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1925
2451
  | `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
2452
  | `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
2453
  | `defaultValue` | `default-value` | `undefined \| string` | Initial selected id when uncontrolled (use the `default-value` attribute in plain HTML). |
@@ -1935,6 +2461,13 @@ _No events._
1935
2461
  |---|---|---|
1936
2462
  | `kai-tab-change` | `CustomEvent<{ value: string }>` | A tab was selected (click, Enter/Space, or arrow-key move). `value` is the item's id. |
1937
2463
 
2464
+ **Methods** (call on the element instance: `document.querySelector('kai-tabs').select(…)`):
2465
+
2466
+ | Method | Signature | Description |
2467
+ |---|---|---|
2468
+ | `select` | `(id: string): void` | Select a tab by id (fires `kai-tab-change`). Ignores unknown/disabled ids. |
2469
+ | `focus` | `(): void` | Focus the active tab (or the first focusable tab). |
2470
+
1938
2471
  **Styleable parts** (restyle from outside via `kai-tabs::part(name)`):
1939
2472
 
1940
2473
  | Part | Description |
@@ -1950,7 +2483,8 @@ _No events._
1950
2483
 
1951
2484
  | Property | Attribute | Type | Description |
1952
2485
  |---|---|---|---|
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. |
2486
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2487
+ | `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
2488
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
1955
2489
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
1956
2490
  | `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 +2497,18 @@ _No events._
1963
2497
 
1964
2498
  | Event | `detail` type | Description |
1965
2499
  |---|---|---|
1966
- | `kai-value-change` | `CustomEvent<{ value: string[] }>` | The selection changed on a toggle the selected ids in input order. |
2500
+ | `kai-value-change` | `CustomEvent<{ value: string[] }>` | The selection changed on a toggle. Carries the selected ids in input order. |
2501
+
2502
+ **Methods** (call on the element instance: `document.querySelector('kai-tasks').select(…)`):
2503
+
2504
+ | Method | Signature | Description |
2505
+ |---|---|---|
2506
+ | `select` | `(taskIds?: string[]): void` | Set the checked task ids (local-only, no emit), respecting disabled/max. With no arg, select all toggleable rows. |
2507
+ | `toggle` | `(taskId: string, checked?: boolean): void` | Toggle one task by id, honoring the max gate (no `checked` = flip). |
2508
+ | `send` | `(): void` | Confirm the current selection: emits the `submit` CardEvent + resolves (only when the min/max gate passes). Named `send`, not `submit`. |
2509
+ | `focus` | `(options?: FocusOptions): void` | Focus the task group (select-all checkbox if shown, else the first row). |
2510
+ | `dismiss` | `(): void` | Trigger the dismiss path (emit `dismiss` + collapse to the re-openable stub). |
2511
+ | `reopen` | `(): void` | Re-open a dismissed card from its stub (emit `reopen`). |
1967
2512
 
1968
2513
  ---
1969
2514
 
@@ -1973,6 +2518,7 @@ _No events._
1973
2518
 
1974
2519
  | Property | Attribute | Type | Description |
1975
2520
  |---|---|---|---|
2521
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1976
2522
  | `text` | `text` | `undefined \| string` | The text to shimmer. |
1977
2523
  | `as` | `as` | `undefined \| string` | Element tag to render as (default `span`). |
1978
2524
  | `duration` | `duration` | `undefined \| number` | Animation duration in seconds. |
@@ -1988,6 +2534,7 @@ _No events._
1988
2534
 
1989
2535
  | Property | Attribute | Type | Description |
1990
2536
  |---|---|---|---|
2537
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
1991
2538
  | `text` | `text` | `undefined \| string` | The shimmering label, e.g. "Thinking…". |
1992
2539
  | `stoppable` | `stoppable` | `undefined \| false \| true` | When true, show a "stop" affordance that fires a `stop` event. |
1993
2540
  | `stopLabel` | `stop-label` | `undefined \| string` | Label for the stop affordance. |
@@ -2006,14 +2553,17 @@ _No events._
2006
2553
 
2007
2554
  | Property | Attribute | Type | Description |
2008
2555
  |---|---|---|---|
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'`. |
2556
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2557
+ | `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). |
2558
+ | `loading` | `loading` | `undefined \| false \| true` | Show a typing indicator on the pending assistant turn. Set it while awaiting the assistant's reply. |
2559
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. |
2012
2560
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name for syntax-highlighted code blocks (e.g. `'github-dark-dimmed'`). |
2013
2561
  | `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
2562
  | `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
2563
  | `scrollButton` | `scroll-button` | `undefined \| false \| true` | Show the scroll-to-bottom button inside the scroll area. Default true. |
2016
2564
  | `class` | `class` | `undefined \| string` | Extra classes applied to the thread's inner root. |
2565
+ | `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. |
2566
+ | `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
2567
 
2018
2568
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2019
2569
 
@@ -2021,6 +2571,12 @@ _No events._
2021
2571
  |---|---|---|
2022
2572
  | `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
2573
 
2574
+ **Methods** (call on the element instance: `document.querySelector('kai-thread').scrollToBottom(…)`):
2575
+
2576
+ | Method | Signature | Description |
2577
+ |---|---|---|
2578
+ | `scrollToBottom` | `(behavior?: ScrollBehavior): void` | Scroll the message list to the bottom (default `'smooth'`). |
2579
+
2024
2580
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2025
2581
 
2026
2582
  | Slot | Mode | Description |
@@ -2035,7 +2591,8 @@ _No events._
2035
2591
 
2036
2592
  | Property | Attribute | Type | Description |
2037
2593
  |---|---|---|---|
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. |
2594
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2595
+ | `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
2596
  | `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
2597
  | `max` | `max` | `undefined \| number` | Max simultaneously-visible toasts; the rest queue. Defaults to `3`. |
2041
2598
  | `stack` | `stack` | `undefined \| "expanded" \| "collapsed"` | Stacking: 'expanded' (default, full column) \| 'collapsed' (Sonner-style pile that expands on hover/focus). Attribute: stack. |
@@ -2048,7 +2605,7 @@ _No events._
2048
2605
  | Event | `detail` type | Description |
2049
2606
  |---|---|---|
2050
2607
  | `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'`. |
2608
+ | `kai-dismiss` | `CustomEvent<{ id: string; reason: "timeout" \| "close" \| "action" }>` | A toast left the stack. `reason` is `'timeout' \| 'close' \| 'action'`. |
2052
2609
 
2053
2610
  ---
2054
2611
 
@@ -2058,10 +2615,11 @@ _No events._
2058
2615
 
2059
2616
  | Property | Attribute | Type | Description |
2060
2617
  |---|---|---|---|
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. |
2618
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2619
+ | `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
2620
  | `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
2621
  | `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. |
2622
+ | `disabled` | `disabled` | `undefined \| false \| true` | Gate the disclosure trigger: programmatic `show()/hide()/toggle()` still work, but the trigger click no longer toggles. |
2065
2623
 
2066
2624
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2067
2625
 
@@ -2069,6 +2627,14 @@ _No events._
2069
2627
  |---|---|---|
2070
2628
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The panel expanded or collapsed (by trigger click or a method). |
2071
2629
 
2630
+ **Methods** (call on the element instance: `document.querySelector('kai-tool').show()`):
2631
+
2632
+ | Method | Signature | Description |
2633
+ |---|---|---|
2634
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
2635
+ | `hide` | `(): void` | Close it programmatically. |
2636
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
2637
+
2072
2638
  ---
2073
2639
 
2074
2640
  ### `kai-tooltip` / `Tooltip`
@@ -2077,6 +2643,7 @@ _No events._
2077
2643
 
2078
2644
  | Property | Attribute | Type | Description |
2079
2645
  |---|---|---|---|
2646
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2080
2647
  | `content` | `content` | `undefined \| string` | The hint text shown on hover/focus of the slotted trigger. |
2081
2648
  | `openDelay` | `open-delay` | `undefined \| number` | Delay (ms) before the tooltip appears on hover. Defaults to 600. Focus shows it immediately regardless. |
2082
2649
  | `closeDelay` | `close-delay` | `undefined \| number` | Delay (ms) before it hides after the pointer leaves. Defaults to 0 (hides immediately). |
@@ -2091,6 +2658,20 @@ _No events._
2091
2658
  |---|---|---|
2092
2659
  | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The tooltip opened or closed (by hover/focus, outside-click, or a method). |
2093
2660
 
2661
+ **Methods** (call on the element instance: `document.querySelector('kai-tooltip').show()`):
2662
+
2663
+ | Method | Signature | Description |
2664
+ |---|---|---|
2665
+ | `show` | `(): void` | Open it programmatically (no-op while disabled). |
2666
+ | `hide` | `(): void` | Close it programmatically. |
2667
+ | `toggle` | `(): void` | Flip the open state (closes while disabled). |
2668
+
2669
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2670
+
2671
+ | Slot | Mode | Description |
2672
+ |---|---|---|
2673
+ | _(default)_ | inject | The TRIGGER the tooltip describes. The tip text is the `text` prop. |
2674
+
2094
2675
  ---
2095
2676
 
2096
2677
  ### `kai-voice-input` / `VoiceInput`
@@ -2099,7 +2680,8 @@ _No events._
2099
2680
 
2100
2681
  | Property | Attribute | Type | Description |
2101
2682
  |---|---|---|---|
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. |
2683
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2684
+ | `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
2685
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the mic button (non-interactive). |
2104
2686
  | `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
2687
  | `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 +2690,17 @@ _No events._
2108
2690
 
2109
2691
  | Event | `detail` type | Description |
2110
2692
  |---|---|---|
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. |
2693
+ | `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). |
2694
+ | `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
2695
  | `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). |
2696
+ | `kai-transcription` | `CustomEvent<{ text: string }>` | Final transcript: the `transcribe` property resolved, OR native `SpeechRecognition` produced final text (no `transcribe` set). |
2697
+
2698
+ **Methods** (call on the element instance: `document.querySelector('kai-voice-input').start()`):
2699
+
2700
+ | Method | Signature | Description |
2701
+ |---|---|---|
2702
+ | `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. |
2703
+ | `stop` | `(): void` | Stop the in-progress recording, producing the blob (→ kai-audio-captured) and running transcription. Pairs with start() for push-to-talk. |
2115
2704
 
2116
2705
  ---
2117
2706
 
@@ -2121,17 +2710,27 @@ _No events._
2121
2710
 
2122
2711
  | Property | Attribute | Type | Description |
2123
2712
  |---|---|---|---|
2713
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2124
2714
  | `text` | `text` | `undefined \| string` | The utterance to read aloud. |
2125
2715
  | `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. |
2716
+ | `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
2717
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the button (non-interactive). |
2128
2718
 
2129
2719
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2130
2720
 
2131
2721
  | Event | `detail` type | Description |
2132
2722
  |---|---|---|
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. |
2723
+ | `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. |
2724
+ | `kai-synthesized` | `CustomEvent<{ blob: Blob }>` | The model path (`synthesize`) resolved audio: the raw `Blob` before playback. |
2725
+
2726
+ **Methods** (call on the element instance: `document.querySelector('kai-voice-output').speak()`):
2727
+
2728
+ | Method | Signature | Description |
2729
+ |---|---|---|
2730
+ | `speak` | `(): void` | Speak the current `text` (native, or via `synthesize` if set). |
2731
+ | `pause` | `(): void` | Pause playback (resumable). |
2732
+ | `resume` | `(): void` | Resume paused playback. |
2733
+ | `stop` | `(): void` | Stop playback and reset. |
2135
2734
 
2136
2735
  **Styleable parts** (restyle from outside via `kai-voice-output::part(name)`):
2137
2736
 
@@ -2147,16 +2746,17 @@ _No events._
2147
2746
 
2148
2747
  | Property | Attribute | Type | Description |
2149
2748
  |---|---|---|---|
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. |
2749
+ | `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
2750
+ | `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. |
2751
+ | `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
2752
  | `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. |
2753
+ | `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
2754
  | `value` | `value` | `undefined \| string` | |
2155
2755
  | `placeholder` | `placeholder` | `undefined \| string` | |
2156
2756
  | `loading` | `loading` | `undefined \| false \| true` | |
2157
2757
  | `suggestions` | — | `undefined \| string[]` | |
2158
2758
  | `suggestionMode` | `suggestion-mode` | `undefined \| "submit" \| "fill"` | |
2159
- | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | |
2759
+ | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | |
2160
2760
  | `codeTheme` | `code-theme` | `undefined \| string` | |
2161
2761
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | |
2162
2762
  | `chatTitle` | `chat-title` | `undefined \| string` | |
@@ -2176,6 +2776,8 @@ _No events._
2176
2776
  | `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
2777
  | `compact` | `compact` | `undefined \| false \| true` | Render Recents as dense single-line rows (a leading dot + title, no count). |
2178
2778
  | `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`. |
2779
+ | `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. |
2780
+ | `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
2781
 
2180
2782
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2181
2783
 
@@ -2192,6 +2794,18 @@ _No events._
2192
2794
  | `kai-value-change` | `CustomEvent<{ value: string }>` | Fired on every input change. |
2193
2795
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Mic / voice button was clicked. |
2194
2796
 
2797
+ **Methods** (call on the element instance: `document.querySelector('kai-workspace').toggleSidebar()`):
2798
+
2799
+ | Method | Signature | Description |
2800
+ |---|---|---|
2801
+ | `toggleSidebar` | `(): void` | Collapse/expand the conversation sidebar and fire `kai-sidebar-toggle`. |
2802
+ | `collapseSidebar` | `(): void` | Force the conversation sidebar collapsed (fires `kai-sidebar-toggle`). |
2803
+ | `expandSidebar` | `(): void` | Force the conversation sidebar expanded (fires `kai-sidebar-toggle`). |
2804
+ | `focus` | `(options?: FocusOptions): void` | Focus the thread's composer. |
2805
+ | `clear` | `(): void` | Clear the thread draft + staged attachments. |
2806
+ | `send` | `(): void` | Submit the current thread draft programmatically (fires `kai-submit`). |
2807
+ | `scrollToBottom` | `(behavior?: ScrollBehavior): void` | Scroll the thread to the newest message. |
2808
+
2195
2809
  **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2196
2810
 
2197
2811
  | Slot | Mode | Description |