@kitn.ai/ui 0.16.0 → 0.18.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 (493) hide show
  1. package/README.md +55 -17
  2. package/dist/components/artifact.d.ts +39 -1
  3. package/dist/components/attachment-types.d.ts +12 -0
  4. package/dist/components/attachments.d.ts +2 -10
  5. package/dist/components/card.d.ts +27 -0
  6. package/dist/components/chain-of-thought.d.ts +78 -2
  7. package/dist/components/chat-scope-picker.d.ts +7 -0
  8. package/dist/components/chat-thread.d.ts +39 -16
  9. package/dist/components/choice-card.d.ts +27 -0
  10. package/dist/components/coachmark.d.ts +49 -0
  11. package/dist/components/composer-dom.d.ts +35 -0
  12. package/dist/components/composer-highlight.d.ts +48 -0
  13. package/dist/components/composer-history.d.ts +34 -0
  14. package/dist/components/composer.d.ts +86 -0
  15. package/dist/components/confirm-card.d.ts +17 -0
  16. package/dist/components/conversation-item.d.ts +9 -0
  17. package/dist/components/conversation-list.d.ts +36 -1
  18. package/dist/components/dismissed-stub.d.ts +22 -0
  19. package/dist/components/file-tree.d.ts +15 -0
  20. package/dist/components/form.d.ts +34 -0
  21. package/dist/components/markdown.d.ts +3 -0
  22. package/dist/components/message.d.ts +71 -2
  23. package/dist/components/model-switcher.d.ts +7 -0
  24. package/dist/components/prompt-input.d.ts +13 -4
  25. package/dist/components/prompt-suggestion.d.ts +9 -1
  26. package/dist/components/reasoning.d.ts +14 -1
  27. package/dist/components/response-compare-types.d.ts +66 -0
  28. package/dist/components/response-compare.d.ts +65 -0
  29. package/dist/components/screen.d.ts +46 -0
  30. package/dist/components/tasks-card.d.ts +50 -1
  31. package/dist/components/toast.d.ts +58 -0
  32. package/dist/components/tool-types.d.ts +10 -0
  33. package/dist/components/tool.d.ts +8 -8
  34. package/dist/components/use-card-resolution.d.ts +10 -6
  35. package/dist/components/voice-input.d.ts +27 -0
  36. package/dist/components/voice-output.d.ts +33 -0
  37. package/dist/core-C8fzo39E.js +12 -0
  38. package/dist/custom-elements.json +8472 -1691
  39. package/dist/elements/agent-card.d.ts +1 -0
  40. package/dist/elements/agent-card.js +1 -0
  41. package/dist/elements/artifact.js +1 -0
  42. package/dist/elements/attachments.js +1 -0
  43. package/dist/elements/autoloader.d.ts +2 -0
  44. package/dist/elements/autoloader.js +1 -0
  45. package/dist/elements/avatar.d.ts +1 -0
  46. package/dist/elements/avatar.js +1 -0
  47. package/dist/elements/badge.d.ts +1 -0
  48. package/dist/elements/badge.js +1 -0
  49. package/dist/elements/button.d.ts +1 -0
  50. package/dist/elements/button.js +1 -0
  51. package/dist/elements/card.js +1 -0
  52. package/dist/elements/cards.d.ts +1 -0
  53. package/dist/elements/cards.js +1 -0
  54. package/dist/elements/chain-of-thought.d.ts +0 -12
  55. package/dist/elements/chain-of-thought.js +1 -0
  56. package/dist/elements/chat-scope-picker.js +1 -0
  57. package/dist/elements/chat-types.d.ts +8 -2
  58. package/dist/elements/chat-workspace.js +1 -0
  59. package/dist/elements/chat.js +1 -0
  60. package/dist/elements/checkpoint.js +1 -0
  61. package/dist/elements/choice.js +1 -0
  62. package/dist/elements/chunks/Icon-DLLBeLG5.js +1 -0
  63. package/dist/elements/chunks/action-icons-UB4V5FNq.js +1 -0
  64. package/dist/elements/chunks/arrow-left-C-gwlUmx.js +1 -0
  65. package/dist/elements/chunks/attachments-CgbErf1r.js +1 -0
  66. package/dist/elements/chunks/badge-DTQpgB6N.js +1 -0
  67. package/dist/elements/chunks/bash-InADTalH.js +1 -0
  68. package/dist/elements/chunks/button-XDUM6lXs.js +1 -0
  69. package/dist/elements/chunks/card-CBlFZ-7I.js +1 -0
  70. package/dist/elements/chunks/card-routing-C986KuUA.js +1 -0
  71. package/dist/elements/chunks/chat-thread-BIhmqwNL.js +1 -0
  72. package/dist/elements/chunks/check-hGUoVIKb.js +1 -0
  73. package/dist/elements/chunks/chevron-down-DDoaSUtO.js +1 -0
  74. package/dist/elements/chunks/chevron-right-CdGEUakB.js +1 -0
  75. package/dist/elements/chunks/circle-BKAZdmzb.js +1 -0
  76. package/dist/elements/chunks/circle-check-CmYG-y3I.js +1 -0
  77. package/dist/elements/chunks/circle-x-DewgAEym.js +1 -0
  78. package/dist/elements/chunks/cn-CU7UAWzk.js +1 -0
  79. package/dist/elements/chunks/code-block-DedwxwFI.js +1 -0
  80. package/dist/elements/chunks/collapsible-FfgmTHtM.js +1 -0
  81. package/dist/elements/chunks/composer-Cfbw5HKT.js +65 -0
  82. package/dist/elements/chunks/context-DwJmxlRE.js +1 -0
  83. package/dist/elements/chunks/conversation-list-DsCQMfPq.js +1 -0
  84. package/dist/elements/chunks/core-AYMC6_lb.js +12 -0
  85. package/dist/elements/chunks/css-M7EaDHN_.js +1 -0
  86. package/dist/elements/chunks/default-input-BuqsoENC.js +1 -0
  87. package/dist/elements/chunks/define-rGsaWsLL.js +1 -0
  88. package/dist/elements/chunks/disclosure-CwoAkpMH.js +1 -0
  89. package/dist/elements/chunks/download-DJ06G3Bz.js +1 -0
  90. package/dist/elements/chunks/dropdown-BFg8ri_R.js +1 -0
  91. package/dist/elements/chunks/ellipsis-dlN6YvGw.js +1 -0
  92. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +141 -0
  93. package/dist/elements/chunks/external-link-DBX2lc_R.js +1 -0
  94. package/dist/elements/chunks/file-text-HfwUx4F9.js +1 -0
  95. package/dist/elements/chunks/file-tree-C6Toj71u.js +1 -0
  96. package/dist/elements/chunks/folder-MYuqa9Xp.js +1 -0
  97. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +1 -0
  98. package/dist/elements/chunks/github-light-JYsPkUQd.js +1 -0
  99. package/dist/elements/chunks/hover-card-gE7iyXdW.js +1 -0
  100. package/dist/elements/chunks/html-CPZ3oZQ7.js +1 -0
  101. package/dist/elements/chunks/icon-Vqz4ivrY.js +1 -0
  102. package/dist/elements/chunks/index-BCRhi84a.js +1 -0
  103. package/dist/elements/chunks/info-B_toFS0l.js +1 -0
  104. package/dist/elements/chunks/input-gzbHYZvQ.js +1 -0
  105. package/dist/elements/chunks/javascript-C25yR2R2.js +1 -0
  106. package/dist/elements/chunks/json-DxJze_jm.js +1 -0
  107. package/dist/elements/chunks/kbd-n49c4KZm.js +1 -0
  108. package/dist/elements/chunks/link-CKPs9yM0.js +1 -0
  109. package/dist/elements/chunks/loader-q6MamGus.js +1 -0
  110. package/dist/elements/chunks/markdown-DBrcqq4S.js +64 -0
  111. package/dist/elements/chunks/message-CTqaYI0s.js +1 -0
  112. package/dist/elements/chunks/message-feedback-Cvzm03w0.js +1 -0
  113. package/dist/elements/chunks/message-square-CUg9eRCi.js +1 -0
  114. package/dist/elements/chunks/minimize-2-l2klxP6K.js +1 -0
  115. package/dist/elements/chunks/model-switcher-CmyjiHO1.js +1 -0
  116. package/dist/elements/chunks/overlay-C8sOYqjT.js +1 -0
  117. package/dist/elements/chunks/paperclip-42Ia7xHD.js +1 -0
  118. package/dist/elements/chunks/progress-bar-tzKMWAwO.js +1 -0
  119. package/dist/elements/chunks/prompt-suggestion-D3F6vP_A.js +1 -0
  120. package/dist/elements/chunks/reasoning-BJNmmyov.js +1 -0
  121. package/dist/elements/chunks/resizable-BRsk19qI.js +1 -0
  122. package/dist/elements/chunks/rotate-cw-BgcggLHC.js +1 -0
  123. package/dist/elements/chunks/scroll-area-CgGbFFbT.js +1 -0
  124. package/dist/elements/chunks/separator-8hM4GdHE.js +1 -0
  125. package/dist/elements/chunks/settings-CaUIYOg-.js +1 -0
  126. package/dist/elements/chunks/settings-group-CYjOVIZQ.js +1 -0
  127. package/dist/elements/chunks/share-BTYM3Ddt.js +1 -0
  128. package/dist/elements/chunks/skeleton-DsTYrPUe.js +1 -0
  129. package/dist/elements/chunks/slots-DkZ-Cmha.js +1 -0
  130. package/dist/elements/chunks/star-9R3LBQ9n.js +1 -0
  131. package/dist/elements/chunks/store-CYl5Y0_u.js +1 -0
  132. package/dist/elements/chunks/svelte--5p79yCD.js +1 -0
  133. package/dist/elements/chunks/text-shimmer-CaYGv8Se.js +1 -0
  134. package/dist/elements/chunks/textarea-NEr2SLcD.js +1 -0
  135. package/dist/elements/chunks/thumbs-up-DZYZkFfc.js +1 -0
  136. package/dist/elements/chunks/toast-store-ClxVZRcc.js +1 -0
  137. package/dist/elements/chunks/tool-CHZOmoe9.js +1 -0
  138. package/dist/elements/chunks/tooltip-WpHElELC.js +1 -0
  139. package/dist/elements/chunks/triangle-alert-BlS80PUg.js +1 -0
  140. package/dist/elements/chunks/tsx-B8rCNbgL.js +1 -0
  141. package/dist/elements/chunks/typescript-RycA9KXf.js +1 -0
  142. package/dist/elements/chunks/use-card-resolution-VSVoxDeJ.js +1 -0
  143. package/dist/elements/chunks/video-DxTPZqT8.js +1 -0
  144. package/dist/elements/chunks/vue-BmIZj4XD.js +1 -0
  145. package/dist/elements/chunks/x-C1qtndxn.js +1 -0
  146. package/dist/elements/coachmark.d.ts +1 -0
  147. package/dist/elements/coachmark.js +1 -0
  148. package/dist/elements/code-block.js +1 -0
  149. package/dist/elements/command.d.ts +1 -0
  150. package/dist/elements/command.js +1 -0
  151. package/dist/elements/compare.d.ts +1 -0
  152. package/dist/elements/compare.js +1 -0
  153. package/dist/elements/composer.d.ts +1 -0
  154. package/dist/elements/composer.js +1 -0
  155. package/dist/elements/confirm-card.js +1 -0
  156. package/dist/elements/context-meter.js +1 -0
  157. package/dist/elements/conversation-list.d.ts +1 -11
  158. package/dist/elements/conversation-list.js +1 -0
  159. package/dist/elements/default-input.d.ts +20 -9
  160. package/dist/elements/define.d.ts +11 -0
  161. package/dist/elements/dialog.d.ts +1 -0
  162. package/dist/elements/dialog.js +1 -0
  163. package/dist/elements/disclosure.d.ts +37 -0
  164. package/dist/elements/editable-label.d.ts +1 -0
  165. package/dist/elements/editable-label.js +1 -0
  166. package/dist/elements/embed.js +1 -0
  167. package/dist/elements/empty.js +1 -0
  168. package/dist/elements/feedback-bar.js +1 -0
  169. package/dist/elements/file-tree.js +1 -0
  170. package/dist/elements/file-upload.js +1 -0
  171. package/dist/elements/form.js +1 -0
  172. package/dist/elements/hover-card.d.ts +1 -0
  173. package/dist/elements/hover-card.js +1 -0
  174. package/dist/elements/icon.d.ts +1 -0
  175. package/dist/elements/icon.js +1 -0
  176. package/dist/elements/image.js +1 -0
  177. package/dist/elements/input.d.ts +1 -0
  178. package/dist/elements/input.js +1 -0
  179. package/dist/elements/kbd.d.ts +1 -0
  180. package/dist/elements/kbd.js +1 -0
  181. package/dist/elements/link-preview.js +1 -0
  182. package/dist/elements/loader.js +1 -0
  183. package/dist/elements/markdown.js +1 -0
  184. package/dist/elements/menu.d.ts +1 -0
  185. package/dist/elements/menu.js +1 -0
  186. package/dist/elements/message-skills.d.ts +0 -16
  187. package/dist/elements/message-skills.js +1 -0
  188. package/dist/elements/message.js +1 -0
  189. package/dist/elements/model-switcher.d.ts +1 -11
  190. package/dist/elements/model-switcher.js +1 -0
  191. package/dist/elements/nav.d.ts +1 -0
  192. package/dist/elements/nav.js +1 -0
  193. package/dist/elements/notice.d.ts +1 -0
  194. package/dist/elements/notice.js +1 -0
  195. package/dist/elements/pane-group.d.ts +1 -0
  196. package/dist/elements/pane-group.js +1 -0
  197. package/dist/elements/pane.d.ts +1 -0
  198. package/dist/elements/pane.js +1 -0
  199. package/dist/elements/popover.js +1 -0
  200. package/dist/elements/progress-bar.d.ts +1 -0
  201. package/dist/elements/progress-bar.js +1 -0
  202. package/dist/elements/prompt-dock.d.ts +1 -0
  203. package/dist/elements/prompt-dock.js +1 -0
  204. package/dist/elements/prompt-input.d.ts +1 -9
  205. package/dist/elements/prompt-input.js +1 -0
  206. package/dist/elements/prompt-suggestions.d.ts +0 -6
  207. package/dist/elements/prompt-suggestions.js +1 -0
  208. package/dist/elements/reasoning.js +1 -0
  209. package/dist/elements/register.d.ts +10 -0
  210. package/dist/elements/remote.js +1 -0
  211. package/dist/elements/resizable.d.ts +1 -17
  212. package/dist/elements/resizable.js +1 -0
  213. package/dist/elements/response-stream.js +14 -0
  214. package/dist/elements/screen.d.ts +1 -0
  215. package/dist/elements/screen.js +1 -0
  216. package/dist/elements/scroll-area.d.ts +1 -0
  217. package/dist/elements/scroll-area.js +1 -0
  218. package/dist/elements/scroll-button.js +1 -0
  219. package/dist/elements/search.d.ts +1 -0
  220. package/dist/elements/search.js +1 -0
  221. package/dist/elements/segmented.d.ts +1 -0
  222. package/dist/elements/segmented.js +1 -0
  223. package/dist/elements/separator.d.ts +1 -0
  224. package/dist/elements/separator.js +1 -0
  225. package/dist/elements/setting-item.d.ts +1 -0
  226. package/dist/elements/setting-item.js +1 -0
  227. package/dist/elements/settings-group.d.ts +1 -0
  228. package/dist/elements/settings-group.js +1 -0
  229. package/dist/elements/skeleton.d.ts +1 -0
  230. package/dist/elements/skeleton.js +1 -0
  231. package/dist/elements/slots.d.ts +176 -0
  232. package/dist/elements/source.d.ts +0 -17
  233. package/dist/elements/source.js +1 -0
  234. package/dist/elements/status.d.ts +1 -0
  235. package/dist/elements/status.js +1 -0
  236. package/dist/elements/switch.js +1 -0
  237. package/dist/elements/tabs.d.ts +1 -0
  238. package/dist/elements/tabs.js +1 -0
  239. package/dist/elements/tasks.js +1 -0
  240. package/dist/elements/text-shimmer.js +1 -0
  241. package/dist/elements/thinking-bar.js +1 -0
  242. package/dist/elements/toast.d.ts +1 -0
  243. package/dist/elements/toast.js +1 -0
  244. package/dist/elements/tool.js +1 -0
  245. package/dist/elements/tooltip.d.ts +1 -0
  246. package/dist/elements/tooltip.js +1 -0
  247. package/dist/elements/voice-input.js +1 -0
  248. package/dist/elements/voice-output.d.ts +1 -0
  249. package/dist/elements/voice-output.js +1 -0
  250. package/dist/elements.d.ts +772 -61
  251. package/dist/engine-javascript-C1x7zo1_.js +141 -0
  252. package/dist/index.d.ts +15 -2
  253. package/dist/index.js +14405 -9810
  254. package/dist/kai.es.js +1 -0
  255. package/dist/llms/llms-full.txt +1234 -107
  256. package/dist/llms/llms.txt +4 -4
  257. package/dist/mcp.es.js +217 -6
  258. package/dist/primitives/card-contract.d.ts +20 -4
  259. package/dist/primitives/card-recovery.d.ts +55 -0
  260. package/dist/primitives/composer-model.d.ts +22 -0
  261. package/dist/primitives/composer-triggers.d.ts +19 -0
  262. package/dist/primitives/create-kai-chat.d.ts +38 -0
  263. package/dist/primitives/message-feedback.d.ts +35 -0
  264. package/dist/primitives/toast-store.d.ts +118 -0
  265. package/dist/primitives/use-speech-recognition.d.ts +26 -0
  266. package/dist/react/index.d.ts +1166 -98
  267. package/dist/react/runtime.d.ts +9 -2
  268. package/dist/react/use-kai-chat.d.ts +41 -0
  269. package/dist/react.js +575 -186
  270. package/dist/register-impl-CYWGkgbI.js +141 -0
  271. package/dist/schemas/choice.schema.json +5 -0
  272. package/dist/schemas/tasks.schema.json +7 -2
  273. package/dist/state/index.d.ts +4 -0
  274. package/dist/state/messages.d.ts +11 -0
  275. package/dist/state/stream.d.ts +22 -0
  276. package/dist/state/suggestions.d.ts +4 -0
  277. package/dist/state.js +117 -0
  278. package/dist/theme.tokens.css +87 -0
  279. package/dist/types.d.ts +4 -0
  280. package/dist/ui/agent-card.d.ts +61 -0
  281. package/dist/ui/button.d.ts +5 -2
  282. package/dist/ui/card.d.ts +61 -0
  283. package/dist/ui/collapsible.d.ts +15 -1
  284. package/dist/ui/command.d.ts +33 -0
  285. package/dist/ui/dialog.d.ts +42 -0
  286. package/dist/ui/dropdown.d.ts +95 -1
  287. package/dist/ui/editable-label.d.ts +26 -0
  288. package/dist/ui/hover-card.d.ts +13 -1
  289. package/dist/ui/icon.d.ts +18 -0
  290. package/dist/ui/input.d.ts +33 -0
  291. package/dist/ui/kbd.d.ts +21 -0
  292. package/dist/ui/nav.d.ts +62 -0
  293. package/dist/ui/notice.d.ts +31 -0
  294. package/dist/ui/overlay.d.ts +12 -0
  295. package/dist/ui/pane-grid.d.ts +58 -0
  296. package/dist/ui/pane-group.d.ts +75 -0
  297. package/dist/ui/pane.d.ts +78 -0
  298. package/dist/ui/popover.d.ts +11 -1
  299. package/dist/ui/progress-bar.d.ts +23 -0
  300. package/dist/ui/prompt-dock.d.ts +96 -0
  301. package/dist/ui/resizable.d.ts +12 -0
  302. package/dist/ui/scroll-area.d.ts +4 -0
  303. package/dist/ui/segmented.d.ts +28 -0
  304. package/dist/ui/settings-group.d.ts +32 -0
  305. package/dist/ui/skeleton.d.ts +12 -0
  306. package/dist/ui/stat.d.ts +16 -0
  307. package/dist/ui/status.d.ts +13 -0
  308. package/dist/ui/switch.d.ts +8 -1
  309. package/dist/ui/tabs.d.ts +35 -0
  310. package/dist/ui/tooltip.d.ts +20 -1
  311. package/frameworks/react/index.tsx +1100 -163
  312. package/frameworks/react/runtime.tsx +30 -1
  313. package/frameworks/react/use-kai-chat.tsx +87 -0
  314. package/llms-full.txt +1234 -107
  315. package/llms.txt +4 -4
  316. package/package.json +36 -12
  317. package/src/agent-tooling/mcp/manifest.ts +14 -0
  318. package/src/agent-tooling/mcp/tools/debug.ts +194 -6
  319. package/src/agent-tooling/mcp/tools/reference.ts +32 -0
  320. package/src/agent-tooling/mcp/tools/scaffold.ts +162 -1
  321. package/src/components/artifact.tsx +98 -13
  322. package/src/components/attachment-types.ts +13 -0
  323. package/src/components/attachments.tsx +4 -14
  324. package/src/components/card.tsx +109 -15
  325. package/src/components/chain-of-thought.tsx +210 -1
  326. package/src/components/chat-scope-picker.tsx +13 -3
  327. package/src/components/chat-thread.tsx +238 -137
  328. package/src/components/choice-card.tsx +136 -15
  329. package/src/components/coachmark.tsx +245 -0
  330. package/src/components/component-meta.json +921 -108
  331. package/src/components/composer-dom.ts +159 -0
  332. package/src/components/composer-highlight.ts +242 -0
  333. package/src/components/composer-history.ts +82 -0
  334. package/src/components/composer.tsx +979 -0
  335. package/src/components/confirm-card.tsx +69 -2
  336. package/src/components/conversation-item.tsx +69 -7
  337. package/src/components/conversation-list.tsx +134 -37
  338. package/src/components/dismissed-stub.tsx +80 -0
  339. package/src/components/file-tree.tsx +197 -47
  340. package/src/components/form-widgets.tsx +9 -7
  341. package/src/components/form.tsx +161 -20
  342. package/src/components/image.tsx +3 -5
  343. package/src/components/link-preview.tsx +5 -4
  344. package/src/components/markdown.tsx +5 -2
  345. package/src/components/message.tsx +174 -14
  346. package/src/components/model-switcher.tsx +19 -4
  347. package/src/components/prompt-input.tsx +46 -75
  348. package/src/components/prompt-suggestion.tsx +48 -16
  349. package/src/components/reasoning.tsx +29 -6
  350. package/src/components/response-compare-types.ts +127 -0
  351. package/src/components/response-compare.tsx +460 -0
  352. package/src/components/screen.tsx +176 -0
  353. package/src/components/tasks-card.tsx +374 -23
  354. package/src/components/toast.tsx +486 -0
  355. package/src/components/tool-types.ts +10 -0
  356. package/src/components/tool.tsx +22 -16
  357. package/src/components/use-card-resolution.ts +24 -8
  358. package/src/components/voice-input.tsx +102 -19
  359. package/src/components/voice-output.tsx +202 -0
  360. package/src/elements/agent-card.tsx +67 -0
  361. package/src/elements/artifact.tsx +53 -4
  362. package/src/elements/attachments.tsx +1 -0
  363. package/src/elements/autoloader.ts +89 -0
  364. package/src/elements/avatar.tsx +33 -0
  365. package/src/elements/badge.tsx +32 -0
  366. package/src/elements/button.tsx +126 -0
  367. package/src/elements/card-media.jpg +0 -0
  368. package/src/elements/card.tsx +124 -31
  369. package/src/elements/cards.tsx +81 -7
  370. package/src/elements/chain-of-thought.tsx +77 -32
  371. package/src/elements/chat-scope-picker.tsx +35 -8
  372. package/src/elements/chat-types.ts +9 -2
  373. package/src/elements/chat-workspace.tsx +157 -43
  374. package/src/elements/chat.tsx +49 -25
  375. package/src/elements/choice.tsx +71 -19
  376. package/src/elements/coachmark.tsx +90 -0
  377. package/src/elements/command.tsx +209 -0
  378. package/src/elements/compare.tsx +110 -0
  379. package/src/elements/compiled.css +1 -1
  380. package/src/elements/composer.tsx +151 -0
  381. package/src/elements/confirm-card.tsx +36 -11
  382. package/src/elements/conversation-list.tsx +80 -11
  383. package/src/elements/default-input.tsx +75 -53
  384. package/src/elements/define.tsx +47 -6
  385. package/src/elements/dialog.tsx +98 -0
  386. package/src/elements/disclosure.ts +73 -0
  387. package/src/elements/editable-label.tsx +125 -0
  388. package/src/elements/element-manifest.json +310 -0
  389. package/src/elements/element-meta.json +5954 -1605
  390. package/src/elements/element-types.d.ts +771 -60
  391. package/src/elements/file-tree.tsx +12 -2
  392. package/src/elements/file-upload.tsx +1 -1
  393. package/src/elements/form.tsx +54 -11
  394. package/src/elements/hover-card.tsx +80 -0
  395. package/src/elements/icon-names.json +48 -0
  396. package/src/elements/icon.tsx +41 -0
  397. package/src/elements/input.tsx +203 -0
  398. package/src/elements/kbd.tsx +45 -0
  399. package/src/elements/menu.tsx +217 -0
  400. package/src/elements/message.tsx +84 -55
  401. package/src/elements/model-switcher.tsx +27 -1
  402. package/src/elements/nav.tsx +87 -0
  403. package/src/elements/notice.tsx +53 -0
  404. package/src/elements/pane-group.tsx +119 -0
  405. package/src/elements/pane.tsx +129 -0
  406. package/src/elements/popover.tsx +39 -17
  407. package/src/elements/progress-bar.tsx +39 -0
  408. package/src/elements/prompt-dock.tsx +81 -0
  409. package/src/elements/prompt-input.tsx +112 -63
  410. package/src/elements/prompt-suggestions.tsx +25 -9
  411. package/src/elements/reasoning.tsx +30 -9
  412. package/src/elements/register-impl.ts +41 -3
  413. package/src/elements/register.ts +20 -3
  414. package/src/elements/remote.tsx +4 -0
  415. package/src/elements/resizable.tsx +48 -11
  416. package/src/elements/screen.tsx +90 -0
  417. package/src/elements/scroll-area.tsx +33 -0
  418. package/src/elements/search.tsx +180 -0
  419. package/src/elements/segmented.tsx +135 -0
  420. package/src/elements/separator.tsx +36 -0
  421. package/src/elements/setting-item.tsx +50 -0
  422. package/src/elements/settings-group.tsx +37 -0
  423. package/src/elements/skeleton.tsx +45 -0
  424. package/src/elements/slots.ts +607 -0
  425. package/src/elements/source.tsx +13 -6
  426. package/src/elements/status.tsx +42 -0
  427. package/src/elements/styles.css +19 -0
  428. package/src/elements/switch.tsx +89 -14
  429. package/src/elements/tabs.tsx +95 -0
  430. package/src/elements/tasks.tsx +70 -14
  431. package/src/elements/toast.tsx +89 -0
  432. package/src/elements/tool.tsx +43 -9
  433. package/src/elements/tooltip.tsx +85 -0
  434. package/src/elements/video-poster.jpg +0 -0
  435. package/src/elements/voice-input.tsx +66 -16
  436. package/src/elements/voice-output.tsx +69 -0
  437. package/src/index.ts +35 -2
  438. package/src/primitives/card-contract.ts +14 -5
  439. package/src/primitives/card-recovery.ts +149 -0
  440. package/src/primitives/card-routing.ts +3 -0
  441. package/src/primitives/card-schemas/choice.schema.json +5 -0
  442. package/src/primitives/card-schemas/tasks.schema.json +7 -2
  443. package/src/primitives/composer-model.ts +49 -0
  444. package/src/primitives/composer-triggers.ts +35 -0
  445. package/src/primitives/create-kai-chat.ts +66 -0
  446. package/src/primitives/message-feedback.ts +123 -0
  447. package/src/primitives/toast-store.ts +276 -0
  448. package/src/primitives/use-speech-recognition.ts +135 -0
  449. package/src/state/index.ts +6 -0
  450. package/src/state/messages.ts +36 -0
  451. package/src/state/stream.ts +92 -0
  452. package/src/state/suggestions.ts +9 -0
  453. package/src/types.ts +4 -0
  454. package/src/ui/agent-card.tsx +212 -0
  455. package/src/ui/button.tsx +16 -5
  456. package/src/ui/card.tsx +245 -0
  457. package/src/ui/collapsible.tsx +30 -4
  458. package/src/ui/command.tsx +99 -0
  459. package/src/ui/dialog.tsx +217 -0
  460. package/src/ui/dropdown.tsx +375 -13
  461. package/src/ui/editable-label.tsx +112 -0
  462. package/src/ui/hover-card.tsx +28 -5
  463. package/src/ui/icon.tsx +110 -0
  464. package/src/ui/input.tsx +132 -0
  465. package/src/ui/kbd.tsx +118 -0
  466. package/src/ui/nav.tsx +282 -0
  467. package/src/ui/notice.tsx +84 -0
  468. package/src/ui/overlay.tsx +108 -2
  469. package/src/ui/pane-grid.tsx +116 -0
  470. package/src/ui/pane-group.tsx +280 -0
  471. package/src/ui/pane.tsx +193 -0
  472. package/src/ui/popover.tsx +16 -2
  473. package/src/ui/progress-bar.tsx +83 -0
  474. package/src/ui/prompt-dock.tsx +145 -0
  475. package/src/ui/resizable.tsx +69 -1
  476. package/src/ui/scroll-area.tsx +14 -3
  477. package/src/ui/segmented.tsx +109 -0
  478. package/src/ui/settings-group.tsx +67 -0
  479. package/src/ui/skeleton.tsx +62 -5
  480. package/src/ui/stat.tsx +41 -0
  481. package/src/ui/status.tsx +48 -0
  482. package/src/ui/switch.tsx +49 -23
  483. package/src/ui/tabs.tsx +168 -0
  484. package/src/ui/tooltip.tsx +57 -7
  485. package/theme.css +113 -0
  486. package/dist/components/slash-command.d.ts +0 -15
  487. package/dist/kitn-chat.es.js +0 -1
  488. package/dist/register-impl-wCsFPoh9.js +0 -77
  489. package/dist/register-ujhr1nBS.js +0 -1
  490. package/src/components/slash-command.tsx +0 -232
  491. package/src/elements/framework-usage.json +0 -563
  492. /package/dist/{kitn-chat-provider.d.ts → kai-provider.d.ts} +0 -0
  493. /package/dist/{kitn-chat-provider.es.js → kai-provider.es.js} +0 -0
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @kitn.ai/ui
5
5
 
6
- > Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. 44 custom elements, every one prefixed `kai-` (e.g. `<kai-chat>`, `<kai-artifact>`): streaming responses, markdown + code rendering, reasoning/tool panels, attachments, conversation sidebar, voice input. Zero framework dependency for consumers; the SolidJS runtime it is authored in is bundled in, so the host needs nothing.
6
+ > Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. 78 custom elements, every one prefixed `kai-` (e.g. `<kai-chat>`, `<kai-artifact>`): streaming responses, markdown + code rendering, reasoning/tool panels, attachments, conversation sidebar, voice input. Zero framework dependency for consumers; the SolidJS runtime it is authored in is bundled in, so the host needs nothing.
7
7
 
8
8
  ## Install
9
9
 
@@ -15,7 +15,7 @@ npm install solid-js
15
15
 
16
16
  ## #1 rule: array/object data goes on JS PROPERTIES, not HTML attributes
17
17
 
18
- This is the single most common mistake. Arrays and objects (`messages`, `models`, `context`, `suggestions`, `slashCommands`, …) MUST be assigned as JavaScript properties on the element. They CANNOT be passed as HTML attributes — an HTML attribute is always a string and will be ignored or mis-parsed.
18
+ This is the single most common mistake. Arrays and objects (`messages`, `models`, `context`, `suggestions`, `triggers`, …) MUST be assigned as JavaScript properties on the element. They CANNOT be passed as HTML attributes — an HTML attribute is always a string and will be ignored or mis-parsed.
19
19
 
20
20
  ```js
21
21
  const chat = document.querySelector('kai-chat');
@@ -37,7 +37,7 @@ Drop an element into any framework (React, Vue, plain HTML). Data in via JS prop
37
37
  - `<kai-prompt-input>` — standalone composer with send button.
38
38
 
39
39
  **Layer 2 — composable primitives** (`import { … } from '@kitn.ai/ui'`):
40
- All 44 elements are also exported individually. Use them for custom layouts or features `<kai-chat>` does not expose (ChainOfThought, FeedbackBar, ThinkingBar, VoiceInput, …). Your bundler tree-shakes the rest.
40
+ All 78 elements are also exported individually. Use them for custom layouts or features `<kai-chat>` does not expose (ChainOfThought, FeedbackBar, ThinkingBar, VoiceInput, …). Your bundler tree-shakes the rest.
41
41
 
42
42
  ## Key rules for the web components
43
43
 
@@ -99,7 +99,7 @@ For Tailwind builds: `@import "@kitn.ai/ui/theme.css"` in your CSS.
99
99
 
100
100
  ## Docs
101
101
 
102
- - Full element reference (all 44 elements, every prop/event): ./llms-full.txt — https://kitn.dev/llms-full.txt
102
+ - Full element reference (all 78 elements, every prop/event): ./llms-full.txt — https://kitn.dev/llms-full.txt
103
103
  - Machine-readable Custom Elements Manifest: https://unpkg.com/@kitn.ai/ui/dist/custom-elements.json
104
104
  - Working examples: https://github.com/kitn-ai/ui/tree/main/examples
105
105
  - Storybook: https://storybook.kitn.dev
@@ -173,10 +173,37 @@ The same rule applies to every array/object property (`models`, `context`, `sugg
173
173
 
174
174
  ---
175
175
 
176
- ## Element reference (44 elements, generated from custom-elements.json)
176
+ ## Element reference (78 elements, generated from custom-elements.json)
177
177
 
178
178
  Every element also accepts the `theme` attribute. Array/object properties are marked with a `—` attribute: they must be set as JS properties.
179
179
 
180
+ ### `kai-agent-card` / `AgentCard`
181
+
182
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
183
+
184
+ | Property | Attribute | Type | Description |
185
+ |---|---|---|---|
186
+ | `name` | `name` | `undefined \| string` | The agent's name — the primary label. Attribute: `name`. |
187
+ | `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 }`. |
190
+
191
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
192
+
193
+ | Event | `detail` type | Description |
194
+ |---|---|---|
195
+ | `kai-activate` | `CustomEvent` | The card was activated — clicked, or Enter / Space while focused. Promote this agent back to focus. |
196
+ | `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
+
198
+ **Styleable parts** (restyle from outside via `kai-agent-card::part(name)`):
199
+
200
+ | Part | Description |
201
+ |---|---|
202
+ | `::part(status)` | The leading tone-colored status dot. — `kai-agent-card::part(status) { width: 0.625rem; height: 0.625rem }` |
203
+ | `::part(menu)` | The trailing overflow ("...") menu button. — `kai-agent-card::part(menu) { opacity: 1 }` |
204
+
205
+ ---
206
+
180
207
  ### `kai-artifact` / `Artifact`
181
208
 
182
209
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
@@ -184,8 +211,9 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
184
211
  | Property | Attribute | Type | Description |
185
212
  |---|---|---|---|
186
213
  | `src` | `src` | `undefined \| string` | URL the preview iframe frames. Consumer-controlled. |
187
- | `files` | — | `{ path: string; url?: undefined \| string; code?: undefined \| string; language?: undefined \| string; type?: undefined \| "html" \| "pdf" \| "image" \| "other" }[]` | Files for the Code tab tree + each file's preview `url`. Set as a JS property (array). |
188
- | `tab` | `tab` | `undefined \| "preview" \| "code"` | Active tab: `preview` (default) or `code`. |
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). |
215
+ | `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
+ | `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`. |
189
217
  | `activeFile` | `active-file` | `undefined \| string` | Selected file path — syncs the tree highlight, Code source, and preview. |
190
218
  | `sandbox` | `sandbox` | `undefined \| string` | iframe `sandbox` override. Secure default `allow-scripts allow-forms` (NOT `allow-same-origin`). |
191
219
  | `iframeTitle` | `iframe-title` | `undefined \| string` | Accessible title for the preview iframe. |
@@ -199,6 +227,7 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
199
227
  | `noTabs` | `no-tabs` | `undefined \| false \| true` | Hide the Preview\|Code toggle. |
200
228
  | `standalone` | `standalone` | `undefined \| false \| true` | Standalone chrome: rounded corners + border (else square, borderless in-panel). |
201
229
  | `readonlyPath` | `readonly-path` | `undefined \| false \| true` | Show the address but make it read-only (visible, nav-tracking, non-editable). |
230
+ | `displayUrl` | `display-url` | `undefined \| string` | Friendly address shown in the path field instead of the real current url (read-only, non-navigable). Use when the framed url is not consumer-facing (e.g. a `data:` blob) so a clean address shows instead of leaking it. Scalar string: set as the `display-url` attribute or the `displayUrl` property. |
202
231
 
203
232
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
204
233
 
@@ -230,34 +259,144 @@ Every element also accepts the `theme` attribute. Array/object properties are ma
230
259
  |---|---|---|
231
260
  | `kai-remove` | `CustomEvent<{ id: string }>` | A remove button was clicked. |
232
261
 
262
+ **Styleable parts** (restyle from outside via `kai-attachments::part(name)`):
263
+
264
+ | Part | Description |
265
+ |---|---|
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 }` |
267
+
233
268
  ---
234
269
 
235
- ### `kai-card` / `Card`
270
+ ### `kai-avatar` / `Avatar`
236
271
 
237
272
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
238
273
 
239
274
  | Property | Attribute | Type | Description |
240
275
  |---|---|---|---|
241
- | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
242
- | `description` | `description` | `undefined \| string` | Supporting text under the heading. Attribute: `description`. |
243
- | `errorMessage` | `error-message` | `undefined \| string` | When set, the card renders its inline error state instead of the body. Attribute: `error-message`. |
244
- | `dense` | `dense` | `undefined \| false \| true` | Compact spacing for dense lists. Attribute: `dense`. |
276
+ | `src` | `src` | `undefined \| string` | Image URL/data-URI. When absent, the `fallback` initials show instead. |
277
+ | `alt` | `alt` | `undefined \| string` | Alt text for the image. Defaults to `fallback`. |
278
+ | `fallback` | `fallback` | `undefined \| string` | Short text shown when there's no image usually initials (e.g. "JD", "AI"). |
279
+ | `size` | `size` | `undefined \| "sm" \| "md" \| "lg"` | Size token: `sm` \| `md` (default) \| `lg`. |
245
280
 
246
281
  _No events._
247
282
 
248
283
  ---
249
284
 
285
+ ### `kai-badge` / `Badge`
286
+
287
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
288
+
289
+ | Property | Attribute | Type | Description |
290
+ |---|---|---|---|
291
+ | `variant` | `variant` | `undefined \| "default" \| "count" \| "citation"` | `default` (muted pill) · `count` (compact number badge) · `citation` (filled primary, for inline citation markers). Defaults to `default`. |
292
+
293
+ _No events._
294
+
295
+ **Styleable parts** (restyle from outside via `kai-badge::part(name)`):
296
+
297
+ | Part | Description |
298
+ |---|---|
299
+ | `::part(badge)` | The badge pill. Restyle its background, color, or shape; the `variant` prop (default/count/citation) sets the defaults. — `kai-badge::part(badge) { background: var(--color-primary); color: var(--color-primary-foreground) }` |
300
+
301
+ ---
302
+
303
+ ### `kai-button` / `Button`
304
+
305
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
306
+
307
+ | Property | Attribute | Type | Description |
308
+ |---|---|---|---|
309
+ | `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`. |
310
+ | `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`. |
311
+ | `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. |
312
+ | `iconTrailing` | `icon-trailing` | `undefined \| string` | Trailing icon, after the label (e.g. `"chevron-down"` for a menu affordance). |
313
+ | `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. |
314
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the button (non-interactive, dimmed). |
315
+ | `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`. |
316
+ | `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. |
317
+ | `type` | `type` | `undefined \| "button" \| "submit" \| "reset"` | Native button `type`. Defaults to `button` (so it never submits a form). |
318
+
319
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
320
+
321
+ | Event | `detail` type | Description |
322
+ |---|---|---|
323
+ | `kai-click` | `CustomEvent` | The button was activated (pointer or keyboard). Carries no detail. The native `click` also bubbles (composed) for consumers who prefer it. |
324
+
325
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
326
+
327
+ | Slot | Mode | Description |
328
+ |---|---|---|
329
+ | `icon` | replace | A custom leading icon (any inline SVG, inherits `currentColor`). Wins over the `icon` prop. |
330
+
331
+ **Styleable parts** (restyle from outside via `kai-button::part(name)`):
332
+
333
+ | Part | Description |
334
+ |---|---|
335
+ | `::part(button)` | The button element. Restyle radius, padding, colors, or weight from outside; the `variant`/`size` props set the defaults. — `kai-button::part(button) { border-radius: 9999px; font-weight: 600 }` |
336
+
337
+ ---
338
+
339
+ ### `kai-card` / `Card`
340
+
341
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
342
+
343
+ | Property | Attribute | Type | Description |
344
+ |---|---|---|---|
345
+ | `appearance` | `appearance` | `undefined \| "outlined" \| "filled" \| "plain" \| "accent"` | Surface treatment: `outlined` (default) \| `filled` \| `plain` \| `accent`. Attribute: `appearance`. |
346
+ | `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`. |
347
+ | `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`. |
348
+ | `dense` | `dense` | `undefined \| false \| true` | Tighter spacing for dense lists. Attribute: `dense`. |
349
+ | `dismissible` | `dismissible` | `undefined \| false \| true` | Show a close (×) that hides the card and emits `kai-dismiss`. Attribute: `dismissible`. Off by default. |
350
+ | `href` | `href` | `undefined \| string` | Render the whole card as a link. Attribute: `href`. Wins over `clickable`. |
351
+ | `target` | `target` | `undefined \| string` | `target` for the `href` anchor. Attribute: `target`. |
352
+ | `rel` | `rel` | `undefined \| string` | `rel` for the `href` anchor. Attribute: `rel`. |
353
+ | `clickable` | `clickable` | `undefined \| false \| true` | Make the whole card a button (`role="button"`, Enter/Space, hover affordance) that emits `kai-card-click`. Attribute: `clickable`. Ignored when `href` is set. |
354
+
355
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
356
+
357
+ | Event | `detail` type | Description |
358
+ |---|---|---|
359
+ | `kai-card-click` | `CustomEvent` | A `clickable`/`href` card was activated (click, or Enter/Space). |
360
+ | `kai-dismiss` | `CustomEvent` | The card was dismissed via its × (it also hides itself). |
361
+
362
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
363
+
364
+ | Slot | Mode | Description |
365
+ |---|---|---|
366
+ | `media` | inject | Full-bleed media (image/video/illustration) at the top (vertical) or start (horizontal). Clipped to the card corners. |
367
+ | `header` | inject | Header content, e.g. a title. Rendered above the body. |
368
+ | `header-actions` | inject | An actions cluster pinned to the end of the header row. |
369
+ | `footer` | inject | Footer content rendered below the body. |
370
+ | `footer-actions` | inject | Action buttons pinned to the end of the footer. Do NOT combine with a clickable/href card (nested interactive). |
371
+
372
+ **Styleable parts** (restyle from outside via `kai-card::part(name)`):
373
+
374
+ | Part | Description |
375
+ |---|---|
376
+ | `::part(card)` | The card root (a div, or an a when href is set). Restyle its radius, border, or background; set --kai-card-spacing for padding/gaps (the dense prop sets the compact default). — `kai-card::part(card) { border-radius: 1rem; --kai-card-spacing: 1.5rem }` |
377
+ | `::part(media)` | The full-bleed media region. Cap or crop it from outside (e.g. a fixed height with object-fit). — `kai-card::part(media) { max-height: 12rem }` |
378
+ | `::part(header)` | The header row (header content + header-actions). Add a divider or adjust its alignment. — `kai-card::part(header) { border-bottom: 1px solid var(--color-border) }` |
379
+ | `::part(body)` | The default-slot body region. — `kai-card::part(body) { font-size: 0.9375rem }` |
380
+ | `::part(footer)` | The footer row (footer content + footer-actions). — `kai-card::part(footer) { border-top: 1px solid var(--color-border) }` |
381
+ | `::part(dismiss)` | The dismiss (×) button shown when dismissible. Recolor or reposition it from outside. — `kai-card::part(dismiss) { color: var(--color-muted-foreground) }` |
382
+
383
+ ---
384
+
250
385
  ### `kai-cards` / `Cards`
251
386
 
252
387
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
253
388
 
254
389
  | Property | Attribute | Type | Description |
255
390
  |---|---|---|---|
256
- | `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 } }[]` | The stream of card envelopes to render. Set as a JS PROPERTY: `el.cards = [...]`. |
391
+ | `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 = [...]`. |
257
392
  | `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. |
258
- | `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; onError?: undefined \| (cardId: string, message: string) => void; maxSendPromptMode?: undefined \| "compose" \| "send" }` | Optional CardPolicy handling child events. Property: `el.policy`. |
393
+ | `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`. |
259
394
 
260
- _No events._
395
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
396
+
397
+ | Event | `detail` type | Description |
398
+ |---|---|---|
399
+ | `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.) |
261
400
 
262
401
  ---
263
402
 
@@ -267,9 +406,16 @@ _No events._
267
406
 
268
407
  | Property | Attribute | Type | Description |
269
408
  |---|---|---|---|
270
- | `steps` | — | `{ label: string; content?: undefined \| string }[]` | The reasoning steps. Set as a JS property. Compound sub-parts collapse to this one data model (Route 1). |
409
+ | `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? }`. |
410
+ | `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). |
411
+ | `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. |
412
+ | `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. |
271
413
 
272
- _No events._
414
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
415
+
416
+ | Event | `detail` type | Description |
417
+ |---|---|---|
418
+ | `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.) |
273
419
 
274
420
  ---
275
421
 
@@ -279,14 +425,14 @@ _No events._
279
425
 
280
426
  | Property | Attribute | Type | Description |
281
427
  |---|---|---|---|
282
- | `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 } }[]` | 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 = [...]`). |
283
- | `value` | `value` | `undefined \| string` | Controlled value of the input. When set, the host owns the input text and must update it on `kai-value-change`; leave unset for uncontrolled behavior. |
428
+ | `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 = [...]`). |
429
+ | `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. |
284
430
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown in the empty input. |
285
431
  | `loading` | `loading` | `undefined \| false \| true` | When true, shows the loading/streaming state and disables submit (use while awaiting the assistant's reply). |
286
432
  | `suggestions` | — | `undefined \| string[]` | Starter prompts shown above the input when the thread is empty. Clicking one follows `suggestionMode`. Set as a JS property. |
287
433
  | `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. |
288
434
  | `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. |
289
- | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. |
435
+ | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. |
290
436
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name for syntax-highlighted code blocks (e.g. `'github-dark-dimmed'`). |
291
437
  | `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. |
292
438
  | `chatTitle` | `chat-title` | `undefined \| string` | Optional header title shown on the left of the header. |
@@ -296,26 +442,53 @@ _No events._
296
442
  | `scrollButton` | `scroll-button` | `undefined \| false \| true` | Show the scroll-to-bottom button inside the scroll area. Default true. |
297
443
  | `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. |
298
444
  | `headerEnd` | `header-end` | `undefined \| false \| true` | Whether the host has `slot="header-end"` content (right of the controls). |
445
+ | `headerFull` | `header-full` | `undefined \| false \| true` | REPLACE — full custom header in place of the built-in title/model/context bar. |
446
+ | `sidebar` | `sidebar` | `undefined \| false \| true` | INJECT — left sidebar column (e.g. a conversation list / your own nav). |
447
+ | `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). |
448
+ | `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). |
449
+ | `composerActions` | `composer-actions` | `undefined \| false \| true` | INJECT — accessory row just above the composer (e.g. extra actions). |
450
+ | `footer` | `footer` | `undefined \| false \| true` | INJECT — footer row below the composer (disclaimers, token meter, …). |
299
451
  | `search` | `search` | `undefined \| false \| true` | Show a Search (Globe) button in the input toolbar; fires a `search` event. |
300
452
  | `voice` | `voice` | `undefined \| false \| true` | Show a Voice (Mic) button in the input toolbar; fires a `voice` event. |
301
- | `slashCommands` | — | `undefined \| { id: string; label: string; description?: undefined \| string; category?: undefined \| string }[]` | Slash commandswhen set, typing `/` in the input opens the command palette and fires `kai-slash-select`. Set as a JS property. |
302
- | `slashActiveIds` | — | `undefined \| string[]` | Command ids to highlight as active in the palette. |
303
- | `slashCompact` | `slash-compact` | `undefined \| false \| true` | Single-line palette rows. |
453
+ | `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. |
454
+ | `kindIcons` | — | `undefined \| Record<string, string>` | Default icon per entity kind (kind image src) for pills/menu items. |
304
455
  | `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'`). |
305
456
 
306
457
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
307
458
 
308
459
  | Event | `detail` type | Description |
309
460
  |---|---|---|
310
- | `kai-message-action` | `CustomEvent<{ messageId: string; action: string }>` | An action button on a message was clicked. `action` is the built-in name or custom id. |
461
+ | `kai-attachments-change` | `CustomEvent<{ attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | The staged attachments changed (file added or removed). Carries the full current list so a consumer can react in real time. |
462
+ | `kai-message-action` | `CustomEvent<{ messageId: string; action: string; state?: undefined \| "on" \| "off" }>` | An action button on a message was clicked. `action` is the built-in name or custom id. `state` is present only for the toggleable feedback votes: `'on'` when a like/dislike is set, `'off'` when re-tapped to clear. |
311
463
  | `kai-model-change` | `CustomEvent<{ modelId: string }>` | The header model switcher changed. |
312
464
  | `kai-search` | `CustomEvent<Record<string, never>>` | The Search button was clicked. |
313
- | `kai-slash-select` | `CustomEvent<{ command: { id: string; label: string; description?: undefined \| string; category?: undefined \| string } }>` | A slash command was chosen from the palette. |
314
465
  | `kai-submit` | `CustomEvent<{ value: string; attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | User submitted a message. |
315
466
  | `kai-suggestion-click` | `CustomEvent<{ value: string }>` | A suggestion chip was clicked (only in `suggestion-mode="fill"`). |
316
467
  | `kai-value-change` | `CustomEvent<{ value: string }>` | Fired on every input change. |
317
468
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Mic / voice button was clicked. |
318
469
 
470
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
471
+
472
+ | Slot | Mode | Description |
473
+ |---|---|---|
474
+ | `header-start` | inject | Leading header controls, left of the title. |
475
+ | `header-end` | inject | Trailing header controls. |
476
+ | `header` | replace | Full custom header; replaces the built-in title/model/context bar. |
477
+ | `sidebar` | inject | Left column (your nav / conversation list). Fixed width; use compose-your-own for resizable. |
478
+ | `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. |
479
+ | `composer` | replace | Full custom composer; you own submit + loading, drive the thread via messages. |
480
+ | `composer-actions` | inject | Accessory row above the composer. |
481
+ | `footer` | inject | Row below the composer (disclaimers, token meter). |
482
+
483
+ **Styleable parts** (restyle from outside via `kai-chat::part(name)`):
484
+
485
+ | Part | Description |
486
+ |---|---|
487
+ | `::part(header-bar)` | The built-in header bar (the title / model-switcher / context row that hosts the header-start/header-end inject slots). Restyle its height, padding, or gap from outside without replacing the whole header via the `header` slot. — `kai-chat::part(header-bar) { height: 3.5rem; padding-inline: 1rem; gap: 0.5rem }` |
488
+ | `::part(header)` | Full custom header; replaces the built-in title/model/context bar. |
489
+ | `::part(sidebar)` | Left column (your nav / conversation list). Fixed width; use compose-your-own for resizable. |
490
+ | `::part(footer)` | Row below the composer (disclaimers, token meter). |
491
+
319
492
  ---
320
493
 
321
494
  ### `kai-checkpoint` / `Checkpoint`
@@ -326,8 +499,8 @@ _No events._
326
499
  |---|---|---|---|
327
500
  | `label` | `label` | `undefined \| string` | Optional text beside the icon. |
328
501
  | `tooltip` | `tooltip` | `undefined \| string` | Tooltip on hover. |
329
- | `variant` | `variant` | `undefined \| "ghost" \| "default" \| "outline"` | Visual button style. |
330
- | `size` | `size` | `undefined \| "sm" \| "lg" \| "md" \| "icon" \| "icon-sm"` | Button size (use an `icon*` size for an icon-only checkpoint). |
502
+ | `variant` | `variant` | `undefined \| "default" \| "ghost" \| "outline"` | Visual button style. |
503
+ | `size` | `size` | `undefined \| "sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | Button size (use an `icon*` size for an icon-only checkpoint). |
331
504
 
332
505
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
333
506
 
@@ -347,8 +520,54 @@ _No events._
347
520
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
348
521
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
349
522
  | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'action', action:'…' }`. |
523
+ | `value` | `value` | `undefined \| string` | Controlled selection — the selected option id. When set, the consumer owns the current pick (RadioGroup `value`). Attribute: `value`. |
524
+ | `defaultValue` | `default-value` | `undefined \| string` | Option id to pre-select on mount (uncontrolled seed). Attribute: `default-value`. |
525
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the whole radiogroup + Submit (e.g. while the agent is busy). Attribute: `disabled`. |
350
526
 
351
- _No events._
527
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
528
+
529
+ | Event | `detail` type | Description |
530
+ |---|---|---|
531
+ | `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. |
532
+
533
+ ---
534
+
535
+ ### `kai-coachmark` / `Coachmark`
536
+
537
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
538
+
539
+ | Property | Attribute | Type | Description |
540
+ |---|---|---|---|
541
+ | `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`. |
542
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
543
+ | `headline` | `headline` | `undefined \| string` | The bold title. Named `headline` because `title` collides with the global `HTMLElement.title` attribute (it throws at registration). |
544
+ | `badge` | `badge` | `undefined \| string` | A small badge pill beside the headline (e.g. "New"). |
545
+ | `placement` | `placement` | `undefined \| string` | Floating placement relative to the anchor (default `bottom`). |
546
+ | `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. |
547
+ | `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. |
548
+
549
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
550
+
551
+ | Event | `detail` type | Description |
552
+ |---|---|---|
553
+ | `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. |
554
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The coachmark opened or closed (a method, the ×, or a driven `open`). |
555
+
556
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
557
+
558
+ | Slot | Mode | Description |
559
+ |---|---|---|
560
+ | `content` | replace | The bubble body text shown under the headline. |
561
+
562
+ **Styleable parts** (restyle from outside via `kai-coachmark::part(name)`):
563
+
564
+ | Part | Description |
565
+ |---|---|
566
+ | `::part(bubble)` | The hint bubble panel. Restyle its background, radius, or padding from outside; the default is bg-primary. — `kai-coachmark::part(bubble) { border-radius: 1rem }` |
567
+ | `::part(arrow)` | The arrow pointing at the anchor. Inherits the bubble color; recolor it alongside the bubble. — `kai-coachmark::part(arrow) { background: var(--color-accent) }` |
568
+ | `::part(badge)` | The small badge pill beside the headline (e.g. "New"). — `kai-coachmark::part(badge) { text-transform: none }` |
569
+ | `::part(title)` | The bold headline text. — `kai-coachmark::part(title) { font-size: 0.9375rem }` |
570
+ | `::part(dismiss)` | The dismiss button. Recolor or reposition it from outside. — `kai-coachmark::part(dismiss) { color: var(--color-primary-foreground) }` |
352
571
 
353
572
  ---
354
573
 
@@ -362,12 +581,93 @@ _No events._
362
581
  | `language` | `language` | `undefined \| string` | Language grammar (e.g. `js`, `python`). Defaults to `tsx`. |
363
582
  | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name. |
364
583
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting (renders plain text, no Shiki). |
365
- | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Code text sizing. |
584
+ | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Code text sizing. |
366
585
 
367
586
  _No events._
368
587
 
369
588
  ---
370
589
 
590
+ ### `kai-command` / `Command`
591
+
592
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
593
+
594
+ | Property | Attribute | Type | Description |
595
+ |---|---|---|---|
596
+ | `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. |
597
+ | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text for the search input. |
598
+ | `emptyLabel` | `empty-label` | `undefined \| string` | Label shown when no items match the current query. |
599
+
600
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
601
+
602
+ | Event | `detail` type | Description |
603
+ |---|---|---|
604
+ | `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. |
605
+ | `kai-query-change` | `CustomEvent<{ value: string }>` | Fired on every keystroke in the search input. |
606
+ | `kai-select` | `CustomEvent<{ id: string }>` | Fired when the user selects an item (click or Enter). |
607
+
608
+ **Styleable parts** (restyle from outside via `kai-command::part(name)`):
609
+
610
+ | Part | Description |
611
+ |---|---|
612
+ | `::part(shortcut)` | The right-aligned per-row keyboard shortcut, rendered as kai-kbd key caps. Shown only when a row carries a `shortcut`. — `kai-command::part(shortcut) { opacity: 0.8 }` |
613
+
614
+ ---
615
+
616
+ ### `kai-compare` / `Compare`
617
+
618
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
619
+
620
+ | Property | Attribute | Type | Description |
621
+ |---|---|---|---|
622
+ | `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. |
623
+ | `compareId` | `compare-id` | `undefined \| string` | Stable id correlating every emitted event. Attribute: `compare-id`. |
624
+ | `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. |
625
+ | `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`. |
626
+ | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Prose/text size for the rendered candidates. Attribute: `prose-size`. |
627
+ | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme for code blocks in the candidates. Attribute: `code-theme`. |
628
+ | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Whether code blocks are syntax-highlighted. Attribute: `code-highlight`. |
629
+
630
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
631
+
632
+ | Event | `detail` type | Description |
633
+ |---|---|---|
634
+ | `kai-compare-select` | `CustomEvent<{ chosenId: string; rejectedIds: string[]; at?: undefined \| number }>` | The user committed a pick. `detail` = `{ chosenId, rejectedIds, at }`. |
635
+ | `kai-error` | `CustomEvent<{ compareId: string; message: string }>` | The definition was unusable. |
636
+ | `kai-ready` | `CustomEvent<{ compareId: string }>` | Both candidates have settled and the pick is live. |
637
+
638
+ ---
639
+
640
+ ### `kai-composer` / `Composer`
641
+
642
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
643
+
644
+ | Property | Attribute | Type | Description |
645
+ |---|---|---|---|
646
+ | `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). |
647
+ | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown when the composer is empty. |
648
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the composer entirely (non-interactive). |
649
+ | `loading` | `loading` | `undefined \| false \| true` | Show a loading/streaming state and block submit. |
650
+ | `maxHeight` | `max-height` | `undefined \| string \| number` | Maximum height in px before the content scrolls. Default 240. |
651
+ | `submitOnEnter` | `submit-on-enter` | `undefined \| false \| true` | Whether pressing Enter (without Shift) submits. Default true. |
652
+ | `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. |
653
+ | `highlights` | — | `undefined \| (string \| { pattern: string; flags?: undefined \| string; class?: undefined \| string })[]` | Keyword highlight rules — set as a JS property. |
654
+ | `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. |
655
+
656
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
657
+
658
+ | Event | `detail` type | Description |
659
+ |---|---|---|
660
+ | `kai-blur` | `CustomEvent<{ originalEvent: FocusEvent }>` | The composer lost focus. |
661
+ | `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. |
662
+ | `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. |
663
+ | `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.) |
664
+ | `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). |
665
+ | `kai-trigger` | `CustomEvent<{ char: string; query: string; rect: DOMRect }>` | A trigger character was detected at the caret (e.g. `/` or `@`). |
666
+ | `kai-trigger-close` | `CustomEvent<Record<string, never>>` | The active trigger was dismissed (Escape, space, or outside click). |
667
+ | `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). |
668
+
669
+ ---
670
+
371
671
  ### `kai-confirm` / `Confirm`
372
672
 
373
673
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
@@ -409,17 +709,96 @@ _No events._
409
709
  | Property | Attribute | Type | Description |
410
710
  |---|---|---|---|
411
711
  | `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. |
412
- | `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 }[]` | A flat list of conversation summaries; the component buckets them by recency for you. Ignored when `groups` is provided. Set as a JS property. |
712
+ | `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. |
413
713
  | `activeId` | `active-id` | `undefined \| string` | The id of the currently-open conversation, highlighted in the list. |
714
+ | `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. |
715
+ | `defaultCollapsed` | `default-collapsed` | `undefined \| false \| true` | Initial collapsed state when uncontrolled (default false). Use the `default-collapsed` attribute to start collapsed in plain HTML. |
414
716
 
415
717
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
416
718
 
417
719
  | Event | `detail` type | Description |
418
720
  |---|---|---|
721
+ | `kai-collapse-toggle` | `CustomEvent<{ collapsed: false \| true }>` | The rail was collapsed or expanded (via the toggle, the reopen button, or a `collapse()`/`expand()`/`toggle()` call). |
419
722
  | `kai-conversation-select` | `CustomEvent<{ id: string }>` | A conversation was selected. |
420
723
  | `kai-new-chat` | `CustomEvent<Record<string, never>>` | The "New chat" button was clicked. |
724
+ | `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. |
421
725
  | `kai-toggle-sidebar` | `CustomEvent<Record<string, never>>` | The sidebar toggle was clicked. |
422
726
 
727
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
728
+
729
+ | Slot | Mode | Description |
730
+ |---|---|---|
731
+ | `header` | replace | Full custom title bar; replaces the built-in toggle / "Chats" / New-chat row. |
732
+ | `empty` | replace | Custom zero-state shown when there are no conversations; replaces the built-in "No conversations yet". |
733
+ | `footer` | inject | A row below the list — account, settings, or usage. |
734
+
735
+ **Styleable parts** (restyle from outside via `kai-conversations::part(name)`):
736
+
737
+ | Part | Description |
738
+ |---|---|
739
+ | `::part(trailing)` | The right-aligned trailing text on each conversation row (a count, status, or relative time). Set it per item via the `trailing` field; otherwise a short auto relative time is derived from `updatedAt`. Recolor or resize it from outside. — `kai-conversations::part(trailing) { color: var(--color-primary); font-variant-numeric: tabular-nums }` |
740
+
741
+ ---
742
+
743
+ ### `kai-dialog` / `Dialog`
744
+
745
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
746
+
747
+ | Property | Attribute | Type | Description |
748
+ |---|---|---|---|
749
+ | `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`. |
750
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
751
+
752
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
753
+
754
+ | Event | `detail` type | Description |
755
+ |---|---|---|
756
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The dialog opened or closed (Escape, backdrop click, a driven `open`, or a method). |
757
+
758
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
759
+
760
+ | Slot | Mode | Description |
761
+ |---|---|---|
762
+ | `header` | inject | Optional title region at the top of the panel. |
763
+ | `footer` | inject | Optional actions region at the bottom of the panel. |
764
+
765
+ **Styleable parts** (restyle from outside via `kai-dialog::part(name)`):
766
+
767
+ | Part | Description |
768
+ |---|---|
769
+ | `::part(backdrop)` | The full-area scrim behind the panel. Restyle its color/blur. — `kai-dialog::part(backdrop) { background: rgb(0 0 0 / 0.6) }` |
770
+ | `::part(panel)` | The centered modal panel. Restyle width, radius, padding. — `kai-dialog::part(panel) { max-width: 32rem }` |
771
+ | `::part(body)` | The scrolling content region (the default slot). — `kai-dialog::part(body) { padding: 1.25rem }` |
772
+ | `::part(header)` | Optional title region at the top of the panel. |
773
+ | `::part(footer)` | Optional actions region at the bottom of the panel. |
774
+
775
+ ---
776
+
777
+ ### `kai-editable-label` / `EditableLabel`
778
+
779
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
780
+
781
+ | Property | Attribute | Type | Description |
782
+ |---|---|---|---|
783
+ | `value` | `value` | `undefined \| string` | The label text — settable and reflected to the `value` attribute. Read `el.value` for live state. |
784
+ | `editing` | `editing` | `undefined \| false \| true` | Controlled edit state. `el.editing = true` opens the field; reflected to the `editing` attribute. |
785
+ | `placeholder` | `placeholder` | `undefined \| string` | Placeholder shown while editing / when the value is empty. |
786
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable entering edit mode. |
787
+
788
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
789
+
790
+ | Event | `detail` type | Description |
791
+ |---|---|---|
792
+ | `kai-cancel` | `CustomEvent<Record<string, never>>` | Edit was cancelled (Esc); the text is restored. |
793
+ | `kai-rename` | `CustomEvent<{ value: string }>` | Committed a changed value (Enter / blur). |
794
+
795
+ **Styleable parts** (restyle from outside via `kai-editable-label::part(name)`):
796
+
797
+ | Part | Description |
798
+ |---|---|
799
+ | `::part(text)` | The read-mode label text. Restyle its typography; it swaps to the input on edit. — `kai-editable-label::part(text) { font-weight: 600 }` |
800
+ | `::part(input)` | The edit-mode input (the composed kai-input field). — `kai-editable-label::part(input) { font: inherit }` |
801
+
423
802
  ---
424
803
 
425
804
  ### `kai-embed` / `Embed`
@@ -478,9 +857,10 @@ _No events._
478
857
 
479
858
  | Property | Attribute | Type | Description |
480
859
  |---|---|---|---|
481
- | `files` | — | `{ path: string; url?: undefined \| string; code?: undefined \| string; language?: undefined \| string; type?: undefined \| "html" \| "pdf" \| "image" \| "other" }[]` | The files to render. Set as a JS property (array of `{ path, url?, code?, language?, type? }`). |
860
+ | `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? }`). |
482
861
  | `activeFile` | `active-file` | `undefined \| string` | Selected file path — highlighted in the tree. |
483
862
  | `defaultExpanded` | — | `undefined \| string[]` | Folder paths expanded initially. Omit to start with all folders open. |
863
+ | `summary` | `summary` | `undefined \| false \| true` | Show a changed-files summary header (file count + summed `+/-` + Collapse-all). Attribute: `summary`. Off by default. |
484
864
 
485
865
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
486
866
 
@@ -488,6 +868,15 @@ _No events._
488
868
  |---|---|---|
489
869
  | `kai-select` | `CustomEvent<{ path: string }>` | Fired when a file is selected. `detail.path` = the file's path. |
490
870
 
871
+ **Styleable parts** (restyle from outside via `kai-file-tree::part(name)`):
872
+
873
+ | Part | Description |
874
+ |---|---|
875
+ | `::part(summary)` | The changed-files summary header (the file count, the summed +additions/-deletions, and the Collapse-all/Expand-all toggle). Rendered only when the `summary` attribute is set; restyle or hide it from outside. — `kai-file-tree::part(summary) { border-bottom: none; padding-block: 0.5rem }` |
876
+ | `::part(status)` | The per-row change-status letter (A/M/D/R/U), shown when a file carries a `status`. Colored with the conventional VCS tool hues; restyle from outside. — `kai-file-tree::part(status) { font-weight: 700 }` |
877
+ | `::part(stat-additions)` | The trailing `+N` additions stat on a file row (success/green tool hue, tabular-nums). Shown only when a file carries `additions`. — `kai-file-tree::part(stat-additions) { color: var(--color-tool-green) }` |
878
+ | `::part(stat-deletions)` | The trailing `-N` deletions stat on a file row (error/red tool hue, tabular-nums). Shown only when a file carries `deletions`. — `kai-file-tree::part(stat-deletions) { color: var(--color-tool-red) }` |
879
+
491
880
  ---
492
881
 
493
882
  ### `kai-file-upload` / `FileUpload`
@@ -519,9 +908,62 @@ _No events._
519
908
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
520
909
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
521
910
  | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'submit', data:{…} }`. |
911
+ | `values` | — | `undefined \| Record<string, unknown>` | Controlled field values (JS property). When set, it wins over local edits. |
912
+ | `defaultValues` | — | `undefined \| Record<string, unknown>` | Initial values overlaying the schema defaults (uncontrolled seed; JS property). |
913
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable all fields + submit. Attribute: `disabled`. |
914
+
915
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
916
+
917
+ | Event | `detail` type | Description |
918
+ |---|---|---|
919
+ | `kai-values-change` | `CustomEvent<{ values: Record<string, unknown>; valid: false \| true }>` | The form's values changed on input — current coerced values + validity. |
920
+
921
+ ---
922
+
923
+ ### `kai-hover-card` / `HoverCard`
924
+
925
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
926
+
927
+ | Property | Attribute | Type | Description |
928
+ |---|---|---|---|
929
+ | `openDelay` | `open-delay` | `undefined \| number` | Delay (ms) before the card opens on hover. Defaults to 0 (focus opens it immediately too). |
930
+ | `closeDelay` | `close-delay` | `undefined \| number` | Delay (ms) before it closes after the pointer leaves. Defaults to 300. |
931
+ | `placement` | `placement` | `undefined \| string` | Preferred placement: `'top' \| 'bottom' \| 'left' \| 'right'` (+ optional `-start`/`-end`). Defaults to `'bottom'`; flips to stay in view. |
932
+ | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute, the element still self-manages on hover). Set `el.open = true`, or `<kai-hover-card open>`; listen for `kai-open-change`. |
933
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
934
+ | `disabled` | `disabled` | `undefined \| false \| true` | Suppress the hover behavior entirely without unmounting. |
935
+
936
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
937
+
938
+ | Event | `detail` type | Description |
939
+ |---|---|---|
940
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The card opened or closed (by hover/focus, outside-click, or a method). |
941
+
942
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
943
+
944
+ | Slot | Mode | Description |
945
+ |---|---|---|
946
+ | `card` | inject | The rich content shown in the floating hover card. |
947
+
948
+ ---
949
+
950
+ ### `kai-icon` / `Icon`
951
+
952
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
953
+
954
+ | Property | Attribute | Type | Description |
955
+ |---|---|---|---|
956
+ | `name` | `name` | `undefined \| string` | A curated icon name (e.g. `"mic"`, `"globe"`), an image URL/data-URI, or plain text. |
957
+ | `size` | `size` | `undefined \| "sm" \| "md" \| "lg"` | Size token: `sm` \| `md` (default) \| `lg`. |
522
958
 
523
959
  _No events._
524
960
 
961
+ **Styleable parts** (restyle from outside via `kai-icon::part(name)`):
962
+
963
+ | Part | Description |
964
+ |---|---|
965
+ | `::part(icon)` | The icon wrapper. Inherits `currentColor` and the `size` prop by default; recolor or resize it from outside. — `kai-icon::part(icon) { color: var(--color-primary) }` |
966
+
525
967
  ---
526
968
 
527
969
  ### `kai-image` / `Image`
@@ -539,105 +981,414 @@ _No events._
539
981
 
540
982
  ---
541
983
 
542
- ### `kai-link-preview` / `LinkPreview`
984
+ ### `kai-input` / `Input`
985
+
986
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
987
+
988
+ | Property | Attribute | Type | Description |
989
+ |---|---|---|---|
990
+ | `type` | `type` | `undefined \| string` | Native input type: `text` (default) · `email` · `url` · `search` · `tel` · `password` · `number`. Single-line only. |
991
+ | `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. |
992
+ | `placeholder` | `placeholder` | `undefined \| string` | Placeholder shown when empty. |
993
+ | `label` | `label` | `undefined \| string` | Field label, linked to the input. |
994
+ | `hint` | `hint` | `undefined \| string` | Helper text below the control. |
995
+ | `error` | `error` | `undefined \| string` | Error text; flips the field invalid (`aria-invalid` + destructive border). |
996
+ | `size` | `size` | `undefined \| "sm" \| "md"` | Control density: `sm` or `md`. Defaults to `md`. |
997
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable interaction. |
998
+ | `readonly` | `readonly` | `undefined \| false \| true` | Make the input read-only. |
999
+ | `required` | `required` | `undefined \| false \| true` | Mark the input required. |
1000
+ | `invalid` | `invalid` | `undefined \| false \| true` | Force the invalid state without an `error` string. |
1001
+ | `name` | `name` | `undefined \| string` | Form-control name. |
1002
+ | `autocomplete` | `autocomplete` | `undefined \| string` | Autofill hint forwarded to the inner input (e.g. `email`, `current-password`). |
1003
+ | `inputmode` | `inputmode` | `undefined \| string` | Virtual-keyboard hint forwarded to the inner input (e.g. `numeric`, `email`). |
1004
+
1005
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1006
+
1007
+ | Event | `detail` type | Description |
1008
+ |---|---|---|
1009
+ | `kai-change` | `CustomEvent<{ value: string }>` | The value was committed (blur). |
1010
+ | `kai-input` | `CustomEvent<{ value: string }>` | The value changed per keystroke. |
1011
+
1012
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1013
+
1014
+ | Slot | Mode | Description |
1015
+ |---|---|---|
1016
+ | `leading` | inject | A glyph, prefix, or affix at the start of the field, inside the border. |
1017
+ | `trailing` | inject | A button, unit, or affix at the end of the field, inside the border. |
1018
+
1019
+ **Styleable parts** (restyle from outside via `kai-input::part(name)`):
1020
+
1021
+ | Part | Description |
1022
+ |---|---|
1023
+ | `::part(field)` | The bordered control box (the row wrapping any affixes plus the input). Restyle its border, radius, surface, or focus ring. — `kai-input::part(field) { border-radius: 0.75rem }` |
1024
+ | `::part(input)` | The inner input element. Restyle its text, padding, or placeholder. — `kai-input::part(input) { font-variant-numeric: tabular-nums }` |
1025
+ | `::part(label)` | The field label above the control. Restyle its typography or spacing. — `kai-input::part(label) { font-weight: 600 }` |
1026
+ | `::part(hint)` | The hint or error line below the control. Restyle its typography. — `kai-input::part(hint) { font-style: italic }` |
1027
+
1028
+ ---
1029
+
1030
+ ### `kai-kbd` / `Kbd`
1031
+
1032
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1033
+
1034
+ | Property | Attribute | Type | Description |
1035
+ |---|---|---|---|
1036
+ | `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. |
1037
+ | `platform` | `platform` | `undefined \| "other" \| "auto" \| "mac"` | `mac` uses ⌘/⌥, `other` uses Ctrl. `auto` (default) sniffs the OS. |
1038
+ | `size` | `size` | `undefined \| "sm" \| "md"` | Cap size: `sm` or `md`. Defaults to `md`. |
1039
+
1040
+ _No events._
1041
+
1042
+ **Styleable parts** (restyle from outside via `kai-kbd::part(name)`):
1043
+
1044
+ | Part | Description |
1045
+ |---|---|
1046
+ | `::part(key)` | Each key cap. Restyle its surface, border, radius, or font. — `kai-kbd::part(key) { border-radius: 0.375rem }` |
1047
+ | `::part(separator)` | The gap between key caps. Inject a literal joiner (e.g. a plus sign) from outside. — `kai-kbd::part(separator)::after { content: "+" }` |
1048
+
1049
+ ---
1050
+
1051
+ ### `kai-link-preview` / `LinkPreview`
1052
+
1053
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1054
+
1055
+ | Property | Attribute | Type | Description |
1056
+ |---|---|---|---|
1057
+ | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted event. Set as an attribute or property. |
1058
+ | `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). |
1059
+
1060
+ _No events._
1061
+
1062
+ ---
1063
+
1064
+ ### `kai-loader` / `Loader`
1065
+
1066
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1067
+
1068
+ | Property | Attribute | Type | Description |
1069
+ |---|---|---|---|
1070
+ | `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'`. |
1071
+ | `size` | `size` | `undefined \| "sm" \| "md" \| "lg"` | Loader size: `'sm' \| 'md' \| 'lg'`. Defaults to `'md'`. |
1072
+ | `text` | `text` | `undefined \| string` | Label for the text-based variants. |
1073
+
1074
+ _No events._
1075
+
1076
+ ---
1077
+
1078
+ ### `kai-markdown` / `Markdown`
1079
+
1080
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1081
+
1082
+ | Property | Attribute | Type | Description |
1083
+ |---|---|---|---|
1084
+ | `content` | `content` | `string` | The markdown source to render. |
1085
+ | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Text/markdown sizing. |
1086
+ | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme for fenced code blocks. |
1087
+ | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting (no Shiki loads). |
1088
+
1089
+ _No events._
1090
+
1091
+ ---
1092
+
1093
+ ### `kai-menu` / `Menu`
1094
+
1095
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1096
+
1097
+ | Property | Attribute | Type | Description |
1098
+ |---|---|---|---|
1099
+ | `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. |
1100
+ | `placement` | `placement` | `undefined \| string` | Optional placement hint (unused by the underlying Dropdown which always positions bottom-start, kept for future extension). |
1101
+ | `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. |
1102
+ | `triggerLabel` | `trigger-label` | `undefined \| string` | Built-in trigger: a text label (e.g. `"High"`). |
1103
+ | `triggerIconTrailing` | `trigger-icon-trailing` | `undefined \| string` | Built-in trigger: a trailing icon (e.g. `"chevron-down"` for a select look). |
1104
+ | `label` | `label` | `undefined \| string` | Accessible name for an icon-only trigger (no visible label). |
1105
+ | `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`. |
1106
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1107
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger — click/keyboard and `show()` no longer open the menu. |
1108
+
1109
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1110
+
1111
+ | Event | `detail` type | Description |
1112
+ |---|---|---|
1113
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The menu opened or closed (by click, keyboard, Escape, outside-click, or a method). |
1114
+ | `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. |
1115
+
1116
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1117
+
1118
+ | Slot | Mode | Description |
1119
+ |---|---|---|
1120
+ | `trigger` | replace | Your own trigger element; replaces the built-in button driven by the `trigger-icon` / `trigger-label` props. |
1121
+
1122
+ **Styleable parts** (restyle from outside via `kai-menu::part(name)`):
1123
+
1124
+ | Part | Description |
1125
+ |---|---|
1126
+ | `::part(shortcut)` | The right-aligned per-item keyboard shortcut, rendered as kai-kbd key caps. Shown only when an item carries a `shortcut`. — `kai-menu::part(shortcut) { opacity: 0.8 }` |
1127
+
1128
+ ---
1129
+
1130
+ ### `kai-message` / `Message`
1131
+
1132
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1133
+
1134
+ | Property | Attribute | Type | Description |
1135
+ |---|---|---|---|
1136
+ | `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. |
1137
+ | `role` | `role` | `undefined \| "user" \| "assistant"` | Convenience for simple cases when not passing a `message` object. |
1138
+ | `content` | `content` | `undefined \| string` | Convenience content (used when `message` is not set). |
1139
+ | `markdown` | `markdown` | `undefined \| false \| true` | Force markdown on/off. Defaults to on for assistant, off for user. |
1140
+ | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | Text/markdown sizing for the message body. |
1141
+ | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name used for fenced code blocks in the content. |
1142
+ | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting for code blocks (no Shiki loads). |
1143
+ | `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'`). |
1144
+ | `avatarSrc` | `avatar-src` | `undefined \| string` | Convenience avatar image URL (used when `message.avatar` is not set). |
1145
+ | `avatarFallback` | `avatar-fallback` | `undefined \| string` | Convenience avatar fallback text (used when `message.avatar` is not set). |
1146
+ | `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). |
1147
+
1148
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1149
+
1150
+ | Event | `detail` type | Description |
1151
+ |---|---|---|
1152
+ | `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. |
1153
+
1154
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1155
+
1156
+ | Slot | Mode | Description |
1157
+ |---|---|---|
1158
+ | `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. |
1159
+ | `after-body` | inject | A row at the BOTTOM of the body, below the action bar — a citation/sources row, a token-cost/latency line. |
1160
+ | `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. |
1161
+
1162
+ **Styleable parts** (restyle from outside via `kai-message::part(name)`):
1163
+
1164
+ | Part | Description |
1165
+ |---|---|
1166
+ | `::part(row)` | The message row wrapper (avatar rail + body column). Restyle its gap or alignment from outside. — `kai-message::part(row) { gap: 0.75rem }` |
1167
+ | `::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) }` |
1168
+ | `::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 }` |
1169
+ | `::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 }` |
1170
+ | `::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. |
1171
+
1172
+ ---
1173
+
1174
+ ### `kai-model-switcher` / `ModelSwitcher`
1175
+
1176
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1177
+
1178
+ | Property | Attribute | Type | Description |
1179
+ |---|---|---|---|
1180
+ | `models` | — | `{ id: string; name: string; provider?: undefined \| string; description?: undefined \| string; group?: undefined \| string }[]` | The selectable models. Set as a JS property (array). |
1181
+ | `currentModel` | `current-model` | `undefined \| string` | The currently-selected model id. Defaults to the first model. |
1182
+ | `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`. |
1183
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1184
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger — click/keyboard and `show()` no longer open the dropdown. |
1185
+
1186
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1187
+
1188
+ | Event | `detail` type | Description |
1189
+ |---|---|---|
1190
+ | `kai-model-change` | `CustomEvent<{ modelId: string }>` | A model was selected. |
1191
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The model dropdown opened or closed (by click, keyboard, Escape, outside-click, or a method). |
1192
+
1193
+ ---
1194
+
1195
+ ### `kai-nav` / `Nav`
1196
+
1197
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1198
+
1199
+ | Property | Attribute | Type | Description |
1200
+ |---|---|---|---|
1201
+ | `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. |
1202
+ | `value` | `value` | `undefined \| string` | Active item id (controlled). |
1203
+ | `defaultValue` | `default-value` | `undefined \| string` | Initial active id when uncontrolled. |
1204
+ | `defaultCollapsed` | — | `undefined \| string[]` | Ids of group items collapsed on first render (groups default to expanded). Set as a JS property (array). |
1205
+
1206
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1207
+
1208
+ | Event | `detail` type | Description |
1209
+ |---|---|---|
1210
+ | `kai-nav-item-action` | `CustomEvent<{ value: string; action?: undefined \| { icon: string; label: string } }>` | A row's trailing `action` button was activated (not a select). `value` is the item id; `action` echoes the item's `{ icon, label }`. |
1211
+ | `kai-nav-item-close` | `CustomEvent<{ value: string }>` | A `closable` row's trailing close button was activated (not a select). `value` is the item id. |
1212
+ | `kai-nav-select` | `CustomEvent<{ id: string }>` | A nav item was activated. |
1213
+
1214
+ **Styleable parts** (restyle from outside via `kai-nav::part(name)`):
1215
+
1216
+ | Part | Description |
1217
+ |---|---|
1218
+ | `::part(nav)` | The nav list container. Restyle its gap or padding from outside. — `kai-nav::part(nav) { gap: 0.25rem }` |
1219
+ | `::part(item)` | A nav item button (leaf or group parent). The active leaf carries aria-current="page" and a group parent carries aria-expanded; target `::part(item)[aria-current]` for the selected look or `::part(item)[aria-expanded]` for a group row. — `kai-nav::part(item)[aria-current] { background: var(--color-accent) }` |
1220
+ | `::part(group)` | The nested child list rendered under an expanded group item. Add a left guide line or tune its indent from outside. — `kai-nav::part(group) { border-left: 1px solid var(--color-border); margin-left: 1.1rem }` |
1221
+ | `::part(chevron)` | The disclosure chevron on a group row (rotates when expanded). Recolor or resize it from outside. — `kai-nav::part(chevron) { opacity: 1; color: var(--color-primary) }` |
1222
+ | `::part(status)` | The per-item status cluster (a colored dot in the tone hue + an optional label). Shown only when an item carries a `status`; the `pulse` flag animates the dot. Restyle from outside. — `kai-nav::part(status) { gap: 0.5rem }` |
1223
+ | `::part(meta)` | The right-aligned muted trailing text on a row (e.g. a relative time). Shown only when an item carries `meta`; restyle from outside. — `kai-nav::part(meta) { color: var(--color-foreground); font-variant-numeric: tabular-nums }` |
1224
+ | `::part(item-action)` | The trailing per-item action / close button, a sibling of the item button. Shown only when an item carries `action` or `closable`; reveal it on hover or pin it visible from outside. — `kai-nav::part(item-action) { opacity: 1 }` |
1225
+
1226
+ ---
1227
+
1228
+ ### `kai-notice` / `Notice`
1229
+
1230
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1231
+
1232
+ | Property | Attribute | Type | Description |
1233
+ |---|---|---|---|
1234
+ | `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`). |
1235
+ | `icon` | `icon` | `undefined \| string` | Leading icon: omit for the severity default, `"none"` to hide it, or a named icon to override. |
1236
+ | `dismissible` | `dismissible` | `undefined \| false \| true` | Show a dismiss (×) that hides the notice and emits `kai-dismiss`. |
1237
+
1238
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1239
+
1240
+ | Event | `detail` type | Description |
1241
+ |---|---|---|
1242
+ | `kai-dismiss` | `CustomEvent` | The notice was dismissed via its × (it also hides itself). |
1243
+
1244
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1245
+
1246
+ | Slot | Mode | Description |
1247
+ |---|---|---|
1248
+ | `action` | inject | A trailing action beside the message — a link or button. |
1249
+ | `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`. |
1250
+
1251
+ ---
1252
+
1253
+ ### `kai-pane` / `Pane`
543
1254
 
544
1255
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
545
1256
 
546
1257
  | Property | Attribute | Type | Description |
547
1258
  |---|---|---|---|
548
- | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted event. Set as an attribute or property. |
549
- | `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). |
1259
+ | `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`. |
1260
+ | `subtitle` | `subtitle` | `undefined \| string` | A role / label shown under the title (e.g. "Reviewer", "claude-sonnet"). Attribute: `subtitle`. |
1261
+ | `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`. |
1262
+ | `focused` | `focused` | `undefined \| false \| true` | Highlight the frame with a ring/border to mark the ACTIVE pane. Attribute: `focused`. |
1263
+ | `showSplit` | `show-split` | `undefined \| false \| true` | Show a split-pane window control that fires `kai-split`. Off by default. Attribute: `show-split`. |
1264
+ | `showDock` | `show-dock` | `undefined \| false \| true` | Show a dock-to-side window control that fires `kai-dock`. Off by default. Attribute: `show-dock`. |
1265
+ | `status` | — | `undefined \| { tone: "working" \| "idle" \| "done" \| "error" \| "blocked"; label?: undefined \| string; pulse?: undefined \| false \| true }` | A tone-colored status dot (+ optional label) in the header. An object `{ tone, label?, pulse? }` set as a JS PROPERTY (not an attribute). |
550
1266
 
551
- _No events._
1267
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
552
1268
 
553
- ---
1269
+ | Event | `detail` type | Description |
1270
+ |---|---|---|
1271
+ | `kai-close` | `CustomEvent` | The close (×) control was clicked. |
1272
+ | `kai-dock` | `CustomEvent` | The dock control was clicked (only present when `show-dock`). |
1273
+ | `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. |
1274
+ | `kai-split` | `CustomEvent` | The split control was clicked (only present when `show-split`). |
554
1275
 
555
- ### `kai-loader` / `Loader`
1276
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
556
1277
 
557
- **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1278
+ | Slot | Mode | Description |
1279
+ |---|---|---|
1280
+ | `leading` | inject | A glyph or avatar at the start of the pane header. |
1281
+ | `actions` | inject | Extra header controls, before the built-in window controls. |
1282
+ | `footer` | inject | A pinned row below the body (e.g. a composer). |
558
1283
 
559
- | Property | Attribute | Type | Description |
560
- |---|---|---|---|
561
- | `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'`. |
562
- | `size` | `size` | `undefined \| "sm" \| "lg" \| "md"` | Loader size: `'sm' \| 'md' \| 'lg'`. Defaults to `'md'`. |
563
- | `text` | `text` | `undefined \| string` | Label for the text-based variants. |
1284
+ **Styleable parts** (restyle from outside via `kai-pane::part(name)`):
564
1285
 
565
- _No events._
1286
+ | Part | Description |
1287
+ |---|---|
1288
+ | `::part(header)` | The pane header bar (leading + title/status + actions + window controls). — `kai-pane::part(header) { padding-inline: 0.75rem }` |
1289
+ | `::part(body)` | The scrolling body region (the default slot). — `kai-pane::part(body) { padding: 1rem }` |
1290
+ | `::part(controls)` | The window-control cluster (maximize/close, and split/dock when enabled). — `kai-pane::part(controls) { gap: 0.25rem }` |
1291
+ | `::part(footer)` | A pinned row below the body (e.g. a composer). |
566
1292
 
567
1293
  ---
568
1294
 
569
- ### `kai-markdown` / `Markdown`
1295
+ ### `kai-pane-group` / `PaneGroup`
570
1296
 
571
1297
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
572
1298
 
573
1299
  | Property | Attribute | Type | Description |
574
1300
  |---|---|---|---|
575
- | `content` | `content` | `string` | The markdown source to render. |
576
- | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing. |
577
- | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme for fenced code blocks. |
578
- | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting (no Shiki loads). |
1301
+ | `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). |
1302
+ | `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). |
1303
+ | `focused` | `focused` | `undefined \| false \| true` | Highlight the frame as the ACTIVE group in a multi-group layout. Attribute: `focused`. |
579
1304
 
580
- _No events._
1305
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1306
+
1307
+ | Event | `detail` type | Description |
1308
+ |---|---|---|
1309
+ | `kai-tab-change` | `CustomEvent<{ id: string }>` | A tab was selected (click, Enter/Space, or arrow-key move). `detail.id` is the tab's id. |
1310
+ | `kai-tab-close` | `CustomEvent<{ id: string }>` | A tab's close (×) was clicked. Drop the tab from `tabs` yourself. |
1311
+ | `kai-tab-menu` | `CustomEvent<{ id: string }>` | A tab's "…" overflow was clicked. Open your own menu from `detail.id`. |
1312
+
1313
+ **Styleable parts** (restyle from outside via `kai-pane-group::part(name)`):
1314
+
1315
+ | Part | Description |
1316
+ |---|---|
1317
+ | `::part(tabs)` | The tab strip (role="tablist"). Restyle its background, height, padding, or gap from outside. — `kai-pane-group::part(tabs) { background: var(--color-card); gap: 0.25rem }` |
1318
+ | `::part(tab)` | A single tab button. The active tab carries `[aria-selected="true"]`; target `::part(tab)[aria-selected="true"]` for the selected look. — `kai-pane-group::part(tab)[aria-selected="true"] { background: var(--color-accent) }` |
1319
+ | `::part(body)` | The active tab's content region (the named/default slot host). — `kai-pane-group::part(body) { padding: 0.75rem }` |
1320
+ | `::part(menu)` | The per-tab "…" overflow button. Reveal it on hover or pin it visible from outside. — `kai-pane-group::part(menu) { opacity: 1 }` |
1321
+ | `::part(close)` | The per-tab close ("×") button. Recolor, resize, or hide it from outside. — `kai-pane-group::part(close) { color: var(--color-muted-foreground) }` |
581
1322
 
582
1323
  ---
583
1324
 
584
- ### `kai-message` / `Message`
1325
+ ### `kai-popover` / `Popover`
585
1326
 
586
1327
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
587
1328
 
588
1329
  | Property | Attribute | Type | Description |
589
1330
  |---|---|---|---|
590
- | `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 } }` | The full message object. Set as a JS property. |
591
- | `role` | `role` | `undefined \| "user" \| "assistant"` | Convenience for simple cases when not passing a `message` object. |
592
- | `content` | `content` | `undefined \| string` | Convenience content (used when `message` is not set). |
593
- | `markdown` | `markdown` | `undefined \| false \| true` | Force markdown on/off. Defaults to on for assistant, off for user. |
594
- | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing for the message body. |
595
- | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name used for fenced code blocks in the content. |
596
- | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting for code blocks (no Shiki loads). |
597
- | `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'`). |
598
- | `avatarSrc` | `avatar-src` | `undefined \| string` | Convenience avatar image URL (used when `message.avatar` is not set). |
599
- | `avatarFallback` | `avatar-fallback` | `undefined \| string` | Convenience avatar fallback text (used when `message.avatar` is not set). |
1331
+ | `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). |
1332
+ | `gutter` | `gutter` | `undefined \| number` | Gap in px between the trigger and the panel. |
1333
+ | `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`. |
1334
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1335
+ | `disabled` | `disabled` | `undefined \| false \| true` | Turn the popover off while keeping the trigger mounted (clicks and `show()` no longer open it). |
600
1336
 
601
1337
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
602
1338
 
603
1339
  | Event | `detail` type | Description |
604
1340
  |---|---|---|
605
- | `kai-message-action` | `CustomEvent<{ messageId: string; action: string }>` | An action button was clicked. `action` is the built-in name or custom id. |
1341
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The popover opened or closed (click, Escape, outside-click, or a method). |
606
1342
 
607
1343
  ---
608
1344
 
609
- ### `kai-model-switcher` / `ModelSwitcher`
1345
+ ### `kai-progress-bar` / `ProgressBar`
610
1346
 
611
1347
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
612
1348
 
613
1349
  | Property | Attribute | Type | Description |
614
1350
  |---|---|---|---|
615
- | `models` | | `{ id: string; name: string; provider?: undefined \| string; description?: undefined \| string; group?: undefined \| string }[]` | The selectable models. Set as a JS property (array). |
616
- | `currentModel` | `current-model` | `undefined \| string` | The currently-selected model id. Defaults to the first model. |
1351
+ | `value` | `value` | `undefined \| number` | Current progress value (0..max). Attribute: `value`. |
1352
+ | `max` | `max` | `undefined \| number` | The value `value` runs to (default 100). Attribute: `max`. |
1353
+ | `label` | `label` | `undefined \| string` | Optional caption above the track. Attribute: `label`. |
1354
+ | `tone` | `tone` | `undefined \| string` | Fill color: `primary` (default), `success`, `warning`, `error`, `info`. Attribute: `tone`. |
617
1355
 
618
- **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1356
+ _No events._
619
1357
 
620
- | Event | `detail` type | Description |
621
- |---|---|---|
622
- | `kai-model-change` | `CustomEvent<{ modelId: string }>` | A model was selected. |
1358
+ **Styleable parts** (restyle from outside via `kai-progress-bar::part(name)`):
1359
+
1360
+ | Part | Description |
1361
+ |---|---|
1362
+ | `::part(track)` | The progress track (the background bar). Restyle its height, radius, or background from outside. — `kai-progress-bar::part(track) { height: 0.5rem }` |
1363
+ | `::part(fill)` | The filled portion; its width follows value/max. Recolor it from outside. — `kai-progress-bar::part(fill) { background: var(--color-tool-green) }` |
623
1364
 
624
1365
  ---
625
1366
 
626
- ### `kai-popover` / `Popover`
1367
+ ### `kai-prompt-dock` / `PromptDock`
627
1368
 
628
1369
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
629
1370
 
630
1371
  | Property | Attribute | Type | Description |
631
1372
  |---|---|---|---|
632
- | `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). |
633
- | `gutter` | `gutter` | `undefined \| number` | Gap in px between the trigger and the panel. |
634
- | `open` | `open` | `undefined \| false \| true` | Controlled open state. Set as a JS property (`el.open = true`) to drive the popover from your app; omit for the default click-to-toggle behaviour. |
1373
+ | `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`. |
1374
+ | `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`. |
635
1375
 
636
- **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1376
+ _No events._
637
1377
 
638
- | Event | `detail` type | Description |
1378
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1379
+
1380
+ | Slot | Mode | Description |
639
1381
  |---|---|---|
640
- | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The popover wants to open or close (click, Escape, or outside-click). |
1382
+ | `top` | inject | The top lip: a notice or banner above the input. Rendered only when filled. |
1383
+ | `bottom` | inject | The bottom lip: a mode or controls row below the input. Rendered only when filled. |
1384
+
1385
+ **Styleable parts** (restyle from outside via `kai-prompt-dock::part(name)`):
1386
+
1387
+ | Part | Description |
1388
+ |---|---|
1389
+ | `::part(tray)` | The recessed tray that frames the input. The `appearance`/`frame` props set the defaults; the --kai-prompt-dock-* tokens fine-tune surface/border/radius/inset. — `kai-prompt-dock::part(tray) { --kai-prompt-dock-radius: 1rem }` |
1390
+ | `::part(top)` | The top lip: a notice or banner above the input. Rendered only when filled. |
1391
+ | `::part(bottom)` | The bottom lip: a mode or controls row below the input. Rendered only when filled. |
641
1392
 
642
1393
  ---
643
1394
 
@@ -647,33 +1398,48 @@ _No events._
647
1398
 
648
1399
  | Property | Attribute | Type | Description |
649
1400
  |---|---|---|---|
650
- | `value` | `value` | `undefined \| string` | Controlled value of the input. When set, the host owns the text and must update it on `kai-value-change`; leave unset for uncontrolled behavior. |
1401
+ | `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`. |
651
1402
  | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown in the empty input. |
652
1403
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the input and submit button entirely (non-interactive). |
653
1404
  | `loading` | `loading` | `undefined \| false \| true` | Show the loading/streaming state and block submit (use while awaiting a reply). |
654
1405
  | `suggestions` | — | `undefined \| string[]` | Starter prompts shown above the input. Clicking one follows `suggestionMode`. Set as a JS property. |
655
1406
  | `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. |
656
- | `slashCommands` | — | `undefined \| { id: string; label: string; description?: undefined \| string; category?: undefined \| string }[]` | Slash commands — when set, typing `/` opens the command palette. Set as a JS property. |
657
- | `slashActiveIds` | — | `undefined \| string[]` | Command ids to highlight as active. |
658
- | `slashCompact` | `slash-compact` | `undefined \| false \| true` | Single-line palette rows. |
659
1407
  | `search` | `search` | `undefined \| false \| true` | Show a Search (Globe) button in the left toolbar; clicking it fires a `search` event. |
660
1408
  | `voice` | `voice` | `undefined \| false \| true` | Show a Voice (Mic) button in the left toolbar; clicking it fires a `voice` event. |
661
1409
  | `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`. |
1410
+ | `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. |
1411
+ | `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`. |
662
1412
  | `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). |
1413
+ | `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. |
1414
+ | `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. |
663
1415
 
664
1416
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
665
1417
 
666
1418
  | Event | `detail` type | Description |
667
1419
  |---|---|---|
1420
+ | `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). |
668
1421
  | `kai-search` | `CustomEvent<Record<string, never>>` | The Search (Globe) toolbar button was clicked. |
669
- | `kai-slash-select` | `CustomEvent<{ command: { id: string; label: string; description?: undefined \| string; category?: undefined \| string } }>` | A slash command was chosen from the palette. |
670
1422
  | `kai-stop` | `CustomEvent<Record<string, never>>` | The Stop button was clicked while `stoppable` and `loading` are both true. |
671
- | `kai-submit` | `CustomEvent<{ value: string; 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) with its attachments. |
1423
+ | `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. |
672
1424
  | `kai-suggestion-click` | `CustomEvent<{ value: string }>` | A suggestion was clicked while `suggestion-mode="fill"`. |
673
1425
  | `kai-toolbar-action` | `CustomEvent<{ action: string }>` | A custom `<kai-action>` toolbar button was clicked. `action` is the `id` of the `<kai-action>` element that was clicked. |
674
- | `kai-value-change` | `CustomEvent<{ value: string }>` | The input text changed (fires on every keystroke). |
1426
+ | `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`. |
675
1427
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Voice (Mic) toolbar button was clicked. |
676
1428
 
1429
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1430
+
1431
+ | Slot | Mode | Description |
1432
+ |---|---|---|
1433
+ | `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. |
1434
+ | `toolbar-start` | inject | Leading controls in the input toolbar — where a + menu goes. |
1435
+ | `toolbar-end` | inject | Trailing controls in the toolbar, before the Send button. |
1436
+
1437
+ **Styleable parts** (restyle from outside via `kai-prompt-input::part(name)`):
1438
+
1439
+ | Part | Description |
1440
+ |---|---|
1441
+ | `::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, … */` |
1442
+
677
1443
  ---
678
1444
 
679
1445
  ### `kai-reasoning` / `Reasoning`
@@ -684,15 +1450,17 @@ _No events._
684
1450
  |---|---|---|---|
685
1451
  | `text` | `text` | `string` | The reasoning text to display. |
686
1452
  | `label` | `label` | `undefined \| string` | Trigger label. |
687
- | `open` | `open` | `undefined \| false \| true` | Controlled open state set as a property (`el.open = true`). Omit for uncontrolled (the trigger toggles it). |
1453
+ | `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`. |
1454
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
688
1455
  | `streaming` | `streaming` | `undefined \| false \| true` | While true, auto-expands (and re-collapses when it flips false). |
689
1456
  | `markdown` | `markdown` | `undefined \| false \| true` | Render `text` as markdown. |
1457
+ | `disabled` | `disabled` | `undefined \| false \| true` | Gate the disclosure trigger — programmatic `show()/hide()/toggle()` still work, but the trigger click no longer toggles. |
690
1458
 
691
1459
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
692
1460
 
693
1461
  | Event | `detail` type | Description |
694
1462
  |---|---|---|
695
- | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | Open state changed (via the trigger or streaming auto-open). |
1463
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The reasoning block expanded or collapsed (via the trigger, streaming auto-open, or a method). |
696
1464
 
697
1465
  ---
698
1466
 
@@ -717,7 +1485,7 @@ _No events._
717
1485
 
718
1486
  | Property | Attribute | Type | Description |
719
1487
  |---|---|---|---|
720
- | `orientation` | `orientation` | `undefined \| "horizontal" \| "vertical"` | Layout axis: `horizontal` (row, default) or `vertical` (column). |
1488
+ | `orientation` | `orientation` | `undefined \| "vertical" \| "horizontal"` | Layout axis: `horizontal` (row, default) or `vertical` (column). |
721
1489
  | `maximizedIndex` | — | `undefined \| null \| number` | Which item index is maximized (null = none). Declarative source of truth. |
722
1490
 
723
1491
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
@@ -740,6 +1508,7 @@ _No events._
740
1508
  | `max` | `max` | `undefined \| string` | Maximum size during resize (px or %). |
741
1509
  | `locked` | `locked` | `undefined \| false \| true` | Fix this panel's size; adjacent dividers become non-draggable. |
742
1510
  | `hidden` | `hidden` | `undefined \| false \| true` | Hide this panel; its divider is dropped and the rest reflow. |
1511
+ | `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. |
743
1512
 
744
1513
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
745
1514
 
@@ -778,15 +1547,74 @@ _No events._
778
1547
  | `availableAuthors` | — | `string[]` | Authors to offer as scope filters. Set as a JS property. |
779
1548
  | `availableTags` | — | `string[]` | Tags to offer as scope filters. Set as a JS property. |
780
1549
  | `currentLabel` | `current-label` | `undefined \| string` | The label shown on the trigger for the active scope. |
1550
+ | `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`. |
1551
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1552
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the trigger — click/keyboard and `show()` no longer open the dropdown. |
781
1553
 
782
1554
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
783
1555
 
784
1556
  | Event | `detail` type | Description |
785
1557
  |---|---|---|
1558
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The scope dropdown opened or closed (by click, keyboard, Escape, outside-click, or a method). |
786
1559
  | `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"). |
787
1560
 
788
1561
  ---
789
1562
 
1563
+ ### `kai-screen` / `Screen`
1564
+
1565
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1566
+
1567
+ | Property | Attribute | Type | Description |
1568
+ |---|---|---|---|
1569
+ | `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`. |
1570
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1571
+ | `headline` | `headline` | `undefined \| string` | Header title text. A projected `title` slot overrides it. (Named `headline` because `title` collides with the global `HTMLElement.title` attribute.) |
1572
+ | `back` | `back` | `undefined \| false \| true` | Show the back button (default true). |
1573
+ | `noInert` | `no-inert` | `undefined \| false \| true` | Opt out of marking sibling elements inert/aria-hidden while open (for unusual layouts). |
1574
+
1575
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1576
+
1577
+ | Event | `detail` type | Description |
1578
+ |---|---|---|
1579
+ | `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). |
1580
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The screen opened or closed (a method, `Escape` close, or driven `open`). |
1581
+
1582
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1583
+
1584
+ | Slot | Mode | Description |
1585
+ |---|---|---|
1586
+ | `title` | replace | Rich header title; overrides the `headline` prop. |
1587
+ | `actions` | inject | Header trailing cluster (e.g. an avatar or overflow menu). |
1588
+
1589
+ **Styleable parts** (restyle from outside via `kai-screen::part(name)`):
1590
+
1591
+ | Part | Description |
1592
+ |---|---|
1593
+ | `::part(header)` | The back-header bar (back button + title + actions). Restyle its height, padding, or border from outside. — `kai-screen::part(header) { height: 3.25rem; padding-inline: 1rem }` |
1594
+ | `::part(back)` | The back button. Restyle or hide it from outside; `back="false"` removes it entirely. — `kai-screen::part(back) { border-radius: 9999px }` |
1595
+ | `::part(body)` | The full-bleed surface that fills the mount point and scrolls its content. Tune padding or background from outside. — `kai-screen::part(body) { background: var(--color-card) }` |
1596
+ | `::part(title)` | Rich header title; overrides the `headline` prop. |
1597
+
1598
+ ---
1599
+
1600
+ ### `kai-scroll-area` / `ScrollArea`
1601
+
1602
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1603
+
1604
+ | Property | Attribute | Type | Description |
1605
+ |---|---|---|---|
1606
+ | `orientation` | `orientation` | `undefined \| "vertical" \| "horizontal" \| "both"` | Which axis scrolls. `vertical` (default) · `horizontal` · `both`. The cross axis is clamped so content can't overflow it. |
1607
+
1608
+ _No events._
1609
+
1610
+ **Styleable parts** (restyle from outside via `kai-scroll-area::part(name)`):
1611
+
1612
+ | Part | Description |
1613
+ |---|---|
1614
+ | `::part(viewport)` | The scrolling container. Add padding or a max-height from outside; the thin scrollbar follows `--color-scrollbar-thumb`. — `kai-scroll-area::part(viewport) { padding-right: 0.5rem }` |
1615
+
1616
+ ---
1617
+
790
1618
  ### `kai-scroll-button` / `ScrollButton`
791
1619
 
792
1620
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
@@ -794,8 +1622,8 @@ _No events._
794
1622
  | Property | Attribute | Type | Description |
795
1623
  |---|---|---|---|
796
1624
  | `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="...">`. |
797
- | `variant` | `variant` | `undefined \| "ghost" \| "default" \| "outline"` | Button visual variant: `'outline' \| 'ghost' \| 'default'`. Defaults to `'outline'`. |
798
- | `size` | `size` | `undefined \| "sm" \| "lg" \| "md" \| "icon" \| "icon-sm"` | Button size token. Defaults to `'icon'` (square). |
1625
+ | `variant` | `variant` | `undefined \| "default" \| "ghost" \| "outline"` | Button visual variant: `'outline' \| 'ghost' \| 'default'`. Defaults to `'outline'`. |
1626
+ | `size` | `size` | `undefined \| "sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | Button size token. Defaults to `'icon'` (square). |
799
1627
 
800
1628
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
801
1629
 
@@ -805,6 +1633,147 @@ _No events._
805
1633
 
806
1634
  ---
807
1635
 
1636
+ ### `kai-search` / `Search`
1637
+
1638
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1639
+
1640
+ | Property | Attribute | Type | Description |
1641
+ |---|---|---|---|
1642
+ | `value` | `value` | `undefined \| string` | Controlled query — settable and reflected to the `value` attribute. Read `el.value` for live state. |
1643
+ | `placeholder` | `placeholder` | `undefined \| string` | Placeholder. Defaults to `Search…`. |
1644
+ | `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`. |
1645
+ | `debounce` | `debounce` | `undefined \| number` | Debounce window for `kai-search`, in ms. Defaults to `200`. |
1646
+ | `loading` | `loading` | `undefined \| false \| true` | Show a spinner in place of the leading icon while results load. |
1647
+ | `shortcut` | `shortcut` | `undefined \| string` | Optional shortcut hint shown (as a `kai-kbd`) while the field is empty, e.g. `Mod+K`. Display only; it does not bind the key. |
1648
+
1649
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1650
+
1651
+ | Event | `detail` type | Description |
1652
+ |---|---|---|
1653
+ | `kai-change` | `CustomEvent<{ value: string }>` | The field committed (blur). |
1654
+ | `kai-search` | `CustomEvent<{ value: string }>` | The query changed (debounced live, and on clear). |
1655
+ | `kai-submit` | `CustomEvent<{ value: string }>` | Enter was pressed. |
1656
+
1657
+ **Styleable parts** (restyle from outside via `kai-search::part(name)`):
1658
+
1659
+ | Part | Description |
1660
+ |---|---|
1661
+ | `::part(field)` | The bordered control box (the row wrapping the search icon, input, and clear button). — `kai-search::part(field) { border-radius: 9999px }` |
1662
+ | `::part(input)` | The inner search input element. — `kai-search::part(input) { font-size: 0.875rem }` |
1663
+ | `::part(clear)` | The trailing clear ("x") button, shown when the field is non-empty. — `kai-search::part(clear) { opacity: 1 }` |
1664
+
1665
+ ---
1666
+
1667
+ ### `kai-segmented` / `Segmented`
1668
+
1669
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1670
+
1671
+ | Property | Attribute | Type | Description |
1672
+ |---|---|---|---|
1673
+ | `options` | — | `{ value: string; label: string; icon?: undefined \| string }[]` | The selectable segments, left to right. Set as a JS property (array). |
1674
+ | `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. |
1675
+ | `size` | `size` | `undefined \| "sm" \| "md"` | Control density: `sm` or `md`. Defaults to `md`. |
1676
+
1677
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1678
+
1679
+ | Event | `detail` type | Description |
1680
+ |---|---|---|
1681
+ | `kai-change` | `CustomEvent<{ value: string }>` | A segment was chosen. |
1682
+
1683
+ **Styleable parts** (restyle from outside via `kai-segmented::part(name)`):
1684
+
1685
+ | Part | Description |
1686
+ |---|---|
1687
+ | `::part(track)` | The segmented track (the pill container holding the segments). Restyle its background, radius, or padding. — `kai-segmented::part(track) { border-radius: 9999px }` |
1688
+ | `::part(segment)` | Each segment button. Restyle padding, font weight, or the selected look. — `kai-segmented::part(segment) { font-weight: 600 }` |
1689
+
1690
+ ---
1691
+
1692
+ ### `kai-separator` / `Separator`
1693
+
1694
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1695
+
1696
+ | Property | Attribute | Type | Description |
1697
+ |---|---|---|---|
1698
+ | `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). |
1699
+
1700
+ _No events._
1701
+
1702
+ **Styleable parts** (restyle from outside via `kai-separator::part(name)`):
1703
+
1704
+ | Part | Description |
1705
+ |---|---|
1706
+ | `::part(separator)` | The divider line. Restyle its color, thickness, or inset from outside. — `kai-separator::part(separator) { background: var(--color-border) }` |
1707
+
1708
+ ---
1709
+
1710
+ ### `kai-setting-item` / `SettingItem`
1711
+
1712
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1713
+
1714
+ | Property | Attribute | Type | Description |
1715
+ |---|---|---|---|
1716
+ | `label` | `label` | `undefined \| string` | Row label (primary text). Attribute: `label`. |
1717
+ | `description` | `description` | `undefined \| string` | Optional secondary description under the label. Attribute: `description`. |
1718
+
1719
+ _No events._
1720
+
1721
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
1722
+
1723
+ | Slot | Mode | Description |
1724
+ |---|---|---|
1725
+ | `control` | inject | The row control (a switch, segmented, select, etc.), right-aligned. Omit it for a label-only row. |
1726
+
1727
+ **Styleable parts** (restyle from outside via `kai-setting-item::part(name)`):
1728
+
1729
+ | Part | Description |
1730
+ |---|---|
1731
+ | `::part(label)` | The label + description block on the left of the row. Restyle its typography or spacing. — `kai-setting-item::part(label) { gap: 0.125rem }` |
1732
+ | `::part(control)` | The row control (a switch, segmented, select, etc.), right-aligned. Omit it for a label-only row. |
1733
+
1734
+ ---
1735
+
1736
+ ### `kai-settings-group` / `SettingsGroup`
1737
+
1738
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1739
+
1740
+ | Property | Attribute | Type | Description |
1741
+ |---|---|---|---|
1742
+ | `heading` | `heading` | `undefined \| string` | Small section heading shown above the card. Attribute: `heading`. |
1743
+ | `description` | `description` | `undefined \| string` | Optional muted description under the heading. Attribute: `description`. |
1744
+
1745
+ _No events._
1746
+
1747
+ **Styleable parts** (restyle from outside via `kai-settings-group::part(name)`):
1748
+
1749
+ | Part | Description |
1750
+ |---|---|
1751
+ | `::part(header)` | The group heading + description block. Restyle its spacing or typography. — `kai-settings-group::part(header) { margin-bottom: 0.75rem }` |
1752
+ | `::part(body)` | The bordered card holding the setting rows. Restyle its surface, border, or radius. — `kai-settings-group::part(body) { border-radius: 1rem }` |
1753
+
1754
+ ---
1755
+
1756
+ ### `kai-skeleton` / `Skeleton`
1757
+
1758
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1759
+
1760
+ | Property | Attribute | Type | Description |
1761
+ |---|---|---|---|
1762
+ | `variant` | `variant` | `undefined \| "text" \| "rect" \| "circle"` | `text` (one or more lines), `rect` (a block), or `circle` (round). Defaults to `text`. |
1763
+ | `width` | `width` | `undefined \| string` | CSS width (e.g. `'12rem'`, `'60%'`). Defaults to full width (responsive); for `circle` it is the diameter. |
1764
+ | `height` | `height` | `undefined \| string` | CSS height. Defaults per variant (a text line height; circle = width). |
1765
+ | `lines` | `lines` | `undefined \| number` | `text` only: number of lines; the last is shorter. Defaults to 1. |
1766
+
1767
+ _No events._
1768
+
1769
+ **Styleable parts** (restyle from outside via `kai-skeleton::part(name)`):
1770
+
1771
+ | Part | Description |
1772
+ |---|---|
1773
+ | `::part(skeleton)` | The shimmer block(s). Recolor or change the opacity from outside; the default is a low-contrast foreground tint that reads in both light and dark. — `kai-skeleton::part(skeleton) { background: var(--color-primary); opacity: 0.15 }` |
1774
+
1775
+ ---
1776
+
808
1777
  ### `kai-skills` / `Skills`
809
1778
 
810
1779
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
@@ -847,15 +1816,37 @@ _No events._
847
1816
 
848
1817
  ---
849
1818
 
1819
+ ### `kai-status` / `Status`
1820
+
1821
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1822
+
1823
+ | Property | Attribute | Type | Description |
1824
+ |---|---|---|---|
1825
+ | `status` | `status` | `undefined \| "new" \| "online" \| "busy" \| "away" \| "offline"` | Presence/notification state → color. `new` (default) maps to the blue hue. |
1826
+ | `pulse` | `pulse` | `undefined \| false \| true` | Animated ping ring (off by default; respects prefers-reduced-motion). |
1827
+ | `label` | `label` | `undefined \| string` | Accessible name. Without it the dot is decorative. |
1828
+ | `size` | `size` | `undefined \| "sm" \| "md"` | `sm` (default) or `md`. |
1829
+
1830
+ _No events._
1831
+
1832
+ **Styleable parts** (restyle from outside via `kai-status::part(name)`):
1833
+
1834
+ | Part | Description |
1835
+ |---|---|
1836
+ | `::part(dot)` | The status dot. Recolor or resize it from outside; the `status` prop sets the default hue. — `kai-status::part(dot) { background: var(--color-tool-green) }` |
1837
+
1838
+ ---
1839
+
850
1840
  ### `kai-suggestions` / `Suggestions`
851
1841
 
852
1842
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
853
1843
 
854
1844
  | Property | Attribute | Type | Description |
855
1845
  |---|---|---|---|
856
- | `suggestions` | — | `(string \| { label: string; value?: undefined \| string })[]` | The suggestions. Strings, or `{ label, value }` when the displayed text and the emitted value differ. Set as a JS property. |
857
- | `variant` | `variant` | `undefined \| "ghost" \| "default" \| "outline"` | Chip style: `'outline'` (default), `'ghost'`, or `'default'` (filled). |
858
- | `size` | `size` | `undefined \| "sm" \| "lg" \| "md" \| "icon" \| "icon-sm"` | Size preset for each chip. Defaults to the pill default (`'lg'`); pass `'sm'` for smaller pills (or `'md'`). |
1846
+ | `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. |
1847
+ | `variant` | `variant` | `undefined \| "default" \| "ghost" \| "outline"` | Chip style: `'outline'` (default), `'ghost'`, or `'default'` (filled). |
1848
+ | `size` | `size` | `undefined \| "md" \| "lg"` | Row height for `layout="list"`: `'md'` (default) or `'lg'` for taller rows. Chips are unaffected. |
1849
+ | `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. |
859
1850
  | `block` | `block` | `undefined \| false \| true` | Full-width left-aligned rows instead of pills. |
860
1851
  | `highlight` | `highlight` | `undefined \| string` | Substring to highlight within each suggestion. |
861
1852
 
@@ -873,9 +1864,12 @@ _No events._
873
1864
 
874
1865
  | Property | Attribute | Type | Description |
875
1866
  |---|---|---|---|
876
- | `checked` | `checked` | `undefined \| false \| true` | Initial checked state. Bare attribute (`<kai-switch checked>`) turns it on. |
1867
+ | `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. |
1868
+ | `defaultChecked` | `default-checked` | `undefined \| false \| true` | Initial checked state on mount (uncontrolled seed). Bare attribute (`<kai-switch default-checked>`) turns it on. |
877
1869
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable interaction. |
878
1870
  | `label` | `label` | `undefined \| string` | Accessible label. |
1871
+ | `name` | `name` | `undefined \| string` | Form-control name (paired with `value`). |
1872
+ | `value` | `value` | `undefined \| string` | Submitted value when checked (paired with `name`). Defaults to `'on'`. |
879
1873
 
880
1874
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
881
1875
 
@@ -885,6 +1879,34 @@ _No events._
885
1879
 
886
1880
  ---
887
1881
 
1882
+ ### `kai-tabs` / `Tabs`
1883
+
1884
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1885
+
1886
+ | Property | Attribute | Type | Description |
1887
+ |---|---|---|---|
1888
+ | `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. |
1889
+ | `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. |
1890
+ | `defaultValue` | `default-value` | `undefined \| string` | Initial selected id when uncontrolled (use the `default-value` attribute in plain HTML). |
1891
+ | `variant` | `variant` | `undefined \| "segmented" \| "underline"` | `segmented` (default, a pill group) or `underline` (an underlined row). |
1892
+ | `block` | `block` | `undefined \| false \| true` | Stretch the strip to full width, each tab sharing the space equally. |
1893
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the whole strip. |
1894
+
1895
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1896
+
1897
+ | Event | `detail` type | Description |
1898
+ |---|---|---|
1899
+ | `kai-tab-change` | `CustomEvent<{ value: string }>` | A tab was selected (click, Enter/Space, or arrow-key move). `value` is the item's id. |
1900
+
1901
+ **Styleable parts** (restyle from outside via `kai-tabs::part(name)`):
1902
+
1903
+ | Part | Description |
1904
+ |---|---|
1905
+ | `::part(tablist)` | The tab strip container (role="tablist"). Restyle its gap, padding, background, or radius from outside; the `variant` prop sets the segmented/underline defaults. — `kai-tabs::part(tablist) { gap: 0.5rem; background: var(--color-card) }` |
1906
+ | `::part(tab)` | A single tab button. Restyle from outside; the active tab carries a `[data-active]` attribute, so target `::part(tab)[data-active]` for the selected look. — `kai-tabs::part(tab)[data-active] { color: var(--color-primary); font-weight: 600 }` |
1907
+
1908
+ ---
1909
+
888
1910
  ### `kai-tasks` / `Tasks`
889
1911
 
890
1912
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
@@ -895,8 +1917,16 @@ _No events._
895
1917
  | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. |
896
1918
  | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. |
897
1919
  | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'submit', data:{ selected:[…] } }`. |
1920
+ | `value` | — | `undefined \| string[]` | Controlled selection (task ids; JS property). When set, it wins over local state. |
1921
+ | `defaultValue` | — | `undefined \| string[]` | Uncontrolled initial selection (task ids; JS property), overlaying per-task `checked`. |
1922
+ | `disabled` | `disabled` | `undefined \| false \| true` | Freeze the whole list + Confirm. Attribute: `disabled`. |
1923
+ | `readonly` | `readonly` | `undefined \| false \| true` | Display-only: rows can't be toggled and show the default cursor (no pointer, hover, or focus affordances). Keeps the look as-is. Attribute: `readonly`. |
898
1924
 
899
- _No events._
1925
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1926
+
1927
+ | Event | `detail` type | Description |
1928
+ |---|---|---|
1929
+ | `kai-value-change` | `CustomEvent<{ value: string[] }>` | The selection changed on a toggle — the selected ids in input order. |
900
1930
 
901
1931
  ---
902
1932
 
@@ -933,6 +1963,29 @@ _No events._
933
1963
 
934
1964
  ---
935
1965
 
1966
+ ### `kai-toast-region` / `ToastRegion`
1967
+
1968
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
1969
+
1970
+ | Property | Attribute | Type | Description |
1971
+ |---|---|---|---|
1972
+ | `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. |
1973
+ | `position` | `position` | `undefined \| "top-center" \| "top-right" \| "top-left" \| "bottom-center" \| "bottom-right" \| "bottom-left"` | Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … |
1974
+ | `max` | `max` | `undefined \| number` | Max simultaneously-visible toasts; the rest queue. Defaults to `3`. |
1975
+ | `stack` | `stack` | `undefined \| "expanded" \| "collapsed"` | Stacking: 'expanded' (default, full column) \| 'collapsed' (Sonner-style pile that expands on hover/focus). Attribute: stack. |
1976
+ | `appearance` | `appearance` | `undefined \| "pill" \| "card"` | Default appearance for this region's toasts: `'pill'` (default, compact) \| `'card'` (richer, with a description line). A per-toast `appearance` wins. Attribute: `appearance`. |
1977
+ | `inverse` | `inverse` | `undefined \| false \| true` | Default high-contrast inverse treatment for this region's toasts. A per-toast `inverse` wins. Off by default. Attribute: `inverse`. |
1978
+ | `target` | — | `undefined \| HTMLElement` | Container element to anchor this region to (JS property). Set by the store for a scoped region; unset = the global viewport region. |
1979
+
1980
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1981
+
1982
+ | Event | `detail` type | Description |
1983
+ |---|---|---|
1984
+ | `kai-action` | `CustomEvent<{ id: string; label: string }>` | A toast's action button was pressed. |
1985
+ | `kai-dismiss` | `CustomEvent<{ id: string; reason: "action" \| "timeout" \| "close" }>` | A toast left the stack. `reason` is `'timeout' \| 'close' \| 'action'`. |
1986
+
1987
+ ---
1988
+
936
1989
  ### `kai-tool` / `Tool`
937
1990
 
938
1991
  **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
@@ -940,9 +1993,37 @@ _No events._
940
1993
  | Property | Attribute | Type | Description |
941
1994
  |---|---|---|---|
942
1995
  | `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. |
943
- | `open` | `open` | `undefined \| false \| true` | Start expanded. |
1996
+ | `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`. |
1997
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
1998
+ | `disabled` | `disabled` | `undefined \| false \| true` | Gate the disclosure trigger — programmatic `show()/hide()/toggle()` still work, but the trigger click no longer toggles. |
944
1999
 
945
- _No events._
2000
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2001
+
2002
+ | Event | `detail` type | Description |
2003
+ |---|---|---|
2004
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The panel expanded or collapsed (by trigger click or a method). |
2005
+
2006
+ ---
2007
+
2008
+ ### `kai-tooltip` / `Tooltip`
2009
+
2010
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
2011
+
2012
+ | Property | Attribute | Type | Description |
2013
+ |---|---|---|---|
2014
+ | `content` | `content` | `undefined \| string` | The hint text shown on hover/focus of the slotted trigger. |
2015
+ | `openDelay` | `open-delay` | `undefined \| number` | Delay (ms) before the tooltip appears on hover. Defaults to 600. Focus shows it immediately regardless. |
2016
+ | `closeDelay` | `close-delay` | `undefined \| number` | Delay (ms) before it hides after the pointer leaves. Defaults to 0 (hides immediately). |
2017
+ | `placement` | `placement` | `undefined \| string` | Preferred placement: `'top' \| 'bottom' \| 'left' \| 'right'` (+ optional `-start`/`-end`). Defaults to `'top'`; flips to stay in view. |
2018
+ | `open` | `open` | `undefined \| false \| true` | Drive/observe open state (Shoelace-style: settable + reflected to the `open` attribute, the element still self-manages on hover/focus). Set `el.open = true`, or `<kai-tooltip open>`; listen for `kai-open-change`. |
2019
+ | `defaultOpen` | `default-open` | `undefined \| false \| true` | Initial open state on mount (uncontrolled seed). |
2020
+ | `disabled` | `disabled` | `undefined \| false \| true` | Turn the tooltip off while keeping the trigger mounted (hover/focus and `show()` no longer open it). |
2021
+
2022
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2023
+
2024
+ | Event | `detail` type | Description |
2025
+ |---|---|---|
2026
+ | `kai-open-change` | `CustomEvent<{ open: false \| true }>` | The tooltip opened or closed (by hover/focus, outside-click, or a method). |
946
2027
 
947
2028
  ---
948
2029
 
@@ -954,13 +2035,43 @@ _No events._
954
2035
  |---|---|---|---|
955
2036
  | `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. |
956
2037
  | `disabled` | `disabled` | `undefined \| false \| true` | Disable the mic button (non-interactive). |
2038
+ | `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. |
2039
+ | `interim` | `interim` | `undefined \| false \| true` | Emit live partial transcripts (`kai-transcript-interim`) during native recognition. Attribute: `interim`. No-op on the transcribe/fallback paths. |
2040
+
2041
+ **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
2042
+
2043
+ | Event | `detail` type | Description |
2044
+ |---|---|---|
2045
+ | `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). |
2046
+ | `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. |
2047
+ | `kai-transcript-interim` | `CustomEvent<{ text: string }>` | Live partial transcript during native recognition (only when `interim` is set). Fires repeatedly before the final `kai-transcription`. |
2048
+ | `kai-transcription` | `CustomEvent<{ text: string }>` | Final transcript — the `transcribe` property resolved, OR native `SpeechRecognition` produced final text (no `transcribe` set). |
2049
+
2050
+ ---
2051
+
2052
+ ### `kai-voice-output` / `VoiceOutput`
2053
+
2054
+ **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes):
2055
+
2056
+ | Property | Attribute | Type | Description |
2057
+ |---|---|---|---|
2058
+ | `text` | `text` | `undefined \| string` | The utterance to read aloud. |
2059
+ | `autoplay` | `autoplay` | `undefined \| false \| true` | Speak automatically when `text` is set/changed. |
2060
+ | `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. |
2061
+ | `disabled` | `disabled` | `undefined \| false \| true` | Disable the button (non-interactive). |
957
2062
 
958
2063
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
959
2064
 
960
2065
  | Event | `detail` type | Description |
961
2066
  |---|---|---|
962
- | `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. |
963
- | `kai-transcription` | `CustomEvent<{ text: string }>` | Transcription completed (the `transcribe` property resolved). |
2067
+ | `kai-speaking-change` | `CustomEvent<{ speaking: false \| true }>` | Playback started or stoppeddrive your own UI in sync. Fires on real transitions only (manual click and programmatic speak()/stop()), never on mount. |
2068
+ | `kai-synthesized` | `CustomEvent<{ blob: Blob }>` | The model path (`synthesize`) resolved audio — the raw `Blob` before playback. |
2069
+
2070
+ **Styleable parts** (restyle from outside via `kai-voice-output::part(name)`):
2071
+
2072
+ | Part | Description |
2073
+ |---|---|
2074
+ | `::part(button)` | The speaker/play button. Restyle radius, size, padding, or colors from outside; it is a ghost icon button by default. — `kai-voice-output::part(button) { border-radius: 9999px; color: var(--color-primary) }` |
964
2075
 
965
2076
  ---
966
2077
 
@@ -971,15 +2082,15 @@ _No events._
971
2082
  | Property | Attribute | Type | Description |
972
2083
  |---|---|---|---|
973
2084
  | `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. |
974
- | `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 }[]` | Flat conversation list (auto-bucketed if `groups` is empty). Set as a JS property. |
2085
+ | `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. |
975
2086
  | `activeId` | `active-id` | `undefined \| string` | Id of the open conversation, highlighted in the sidebar. |
976
- | `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 } }[]` | The active conversation's message thread, newest last. Set as a JS property. |
2087
+ | `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. |
977
2088
  | `value` | `value` | `undefined \| string` | |
978
2089
  | `placeholder` | `placeholder` | `undefined \| string` | |
979
2090
  | `loading` | `loading` | `undefined \| false \| true` | |
980
2091
  | `suggestions` | — | `undefined \| string[]` | |
981
2092
  | `suggestionMode` | `suggestion-mode` | `undefined \| "submit" \| "fill"` | |
982
- | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | |
2093
+ | `proseSize` | `prose-size` | `undefined \| "sm" \| "lg" \| "xs" \| "base"` | |
983
2094
  | `codeTheme` | `code-theme` | `undefined \| string` | |
984
2095
  | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | |
985
2096
  | `chatTitle` | `chat-title` | `undefined \| string` | |
@@ -989,30 +2100,46 @@ _No events._
989
2100
  | `scrollButton` | `scroll-button` | `undefined \| false \| true` | |
990
2101
  | `search` | `search` | `undefined \| false \| true` | |
991
2102
  | `voice` | `voice` | `undefined \| false \| true` | |
992
- | `slashCommands` | — | `undefined \| { id: string; label: string; description?: undefined \| string; category?: undefined \| string }[]` | |
993
- | `slashActiveIds` | — | `undefined \| string[]` | |
994
- | `slashCompact` | `slash-compact` | `undefined \| false \| true` | |
995
- | `sidebarWidth` | `sidebar-width` | `undefined \| number` | Sidebar default width as a percent of the workspace (default 22). |
996
- | `sidebarMinWidth` | `sidebar-min-width` | `undefined \| number` | Sidebar min width in px (default 200). |
2103
+ | `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 (`/` skills, `@` agents/plugins) forwarded to the input. |
2104
+ | `kindIcons` | — | `undefined \| Record<string, string>` | Default icon per entity kind (kind → image src) forwarded to the input. |
2105
+ | `sidebarWidth` | `sidebar-width` | `undefined \| number` | Sidebar default width as a percent of the workspace (default 26). |
2106
+ | `sidebarMinWidth` | `sidebar-min-width` | `undefined \| number` | Sidebar min width in px (default 240). |
997
2107
  | `sidebarMaxWidth` | `sidebar-max-width` | `undefined \| number` | Sidebar max width in px (default 420). |
998
2108
  | `sidebarCollapsed` | `sidebar-collapsed` | `undefined \| false \| true` | Controlled collapsed state. Set this as a JS property (`el.sidebarCollapsed = true`) to drive the sidebar from your app, updating it in response to the `kai-sidebar-toggle` event. Omit for uncontrolled (the element manages it). |
999
2109
  | `defaultSidebarCollapsed` | `default-sidebar-collapsed` | `undefined \| false \| true` | Initial collapsed state when uncontrolled (default false). Use the `default-sidebar-collapsed` attribute to start collapsed in plain HTML. |
2110
+ | `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`. |
2111
+ | `compact` | `compact` | `undefined \| false \| true` | Render Recents as dense single-line rows (a leading dot + title, no count). |
2112
+ | `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`. |
1000
2113
 
1001
2114
  **Events** (non-bubbling `CustomEvent`s — listen directly on the element):
1002
2115
 
1003
2116
  | Event | `detail` type | Description |
1004
2117
  |---|---|---|
1005
2118
  | `kai-conversation-select` | `CustomEvent<{ id: string }>` | A conversation was selected in the sidebar. |
1006
- | `kai-message-action` | `CustomEvent<{ messageId: string; action: string }>` | An action button on a message was clicked. |
2119
+ | `kai-message-action` | `CustomEvent<{ messageId: string; action: string; state?: undefined \| "on" \| "off" }>` | An action button on a message was clicked. `state` is present only for the toggleable feedback votes: `'on'` when a like/dislike is set, `'off'` when re-tapped to clear. |
1007
2120
  | `kai-model-change` | `CustomEvent<{ modelId: string }>` | The header model switcher changed. |
1008
2121
  | `kai-new-chat` | `CustomEvent<Record<string, never>>` | The "New chat" button was clicked. |
1009
2122
  | `kai-search` | `CustomEvent<Record<string, never>>` | The Search button was clicked. |
1010
2123
  | `kai-sidebar-toggle` | `CustomEvent<{ collapsed: false \| true }>` | The sidebar was collapsed or expanded. |
1011
- | `kai-slash-select` | `CustomEvent<{ command: { id: string; label: string; description?: undefined \| string; category?: undefined \| string } }>` | A slash command was chosen from the palette. |
1012
2124
  | `kai-submit` | `CustomEvent<{ value: string; attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | User submitted a message. |
1013
2125
  | `kai-suggestion-click` | `CustomEvent<{ value: string }>` | A suggestion chip was clicked (only in `suggestion-mode="fill"`). |
1014
2126
  | `kai-value-change` | `CustomEvent<{ value: string }>` | Fired on every input change. |
1015
2127
  | `kai-voice` | `CustomEvent<Record<string, never>>` | The Mic / voice button was clicked. |
1016
2128
 
2129
+ **Slots** (project your own markup via `slot="name"` on a light-DOM child):
2130
+
2131
+ | Slot | Mode | Description |
2132
+ |---|---|---|
2133
+ | `sidebar-header` | inject | Top of the conversation rail (brand, a kai-tabs strip). |
2134
+ | `sidebar-footer` | inject | Bottom of the rail: an upgrade card, a Design trigger, a user-menu cluster. |
2135
+ | `main-header` | inject | Top of the main region (a top-placed banner or a corner action). |
2136
+ | `main` | replace | Replace the built-in chat thread with your own main view (a home or dashboard screen). Omit to keep the thread. |
2137
+
2138
+ **Styleable parts** (restyle from outside via `kai-workspace::part(name)`):
2139
+
2140
+ | Part | Description |
2141
+ |---|---|
2142
+ | `::part(sidebar)` | The conversation rail. Carries a subtle, theme-aware default background (bg-surface); override its background, border, or width from outside. `sidebar-min-width` sets its min px width and `collapse-below` auto-collapses it under a width. — `kai-workspace::part(sidebar) { background: var(--color-card); border-right: 1px solid var(--color-border) }` |
2143
+
1017
2144
  ---
1018
2145