@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
@@ -37,6 +37,63 @@ const useDropdown = () => {
37
37
  // a parent's querySelectorAll scoped to its own menu never reaches sub items.
38
38
  const ITEM_SELECTOR = '[role="menuitem"]:not([aria-disabled="true"]), [role="menuitemcheckbox"]:not([aria-disabled="true"]), [role="menuitemradio"]:not([aria-disabled="true"])';
39
39
 
40
+ /**
41
+ * The roving-focus set, in FLAT-TREE order.
42
+ *
43
+ * ★ NOT `querySelectorAll`, and the difference is the whole point: a `<slot>` in the
44
+ * shadow tree contains NONE of the light-DOM nodes it projects, so a plain query
45
+ * returns zero items for any facade whose rows are SLOTTED (`<kai-dropdown>`) and
46
+ * ArrowUp/Down, Home/End and typeahead all reach nothing. The items-tree facades
47
+ * (`kai-menu`, `kai-model-switcher`, `kai-scope-picker`) render their rows INTO the
48
+ * shadow tree, so both spellings agree for them — which is exactly why this went
49
+ * unnoticed until a slotted menu body existed.
50
+ *
51
+ * Same class as `hasFocusableChild` in ./hover-card.tsx, which asks the boolean
52
+ * version of this question. This one needs the ORDER as well, so it walks the tree
53
+ * and splices each slot's assigned elements in AT THE SLOT'S POSITION rather than
54
+ * appending them; a menu may mix rendered and slotted rows. `flatten: true` follows
55
+ * a slot assigned to another slot.
56
+ *
57
+ * Scope is unchanged: submenu content is portaled to a SIBLING node, so a parent's
58
+ * walk still never reaches sub items.
59
+ */
60
+ function menuItems(root: HTMLElement | undefined): HTMLElement[] {
61
+ if (!root) return [];
62
+ const out: HTMLElement[] = [];
63
+ const collect = (el: Element): void => {
64
+ if (el.matches(ITEM_SELECTOR)) out.push(el as HTMLElement);
65
+ for (const child of Array.from(el.children)) visit(child);
66
+ };
67
+ const visit = (el: Element): void => {
68
+ if (el.localName === 'slot') {
69
+ for (const assigned of (el as HTMLSlotElement).assignedElements({ flatten: true })) collect(assigned);
70
+ return;
71
+ }
72
+ collect(el);
73
+ };
74
+ for (const child of Array.from(root.children)) visit(child);
75
+ return out;
76
+ }
77
+
78
+ /** The first roving-focus item, in flat-tree order. Replaces
79
+ * `querySelector(ITEM_SELECTOR)` for the reason `menuItems` replaces the `All`. */
80
+ const firstMenuItem = (root: HTMLElement | undefined): HTMLElement | undefined => menuItems(root)[0];
81
+
82
+ /**
83
+ * The focused item, resolved across BOTH trees.
84
+ *
85
+ * Inside a Shadow DOM `document.activeElement` returns the HOST rather than the
86
+ * focused menu item, so the menu's own root node is asked first. But a SLOTTED row
87
+ * lives in the LIGHT DOM: focus is then outside the shadow tree, that root reports
88
+ * null, and the document's answer is the real one. Asking only the shadow root would
89
+ * leave `currentIndex()` at -1 for every slotted row, so ArrowDown would restart at
90
+ * the top instead of advancing.
91
+ */
92
+ function activeMenuItem(root: HTMLElement | undefined): Element | null {
93
+ const tree = root?.getRootNode() as Document | ShadowRoot | undefined;
94
+ return (tree?.activeElement ?? document.activeElement) as Element | null;
95
+ }
96
+
40
97
  /** Imperative open controller, handed to a parent (e.g. the kai-menu facade) via
41
98
  * `controllerRef` so it can drive/observe the Dropdown's open state. */
42
99
  export interface DropdownController { open: Accessor<boolean>; setOpen: (v: boolean) => void; }
@@ -103,8 +160,8 @@ export function Dropdown(props: DropdownProps) {
103
160
  // attempt focus now and re-assert in the menu ref's microtask so it lands
104
161
  // once the node exists. Skip disabled items (roving-focus contract).
105
162
  if (opts?.viaKeyboard) {
106
- queueMicrotask(() => menu()?.querySelector<HTMLElement>(ITEM_SELECTOR)?.focus());
107
- menu()?.querySelector<HTMLElement>(ITEM_SELECTOR)?.focus();
163
+ queueMicrotask(() => firstMenuItem(menu())?.focus());
164
+ firstMenuItem(menu())?.focus();
108
165
  }
109
166
  } else if (opts?.returnFocus !== false) {
110
167
  // Closing via keyboard/select: return focus to the trigger. The menu
@@ -179,23 +236,14 @@ export function DropdownContent(props: DropdownContentProps) {
179
236
  refs: () => [ctx.trigger(), ctx.menu(), ...ctx.subMenus()],
180
237
  });
181
238
 
182
- const items = () => Array.from(ctx.menu()?.querySelectorAll<HTMLElement>(ITEM_SELECTOR) ?? []);
239
+ const items = () => menuItems(ctx.menu());
183
240
  const focusIndex = (i: number) => {
184
241
  const list = items();
185
242
  if (!list.length) return;
186
243
  const idx = ((i % list.length) + list.length) % list.length;
187
244
  list[idx].focus();
188
245
  };
189
- // Resolve the focused item via the menu's own root node. Inside a Shadow DOM
190
- // (every kitn-* element), `document.activeElement` returns the host element,
191
- // not the focused menu item, which would break ArrowUp/Down roving focus.
192
- // `getRootNode().activeElement` correctly returns the active node within the
193
- // same tree (ShadowRoot or Document).
194
- const activeItem = () => {
195
- const root = ctx.menu()?.getRootNode() as Document | ShadowRoot | undefined;
196
- return (root?.activeElement ?? document.activeElement) as Element | null;
197
- };
198
- const currentIndex = () => items().findIndex((el) => el === activeItem());
246
+ const currentIndex = () => items().findIndex((el) => el === activeMenuItem(ctx.menu()));
199
247
 
200
248
  const onKeyDown = (e: KeyboardEvent) => {
201
249
  const list = items();
@@ -226,7 +274,7 @@ export function DropdownContent(props: DropdownContentProps) {
226
274
  // synchronously; this ref-time microtask re-asserts focus once the
227
275
  // menu node exists. Skip disabled items.
228
276
  if (ctx.openedViaKeyboard()) {
229
- queueMicrotask(() => el.querySelector<HTMLElement>(ITEM_SELECTOR)?.focus());
277
+ queueMicrotask(() => firstMenuItem(el)?.focus());
230
278
  }
231
279
  }}
232
280
  id={ctx.menuId}
@@ -424,8 +472,8 @@ export function DropdownSub(props: DropdownSubProps) {
424
472
  setOpenSig(v);
425
473
  if (v) {
426
474
  if (opts?.viaKeyboard) {
427
- queueMicrotask(() => menu()?.querySelector<HTMLElement>(ITEM_SELECTOR)?.focus());
428
- menu()?.querySelector<HTMLElement>(ITEM_SELECTOR)?.focus();
475
+ queueMicrotask(() => firstMenuItem(menu())?.focus());
476
+ firstMenuItem(menu())?.focus();
429
477
  }
430
478
  } else if (opts?.returnFocus !== false) {
431
479
  const el = trigger();
@@ -516,18 +564,14 @@ export function DropdownSubContent(props: DropdownSubContentProps) {
516
564
  // useDismiss here would double-fire Escape because document listeners run
517
565
  // after stopPropagation on the element, not on the document.
518
566
 
519
- const items = () => Array.from(sub.menu()?.querySelectorAll<HTMLElement>(ITEM_SELECTOR) ?? []);
567
+ const items = () => menuItems(sub.menu());
520
568
  const focusIndex = (i: number) => {
521
569
  const list = items();
522
570
  if (!list.length) return;
523
571
  const idx = ((i % list.length) + list.length) % list.length;
524
572
  list[idx].focus();
525
573
  };
526
- const activeItem = () => {
527
- const root = sub.menu()?.getRootNode() as Document | ShadowRoot | undefined;
528
- return (root?.activeElement ?? document.activeElement) as Element | null;
529
- };
530
- const currentIndex = () => items().findIndex((el) => el === activeItem());
574
+ const currentIndex = () => items().findIndex((el) => el === activeMenuItem(sub.menu()));
531
575
 
532
576
  const onKeyDown = (e: KeyboardEvent) => {
533
577
  const list = items();
@@ -559,7 +603,7 @@ export function DropdownSubContent(props: DropdownSubContentProps) {
559
603
  const unregister = parent.registerSubMenu(el);
560
604
  onCleanup(unregister);
561
605
  if (sub.openedViaKeyboard()) {
562
- queueMicrotask(() => el.querySelector<HTMLElement>(ITEM_SELECTOR)?.focus());
606
+ queueMicrotask(() => firstMenuItem(el)?.focus());
563
607
  }
564
608
  }}
565
609
  id={sub.menuId}
@@ -1,5 +1,5 @@
1
1
  import {
2
- createContext, useContext, createSignal, Show, splitProps, onCleanup,
2
+ createContext, useContext, createSignal, createUniqueId, Show, splitProps, onCleanup,
3
3
  type JSX, type Accessor,
4
4
  } from 'solid-js';
5
5
  import { Portal } from 'solid-js/web';
@@ -17,6 +17,12 @@ interface HoverCardCtx {
17
17
  setContent: (el: HTMLElement) => void;
18
18
  trigger: Accessor<HTMLElement | undefined>;
19
19
  content: Accessor<HTMLElement | undefined>;
20
+ /** Ties a focused trigger to the card it reveals. Without it a keyboard tab
21
+ * stop announces nothing, which is a focus stop that wastes the user's time. */
22
+ contentId: string;
23
+ /** Whether this card refuses to open. Read by the TRIGGER, not just by
24
+ * `enter()`: a card that cannot open must not be in the tab order either. */
25
+ disabled: Accessor<boolean>;
20
26
  }
21
27
  const Ctx = createContext<HoverCardCtx>();
22
28
  const useHoverCard = () => {
@@ -45,6 +51,7 @@ export function HoverCardRoot(props: HoverCardRootProps) {
45
51
  const [open, setOpen] = createSignal(props.defaultOpen ?? false);
46
52
  const [trigger, setTrigger] = createSignal<HTMLElement>();
47
53
  const [content, setContent] = createSignal<HTMLElement>();
54
+ const contentId = createUniqueId();
48
55
  let timer: number | undefined;
49
56
  props.controllerRef?.({ open, setOpen });
50
57
 
@@ -73,25 +80,167 @@ export function HoverCardRoot(props: HoverCardRootProps) {
73
80
  open, enter, leave, close,
74
81
  setTrigger, setContent,
75
82
  trigger, content,
83
+ contentId,
84
+ disabled: () => props.disabled ?? false,
76
85
  }}>
77
86
  {props.children}
78
87
  </Ctx.Provider>
79
88
  );
80
89
  }
81
90
 
82
- export interface HoverCardTriggerProps { children: JSX.Element; class?: string; }
91
+ export interface HoverCardTriggerProps {
92
+ children: JSX.Element;
93
+ class?: string;
94
+ /**
95
+ * Whether the trigger is its own tab stop. Omit for the automatic behaviour,
96
+ * which is right for every case in this repo: the trigger takes focus only
97
+ * when nothing inside it already can.
98
+ *
99
+ * Pass `false` to keep a trigger out of the tab order entirely, or `true` to
100
+ * force a stop. Both are escape hatches — reach for them when the automatic
101
+ * answer is wrong, not to restate it.
102
+ */
103
+ focusable?: boolean;
104
+ }
105
+
106
+ /** Anything the platform puts in the tab order by default, plus anything a
107
+ * consumer opted in with `tabindex`. `tabindex="-1"` is deliberately excluded:
108
+ * it is programmatically focusable but the Tab key skips it, so a trigger
109
+ * containing only such an element still needs a stop of its own. */
110
+ const FOCUSABLE_CHILD =
111
+ 'a[href],button,input,select,textarea,summary,[contenteditable=""],[contenteditable="true"],[tabindex]:not([tabindex^="-"])';
112
+
113
+ /** Does this subtree already offer the keyboard a way in? Slotted content is
114
+ * checked through `assignedElements()`, because a `<slot>` in the shadow tree
115
+ * contains none of the light-DOM nodes it projects — the `kai-hover-card`
116
+ * facade wraps a bare `<slot />`, so without this branch every use of that
117
+ * element would be told its children are inert.
118
+ *
119
+ * ★ WHEN this runs is as load-bearing as what it asks. At ref time the facade's
120
+ * trigger contains `<slot></slot>` with NOTHING assigned yet, so a single call
121
+ * from the ref answers "inert" for a slot that is about to receive a focusable
122
+ * `<a>` — and the trigger stamps a second tab stop on top of the child's. That
123
+ * is the very regression the `assignedElements()` branch was added to prevent,
124
+ * reintroduced by timing rather than by logic. Measured with a real Tab key:
125
+ * 4 stops across a 3-widget fixture. So the caller re-runs this on
126
+ * `slotchange`; see `HoverCardTrigger`. */
127
+ const hasFocusableChild = (root: HTMLElement): boolean => {
128
+ if (root.querySelector(FOCUSABLE_CHILD)) return true;
129
+ return Array.from(root.querySelectorAll('slot')).some((slot) =>
130
+ (slot as HTMLSlotElement).assignedElements().some(
131
+ (el) => el.matches(FOCUSABLE_CHILD) || el.querySelector(FOCUSABLE_CHILD),
132
+ ),
133
+ );
134
+ };
83
135
 
136
+ /**
137
+ * ★ THE TRIGGER IS A TAB STOP WHEN, AND ONLY WHEN, ITS CHILDREN ARE NOT.
138
+ *
139
+ * `onFocusIn`/`onFocusOut` were here from the start and the focus-open path was
140
+ * plainly intended, but the span never set `tabindex`, so the Tab key could not
141
+ * land on it and neither handler could fire on a keyboard. It went unnoticed for
142
+ * as long as it did because `focusin` BUBBLES and every consumer at the time put
143
+ * something focusable inside the trigger — `source.tsx` an `<a>`, `context.tsx`
144
+ * a `<Button>` — so the card opened via the child and the span's own inertness
145
+ * never showed. The first trigger with inert children (an attachment tile: a
146
+ * div, an img, an svg) had no tab stop anywhere in it.
147
+ *
148
+ * Hence delegation rather than an unconditional `tabindex`: adding one always
149
+ * would give every existing consumer TWO stops for one card, which is a worse
150
+ * bug than the one being fixed. The check runs in the ref AND again on every
151
+ * `slotchange`, because slot assignment happens after the ref and a one-shot
152
+ * check calls a not-yet-filled `<slot>` inert (see `hasFocusableChild`).
153
+ *
154
+ * ★ THE FOCUS LISTENERS ARE NATIVE, NOT SOLID'S DELEGATED `onFocusIn`. Solid
155
+ * delegates a fixed set of events from the document and retargets them into
156
+ * component trees; inside these shadow roots that path runs for a PROGRAMMATIC
157
+ * `.focus()` and, in the deeper trees, not for a real Tab. Measured: tabbing to
158
+ * an attachment tile in a mounted `<kai-chat>` left the card shut and
159
+ * `aria-describedby` null while `.focus()` on the same element opened it — so
160
+ * every keyboard user got a tab stop that announced nothing and showed nothing,
161
+ * which is worse than no stop at all. `addEventListener` in the ref does not
162
+ * care how focus arrived. Anything in this kit relying on delegated focus
163
+ * events inside a shadow root is suspect for the same reason.
164
+ *
165
+ * `aria-describedby` is what makes the stop worth arriving at: the card is
166
+ * DESCRIPTIVE, not an action, so the trigger gets no `role="button"` — that
167
+ * would promise an activation that does not exist — and instead points at the
168
+ * content it reveals so a screen reader reads it out.
169
+ */
84
170
  export function HoverCardTrigger(props: HoverCardTriggerProps) {
85
171
  const ctx = useHoverCard();
172
+ const [detectedInert, setDetectedInert] = createSignal(false);
173
+ // `&& !disabled` last, and it beats an explicit `focusable` — a disabled card
174
+ // early-returns out of `enter()`, so a stop here could never open anything.
175
+ // Landing on it would announce nothing and reveal nothing, which is the dead
176
+ // stop the note above argues against; forcing one is not an escape hatch
177
+ // anybody should get.
178
+ const isFocusable = () => (props.focusable ?? detectedInert()) && !ctx.disabled();
179
+
86
180
  return (
87
181
  <As
88
182
  as="span"
89
- class={cn('inline-block', props.class)}
90
- ref={ctx.setTrigger}
183
+ class={cn(
184
+ 'inline-block',
185
+ // A tab stop nobody can SEE is barely an improvement on no tab stop —
186
+ // WCAG 2.4.7. Only when this element is the stop; a delegating trigger
187
+ // must not draw a ring around its child's own focus state.
188
+ //
189
+ // AN OUTLINE, NOT A RING, and both halves of that were measured rather
190
+ // than assumed:
191
+ //
192
+ // 1. ★ A TAILWIND v4 UTILITY THAT ROUTES THROUGH `@property` IS INERT
193
+ // INSIDE THESE SHADOW ROOTS, and that is a trap far wider than this
194
+ // line. v4 gives `--tw-*` custom properties their defaults with
195
+ // `@property`, and an `@property` rule delivered through a shadow
196
+ // root's `adoptedStyleSheets` never registers — so the var resolves
197
+ // to nothing, the declaration is invalid, and the property falls
198
+ // back to its initial value. Measured in Chrome, twice:
199
+ // `ring-2 ring-offset-1` set `--tw-ring-shadow` correctly and
200
+ // computed `box-shadow: none`; `inset-ring-2` did the same; and
201
+ // `outline-2` alone computed `outline-style: none` while width,
202
+ // colour and offset all landed, because only the style goes
203
+ // through `var(--tw-outline-style)`. Hence the literal
204
+ // `[outline-style:solid]` — the one part that cannot be a var.
205
+ // 2. The offset is NEGATIVE so the outline lands inside the border
206
+ // box. A trigger that fills its container (`block size-full` inside
207
+ // the attachment tile's `overflow-hidden rounded-lg`) would
208
+ // otherwise have its focus indicator painted straight into the
209
+ // clip, and a trigger cannot know whether its container clips.
210
+ isFocusable() &&
211
+ 'rounded-sm focus-visible:[outline-style:solid] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring',
212
+ props.class,
213
+ )}
214
+ ref={(el: HTMLElement) => {
215
+ ctx.setTrigger(el);
216
+
217
+ // Native listeners, deliberately — see the note above. `focusin` /
218
+ // `focusout` rather than `focus` / `blur` because these have to fire
219
+ // for a focusable CHILD too, and only the -in/-out pair bubbles.
220
+ const enter = () => ctx.enter();
221
+ const leave = () => ctx.leave();
222
+ el.addEventListener('focusin', enter);
223
+ el.addEventListener('focusout', leave);
224
+ onCleanup(() => {
225
+ el.removeEventListener('focusin', enter);
226
+ el.removeEventListener('focusout', leave);
227
+ });
228
+
229
+ // Ask now for the direct-children case, and again whenever a slot is
230
+ // filled or emptied. Both are needed: `slotchange` never fires for a
231
+ // trigger with no slot in it, and the ref-time answer is wrong for one
232
+ // that has.
233
+ const evaluate = () => setDetectedInert(!hasFocusableChild(el));
234
+ evaluate();
235
+ for (const slot of Array.from(el.querySelectorAll('slot'))) {
236
+ slot.addEventListener('slotchange', evaluate);
237
+ onCleanup(() => slot.removeEventListener('slotchange', evaluate));
238
+ }
239
+ }}
240
+ tabIndex={isFocusable() ? 0 : undefined}
241
+ aria-describedby={isFocusable() && ctx.open() ? ctx.contentId : undefined}
91
242
  onPointerEnter={ctx.enter}
92
243
  onPointerLeave={ctx.leave}
93
- onFocusIn={ctx.enter}
94
- onFocusOut={ctx.leave}
95
244
  >
96
245
  {props.children}
97
246
  </As>
@@ -166,6 +315,7 @@ export function HoverCardContent(props: HoverCardContentProps) {
166
315
  >
167
316
  {/* Inner card: all visual + animation classes and the presence state. */}
168
317
  <div
318
+ id={ctx.contentId}
169
319
  data-expanded={presence.state() === 'open' ? '' : undefined}
170
320
  data-closed={presence.state() === 'closed' ? '' : undefined}
171
321
  class={cn(
package/src/ui/icon.tsx CHANGED
@@ -9,6 +9,16 @@ import {
9
9
  Archive, ArrowUp, Bookmark, Copy, Ellipsis, ExternalLink, Flag,
10
10
  GitBranch, GitPullRequest, ListFilter, RotateCw,
11
11
  Moon, Sun,
12
+ // Device / viewport glyphs.
13
+ Tablet, Smartphone, Laptop,
14
+ // Direction glyphs completing the chevron + arrow quartets.
15
+ ChevronRight, ChevronUp, ArrowRight, ArrowDown, PanelRight,
16
+ // Status glyphs (kai-notice / kai-toast / kai-tool render these already).
17
+ Check, Info, TriangleAlert, CircleCheck, CircleAlert, CircleX,
18
+ // Action glyphs the kit performs but could not be named.
19
+ Trash2, Download, Upload, Link, RotateCcw,
20
+ // Pane / media chrome.
21
+ Maximize2, Minimize2, Eye, EyeOff, Bell, Square, Minus, Play,
12
22
  } from 'lucide-solid';
13
23
 
14
24
  type IconComponent = Component<{ class?: string }>;
@@ -68,8 +78,51 @@ const NAMED_ICONS: Record<string, IconComponent> = {
68
78
  // Theme / appearance glyphs.
69
79
  moon: Moon,
70
80
  sun: Sun,
81
+ // Device / viewport glyphs. `desktop` and `mobile` are aliases onto the same
82
+ // components as `monitor`/`smartphone` — lucide's names are not the words a
83
+ // consumer reaches for, and an alias costs nothing in the bundle.
84
+ desktop: Monitor,
85
+ laptop: Laptop,
86
+ tablet: Tablet,
87
+ smartphone: Smartphone,
88
+ mobile: Smartphone,
89
+ // Direction glyphs — chevron-down/left and arrow-left/up were here without
90
+ // their opposites; both quartets are now complete.
91
+ 'chevron-right': ChevronRight,
92
+ 'chevron-up': ChevronUp,
93
+ 'arrow-right': ArrowRight,
94
+ 'arrow-down': ArrowDown,
95
+ 'panel-right': PanelRight,
96
+ // Status glyphs. kai-notice / kai-toast / kai-tool already paint these, so a
97
+ // host building matching UI needs to be able to name them.
98
+ check: Check,
99
+ info: Info,
100
+ 'triangle-alert': TriangleAlert,
101
+ 'circle-check': CircleCheck,
102
+ 'circle-alert': CircleAlert,
103
+ 'circle-x': CircleX,
104
+ // Action glyphs. `trash`/`download`/`link` match the keys action-icons.ts
105
+ // already uses for message actions; `upload` is download's missing sibling.
106
+ trash: Trash2,
107
+ download: Download,
108
+ upload: Upload,
109
+ link: Link,
110
+ 'rotate-ccw': RotateCcw,
111
+ // Pane / media chrome (kai-pane, kai-artifact, kai-embed, kai-agent-card).
112
+ 'maximize-2': Maximize2,
113
+ 'minimize-2': Minimize2,
114
+ eye: Eye,
115
+ 'eye-off': EyeOff,
116
+ bell: Bell,
117
+ square: Square,
118
+ minus: Minus,
119
+ play: Play,
71
120
  };
72
121
 
122
+ /** Every curated icon name, sorted. Derived from `NAMED_ICONS` so a gallery,
123
+ * a story control or a test can never drift from the map itself. */
124
+ export const ICON_NAMES: readonly string[] = Object.keys(NAMED_ICONS).sort();
125
+
73
126
  /** Render an item icon.
74
127
  *
75
128
  * Resolution order: