@kitn.ai/ui 0.25.2 → 0.27.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 (603) hide show
  1. package/README.md +17 -6
  2. package/bin/mcp.js +16 -3
  3. package/bin/route.js +23 -0
  4. package/bin/route.test.js +58 -0
  5. package/dist/{elements/chunks/Icon-b5A8hlHs.js → Icon-BnFKqqtY.js} +1 -1
  6. package/dist/action-icons-CBskfBMz.js +1 -0
  7. package/dist/arrow-down-Br0qtpsz.js +1 -0
  8. package/dist/arrow-left-Dj7xgE12.js +1 -0
  9. package/dist/artifact-CvXEm1lS.js +1 -0
  10. package/dist/assets/dev-5XzXYR21.js +91 -0
  11. package/dist/attachments-C43ZegEq.js +1 -0
  12. package/dist/audio-visualizer-B-79y1Tj.js +1 -0
  13. package/dist/badge-BpUbq8c0.js +1 -0
  14. package/dist/bash-InADTalH.js +1 -6
  15. package/dist/bell-sn17Ip9v.js +1 -0
  16. package/dist/{elements/chunks/button-B9okqG73.js → button-BH6rN0U-.js} +1 -1
  17. package/dist/card-renderer-Cb1gQmth.js +1 -0
  18. package/dist/card-routing-DifQgS7n.js +1 -0
  19. package/dist/check-DPeeMUYx.js +1 -0
  20. package/dist/checkbox-Dc9Yov_B.js +1 -0
  21. package/dist/checkbox-group-B1kDLLYk.js +1 -0
  22. package/dist/chevron-down-CHSWk7ZT.js +1 -0
  23. package/dist/chevron-right-B7l_fMgx.js +1 -0
  24. package/dist/choice-card-BT3c1S8g.js +1 -0
  25. package/dist/circle-Bb0yOpqM.js +1 -0
  26. package/dist/circle-check-BGZpCiz7.js +1 -0
  27. package/dist/circle-x-wobWkoSx.js +1 -0
  28. package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
  29. package/dist/code-block-CUQ5mtv0.js +1 -0
  30. package/dist/collapsible-CPJbf6FM.js +1 -0
  31. package/dist/components/attachment-types.d.ts +53 -1
  32. package/dist/components/attachments.d.ts +31 -0
  33. package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
  34. package/dist/components/audio-visualizer/index.d.ts +15 -1
  35. package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
  36. package/dist/components/card-renderer.d.ts +13 -0
  37. package/dist/components/card.d.ts +4 -0
  38. package/dist/components/chat-thread.d.ts +49 -42
  39. package/dist/components/code-block.d.ts +22 -0
  40. package/dist/components/conversation-item.d.ts +66 -1
  41. package/dist/components/conversation-list.d.ts +77 -0
  42. package/dist/components/form-widgets.d.ts +67 -2
  43. package/dist/components/form.d.ts +89 -0
  44. package/dist/components/message.d.ts +29 -0
  45. package/dist/components/reasoning.d.ts +6 -0
  46. package/dist/components/scroll-button.d.ts +11 -0
  47. package/dist/components/thread.d.ts +5 -0
  48. package/dist/components/toast.d.ts +2 -1
  49. package/dist/components/tool-types.d.ts +9 -0
  50. package/dist/components/voice-input.d.ts +8 -0
  51. package/dist/components/voice-output.d.ts +12 -1
  52. package/dist/components/workspace-shell.d.ts +87 -0
  53. package/dist/composer-CjngLzal.js +106 -0
  54. package/dist/confirm-card-DCx_A4bU.js +1 -0
  55. package/dist/construct-cli.es.js +1169 -0
  56. package/dist/context-BY8V-Nlp.js +1 -0
  57. package/dist/controllable-D43BKmtX.js +1 -0
  58. package/dist/conversation-list-D4lLU_yS.js +1 -0
  59. package/dist/copy-B6DO4PqB.js +1 -0
  60. package/dist/core-AYMC6_lb.js +12 -5874
  61. package/dist/{create-tween-CUzOlaMS.js → create-tween-BPzaefTp.js} +1 -1
  62. package/dist/{create-tween-B5Y4Im8G.js → create-tween-CnMHviKB.js} +1 -1
  63. package/dist/{create-tween-4z1XYLkR.js → create-tween-D1aiGZZh.js} +1 -1
  64. package/dist/{elements/chunks/create-tween-CUrM6D_o.js → create-tween-DUSQUKpr.js} +2 -2
  65. package/dist/css-M7EaDHN_.js +1 -6
  66. package/dist/custom-elements.json +3468 -1927
  67. package/dist/default-input-CucQEOqH.js +1 -0
  68. package/dist/define-CGGX-7Vg.js +1 -0
  69. package/dist/define.d.ts +4 -0
  70. package/dist/define.js +510 -0
  71. package/dist/define.server.d.ts +5 -0
  72. package/dist/define.server.js +506 -0
  73. package/dist/diagnostics/hook.d.ts +81 -0
  74. package/dist/diagnostics/index.d.ts +7 -0
  75. package/dist/diagnostics/report-request.d.ts +61 -0
  76. package/dist/diagnostics.d.ts +5 -0
  77. package/dist/diagnostics.js +209 -0
  78. package/dist/disclosure-CbLtajUQ.js +1 -0
  79. package/dist/dropdown-BwhViJFd.js +1 -0
  80. package/dist/elements/agent-card.js +1 -1
  81. package/dist/elements/artifact.js +1 -1
  82. package/dist/elements/attachments.js +1 -1
  83. package/dist/elements/audio-visualizer.js +1 -1
  84. package/dist/elements/autoloader.js +1 -1
  85. package/dist/elements/avatar.js +1 -1
  86. package/dist/elements/badge.js +1 -1
  87. package/dist/elements/button.js +1 -1
  88. package/dist/elements/card.js +1 -1
  89. package/dist/elements/cards.js +1 -1
  90. package/dist/elements/chain-of-thought.js +1 -1
  91. package/dist/elements/chat-scope-picker.js +1 -1
  92. package/dist/elements/chat-workspace.js +1 -1
  93. package/dist/elements/chat.js +1 -1
  94. package/dist/elements/checkbox-group.d.ts +1 -0
  95. package/dist/elements/checkbox-group.js +1 -0
  96. package/dist/elements/checkbox.d.ts +1 -0
  97. package/dist/elements/checkbox.js +1 -0
  98. package/dist/elements/checkpoint.js +1 -1
  99. package/dist/elements/choice.js +1 -1
  100. package/dist/elements/coachmark.js +1 -1
  101. package/dist/elements/code-block.js +1 -1
  102. package/dist/elements/command.js +1 -1
  103. package/dist/elements/compare.js +1 -1
  104. package/dist/elements/composer.js +1 -1
  105. package/dist/elements/confirm-card.js +1 -1
  106. package/dist/elements/context-meter.js +1 -1
  107. package/dist/elements/conversation-item.d.ts +1 -0
  108. package/dist/elements/conversation-item.js +1 -0
  109. package/dist/elements/conversation-list.js +1 -1
  110. package/dist/elements/default-input.d.ts +3 -3
  111. package/dist/elements/define-entry.d.ts +12 -0
  112. package/dist/elements/define.d.ts +27 -0
  113. package/dist/elements/diagnostic-events.d.ts +74 -0
  114. package/dist/elements/dialog.js +1 -1
  115. package/dist/elements/dock.d.ts +1 -0
  116. package/dist/elements/dock.js +243 -0
  117. package/dist/elements/dropdown.d.ts +1 -0
  118. package/dist/elements/dropdown.js +1 -0
  119. package/dist/elements/editable-label.js +1 -1
  120. package/dist/elements/element-data-types.d.ts +37 -0
  121. package/dist/elements/element-diagnostics.d.ts +81 -0
  122. package/dist/elements/embed.js +1 -1
  123. package/dist/elements/empty.js +1 -1
  124. package/dist/elements/feedback-bar.js +1 -1
  125. package/dist/elements/file-tree.js +1 -1
  126. package/dist/elements/file-upload.js +1 -1
  127. package/dist/elements/form.js +1 -1
  128. package/dist/elements/hover-card.js +1 -1
  129. package/dist/elements/icon.js +1 -1
  130. package/dist/elements/image.js +1 -1
  131. package/dist/elements/input.js +1 -1
  132. package/dist/elements/kbd.js +1 -1
  133. package/dist/elements/link-preview.js +1 -1
  134. package/dist/elements/loader.js +1 -1
  135. package/dist/elements/markdown.js +1 -1
  136. package/dist/elements/menu.js +1 -1
  137. package/dist/elements/message-skills.js +1 -1
  138. package/dist/elements/message.js +1 -1
  139. package/dist/elements/model-switcher.js +1 -1
  140. package/dist/elements/nav.js +1 -1
  141. package/dist/elements/notice.js +1 -1
  142. package/dist/elements/pane-grid.d.ts +1 -0
  143. package/dist/elements/pane-grid.js +1 -0
  144. package/dist/elements/pane-group.js +1 -1
  145. package/dist/elements/pane.js +1 -1
  146. package/dist/elements/popover.js +1 -1
  147. package/dist/elements/progress-bar.js +1 -1
  148. package/dist/elements/prompt-dock.js +1 -1
  149. package/dist/elements/prompt-input.js +1 -1
  150. package/dist/elements/prompt-suggestions.js +1 -1
  151. package/dist/elements/radio-group.d.ts +1 -0
  152. package/dist/elements/radio-group.js +1 -0
  153. package/dist/elements/reasoning.js +1 -1
  154. package/dist/elements/register.d.ts +2 -0
  155. package/dist/elements/remote.js +1 -1
  156. package/dist/elements/resizable.js +1 -1
  157. package/dist/elements/response-stream.js +3 -3
  158. package/dist/elements/screen.js +1 -1
  159. package/dist/elements/scroll-area.js +1 -1
  160. package/dist/elements/scroll-button.js +1 -1
  161. package/dist/elements/search.js +1 -1
  162. package/dist/elements/segmented.js +1 -1
  163. package/dist/elements/select.d.ts +1 -0
  164. package/dist/elements/select.js +1 -0
  165. package/dist/elements/separator.js +1 -1
  166. package/dist/elements/setting-item.js +1 -1
  167. package/dist/elements/settings-group.js +1 -1
  168. package/dist/elements/skeleton.js +1 -1
  169. package/dist/elements/slider.d.ts +1 -0
  170. package/dist/elements/slider.js +1 -0
  171. package/dist/elements/slots.d.ts +36 -4
  172. package/dist/elements/source.js +1 -1
  173. package/dist/elements/status.js +1 -1
  174. package/dist/elements/switch.js +1 -1
  175. package/dist/elements/tabs.js +1 -1
  176. package/dist/elements/tasks.js +1 -1
  177. package/dist/elements/text-shimmer.js +1 -1
  178. package/dist/elements/thinking-bar.js +1 -1
  179. package/dist/elements/thread.js +1 -1
  180. package/dist/elements/toast.js +1 -1
  181. package/dist/elements/tool.js +1 -1
  182. package/dist/elements/tooltip.js +1 -1
  183. package/dist/elements/voice-input.js +1 -1
  184. package/dist/elements/voice-output.js +1 -1
  185. package/dist/elements.d.ts +589 -167
  186. package/dist/{elements/chunks/ellipsis-CVxqfsDZ.js → ellipsis-DFr4Vqr3.js} +1 -1
  187. package/dist/embed-5s_vU4J0.js +1 -0
  188. package/dist/engine-javascript-vq0WuIJl.js +14 -2516
  189. package/dist/{elements/chunks/external-link-DG2sRdQL.js → external-link-u9d85ORQ.js} +1 -1
  190. package/dist/file-text-CX7_x42o.js +1 -0
  191. package/dist/{elements/chunks/file-tree-yGWNK0Js.js → file-tree-D3kBjXI5.js} +1 -1
  192. package/dist/folder-V-YdIL8f.js +1 -0
  193. package/dist/form-BRNUMCN2.js +1 -0
  194. package/dist/github-dark-dimmed-DUshB20C.js +1 -4
  195. package/dist/github-light-JYsPkUQd.js +1 -4
  196. package/dist/hover-card-DaeiYrTA.js +1 -0
  197. package/dist/html-CPZ3oZQ7.js +1 -10
  198. package/dist/icon-BpixdZNq.js +1 -0
  199. package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
  200. package/dist/index.d.ts +3 -1
  201. package/dist/index.js +8473 -6470
  202. package/dist/index.server.js +6495 -4958
  203. package/dist/info-8mFsIjS3.js +1 -0
  204. package/dist/input-d1Yuu-hz.js +1 -0
  205. package/dist/javascript-C25yR2R2.js +1 -6
  206. package/dist/json-DxJze_jm.js +1 -6
  207. package/dist/kai-provider.es.js +40 -40
  208. package/dist/kai.es.js +1 -1
  209. package/dist/kbd-961zMPFB.js +1 -0
  210. package/dist/link-DlZZBGaR.js +1 -0
  211. package/dist/{elements/chunks/link-preview-Cf5KOP-i.js → link-preview-DIIbV2Sd.js} +1 -1
  212. package/dist/loader-YQszHFaW.js +1 -0
  213. package/dist/{elements/chunks/markdown-DbozyYqa.js → markdown-CVk1MPsj.js} +1 -1
  214. package/dist/mcp.es.js +3873 -544
  215. package/dist/message-CwKVEdZg.js +1 -0
  216. package/dist/message-DS8lzHc1.js +1 -0
  217. package/dist/message-circle-Xf8Q-vZp.js +1 -0
  218. package/dist/message-square-Cxnwgkns.js +1 -0
  219. package/dist/{elements/chunks/minimize-2-SegJ_oku.js → minimize-2-UEZmP2QT.js} +1 -1
  220. package/dist/{elements/chunks/model-switcher-D7JrFFHp.js → model-switcher-Kk1jEQ5t.js} +1 -1
  221. package/dist/{elements/chunks/overlay-CV7z2YuX.js → overlay-C1od6LOz.js} +1 -1
  222. package/dist/panel-right-ryET0yyn.js +1 -0
  223. package/dist/paperclip-BDNoZFJP.js +1 -0
  224. package/dist/play-OFIvYXUQ.js +1 -0
  225. package/dist/primitives/card-data-types.d.ts +49 -0
  226. package/dist/primitives/card-routing.d.ts +1 -33
  227. package/dist/primitives/card-validate.d.ts +2 -2
  228. package/dist/primitives/chat-config.d.ts +29 -2
  229. package/dist/primitives/field-mask.d.ts +96 -0
  230. package/dist/primitives/field-semantics.d.ts +36 -0
  231. package/dist/primitives/input-mask.d.ts +46 -0
  232. package/dist/primitives/url-scheme-policy.d.ts +34 -0
  233. package/dist/primitives/use-resize-observer.d.ts +23 -0
  234. package/dist/primitives/use-speech-recognition.d.ts +8 -0
  235. package/dist/primitives/use-text-stream.d.ts +5 -0
  236. package/dist/progress-bar-BvYX0aoy.js +1 -0
  237. package/dist/prompt-suggestion-BRFu2lcu.js +1 -0
  238. package/dist/radio-DcL23cNJ.js +1 -0
  239. package/dist/react/index.d.ts +306 -283
  240. package/dist/react.js +309 -247
  241. package/dist/reasoning-Cackq4Rh.js +1 -0
  242. package/dist/register-impl-C7rENvPK.js +424 -0
  243. package/dist/{elements/chunks/resizable-Dpzjlvp4.js → resizable-Cp9HUYih.js} +1 -1
  244. package/dist/rotate-ccw-0DLRDHH5.js +1 -0
  245. package/dist/rotate-cw-DOB9IGcU.js +1 -0
  246. package/dist/schemas/form.schema.json +21 -1
  247. package/dist/schemas/index.d.ts +1 -1
  248. package/dist/schemas/tool-defs.d.ts +45 -0
  249. package/dist/schemas.js +398 -305
  250. package/dist/scroll-area-emVmWP4K.js +1 -0
  251. package/dist/scroll-button-CeP_gv1x.js +1 -0
  252. package/dist/select-CWmu00tS.js +1 -0
  253. package/dist/{elements/chunks/separator-DH2r_ZSZ.js → separator-CE6vly6z.js} +1 -1
  254. package/dist/{elements/chunks/settings-BFMOd6yB.js → settings-CE-78SvZ.js} +1 -1
  255. package/dist/{elements/chunks/settings-group-B3VZKIcz.js → settings-group-ZFWNxFQR.js} +1 -1
  256. package/dist/{elements/chunks/skeleton-N8BuWYmf.js → skeleton-BebdP4mf.js} +1 -1
  257. package/dist/slider-BAKRYUlz.js +1 -0
  258. package/dist/slots-DaNjX0rc.js +1 -0
  259. package/dist/{solid-BMuDmFo_.js → solid-BE5ui6sy.js} +10672 -8116
  260. package/dist/{solid-CGYNdLRR.js → solid-DFhsce3O.js} +8137 -6056
  261. package/dist/solid.d.ts +15 -0
  262. package/dist/solid.js +248 -235
  263. package/dist/solid.server.js +248 -235
  264. package/dist/source-B0FlguSx.js +1 -0
  265. package/dist/star-CBMZvLuN.js +1 -0
  266. package/dist/state/index.d.ts +5 -1
  267. package/dist/state/mock.d.ts +32 -2
  268. package/dist/state/persistence.d.ts +51 -0
  269. package/dist/state/stream.d.ts +50 -0
  270. package/dist/state/threads.d.ts +71 -0
  271. package/dist/state.js +297 -172
  272. package/dist/store-7eoadydQ.js +1 -0
  273. package/dist/svelte--5p79yCD.js +1 -15
  274. package/dist/switch-CViARZCy.js +1 -0
  275. package/dist/tasks-card-MjCcOhR_.js +1 -0
  276. package/dist/text-shimmer-DKlwRF2G.js +1 -0
  277. package/dist/textarea-3ay2Me2-.js +1 -0
  278. package/dist/theme.tokens.css +82 -13
  279. package/dist/{elements/chunks/thumbs-up-DQe0hvkZ.js → thumbs-up-DQcu2HMO.js} +1 -1
  280. package/dist/toast-store-C6yKOhbk.js +1 -0
  281. package/dist/{elements/chunks/tool-C1YtxwPu.js → tool-BCYNlfKy.js} +1 -1
  282. package/dist/{elements/chunks/tooltip-BqwnFWhn.js → tooltip-05zI4hjs.js} +1 -1
  283. package/dist/trash-2-BR1RxGky.js +1 -0
  284. package/dist/{elements/chunks/triangle-alert-B6or7F5u.js → triangle-alert-DHohDOCy.js} +1 -1
  285. package/dist/tsx-B8rCNbgL.js +1 -6
  286. package/dist/types.d.ts +4 -2
  287. package/dist/typescript-RycA9KXf.js +1 -6
  288. package/dist/ui/checkbox-group.d.ts +83 -0
  289. package/dist/ui/checkbox.d.ts +40 -0
  290. package/dist/ui/dock.d.ts +131 -0
  291. package/dist/ui/hover-card.d.ts +44 -0
  292. package/dist/ui/icon.d.ts +3 -0
  293. package/dist/ui/input.d.ts +48 -2
  294. package/dist/ui/radio.d.ts +85 -0
  295. package/dist/ui/select.d.ts +71 -0
  296. package/dist/ui/slider.d.ts +65 -0
  297. package/dist/ui/switch.d.ts +9 -2
  298. package/dist/upload-BbGW6A_u.js +1 -0
  299. package/dist/url-scheme-policy-DHpoTJwB.js +1 -0
  300. package/dist/use-card-resolution-CJW4grFa.js +1 -0
  301. package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-BUOVgPOB.js} +27 -27
  302. package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CUsUJvFI.js} +28 -28
  303. package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CW2i4bfr.js} +2 -2
  304. package/dist/{elements/chunks/variant-aurora-QXTZPyST.js → variant-aurora-xiMPty8f.js} +2 -2
  305. package/dist/variant-custom-BQtOnmeO.js +1 -0
  306. package/dist/{variant-custom-BhS9wIpU.js → variant-custom-CAm9L-E5.js} +65 -65
  307. package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-CAnzzppW.js} +40 -40
  308. package/dist/variant-custom-D91HjAKz.js +1 -0
  309. package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-Ca1Ls4_y.js} +21 -21
  310. package/dist/{variant-wave-CbVsmgwT.js → variant-wave-CxX39DKd.js} +25 -25
  311. package/dist/{variant-wave-BwzrHD1s.js → variant-wave-DYyzIIZv.js} +2 -2
  312. package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-uY5crU_K.js} +2 -2
  313. package/dist/vue-BmIZj4XD.js +1 -33
  314. package/dist/wire/chunk.d.ts +47 -0
  315. package/dist/wire/diagnostics.d.ts +575 -0
  316. package/dist/wire/encode-probe.d.ts +70 -0
  317. package/dist/wire/encode.d.ts +24 -0
  318. package/dist/wire/index.d.ts +2 -0
  319. package/dist/wire/sse.d.ts +10 -2
  320. package/dist/wire.js +917 -480
  321. package/dist/x-C5m1JFgi.js +1 -0
  322. package/frameworks/react/index.tsx +325 -108
  323. package/llms-full.txt +1354 -82
  324. package/llms.txt +6 -3
  325. package/package.json +39 -6
  326. package/src/agent-tooling/archetypes.ts +41 -9
  327. package/src/agent-tooling/catalog/README.md +676 -0
  328. package/src/agent-tooling/catalog/catalog-types.ts +231 -0
  329. package/src/agent-tooling/catalog/fabrications.ts +96 -0
  330. package/src/agent-tooling/catalog/invariants.ts +284 -0
  331. package/src/agent-tooling/catalog/labs-titles.ts +114 -0
  332. package/src/agent-tooling/catalog/scenarios.ts +87 -0
  333. package/src/agent-tooling/catalog/surfaces.ts +347 -0
  334. package/src/agent-tooling/construct/cli-entry.ts +8 -0
  335. package/src/agent-tooling/construct/cli.ts +131 -0
  336. package/src/agent-tooling/construct/codegen.ts +1353 -0
  337. package/src/agent-tooling/construct/construct.v1.schema.json +250 -0
  338. package/src/agent-tooling/construct/dev.ts +148 -0
  339. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +7 -0
  340. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +44 -0
  341. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +23 -0
  342. package/src/agent-tooling/construct/schema.ts +371 -0
  343. package/src/agent-tooling/integrations/anthropic.ts +7 -1
  344. package/src/agent-tooling/integrations/cloudflare.ts +43 -2
  345. package/src/agent-tooling/integrations/langgraph.ts +7 -1
  346. package/src/agent-tooling/integrations/mastra.ts +7 -1
  347. package/src/agent-tooling/integrations/mock.ts +92 -17
  348. package/src/agent-tooling/integrations/ollama.ts +7 -1
  349. package/src/agent-tooling/integrations/openai.ts +7 -1
  350. package/src/agent-tooling/integrations/openrouter.ts +7 -1
  351. package/src/agent-tooling/integrations/pi.ts +56 -1
  352. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
  353. package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
  354. package/src/agent-tooling/mcp/manifest.ts +100 -1
  355. package/src/agent-tooling/mcp/server.ts +63 -6
  356. package/src/agent-tooling/mcp/tools/construct.ts +130 -0
  357. package/src/agent-tooling/mcp/tools/debug.ts +126 -4
  358. package/src/agent-tooling/mcp/tools/reference.ts +542 -5
  359. package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
  360. package/src/agent-tooling/mcp/tools/theme.ts +146 -30
  361. package/src/agent-tooling/mcp/validate-args.ts +141 -0
  362. package/src/agent-tooling/recipes/composed-thread.ts +714 -0
  363. package/src/agent-tooling/recipes/index.ts +22 -0
  364. package/src/agent-tooling/recipes/types.ts +32 -0
  365. package/src/agent-tooling/registry.ts +24 -7
  366. package/src/agent-tooling/route-emit.ts +26 -2
  367. package/src/components/attachment-types.ts +54 -1
  368. package/src/components/attachments.tsx +169 -22
  369. package/src/components/audio-visualizer/fit-scale.ts +112 -0
  370. package/src/components/audio-visualizer/index.tsx +80 -3
  371. package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
  372. package/src/components/audio-visualizer/variant-bar.tsx +38 -6
  373. package/src/components/audio-visualizer/variant-custom.tsx +8 -2
  374. package/src/components/audio-visualizer/variant-grid.tsx +16 -11
  375. package/src/components/audio-visualizer/variant-radial.tsx +15 -10
  376. package/src/components/audio-visualizer/variant-wave.tsx +7 -2
  377. package/src/components/card-renderer.tsx +39 -8
  378. package/src/components/card.tsx +10 -2
  379. package/src/components/chat-container.tsx +4 -1
  380. package/src/components/chat-scope-picker.tsx +2 -2
  381. package/src/components/chat-thread.tsx +105 -13
  382. package/src/components/choice-card.tsx +102 -37
  383. package/src/components/coachmark.tsx +1 -1
  384. package/src/components/code-block.tsx +81 -5
  385. package/src/components/composer.tsx +89 -8
  386. package/src/components/confirm-card.tsx +20 -4
  387. package/src/components/conversation-item.tsx +138 -4
  388. package/src/components/conversation-list.tsx +230 -15
  389. package/src/components/embed.tsx +4 -0
  390. package/src/components/empty.tsx +14 -3
  391. package/src/components/file-tree.tsx +5 -1
  392. package/src/components/form-widgets.tsx +257 -195
  393. package/src/components/form.tsx +336 -21
  394. package/src/components/loader.tsx +12 -5
  395. package/src/components/message-skills.tsx +1 -1
  396. package/src/components/message.tsx +142 -12
  397. package/src/components/prompt-input.tsx +7 -1
  398. package/src/components/prompt-suggestion.tsx +4 -1
  399. package/src/components/reasoning.tsx +30 -3
  400. package/src/components/response-compare.tsx +2 -2
  401. package/src/components/response-stream.tsx +13 -7
  402. package/src/components/screen.tsx +5 -0
  403. package/src/components/scroll-button.tsx +63 -4
  404. package/src/components/source.tsx +6 -1
  405. package/src/components/tasks-card.tsx +58 -16
  406. package/src/components/thread.tsx +10 -1
  407. package/src/components/toast.tsx +11 -5
  408. package/src/components/tool-types.ts +9 -0
  409. package/src/components/voice-input.tsx +35 -3
  410. package/src/components/voice-output.tsx +46 -6
  411. package/src/components/workspace-shell.tsx +285 -0
  412. package/src/diagnostics/hook.ts +352 -0
  413. package/src/diagnostics/index.ts +82 -0
  414. package/src/diagnostics/report-request.ts +296 -0
  415. package/src/elements/attachments.tsx +9 -8
  416. package/src/elements/audio-visualizer.tsx +48 -4
  417. package/src/elements/chat-workspace.tsx +159 -287
  418. package/src/elements/chat.tsx +31 -10
  419. package/src/elements/checkbox-group.tsx +164 -0
  420. package/src/elements/checkbox.tsx +134 -0
  421. package/src/elements/code-block.tsx +19 -1
  422. package/src/elements/command.tsx +4 -1
  423. package/src/elements/compiled.css +1 -1
  424. package/src/elements/composer.tsx +12 -1
  425. package/src/elements/conversation-item.tsx +120 -0
  426. package/src/elements/conversation-list.tsx +60 -11
  427. package/src/elements/default-input.tsx +5 -5
  428. package/src/elements/define-entry.ts +12 -0
  429. package/src/elements/define.tsx +168 -8
  430. package/src/elements/diagnostic-events.ts +114 -0
  431. package/src/elements/dialog.tsx +47 -0
  432. package/src/elements/disclosure.ts +34 -12
  433. package/src/elements/dock.tsx +215 -0
  434. package/src/elements/dropdown.tsx +123 -0
  435. package/src/elements/element-data-types.ts +40 -0
  436. package/src/elements/element-diagnostics.ts +392 -0
  437. package/src/elements/element-manifest.json +36 -0
  438. package/src/elements/element-meta.json +1483 -560
  439. package/src/elements/element-nonscalar.json +185 -0
  440. package/src/elements/element-types.d.ts +589 -167
  441. package/src/elements/feedback-bar.tsx +5 -0
  442. package/src/elements/icon-names.json +29 -0
  443. package/src/elements/input.tsx +312 -23
  444. package/src/elements/menu.tsx +13 -0
  445. package/src/elements/message.tsx +2 -0
  446. package/src/elements/pane-grid.tsx +111 -0
  447. package/src/elements/prompt-dock.tsx +2 -1
  448. package/src/elements/prompt-input.tsx +16 -10
  449. package/src/elements/radio-group.tsx +129 -0
  450. package/src/elements/register-impl.ts +52 -0
  451. package/src/elements/register.ts +28 -0
  452. package/src/elements/resizable.tsx +52 -25
  453. package/src/elements/scroll-button.tsx +59 -6
  454. package/src/elements/select.tsx +165 -0
  455. package/src/elements/slider.tsx +171 -0
  456. package/src/elements/slots.ts +108 -13
  457. package/src/elements/styles.css +119 -9
  458. package/src/elements/thinking-bar.tsx +16 -7
  459. package/src/elements/thread.tsx +2 -0
  460. package/src/elements/toast.tsx +17 -1
  461. package/src/elements/tool.tsx +26 -5
  462. package/src/elements/voice-input.tsx +11 -2
  463. package/src/elements/voice-output.tsx +16 -3
  464. package/src/index.ts +4 -1
  465. package/src/primitives/card-data-types.ts +49 -0
  466. package/src/primitives/card-host.tsx +4 -2
  467. package/src/primitives/card-routing.ts +7 -56
  468. package/src/primitives/card-schemas/form.schema.json +21 -1
  469. package/src/primitives/card-validate.ts +10 -2
  470. package/src/primitives/chat-config.tsx +33 -6
  471. package/src/primitives/field-mask.ts +256 -0
  472. package/src/primitives/field-semantics.ts +115 -0
  473. package/src/primitives/input-mask.ts +853 -0
  474. package/src/primitives/toast-store.ts +66 -4
  475. package/src/primitives/url-scheme-policy.ts +70 -0
  476. package/src/primitives/use-resize-observer.ts +33 -0
  477. package/src/primitives/use-speech-recognition.ts +12 -1
  478. package/src/primitives/use-stick-to-bottom.ts +34 -2
  479. package/src/primitives/use-text-stream.ts +33 -2
  480. package/src/remote/provider-runtime.ts +6 -3
  481. package/src/schemas/index.ts +1 -0
  482. package/src/schemas/tool-defs.ts +307 -6
  483. package/src/solid.ts +15 -0
  484. package/src/state/index.ts +13 -1
  485. package/src/state/mock.ts +92 -6
  486. package/src/state/persistence.ts +180 -0
  487. package/src/state/stream.ts +71 -4
  488. package/src/state/threads.ts +142 -0
  489. package/src/types.ts +4 -2
  490. package/src/ui/agent-card.tsx +2 -2
  491. package/src/ui/avatar.tsx +1 -1
  492. package/src/ui/checkbox-group.tsx +153 -0
  493. package/src/ui/checkbox.tsx +63 -0
  494. package/src/ui/dialog.tsx +6 -0
  495. package/src/ui/dock.tsx +584 -0
  496. package/src/ui/dropdown.tsx +67 -23
  497. package/src/ui/hover-card.tsx +156 -6
  498. package/src/ui/icon.tsx +53 -0
  499. package/src/ui/input.tsx +275 -16
  500. package/src/ui/kbd.tsx +1 -1
  501. package/src/ui/nav.tsx +1 -1
  502. package/src/ui/pane-group.tsx +2 -2
  503. package/src/ui/radio.tsx +150 -0
  504. package/src/ui/resizable.tsx +11 -0
  505. package/src/ui/select.tsx +168 -0
  506. package/src/ui/slider.tsx +178 -0
  507. package/src/ui/switch.tsx +49 -18
  508. package/src/utils/cn.ts +11 -5
  509. package/src/wire/chunk.ts +47 -0
  510. package/src/wire/consume.ts +185 -26
  511. package/src/wire/diagnostics.ts +727 -0
  512. package/src/wire/encode-probe.ts +214 -0
  513. package/src/wire/encode.ts +267 -14
  514. package/src/wire/formats/anthropic.ts +8 -1
  515. package/src/wire/formats/openai.ts +5 -0
  516. package/src/wire/index.ts +22 -0
  517. package/src/wire/read.ts +250 -6
  518. package/src/wire/sse.ts +16 -3
  519. package/theme.css +84 -13
  520. package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
  521. package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
  522. package/dist/elements/chunks/artifact-BSuPYFHY.js +0 -1
  523. package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
  524. package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
  525. package/dist/elements/chunks/badge-TIMiJFKc.js +0 -1
  526. package/dist/elements/chunks/bash-InADTalH.js +0 -1
  527. package/dist/elements/chunks/card-renderer-CSoTL_e1.js +0 -1
  528. package/dist/elements/chunks/card-routing-BK09BdmL.js +0 -1
  529. package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
  530. package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
  531. package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
  532. package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
  533. package/dist/elements/chunks/choice-card-CGMc3kek.js +0 -1
  534. package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
  535. package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
  536. package/dist/elements/chunks/circle-x-mhH7pOK2.js +0 -1
  537. package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
  538. package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
  539. package/dist/elements/chunks/composer-CZZmtYOT.js +0 -69
  540. package/dist/elements/chunks/confirm-card-BWjf2-xo.js +0 -1
  541. package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
  542. package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
  543. package/dist/elements/chunks/core-AYMC6_lb.js +0 -12
  544. package/dist/elements/chunks/css-M7EaDHN_.js +0 -1
  545. package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
  546. package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
  547. package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
  548. package/dist/elements/chunks/download-DnCyWFJE.js +0 -1
  549. package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
  550. package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
  551. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +0 -141
  552. package/dist/elements/chunks/file-text-D0J9F6M7.js +0 -1
  553. package/dist/elements/chunks/folder-NV1pi_ud.js +0 -1
  554. package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
  555. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +0 -1
  556. package/dist/elements/chunks/github-light-JYsPkUQd.js +0 -1
  557. package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
  558. package/dist/elements/chunks/html-CPZ3oZQ7.js +0 -1
  559. package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
  560. package/dist/elements/chunks/info-C8r5Q6cc.js +0 -1
  561. package/dist/elements/chunks/input-DrseEjhp.js +0 -1
  562. package/dist/elements/chunks/javascript-C25yR2R2.js +0 -1
  563. package/dist/elements/chunks/json-DxJze_jm.js +0 -1
  564. package/dist/elements/chunks/kbd-OGJDUT-O.js +0 -1
  565. package/dist/elements/chunks/link-BdsfLB7b.js +0 -1
  566. package/dist/elements/chunks/loader-B5QzK_8_.js +0 -1
  567. package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
  568. package/dist/elements/chunks/message-DtXKPqiR.js +0 -1
  569. package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
  570. package/dist/elements/chunks/paperclip-Bg0ITU3b.js +0 -1
  571. package/dist/elements/chunks/progress-bar-D1_rzt6N.js +0 -1
  572. package/dist/elements/chunks/prompt-suggestion-D5Sm8xF1.js +0 -1
  573. package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
  574. package/dist/elements/chunks/rotate-cw-DzVDJOA1.js +0 -1
  575. package/dist/elements/chunks/scroll-area-93A1bbeG.js +0 -1
  576. package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
  577. package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
  578. package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
  579. package/dist/elements/chunks/source-w9V0NnBn.js +0 -1
  580. package/dist/elements/chunks/star-DHvACxtm.js +0 -1
  581. package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
  582. package/dist/elements/chunks/svelte--5p79yCD.js +0 -1
  583. package/dist/elements/chunks/tasks-card-lPxoodlA.js +0 -1
  584. package/dist/elements/chunks/text-shimmer-BY_rI3s5.js +0 -1
  585. package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
  586. package/dist/elements/chunks/toast-store-VUZZBhzH.js +0 -1
  587. package/dist/elements/chunks/tsx-B8rCNbgL.js +0 -1
  588. package/dist/elements/chunks/typescript-RycA9KXf.js +0 -1
  589. package/dist/elements/chunks/use-card-resolution-DJg32jGH.js +0 -1
  590. package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
  591. package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
  592. package/dist/elements/chunks/vue-BmIZj4XD.js +0 -1
  593. package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
  594. package/dist/llms/llms-full.txt +0 -2834
  595. package/dist/llms/llms.txt +0 -156
  596. package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
  597. package/dist/register-impl-DGmGzrEM.js +0 -145
  598. package/dist/ui/stat.d.ts +0 -16
  599. package/dist/variant-custom-MkTNTycd.js +0 -1
  600. package/src/primitives/card-validate-generator.testlib.ts +0 -38
  601. package/src/ui/stat.tsx +0 -41
  602. /package/dist/{elements/chunks/card-tags-D8lZ-C_U.js → card-tags-D8lZ-C_U.js} +0 -0
  603. /package/dist/{elements/chunks/link-preview-DNILK391.js → link-preview-DNILK391.js} +0 -0
@@ -2,7 +2,7 @@
2
2
  * Composition slots for `kai-*` elements. A slot is a named region a consumer
3
3
  * fills with their own markup. This registry is the SINGLE SOURCE OF TRUTH:
4
4
  * the facade derives its detection from it (and, later, docs are generated
5
- * from it). See docs/superpowers/specs/2026-06-23-kai-chat-composition-seams-design.md.
5
+ * from it).
6
6
  */
7
7
 
8
8
  /** `inject` = additive (the built-in region still renders, your markup is added
@@ -62,6 +62,39 @@ export const CONVERSATIONS_PARTS: PartDef[] = [
62
62
  doc: '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.',
63
63
  recipe: 'kai-conversations::part(trailing) { color: var(--color-primary); font-variant-numeric: tabular-nums }',
64
64
  },
65
+ {
66
+ name: 'items',
67
+ doc: 'The item-mode listbox region wrapping your slotted `<kai-conversation-item>` children.',
68
+ recipe: 'kai-conversations::part(items) { gap: 2px }',
69
+ },
70
+ ];
71
+
72
+ /** Slots of `<kai-conversation-item>` — one composed row of a consumer-owned
73
+ * conversation loop (the default slot is the title). */
74
+ export const CONVERSATION_ITEM_SLOTS: SlotDef[] = [
75
+ { name: 'leading', mode: 'inject', part: true, doc: 'Leading region before the title (an icon or avatar).' },
76
+ { name: 'meta', mode: 'inject', part: true, doc: 'Meta region under the title (a timestamp or status line).' },
77
+ { name: 'menu', mode: 'inject', part: true, doc: 'Your own row menu (a popover trigger). Never selects the row.' },
78
+ ];
79
+
80
+ /** Styleable `::part`s of `<kai-conversation-item>`. (`leading`/`meta`/`menu`
81
+ * are covered by their slot defs' `part: true` flags.) */
82
+ export const CONVERSATION_ITEM_PARTS: PartDef[] = [
83
+ {
84
+ name: 'body',
85
+ doc: 'The activation surface inside the row (button role; carries aria-current and the roving tabindex). The focus ring paints here.',
86
+ recipe: 'kai-conversation-item::part(body) { outline-offset: 2px }',
87
+ },
88
+ {
89
+ name: 'row',
90
+ doc: 'The whole row surface. Carries `data-active` while selected.',
91
+ recipe: 'kai-conversation-item::part(row) { border-radius: 0.5rem }',
92
+ },
93
+ {
94
+ name: 'title',
95
+ doc: 'The title line (the default slot renders inside it).',
96
+ recipe: 'kai-conversation-item::part(title) { font-weight: 600 }',
97
+ },
65
98
  ];
66
99
 
67
100
  /** Slots of `<kai-message>` — per-message composition seams. `before-body` and
@@ -183,6 +216,33 @@ export const SKELETON_PARTS: PartDef[] = [
183
216
  },
184
217
  ];
185
218
 
219
+ /**
220
+ * The parts of ONE attachment item, declared by `components/attachments.tsx`
221
+ * and therefore surfaced by every element that renders an attachment — the
222
+ * standalone `<kai-attachments>` AND the thread inside `<kai-message>` /
223
+ * `<kai-chat>`. Shared rather than duplicated so the two can never document
224
+ * different names for the same node.
225
+ *
226
+ * These are a styling seam and a TESTING seam, and the second is why they exist
227
+ * at all. `examples/internal/openrouter-spike` proves "a user can see which file
228
+ * was attached" by locating these; before they existed it pinned the chip by
229
+ * `span.truncate` and broke the day the thread moved to the grid tile, while the
230
+ * filename was still plainly on screen. A published part is what lets an
231
+ * assertion name the thing rather than its current markup.
232
+ */
233
+ export const ATTACHMENT_ITEM_PARTS: PartDef[] = [
234
+ {
235
+ name: 'attachment',
236
+ doc: 'One attachment item: the chip, row or tile, whichever variant is rendering. Restyle its background, radius or border from outside without caring which layout it is.',
237
+ recipe: 'kai-chat::part(attachment) { border-radius: 0.25rem }',
238
+ },
239
+ {
240
+ name: 'attachment-name',
241
+ doc: 'The attachment’s filename label. Present in every variant that shows one (a grid tile omits it for an image, which is its own label). Retune its type or hide it entirely.',
242
+ recipe: 'kai-chat::part(attachment-name) { font-size: 0.75rem }',
243
+ },
244
+ ];
245
+
186
246
  /** Styleable `::part`s of `<kai-message>`. (The `avatar` part is contributed by
187
247
  * the `avatar` slot's `part: true` flag, so it is not repeated here.) */
188
248
  export const MESSAGE_PARTS: PartDef[] = [
@@ -211,6 +271,10 @@ export const MESSAGE_PARTS: PartDef[] = [
211
271
  doc: 'The citation row rendered from the message’s `source` parts: a wrapped row of chips below the bubble, never inside it. Restyle its spacing or hide it entirely from outside.',
212
272
  recipe: 'kai-message::part(citations) { gap: 0.5rem }',
213
273
  },
274
+ // A message renders its `file` parts through the same Attachment chrome
275
+ // `<kai-attachments>` uses, so it exposes the same handles. Shared, not
276
+ // restated — see ATTACHMENT_ITEM_PARTS.
277
+ ...ATTACHMENT_ITEM_PARTS,
214
278
  ];
215
279
 
216
280
  /** Styleable `::part`s of `<kai-attachments>`. */
@@ -220,6 +284,7 @@ export const ATTACHMENTS_PARTS: PartDef[] = [
220
284
  doc: '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.',
221
285
  recipe: 'kai-attachments::part(preview) { max-width: 32rem; max-height: 24rem }',
222
286
  },
287
+ ...ATTACHMENT_ITEM_PARTS,
223
288
  ];
224
289
 
225
290
  /** Styleable `::part`s of `<kai-status>`. */
@@ -323,21 +388,21 @@ export const CARD_PARTS: PartDef[] = [
323
388
  },
324
389
  ];
325
390
 
326
- /** Injection slots of `<kai-workspace>` (the carrier regions the consumer drops
327
- * content into: brand/tabs, the upgrade/Design/user-menu cluster, a top banner). */
391
+ /** Slots of `<kai-workspace>` (the five shell regions; main also takes the default slot). */
328
392
  export const WORKSPACE_SLOTS: SlotDef[] = [
329
- { name: 'sidebar-header', mode: 'inject', doc: 'Top of the conversation rail (brand, a kai-tabs strip).' },
330
- { name: 'sidebar-footer', mode: 'inject', doc: 'Bottom of the rail: an upgrade card, a Design trigger, a user-menu cluster.' },
331
- { name: 'main-header', mode: 'inject', doc: 'Top of the main region (a top-placed banner or a corner action).' },
332
- { name: 'main', mode: 'replace', doc: 'Replace the built-in chat thread with your own main view (a home or dashboard screen). Omit to keep the thread.' },
393
+ { name: 'header', mode: 'inject', part: true, doc: 'The top band across the full shell width (app bar, tabs, breadcrumbs).' },
394
+ { name: 'start', mode: 'inject', part: true, doc: 'The inline-start aside column (a conversation rail, a nav, a file tree). Resizable and collapsible.' },
395
+ { name: 'main', mode: 'inject', part: true, doc: 'The main region. Unnamed children project here too, via the default slot.' },
396
+ { name: 'end', mode: 'inject', part: true, doc: 'The inline-end aside column (inspector, notes, preview). Resizable and collapsible.' },
397
+ { name: 'footer', mode: 'inject', part: true, doc: 'The bottom band across the full shell width (status bar, disclaimers).' },
333
398
  ];
334
399
 
335
- /** Styleable `::part`s of `<kai-workspace>`. */
400
+ /** Styleable `::part`s of `<kai-workspace>` (beyond the slot-backed header/start/main/end/footer parts). */
336
401
  export const WORKSPACE_PARTS: PartDef[] = [
337
402
  {
338
- name: 'sidebar',
339
- doc: '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.',
340
- recipe: 'kai-workspace::part(sidebar) { background: var(--color-card); border-right: 1px solid var(--color-border) }',
403
+ name: 'aside',
404
+ doc: 'Both aside columns match this part (each also matches its own start/end part). Restyle the shared aside surface or border from outside; the --kai-workspace-start-* and --kai-workspace-end-* custom properties set the widths.',
405
+ recipe: 'kai-workspace::part(aside) { background: var(--color-card) }',
341
406
  },
342
407
  ];
343
408
 
@@ -474,6 +539,18 @@ export const PROMPT_DOCK_PARTS: PartDef[] = [
474
539
  },
475
540
  ];
476
541
 
542
+ /** Slots + styleable `::part`s of `<kai-dock>` (the floating corner launcher — not
543
+ * `<kai-prompt-dock>`, which is an in-flow tray around a prompt input). */
544
+ export const DOCK_SLOTS: SlotDef[] = [
545
+ { name: 'panel', mode: 'replace', part: true, doc: 'The panel body. ANY element: a `<kai-chat>`, a form, your own component. The dock never reads or types it, and the default slot is the same region.' },
546
+ { name: 'launcher', mode: 'inject', doc: 'Content inside the built-in button while CLOSED; defaults to a chat glyph. Text works as well as an icon: the button keeps its height and grows sideways into a pill, so a label like "Support" is not clipped. The BUTTON is never slotted away, because it owns aria-expanded, aria-controls, the toggle wiring and the focus return.' },
547
+ { name: 'launcher-open', mode: 'inject', doc: 'Content inside the button while OPEN; defaults to a ✕. Fill only `launcher` and that glyph stays while open rather than morphing into a built-in that clashes with it.' },
548
+ ];
549
+ export const DOCK_PARTS: PartDef[] = [
550
+ { name: 'launcher', doc: 'The launcher button pinned to the corner: a disc by default, a pill once you slot a text label. Restyle its surface or shadow; --kai-dock-launcher-size sets its height and its minimum width.', recipe: 'kai-dock::part(launcher) { background: var(--color-info) }' },
551
+ { name: 'badge', doc: 'The unread dot on the launcher, rendered only while closed and only when `unread` is set. Restyle its color or size.', recipe: 'kai-dock::part(badge) { background: var(--color-success) }' },
552
+ ];
553
+
477
554
  /** Styleable `::part`s of `<kai-segmented>`. */
478
555
  export const SEGMENTED_PARTS: PartDef[] = [
479
556
  { name: 'track', doc: 'The segmented track (the pill container holding the segments). Restyle its background, radius, or padding.', recipe: 'kai-segmented::part(track) { border-radius: 9999px }' },
@@ -512,6 +589,13 @@ export const AGENT_CARD_PARTS: PartDef[] = [
512
589
  { name: 'menu', doc: 'The trailing overflow ("...") menu button.', recipe: 'kai-agent-card::part(menu) { opacity: 1 }' },
513
590
  ];
514
591
 
592
+ /** Styleable `::part`s of `<kai-code-block>` (one syntax-highlighted code block).
593
+ * The code region itself is not a part — it is rebuilt when the highlight lands,
594
+ * and its look is owned by the Shiki theme (`code-theme`). */
595
+ export const CODE_BLOCK_PARTS: PartDef[] = [
596
+ { name: 'copy', doc: 'The copy-to-clipboard button in the header row. Hide it with `copy="false"` rather than CSS.', recipe: 'kai-code-block::part(copy) { color: var(--color-primary) }' },
597
+ ];
598
+
515
599
  /** Slots + styleable `::part`s of `<kai-dialog>` (the centered modal). */
516
600
  export const DIALOG_SLOTS: SlotDef[] = [
517
601
  { name: 'header', mode: 'inject', part: true, doc: 'Optional title region at the top of the panel.' },
@@ -594,6 +678,13 @@ export const POPOVER_SLOTS: SlotDef[] = [
594
678
  { name: 'trigger', mode: 'replace', doc: 'The control that opens the popover (a button, an avatar, …). The panel anchors to it.' },
595
679
  ];
596
680
 
681
+ /** Slots of `<kai-dropdown>`. The menu body is the default slot; `trigger` is the
682
+ * VISUAL content of the built-in trigger button (never your own `<button>`/`<a>`,
683
+ * which would nest interactive elements — the `kai-menu` rule). */
684
+ export const DROPDOWN_SLOTS: SlotDef[] = [
685
+ { name: 'trigger', mode: 'replace', doc: 'Visual content of the trigger button (an icon, text, an `<svg>`). Replaces the built-in trigger* content; name it with `label`.' },
686
+ ];
687
+
597
688
  /** Styleable `::part`s of `<kai-audio-visualizer>`. Every DOM variant (`bar`,
598
689
  * `grid`, `radial`) shares the `bar`/`cell` markup pattern; the shader variants
599
690
  * (`wave`, `aurora`) share the `canvas` part. Lit items ALSO carry the
@@ -631,7 +722,8 @@ export const AUDIO_VISUALIZER_PARTS: PartDef[] = [
631
722
  export const ELEMENT_COMPOSITION: Record<string, ElementComposition> = {
632
723
  'kai-chat': { slots: CHAT_SLOTS, parts: CHAT_PARTS },
633
724
  'kai-command': { parts: COMMAND_PARTS },
634
- 'kai-conversations': { slots: CONVERSATIONS_SLOTS, parts: CONVERSATIONS_PARTS },
725
+ 'kai-conversations': { slots: CONVERSATIONS_SLOTS, parts: CONVERSATIONS_PARTS, children: 'Your own `<kai-conversation-item>` rows (item mode: the consumer-owned loop). Data rows do not render while any are present.' },
726
+ 'kai-conversation-item': { slots: CONVERSATION_ITEM_SLOTS, parts: CONVERSATION_ITEM_PARTS, children: 'The row title. `leading`, `meta` and `menu` are the named regions around it.' },
635
727
  'kai-message': { slots: MESSAGE_SLOTS, parts: MESSAGE_PARTS },
636
728
  'kai-thread': { slots: THREAD_SLOTS },
637
729
  'kai-prompt-input': { slots: PROMPT_INPUT_SLOTS, parts: PROMPT_INPUT_PARTS },
@@ -650,7 +742,7 @@ export const ELEMENT_COMPOSITION: Record<string, ElementComposition> = {
650
742
  'kai-voice-output': { parts: VOICE_OUTPUT_PARTS },
651
743
  'kai-screen': { slots: SCREEN_SLOTS, parts: SCREEN_PARTS, children: 'The screen body, below the title bar.' },
652
744
  'kai-card': { slots: CARD_SLOTS, parts: CARD_PARTS, children: 'The card body, below the header/media regions.' },
653
- 'kai-workspace': { slots: WORKSPACE_SLOTS, parts: WORKSPACE_PARTS },
745
+ 'kai-workspace': { slots: WORKSPACE_SLOTS, parts: WORKSPACE_PARTS, children: 'The main region content (same region as the `main` slot): your `<kai-chat>`, or any app view.' },
654
746
  'kai-nav': { parts: NAV_PARTS },
655
747
  'kai-coachmark': { slots: COACHMARK_SLOTS, parts: COACHMARK_PARTS, children: 'The ANCHOR the coachmark points at: the element it attaches to and positions against. The bubble body is the `content` slot.' },
656
748
  'kai-progress-bar': { parts: PROGRESS_BAR_PARTS },
@@ -664,6 +756,7 @@ export const ELEMENT_COMPOSITION: Record<string, ElementComposition> = {
664
756
  'kai-pane-group': { parts: PANE_GROUP_PARTS, children: 'Content shown for every tab. Use it INSTEAD of the per-tab `slot="<tab id>"` seams when you swap the content yourself.' },
665
757
  'kai-agent-card': { parts: AGENT_CARD_PARTS },
666
758
  'kai-dialog': { slots: DIALOG_SLOTS, parts: DIALOG_PARTS, children: 'The dialog body, between the `header` and `footer` slots.' },
759
+ 'kai-dock': { slots: DOCK_SLOTS, parts: DOCK_PARTS, children: 'The panel body, the same region as `slot="panel"`.' },
667
760
  'kai-input': { slots: INPUT_SLOTS, parts: INPUT_PARTS },
668
761
  'kai-search': { parts: SEARCH_PARTS },
669
762
  'kai-kbd': { parts: KBD_PARTS, children: 'Literal key text, when you are not using the `keys` prop to render key caps.' },
@@ -671,9 +764,11 @@ export const ELEMENT_COMPOSITION: Record<string, ElementComposition> = {
671
764
  'kai-empty': { slots: EMPTY_SLOTS, children: 'The empty-state body below the title/description, usually the call to action.' },
672
765
  'kai-file-upload': { children: 'Custom dropzone content, replacing the default label (the `label` prop is the fallback).' },
673
766
  'kai-popover': { slots: POPOVER_SLOTS, children: 'The popover panel body. The control that opens it is the `trigger` slot.' },
767
+ 'kai-dropdown': { slots: DROPDOWN_SLOTS, children: 'The menu body: your own rows. Give each `role="menuitem"`. The control that opens it is the `trigger` slot.' },
674
768
  'kai-resizable': { children: 'The `<kai-resizable-item>` panels, in order. Dividers are inserted between them.' },
675
769
  'kai-resizable-item': { children: 'This panel\'s content.' },
676
770
  'kai-tooltip': { children: 'The TRIGGER the tooltip describes. The tip text is the `text` prop.' },
771
+ 'kai-code-block': { parts: CODE_BLOCK_PARTS },
677
772
  'kai-audio-visualizer': { parts: AUDIO_VISUALIZER_PARTS },
678
773
  };
679
774
 
@@ -1,5 +1,18 @@
1
- /* Element-bundle stylesheet. Compiled to compiled.css and injected into each
2
- custom element's shadow root. Mirrors .storybook/styles.css but scans src/. */
1
+ /* Element-bundle stylesheet, and the SOURCE OF TRUTH for element CSS. Compiled to
2
+ compiled.css and injected into each custom element's shadow root;
3
+ .storybook/styles.css @imports this file rather than restating it, so a rule
4
+ added here reaches the shadow roots AND the light-DOM Solid stories at once.
5
+
6
+ It used to be the other way round — Storybook hand-copied a few of these rules
7
+ under "Mirror the shipped ..." comments. Nothing enforced the copy, so
8
+ .kai-radio, .kai-checkbox, .kai-range and .kai-focus-inset were simply missing
9
+ there: three form controls rendered as raw OS chrome in every story while the
10
+ shipped element rendered them fully designed, and an agent and the owner each
11
+ concluded the kit had unstyled controls. Never copy a rule from here into the
12
+ Storybook sheet; add it here.
13
+
14
+ The one thing that cannot cross is :host, which selects nothing in the light
15
+ DOM. Its defaults are re-stated on `body` in the Storybook sheet's shim. */
3
16
  @import "tailwindcss";
4
17
  /* tw-animate-css supplies the animate-in/animate-out + slide/fade/zoom utilities
5
18
  the kit uses for every overlay enter/exit (toast, popover, tooltip, dropdown,
@@ -16,10 +29,19 @@
16
29
  /* Base typography is token-driven so a theme can restyle it. --kai-font-base is
17
30
  the family for ALL text/UI (named "base", not "sans", so a serif body works);
18
31
  --kai-tracking is global letter-spacing. Both pierce the Shadow DOM via the
19
- var() fallback, exactly like the --kai-color-* tokens. */
32
+ var() fallback, exactly like the --kai-color-* tokens.
33
+
34
+ font-size is the third: without it every shadow root inherited the DOCUMENT's
35
+ size (16px in a stock page), so any surface that never set an explicit text-*
36
+ class rendered a step larger than the rest of the kit and looked like a bug in
37
+ that one component. --text-body is the scale's primary reading size and itself
38
+ resolves through --kai-text-body, so overriding that one token still moves the
39
+ default. The literal fallback matches theme.css's --text-body so the rule is
40
+ still correct if this sheet is ever used without the @theme block. */
20
41
  :host {
21
42
  display: block;
22
43
  font-family: var(--kai-font-base, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
44
+ font-size: var(--text-body, var(--kai-text-body, 0.875rem));
23
45
  letter-spacing: var(--kai-tracking, normal);
24
46
  }
25
47
  /* Code/monospace surfaces use --kai-font-code (named for its ROLE — code — not
@@ -40,12 +62,23 @@
40
62
  /* One token-driven focus ring for EVERY focusable element in the shadow root.
41
63
  Blue by default (via --color-ring), consistent in light + dark, and
42
64
  overridable through --kai-color-ring. Uses `outline` (not a box-shadow
43
- ring) so it never clips on overflow and follows the element's radius;
44
- `:focus-visible` so it only appears for keyboard navigation, never on a
45
- mouse click. Components that render their own ring set
46
- `focus-visible:outline-none` (a utility in a later cascade layer) and win
47
- over this so there's no double ring, just a guaranteed blue fallback for
48
- the many controls that have no explicit focus style. */
65
+ ring) because it follows the element's radius and unlike a `ring-*`
66
+ utility is written in literal values, so it does not depend on any
67
+ `--tw-*` custom property. That matters more than it looks: `@property`
68
+ registration is document-global and is ignored inside a shadow root, so
69
+ every `--tw-*` here is unset and every `ring-*` utility computes to
70
+ nothing. This literal rule is the only reason most of the kit had a focus
71
+ indicator at all. `:focus-visible` so it only appears for keyboard
72
+ navigation, never on a mouse click. Components that render their own ring
73
+ set `focus-visible:outline-none` (a utility in a later cascade layer) and
74
+ win over this — so there's no double ring, just a guaranteed blue fallback
75
+ for the many controls that have no explicit focus style.
76
+
77
+ CAVEAT, learned the hard way: an outline is NOT immune to clipping. It is
78
+ unaffected by the element's OWN overflow, but `outline-offset: 2px` draws
79
+ it entirely outside the border box, where an ANCESTOR with
80
+ `overflow: clip`/`hidden` erases it completely. Controls that sit flush
81
+ inside such a container need `.kai-focus-inset` below. */
49
82
  :focus-visible {
50
83
  outline: 2px solid var(--color-ring);
51
84
  outline-offset: 2px;
@@ -72,6 +105,40 @@
72
105
  *::-webkit-scrollbar-thumb:hover { background-color: var(--color-scrollbar-thumb-hover); }
73
106
  *::-webkit-scrollbar-corner { background: transparent; }
74
107
 
108
+ /* SELECTED text, kit-owned. Scoped to the shadow root — deliberately, and this
109
+ is the whole design decision, so read it before "simplifying" it into
110
+ theme.css.
111
+
112
+ WHY THE KIT OWNS IT AT ALL: left to the UA, the browser's blue selection was
113
+ visually stronger than the kit's OWN deliberate emphasis (the composer's
114
+ --color-highlight mark), so an incidental drag-select outranked a keyword the
115
+ app had flagged on purpose. How selected text reads is a fact about the
116
+ medium, which is the kit's call.
117
+
118
+ WHY ONLY HERE, AND NOT IN theme.css: theme.css is imported into a CONSUMER's
119
+ Tailwind build, so a bare ::selection there would restyle every paragraph on
120
+ their page — the kit deciding for the app's own content, which is not ours to
121
+ decide. The shadow root IS the kit's DOM, so the same rule is unambiguous
122
+ here. Identical reasoning to the scrollbar and :focus-visible rules above,
123
+ which are scoped the same way and for the same reason. theme.css therefore
124
+ carries the TOKENS only, and a Solid-layer consumer rendering our components
125
+ into their own light DOM opts in with one rule of their own:
126
+ `.my-chat ::selection { background-color: var(--color-selection); … }`.
127
+
128
+ WHY THE RULE MUST BE IN THIS SHEET AND NOT INHERITED: ::selection does NOT
129
+ cross the shadow boundary the way ordinary inherited properties do — a
130
+ document-level ::selection leaves shadow content on the UA default. The
131
+ --kai-color-selection TOKEN does pierce (custom properties inherit normally),
132
+ which is what keeps a consumer override working.
133
+
134
+ Both background AND color are set; see the theme.css note for why the
135
+ foreground is load-bearing rather than cosmetic (coloured text falls under
136
+ WCAG AA on this fill if it keeps its own colour). */
137
+ ::selection {
138
+ background-color: var(--color-selection);
139
+ color: var(--color-selection-foreground);
140
+ }
141
+
75
142
  /* Respect a user's reduced-motion preference: disable the tw-animate-css
76
143
  enter/exit keyframe animations (toasts, overlays, cards) so they appear and
77
144
  disappear instantly instead of sliding/fading. Targets the utility classes
@@ -87,6 +154,19 @@
87
154
  }
88
155
 
89
156
  @layer components {
157
+ /* Focus ring drawn INSIDE the element's border box, for focusable regions
158
+ that sit flush against a clipping ancestor. The scrollable code-block body
159
+ and the message log are both `overflow-*` regions filling a rounded
160
+ `overflow: clip` container, so the default `outline-offset: 2px` put the
161
+ whole ring outside the clip and it never painted — the outline computed
162
+ correctly, matched `:focus-visible`, and was erased before it reached the
163
+ screen. A negative offset keeps it clear of the boundary. Same token and
164
+ width as the base ring, so it reads identically. */
165
+ .kai-focus-inset:focus-visible {
166
+ outline: 2px solid var(--color-ring);
167
+ outline-offset: -2px;
168
+ }
169
+
90
170
  /* Designed radio + checkbox (appearance-none) so the form controls read as
91
171
  intentional, theme correctly in light/dark, and have comfortable targets —
92
172
  replacing the weak native defaults. Used via `.kai-radio` / `.kai-checkbox`. */
@@ -145,6 +225,36 @@
145
225
  .kai-checkbox:checked {
146
226
  box-shadow: 0 1px 4px color-mix(in srgb, var(--color-primary) 26%, transparent);
147
227
  }
228
+ /* The mixed state ("some of the children below are ticked"). `indeterminate` is a
229
+ DOM property with no HTML attribute, so a rule is the ONLY way it can be seen —
230
+ without one a mixed box paints pixel-identically to an unchecked one and lies
231
+ about its state. That was live: `<kai-tasks>` in `select` mode puts an
232
+ indeterminate select-all above a partial selection, and it read as "none
233
+ selected".
234
+
235
+ Same fill and the same masked `::after` as `:checked`, with the tick mask
236
+ swapped for a bar, so the two states are siblings of one technique rather than
237
+ two: the bar is `--color-primary-foreground` on `--color-primary`, exactly the
238
+ pair the tick already uses, so it themes and contrasts identically in both
239
+ themes and stays consumer-themable through the same tokens.
240
+
241
+ Ordered AFTER the `:checked` rules on purpose. `indeterminate` is independent of
242
+ `checked`, so a box can be both; specificity is equal, so source order decides,
243
+ and mixed-wins is what the browser's own native rendering does.
244
+
245
+ Scoped to `.kai-checkbox` on purpose: `:indeterminate` also matches every radio
246
+ in a group with nothing selected, and an empty radio group must go on reading as
247
+ empty rather than sprouting a third state. */
248
+ .kai-checkbox:indeterminate {
249
+ background-color: var(--color-primary);
250
+ border-color: var(--color-primary);
251
+ box-shadow: 0 1px 4px color-mix(in srgb, var(--color-primary) 26%, transparent);
252
+ }
253
+ .kai-checkbox:indeterminate::after {
254
+ transform: scale(1);
255
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E") center / contain no-repeat;
256
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E") center / contain no-repeat;
257
+ }
148
258
  .kai-radio:disabled,
149
259
  .kai-checkbox:disabled { opacity: 0.5; cursor: not-allowed; }
150
260
 
@@ -25,10 +25,19 @@ defineWebComponent<Props, Events>('kai-thinking-bar', {
25
25
  text: 'Thinking',
26
26
  stoppable: false,
27
27
  stopLabel: 'Answer now',
28
- }, (props, { dispatch, flag }) => (
29
- <ThinkingBar
30
- text={props.text}
31
- stopLabel={props.stopLabel}
32
- onStop={flag('stoppable') ? () => dispatch('kai-stop') : undefined}
33
- />
34
- ));
28
+ }, (props, { dispatch, flag, reflectFlag }) => {
29
+ // `stoppable` reflects, following kai-tool's `disabled` (its FIX1). Without it the
30
+ // remove direction is dead: `el.removeAttribute('stoppable')` parses to `undefined`,
31
+ // the prop was already `undefined` after a bare-attribute add, so no prop change
32
+ // fires and `flag()`'s non-reactive hasAttribute fallback is never re-read the
33
+ // stop affordance stays rendered forever. reflectFlag's coercing read-back resolves
34
+ // every write through the same `flag()` policy, making both directions reactive.
35
+ reflectFlag('stoppable');
36
+ return (
37
+ <ThinkingBar
38
+ text={props.text}
39
+ stopLabel={props.stopLabel}
40
+ onStop={flag('stoppable') ? () => dispatch('kai-stop') : undefined}
41
+ />
42
+ );
43
+ });
@@ -128,6 +128,8 @@ defineWebComponent<Props, Events>('kai-thread', {
128
128
  scrollButton={props.scrollButton !== false}
129
129
  cardTypes={cardComponentsFromTags(props.cardTypes as Record<string, string> | undefined, (props as { theme?: string }).theme)}
130
130
  cardSchemas={props.cardSchemas as Record<string, object> | undefined}
131
+ /* F-26: card parts emit off THIS element as the bubbling `kai-card` event. */
132
+ cardHostElement={element}
131
133
  empty={slots()['empty'] ? <slot name="empty" /> : undefined}
132
134
  onMessageAction={(detail) => dispatch('kai-message-action', detail)}
133
135
  controllerRef={(c) => (controller = c)}
@@ -5,7 +5,11 @@ import { toast as toastStore, type ToastItem } from '../primitives/toast-store';
5
5
  interface Props extends Record<string, unknown> {
6
6
  /** The toasts to render. Newest is shown on top. Set as a JS property (array);
7
7
  * pass a new array reference to update. Omit for an empty region, which is
8
- * the normal resting state and how the imperative `toast()` API starts. */
8
+ * the normal resting state and how the imperative `toast()` API starts.
9
+ * Note the handover: the first `toast()` call ADOPTS a region you placed in
10
+ * markup (no second region mounts) and binds the imperative store to this
11
+ * property, replacing any array you set. Drive a region as data OR via
12
+ * `toast()`, not both at once. */
9
13
  toasts?: ToastItem[];
10
14
  /** Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … */
11
15
  position?: ToastPosition;
@@ -41,12 +45,24 @@ interface Events {
41
45
  * also usable declaratively — set `el.toasts = [...]` (a JS property, never an
42
46
  * attribute) and listen for `kai-dismiss` / `kai-action`.
43
47
  *
48
+ * The two modes meet gracefully: `toast()` ADOPTS the first connected
49
+ * `<kai-toast-region>` already in the document instead of mounting a second
50
+ * one, and creates its own on `document.body` only when none exists. Adoption
51
+ * keeps your authored attributes (position/stack/appearance) but binds the
52
+ * imperative store to `toasts`, replacing an array you were driving as data —
53
+ * so within one app, either own the array or call `toast()`, not both. With
54
+ * two or more regions placed, the first in document order is adopted and a
55
+ * one-time console.warn flags the ambiguity.
56
+ *
44
57
  * Because it is a real `kai-*` element it carries its own shadow root + the
45
58
  * shared kit stylesheet, so it is viewport-positioned AND kit-styled.
46
59
  *
47
60
  * Stack-level defaults are also declarative: `<kai-toast-region appearance="card"
48
61
  * inverse>` makes every toast that doesn't set its own `appearance`/`inverse`
49
62
  * render as an inverted card. A per-toast value always wins.
63
+ *
64
+ * Your app chrome must stay below the toast layer; set `--kai-toast-z`
65
+ * (default 100) on the host to move the layer.
50
66
  */
51
67
  defineWebComponent<Props, Events>('kai-toast-region', {
52
68
  toasts: [],
@@ -1,4 +1,4 @@
1
- import { Show } from 'solid-js';
1
+ import { createSignal, Show } from 'solid-js';
2
2
  import { defineWebComponent } from './define';
3
3
  import { Tool, type ToolPart } from '../components/tool';
4
4
  import { wireDisclosure } from './disclosure';
@@ -36,13 +36,34 @@ defineWebComponent<Props, Events>('kai-tool', {
36
36
  defaultOpen: undefined,
37
37
  disabled: undefined,
38
38
  }, (props, ctx) => {
39
- const { flag } = ctx;
40
- let api: CollapsibleController | undefined;
39
+ const { flag, reflectFlag } = ctx;
40
+
41
+ // A SIGNAL, not a `let`. This facade gates its whole subtree on `<Show when={props.tool}>`,
42
+ // and `tool` is an object prop, so the only sequence a consumer of the HTML spelling
43
+ // has is markup-then-assign: the element connects with no tool, renders nothing, and
44
+ // the Collapsible — and therefore the controller — arrives a tick later.
45
+ //
46
+ // With a plain `let`, wireDisclosure's effects ran once against `undefined` and never
47
+ // again, because the getter they were handed was not reactive and a bare `open`
48
+ // attribute never changes `props.open` to re-trigger them. Net result: `<kai-tool open>`
49
+ // silently stayed shut while `el.open` read back `true` — the property and the
50
+ // rendering disagreed. Making the controller reactive is the whole fix: the effects
51
+ // re-run the moment it lands. `Tool` already tracks its own copy this way internally.
52
+ // Pinned by the ordering rows in tests/elements/tool.test.tsx.
53
+ const [api, setApi] = createSignal<CollapsibleController>();
41
54
 
42
55
  // The standard disclosure surface: settable+reflecting `open`, kai-open-change,
43
56
  // show/hide/toggle, disabled-gating. See ./disclosure. This is the SOLE emitter
44
57
  // of kai-open-change — the Tool/Collapsible no longer wires its own.
45
- wireDisclosure(ctx, () => api, () => props.open);
58
+ wireDisclosure(ctx, api, () => props.open);
59
+
60
+ // `disabled` reflects too, following kai-dock. Without it the prop is write-only in
61
+ // the read direction AND deaf in the write one: `el.setAttribute('disabled', '')`
62
+ // after mount is parsed by component-register to `undefined`, so the prop never
63
+ // changes and nothing re-renders. reflectFlag's coercing setter resolves that
64
+ // `undefined` back through the same `flag()` policy, which is what makes the plain
65
+ // HTML gesture work at runtime and not only at parse time (findings G-05).
66
+ reflectFlag('disabled');
46
67
 
47
68
  return (
48
69
  <Show when={props.tool}>
@@ -50,7 +71,7 @@ defineWebComponent<Props, Events>('kai-tool', {
50
71
  toolPart={props.tool!}
51
72
  defaultOpen={flag('defaultOpen')}
52
73
  disabled={flag('disabled')}
53
- controllerRef={(a) => (api = a)}
74
+ controllerRef={(a) => setApi(a)}
54
75
  />
55
76
  </Show>
56
77
  );
@@ -39,6 +39,13 @@ interface Events {
39
39
  * push-to-talk indicator) in sync with the mic. Fires on real transitions
40
40
  * only (manual click and programmatic start()/stop()), never on mount. */
41
41
  'kai-recording-change': { recording: boolean };
42
+ /** A voice session failed, so no failure is ever silent. `detail.source` names
43
+ * the failing side (`recognition` on `<kai-voice-input>`, `synthesis` on
44
+ * `<kai-voice-output>`), `detail.error` carries the platform error code, the
45
+ * thrown exception's name, or `no-result` when recognition ended with no error
46
+ * and no text (the user said nothing), and `detail.message` is human-readable.
47
+ * Deliberate cancellation does not fire. */
48
+ 'kai-voice-error': { source: 'recognition'; error: string; message: string };
42
49
  }
43
50
 
44
51
  /**
@@ -47,8 +54,9 @@ interface Events {
47
54
  * `SpeechRecognition` (Chrome/Safari; no Firefox — and cloud-based in Chrome).
48
55
  * Set `el.transcribe` to route audio through your own async transcriber instead.
49
56
  * Where neither is available it records the blob and emits `kai-audio-captured`
50
- * with no text. Also emits `kai-transcription` (final text) and, with `interim`,
51
- * `kai-transcript-interim` (live partials).
57
+ * with no text. Also emits `kai-transcription` (final text), `kai-voice-error`
58
+ * (a failed or empty session) and, with `interim`, `kai-transcript-interim`
59
+ * (live partials).
52
60
  */
53
61
  defineWebComponent<Props, Events>('kai-voice-input', {
54
62
  transcribe: undefined,
@@ -85,6 +93,7 @@ defineWebComponent<Props, Events>('kai-voice-input', {
85
93
  onTranscription={(text) => dispatch('kai-transcription', { text })}
86
94
  onInterim={(text) => dispatch('kai-transcript-interim', { text })}
87
95
  onRecordingChange={(recording) => dispatch('kai-recording-change', { recording })}
96
+ onError={(detail) => dispatch('kai-voice-error', detail)}
88
97
  controllerRef={(c) => (controller = c)}
89
98
  />
90
99
  );
@@ -20,18 +20,30 @@ interface Props extends Record<string, unknown> {
20
20
 
21
21
  /** Events fired by `<kai-voice-output>`. */
22
22
  interface Events {
23
- /** Playback started or stopped. Drive your own UI in sync. Fires on real transitions
24
- * only (manual click and programmatic speak()/stop()), never on mount. */
23
+ /** Playback started or stopped. Drive your own UI in sync. `speaking: true`
24
+ * fires when audio actually starts (utterance.onstart natively; audio playback
25
+ * beginning on the `synthesize` path), not when speak() is called; earlier
26
+ * releases fired it optimistically inside speak() itself. Fires on real
27
+ * transitions only (manual click and programmatic speak()/stop()), never on
28
+ * mount. */
25
29
  'kai-speaking-change': { speaking: boolean };
26
30
  /** The model path (`synthesize`) resolved audio: the raw `Blob` before playback. */
27
31
  'kai-synthesized': { blob: Blob };
32
+ /** A voice session failed, so no failure is ever silent. `detail.source` names
33
+ * the failing side (`recognition` on `<kai-voice-input>`, `synthesis` on
34
+ * `<kai-voice-output>`), `detail.error` carries the platform error code, the
35
+ * thrown exception's name, or `no-result` when recognition ended with no error
36
+ * and no text (the user said nothing), and `detail.message` is human-readable.
37
+ * Deliberate cancellation does not fire. */
38
+ 'kai-voice-error': { source: 'synthesis'; error: string; message: string };
28
39
  }
29
40
 
30
41
  /**
31
42
  * `<kai-voice-output>` — a speaker button that reads `text` aloud. Native by
32
43
  * default (`speechSynthesis`); set `el.synthesize` to route through your TTS
33
44
  * model instead. The output sibling of `<kai-voice-input>`. Emits
34
- * `kai-speaking-change` and (model path) `kai-synthesized`.
45
+ * `kai-speaking-change`, `kai-voice-error` (a failed synthesis) and (model
46
+ * path) `kai-synthesized`.
35
47
  */
36
48
  defineWebComponent<Props, Events>('kai-voice-output', {
37
49
  text: '',
@@ -63,6 +75,7 @@ defineWebComponent<Props, Events>('kai-voice-output', {
63
75
  onSynthesize={props.synthesize ? (text) => props.synthesize!(text) : undefined}
64
76
  onSpeakingChange={(speaking) => dispatch('kai-speaking-change', { speaking })}
65
77
  onSynthesized={(blob) => dispatch('kai-synthesized', { blob })}
78
+ onError={(detail) => dispatch('kai-voice-error', detail)}
66
79
  controllerRef={(c) => (controller = c)}
67
80
  />
68
81
  );
package/src/index.ts CHANGED
@@ -339,6 +339,8 @@ export type { VoiceInputProps, VoiceInputController } from './components/voice-i
339
339
  export { ConversationList } from './components/conversation-list';
340
340
  export type { ConversationListProps, ConversationListController } from './components/conversation-list';
341
341
  export { ConversationItem } from './components/conversation-item';
342
+ export { SlottedConversationItem } from './components/conversation-item';
343
+ export type { SlottedConversationItemProps } from './components/conversation-item';
342
344
  export type { ConversationItemProps } from './components/conversation-item';
343
345
  export { ModelSwitcher } from './components/model-switcher';
344
346
  export type { ModelSwitcherProps } from './components/model-switcher';
@@ -417,7 +419,8 @@ export type { KaiTabItem } from './ui/tabs';
417
419
  export type { PaneStatus } from './ui/pane';
418
420
  export type { PaneTab } from './ui/pane-group';
419
421
  export type {
420
- KaiCommandItem, KaiContextUsage, KaiMenuItem, KaiSegmentedOption, KaiSourceItem,
422
+ KaiCheckboxOption, KaiCommandItem, KaiContextUsage, KaiMenuItem, KaiRadioOption, KaiSegmentedOption, KaiSelectOption,
423
+ KaiSourceItem,
421
424
  } from './elements/element-data-types';
422
425
  // `kai-audio-visualizer.shader` takes a ShaderSpec. The element arrived from main
423
426
  // after this block was written, and prop-types-exported.test.ts caught it, which