@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
@@ -14,6 +14,7 @@ import {
14
14
  } from 'solid-js';
15
15
  import { cn } from '../utils/cn';
16
16
  import { Button } from '../ui/button';
17
+ import { Checkbox } from '../ui/checkbox';
17
18
  import { ProgressBar } from '../ui/progress-bar';
18
19
  import { Card } from './card';
19
20
  import { DismissedStub } from './dismissed-stub';
@@ -21,7 +22,7 @@ import type { CardEnvelope, CardEvent, CardHost, CardResolution } from '../primi
21
22
  import { useCardResolution } from './use-card-resolution';
22
23
  import { emitCardEvent } from '../primitives/card-routing';
23
24
  import { useCardHost } from '../primitives/card-host';
24
- import { Check, Circle, CircleCheck, X } from 'lucide-solid';
25
+ import { Check, Circle, CircleCheck } from 'lucide-solid';
25
26
 
26
27
  // ─────────────────────────────────────────────────────────────────────────────
27
28
  // Types (tasks.schema.json) — AUTHORED IN ../primitives/card-data-types.ts.
@@ -415,14 +416,23 @@ export function TasksCard(props: TasksCardProps): JSX.Element {
415
416
  progress() || res.isResolved() || readOnly() ? undefined : (
416
417
  <div class="flex w-full flex-wrap items-center justify-between gap-2">
417
418
  <Show when={local.data?.dismissible === true}>
419
+ {/* The contract `dismiss` verb — a footer ACTION (it emits
420
+ `{kind:'dismiss'}` and collapses the card to a re-openable
421
+ stub), not the chrome close in `Card`'s own `dismissible`
422
+ prop, which the contract cards deliberately never set.
423
+ Labelled rather than a bare ghost ✕: every other control in
424
+ this row says what it does, and an unlabelled 28px glyph
425
+ 300px from the primary button reads as stray chrome. Same
426
+ shape in all four cards — `form` already looked like this.
427
+ `aria-label` is kept (redundant with the text) so the
428
+ element tests that select on it keep working. */}
418
429
  <Button
419
430
  type="button"
420
431
  variant="ghost"
421
- size="icon-sm"
422
432
  aria-label="Dismiss"
423
433
  onClick={onDismiss}
424
434
  >
425
- <X size={16} aria-hidden="true" />
435
+ Dismiss
426
436
  </Button>
427
437
  </Show>
428
438
  <span id={countId} aria-live="polite" class="text-xs text-muted-foreground">
@@ -485,7 +495,7 @@ export function TasksCard(props: TasksCardProps): JSX.Element {
485
495
  if (confirmEnabled()) onConfirm();
486
496
  }}
487
497
  >
488
- <div class="divide-y divide-border overflow-hidden rounded-lg border border-input">
498
+ <div class="divide-y divide-border overflow-hidden rounded-lg border border-border">
489
499
  <Show when={showMaster()}>
490
500
  {(() => {
491
501
  const indeterminate = () => masterState() === 'indeterminate';
@@ -501,17 +511,20 @@ export function TasksCard(props: TasksCardProps): JSX.Element {
501
511
  : 'text-foreground hover:bg-muted/50',
502
512
  )}
503
513
  >
504
- <input
505
- type="checkbox"
506
- class="kai-checkbox"
514
+ {/* SQUARE `Checkbox` (the `.kai-checkbox` look), and the checklist below draws a
515
+ ROUND lucide Circle/CircleCheck instead. Deliberate, not drift: this
516
+ is `select` mode — a pick-list whose rows are a pending selection that
517
+ only commits when the confirm button fires, which is what a checkbox
518
+ means everywhere else. `progress` mode has no confirm and toggling a
519
+ row IS the action, so it uses the done/not-done affordance of a todo
520
+ list. See the D-2 row in
521
+ docs/superpowers/plans/2026-08-25-form-control-primitives.md; owner has
522
+ not ruled, so do not converge these on your own. */}
523
+ <Checkbox
507
524
  checked={masterState() === 'checked'}
508
525
  disabled={groupDisabled() || readOnly()}
526
+ indeterminate={indeterminate()}
509
527
  aria-checked={indeterminate() ? 'mixed' : masterState() === 'checked'}
510
- ref={(el) => {
511
- createEffect(() => {
512
- el.indeterminate = indeterminate();
513
- });
514
- }}
515
528
  onChange={(e) => toggleAll(e.currentTarget.checked)}
516
529
  />
517
530
  <span>Select all</span>
@@ -541,9 +554,8 @@ export function TasksCard(props: TasksCardProps): JSX.Element {
541
554
  )}
542
555
  data-task-id={task.id}
543
556
  >
544
- <input
545
- type="checkbox"
546
- class="kai-checkbox mt-0.5"
557
+ <Checkbox
558
+ class="mt-0.5"
547
559
  checked={checked()}
548
560
  disabled={blocked() || readOnly()}
549
561
  aria-disabled={blocked() ? 'true' : undefined}
@@ -603,7 +615,10 @@ function ProgressChecklist(props: {
603
615
  <div class={cn('flex flex-col gap-3', props.groupClass)}>
604
616
  <Show when={props.heading}>
605
617
  <div class="flex items-center justify-between gap-3">
606
- <span id={props.headingId} class="text-sm font-medium text-foreground">
618
+ <span
619
+ id={props.headingId}
620
+ class="text-title font-semibold tracking-tight text-foreground"
621
+ >
607
622
  {props.heading}
608
623
  </span>
609
624
  <span
@@ -646,8 +661,23 @@ function ProgressChecklist(props: {
646
661
  // row doesn't leave a noisy persistent outline — and it makes a
647
662
  // Tab-focused completed row's open description perceivable. Dropped
648
663
  // entirely when readonly (the row isn't focusable then).
664
+ //
665
+ // TWO tab stops exist per row and the ring has to cover both.
666
+ // `focus-visible:` matches only when the LABEL itself is focused,
667
+ // which is completed rows only (the `tabindex` below). Every row's
668
+ // other — and for an incomplete row, only — tab stop is the sr-only
669
+ // checkbox inside, so without `has-[:focus-visible]:` tabbing to an
670
+ // unchecked row put focus on an off-screen input with no indicator
671
+ // anywhere on screen. Both halves stay on the focus-VISIBLE axis on
672
+ // purpose: `focus-within:` would also fire for a mouse click (which
673
+ // forwards focus from the label to the checkbox), which is exactly
674
+ // the persistent-outline noise the paragraph above rejects. The
675
+ // description reveal below uses `group-focus-within:` because a
676
+ // hover-or-focus reveal wants the looser condition.
649
677
  !props.readonly &&
650
678
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring',
679
+ !props.readonly &&
680
+ 'has-[:focus-visible]:outline-none has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-inset has-[:focus-visible]:ring-ring',
651
681
  props.readonly
652
682
  ? 'cursor-default'
653
683
  : blocked()
@@ -680,6 +710,18 @@ function ProgressChecklist(props: {
680
710
  )}
681
711
  aria-hidden="true"
682
712
  >
713
+ {/* ROUND, where `select` mode uses the square `Checkbox`. Deliberate:
714
+ this mode has no confirm button, so ticking a row IS the action and the
715
+ row is done-or-not rather than selected-or-not — the todo-list
716
+ affordance, which is how this mode has been described since it landed
717
+ (see the `mode` argType in tasks-card.stories.tsx and the JSDoc on
718
+ src/elements/tasks.tsx). D-2 in
719
+ docs/superpowers/plans/2026-08-25-form-control-primitives.md tracks
720
+ whether to keep the split; unruled, so leave both. NOTE the cost: the
721
+ icon replaces the visible native control, which is why the real input
722
+ above is `sr-only` and why the row's focus ring has to be driven by
723
+ `has-[:focus-visible]:`. Converging on `Checkbox` would take that
724
+ hazard away with it. */}
683
725
  <Show
684
726
  when={checked()}
685
727
  fallback={<Circle size={18} />}
@@ -34,6 +34,11 @@ export interface ThreadProps {
34
34
  * the kit checks its own seven built-ins and leaves your own card type
35
35
  * unvalidated. A schema here WINS over a built-in of the same name. */
36
36
  cardSchemas?: CardSchemaMap;
37
+ /** The custom-element host node to emit card events off when no `CardProvider`
38
+ * is present, forwarded through `MessageBody` to `CardRenderer`. The
39
+ * element facades pass their own host element so card events leave as the
40
+ * bubbling `kai-card` CustomEvent. */
41
+ cardHostElement?: HTMLElement;
37
42
  /** Show a typing indicator on the pending assistant turn — use while awaiting
38
43
  * the assistant's reply. */
39
44
  loading?: boolean;
@@ -146,6 +151,7 @@ export function Thread(props: ThreadProps) {
146
151
  parts={m().parts}
147
152
  cardTypes={props.cardTypes}
148
153
  cardSchemas={props.cardSchemas}
154
+ cardHostElement={props.cardHostElement}
149
155
  isUser={m().role === 'user'}
150
156
  markdown={m().role === 'assistant'}
151
157
  actions={m().actions}
@@ -209,7 +215,10 @@ export function Thread(props: ThreadProps) {
209
215
  </ChatContainerContent>
210
216
  <Show when={showScrollButton()}>
211
217
  <div class="absolute bottom-4 left-1/2 flex w-full max-w-3xl -translate-x-1/2 justify-center px-5">
212
- <ScrollButton class="shadow-sm" />
218
+ {/* The button now owns its elevation (kai-elevation); a `shadow-sm`
219
+ here would set box-shadow a second time and the winner would
220
+ be stylesheet order, not this call site. */}
221
+ <ScrollButton />
213
222
  </div>
214
223
  </Show>
215
224
  </ChatContainer>
@@ -320,11 +320,17 @@ function anchorStyle(position: ToastPosition, r: TargetRect): Record<string, str
320
320
  }
321
321
  }
322
322
 
323
+ /** The overlay layer both region roots sit on. One token, one default:
324
+ * your app chrome must stay below the toast layer; set `--kai-toast-z`
325
+ * (default 100) on the host to move the layer. */
326
+ const TOAST_Z = 'var(--kai-toast-z, 100)';
327
+
323
328
  /**
324
329
  * Renders a stack of toasts at a viewport edge. Caps the visible count at `max`
325
330
  * (newest on top); overflow waits in a queue and is promoted as visible toasts
326
331
  * leave. `aria-live=polite` / `role=region` so screen readers announce new
327
- * toasts without stealing focus.
332
+ * toasts without stealing focus. Your app chrome must stay below the toast
333
+ * layer; set `--kai-toast-z` (default 100) on the host to move the layer.
328
334
  */
329
335
  export function ToastRegion(props: ToastRegionProps) {
330
336
  const max = () => props.max ?? 3;
@@ -425,12 +431,12 @@ export function ToastRegion(props: ToastRegionProps) {
425
431
  aria-live="polite"
426
432
  data-stack="expanded"
427
433
  class={cn(
428
- 'pointer-events-none fixed z-[100] flex flex-col gap-2',
434
+ 'pointer-events-none fixed flex flex-col gap-2',
429
435
  anchor()
430
436
  ? ANCHOR_FLEX[position()]
431
437
  : cn('max-w-[min(30rem,calc(100vw-2rem))]', POSITION_CLASSES[position()]),
432
438
  )}
433
- style={anchor() ? anchorStyle(position(), anchor()!) : undefined}
439
+ style={{ 'z-index': TOAST_Z, ...(anchor() ? anchorStyle(position(), anchor()!) : undefined) }}
434
440
  >
435
441
  <For each={visible()}>
436
442
  {(item) => (
@@ -456,8 +462,8 @@ export function ToastRegion(props: ToastRegionProps) {
456
462
  onPointerLeave={() => setHovered(false)}
457
463
  onFocusIn={() => setFocused(true)}
458
464
  onFocusOut={() => setFocused(false)}
459
- class={cn('pointer-events-none fixed z-[100]', !anchor() && POSITION_CLASSES[position()])}
460
- style={anchor() ? anchorStyle(position(), anchor()!) : undefined}
465
+ class={cn('pointer-events-none fixed', !anchor() && POSITION_CLASSES[position()])}
466
+ style={{ 'z-index': TOAST_Z, ...(anchor() ? anchorStyle(position(), anchor()!) : undefined) }}
461
467
  >
462
468
  {/* Relative stage with an explicit height so it's ONE continuous hover
463
469
  surface (pointer-events-auto) — no flicker when the cursor crosses gaps
@@ -25,6 +25,15 @@ export interface ToolPart {
25
25
  type: string;
26
26
  /** Semantic classification for rendering. Derive with classifyTool(type). */
27
27
  kind?: ToolKind;
28
+ /** The call's lifecycle. A provider stream (real or `createMockResponder`)
29
+ * only ever ANNOUNCES a call: arguments stream in (`input-streaming`), then
30
+ * the part parks at `input-available` — and stays there. The kit parses and
31
+ * renders the call; EXECUTING it and answering is the host's side of the
32
+ * seam: after the read settles, run the tool and patch the part forward with
33
+ * `stream.upsertTool(id, { state: 'output-available', output })` (or
34
+ * `upsertToolPart`), or `output-error` + `errorText` on failure. The one
35
+ * exception is a call the provider ran itself (`raw` carries the result and
36
+ * the wire sets `output` directly) — never re-execute those. */
28
37
  state: 'input-streaming' | 'input-available' | 'output-available' | 'output-error';
29
38
  /** Last VALID parsed snapshot, fingerprint-deduped. The primary channel: this is
30
39
  * what the kit renders. */
@@ -38,6 +38,10 @@ export interface VoiceInputProps {
38
38
  /** Receive the imperative controller once mounted. The `<kai-voice-input>`
39
39
  * facade forwards these as element methods (start/stop). */
40
40
  controllerRef?: (controller: VoiceInputController) => void;
41
+ /** A recognition session failed or produced nothing. `error` is the platform
42
+ * error code, the thrown exception's name, or `no-result` when the session
43
+ * ended with no error and no text. The facade maps this to kai-voice-error. */
44
+ onError?: (detail: { source: 'recognition'; error: string; message: string }) => void;
41
45
  }
42
46
 
43
47
  export function VoiceInput(props: VoiceInputProps) {
@@ -59,15 +63,43 @@ export function VoiceInput(props: VoiceInputProps) {
59
63
 
60
64
  // Native path: SpeechRecognition resolves with the final transcript when it
61
65
  // ends (click again or controller.stop()). Final text → onTranscription.
66
+ // Every other outcome reaches onError, so a session can never end in silence
67
+ // (W4 symptom 1: runtime errors set a signal nothing read, and an empty
68
+ // result fired no event at all).
62
69
  async function beginRecognition() {
70
+ let errored = false;
63
71
  try {
64
72
  const text = await speech.start({
65
73
  lang: props.lang,
66
74
  onInterim: props.interim ? (t) => props.onInterim?.(t) : undefined,
75
+ onError: (error, message) => {
76
+ errored = true;
77
+ props.onError?.({ source: 'recognition', error, message });
78
+ },
67
79
  });
68
- if (text.trim()) local.onTranscription(text.trim());
69
- } catch {
70
- /* surfaced via speech.error; the mic returns to idle */
80
+ if (text.trim()) {
81
+ local.onTranscription(text.trim());
82
+ } else if (!errored) {
83
+ // No error, no text: the session ended cleanly having heard nothing.
84
+ // Reported through the SAME error event (as `no-result`) rather than an
85
+ // empty kai-transcription, so kai-transcription keeps its contract
86
+ // (final text exists) and one listener covers every failed session; the
87
+ // code distinguishes "user said nothing" from "recognition broke".
88
+ props.onError?.({
89
+ source: 'recognition',
90
+ error: 'no-result',
91
+ message: 'Speech recognition ended without capturing any text',
92
+ });
93
+ }
94
+ } catch (err) {
95
+ // start() itself threw (unsupported, or the platform refused to start).
96
+ if (!errored) {
97
+ props.onError?.({
98
+ source: 'recognition',
99
+ error: err instanceof Error ? err.name : 'unknown',
100
+ message: err instanceof Error ? err.message : String(err),
101
+ });
102
+ }
71
103
  }
72
104
  }
73
105
 
@@ -26,8 +26,15 @@ export interface VoiceOutputProps {
26
26
  /** TTS model seam: given text, return an audio Blob to play. When set, the
27
27
  * native speechSynthesis path is bypassed. Mirrors VoiceInput's `onTranscribe`. */
28
28
  onSynthesize?: (text: string) => Promise<Blob>;
29
- /** Fires whenever playback starts or stops. */
29
+ /** Fires whenever playback starts or stops. `speaking: true` means audio has
30
+ * actually started (utterance.onstart on the native path; audio.play()
31
+ * resolving on the model path), not that speak() was called. */
30
32
  onSpeakingChange?: (speaking: boolean) => void;
33
+ /** Synthesis failed: a native utterance error, a rejecting `onSynthesize`, or
34
+ * audio playback failing to start. `error` is the platform error code or the
35
+ * exception's name. Deliberate cancellation (stop(), a new speak()) is not a
36
+ * failure and does not fire. The facade maps this to kai-voice-error. */
37
+ onError?: (detail: { source: 'synthesis'; error: string; message: string }) => void;
31
38
  /** Fires once the model path resolves audio (model path only). */
32
39
  onSynthesized?: (blob: Blob) => void;
33
40
  disabled?: boolean;
@@ -68,13 +75,30 @@ export function VoiceOutput(props: VoiceOutputProps) {
68
75
  }
69
76
 
70
77
  // Native path: speechSynthesis.speak(new SpeechSynthesisUtterance(text)).
78
+ // isSpeaking waits for utterance.onstart, so onSpeakingChange(true) means the
79
+ // audio actually began. It used to flip optimistically before speak(), which
80
+ // made "speaking" mean "speak() was called" and raced consumers watching for
81
+ // playback to start (W4 symptom 3).
71
82
  function speakNative() {
72
83
  if (!hasSpeechSynthesis() || !local.text) return;
73
84
  window.speechSynthesis.cancel();
74
85
  utterance = new SpeechSynthesisUtterance(local.text);
86
+ utterance.onstart = () => setIsSpeaking(true);
75
87
  utterance.onend = () => setIsSpeaking(false);
76
- utterance.onerror = () => setIsSpeaking(false);
77
- setIsSpeaking(true);
88
+ utterance.onerror = (event) => {
89
+ setIsSpeaking(false);
90
+ const code = event?.error ?? 'unknown';
91
+ // `canceled`/`interrupted` are what the platform reports when the app
92
+ // itself cancels (stop(), or a new speak() pre-empting): deliberate, not
93
+ // a failure. Everything else surfaces (W4 traced a `not-allowed` here
94
+ // that vanished without a word).
95
+ if (code === 'canceled' || code === 'interrupted') return;
96
+ props.onError?.({
97
+ source: 'synthesis',
98
+ error: code,
99
+ message: `Speech synthesis error: ${code}`,
100
+ });
101
+ };
78
102
  window.speechSynthesis.speak(utterance);
79
103
  }
80
104
 
@@ -90,11 +114,27 @@ export function VoiceOutput(props: VoiceOutputProps) {
90
114
  audio ??= new Audio();
91
115
  audio.src = objectUrl;
92
116
  audio.onended = () => setIsSpeaking(false);
93
- audio.onerror = () => setIsSpeaking(false);
94
- setIsSpeaking(true);
117
+ audio.onerror = () => {
118
+ setIsSpeaking(false);
119
+ props.onError?.({
120
+ source: 'synthesis',
121
+ error: 'playback-failed',
122
+ message: 'The synthesized audio failed to play',
123
+ });
124
+ };
95
125
  await audio.play();
96
- } catch {
126
+ // play() resolving is this path's "audio actually started" (the model
127
+ // path's counterpart of utterance.onstart), so isSpeaking flips only now.
128
+ setIsSpeaking(true);
129
+ } catch (err) {
130
+ // onSynthesize rejected, or play() did (e.g. NotAllowedError under an
131
+ // autoplay policy). Either way the reply is silent: say so.
97
132
  setIsSpeaking(false);
133
+ props.onError?.({
134
+ source: 'synthesis',
135
+ error: err instanceof Error ? err.name : 'unknown',
136
+ message: err instanceof Error ? err.message : String(err),
137
+ });
98
138
  } finally {
99
139
  setIsLoading(false);
100
140
  }
@@ -0,0 +1,285 @@
1
+ import { Show, createSignal, createMemo, onMount, onCleanup, type JSX } from 'solid-js';
2
+ import { createControllableSignal } from '../primitives/controllable';
3
+ import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '../ui/resizable';
4
+ import { cn } from '../utils/cn';
5
+
6
+ /** The two aside columns of the shell, named logically (`start` follows the
7
+ * writing direction; on an RTL page it is the right column). */
8
+ export type WorkspaceAsideSide = 'start' | 'end';
9
+
10
+ /** Detail of an aside collapse/expand, however it happened (a method, the
11
+ * breakpoint, the drawer's Escape). */
12
+ export interface WorkspaceAsideToggleDetail {
13
+ side: WorkspaceAsideSide;
14
+ collapsed: boolean;
15
+ }
16
+
17
+ /** Detail of an aside resize (fires per drag step, keyboard nudge, or a handle double-click reset), width in px. */
18
+ export interface WorkspaceAsideResizeDetail {
19
+ side: WorkspaceAsideSide;
20
+ width: number;
21
+ }
22
+
23
+ /** Imperative surface exposed through `controllerRef` (the facade forwards it
24
+ * as element methods). */
25
+ export interface WorkspaceShellController {
26
+ /** Collapse/expand one aside (reports through onAsideToggle). */
27
+ toggleAside(side: WorkspaceAsideSide): void;
28
+ /** Force one aside collapsed (reports through onAsideToggle). */
29
+ collapseAside(side: WorkspaceAsideSide): void;
30
+ /** Force one aside expanded (reports through onAsideToggle). */
31
+ expandAside(side: WorkspaceAsideSide): void;
32
+ }
33
+
34
+ export interface WorkspaceShellProps {
35
+ /** Header band content. The region renders only when this is provided. */
36
+ header?: JSX.Element;
37
+ /** Start aside content (the inline-start column: a rail, a nav, a file tree).
38
+ * The region renders only when this is provided. */
39
+ start?: JSX.Element;
40
+ /** End aside content (the inline-end column: inspector, notes, preview).
41
+ * The region renders only when this is provided. */
42
+ end?: JSX.Element;
43
+ /** Footer band content. The region renders only when this is provided. */
44
+ footer?: JSX.Element;
45
+ /** Main region content. Always rendered. */
46
+ children?: JSX.Element;
47
+
48
+ /** Controlled collapsed state of the start aside. Omit for uncontrolled. */
49
+ startCollapsed?: boolean;
50
+ /** Initial collapsed state of the start aside when uncontrolled (default false). */
51
+ defaultStartCollapsed?: boolean;
52
+ /** Controlled collapsed state of the end aside. Omit for uncontrolled. */
53
+ endCollapsed?: boolean;
54
+ /** Initial collapsed state of the end aside when uncontrolled (default false). */
55
+ defaultEndCollapsed?: boolean;
56
+
57
+ /** Start aside default width in px (default 280). */
58
+ startWidth?: number;
59
+ /** Start aside minimum width in px during resize (default 200). */
60
+ startMinWidth?: number;
61
+ /** Start aside maximum width in px during resize (default 480). */
62
+ startMaxWidth?: number;
63
+ /** End aside default width in px (default 320). */
64
+ endWidth?: number;
65
+ /** End aside minimum width in px during resize (default 200). */
66
+ endMinWidth?: number;
67
+ /** End aside maximum width in px during resize (default 480). */
68
+ endMaxWidth?: number;
69
+
70
+ /** Auto-collapse both asides when the shell's own width drops below this many
71
+ * px, and re-expand when it grows back above. Applies to uncontrolled asides
72
+ * only (it never fights an app-driven collapsed prop); omit to disable. */
73
+ collapseBelow?: number;
74
+ /** Below this shell width in px, an expanded aside renders as an overlay
75
+ * drawer over the main region instead of a column beside it. Escape inside
76
+ * the drawer closes it and returns focus to the element focused before it
77
+ * opened. Omit to disable. */
78
+ drawerBelow?: number;
79
+ /** Density hint. Reflected as a `data-compact` hook on the root (and as the
80
+ * `compact` attribute on the element) for your CSS and slotted content; the
81
+ * shell itself keeps no other opinion about density. */
82
+ compact?: boolean;
83
+
84
+ /** An aside collapsed or expanded (any path: method, breakpoint, drawer Escape). */
85
+ onAsideToggle?: (detail: WorkspaceAsideToggleDetail) => void;
86
+ /** The aside was resized (fires per drag step, keyboard nudge, or a handle double-click reset), width in px. */
87
+ onAsideResize?: (detail: WorkspaceAsideResizeDetail) => void;
88
+
89
+ /** Receives the imperative controller once on mount. */
90
+ controllerRef?: (controller: WorkspaceShellController) => void;
91
+ class?: string;
92
+ }
93
+
94
+ /**
95
+ * The chat-agnostic workspace layout shell: five regions (header · start aside ·
96
+ * main · end aside · footer) with resize handles between the columns, per-aside
97
+ * controlled/uncontrolled collapse, collapse-below-breakpoint, and a mobile
98
+ * drawer mode for the asides. It knows nothing about chat: a file tree in
99
+ * `start` is as valid as a conversation rail.
100
+ */
101
+ export function WorkspaceShell(props: WorkspaceShellProps) {
102
+ let rootEl!: HTMLDivElement;
103
+
104
+ // --- host width (drives collapseBelow + drawerBelow) ---
105
+ const [hostWidth, setHostWidth] = createSignal<number | undefined>(undefined);
106
+
107
+ // --- per-aside collapse: controlled wins, otherwise internal state ---
108
+ const [startCollapsed, setStartCollapsed] = createControllableSignal(
109
+ () => props.startCollapsed,
110
+ props.defaultStartCollapsed ?? false,
111
+ );
112
+ const [endCollapsed, setEndCollapsed] = createControllableSignal(
113
+ () => props.endCollapsed,
114
+ props.defaultEndCollapsed ?? false,
115
+ );
116
+ const collapsed = (side: WorkspaceAsideSide) => (side === 'start' ? startCollapsed() : endCollapsed());
117
+ const isControlled = (side: WorkspaceAsideSide) =>
118
+ (side === 'start' ? props.startCollapsed : props.endCollapsed) !== undefined;
119
+
120
+ const setCollapsedTo = (side: WorkspaceAsideSide, next: boolean) => {
121
+ if (side === 'start') setStartCollapsed(next); else setEndCollapsed(next);
122
+ if (next) restoreDrawerFocus(side);
123
+ props.onAsideToggle?.({ side, collapsed: next });
124
+ };
125
+
126
+ // --- drawer mode + focus return ---
127
+ const drawerMode = createMemo(() => {
128
+ const below = props.drawerBelow;
129
+ const width = hostWidth();
130
+ return below != null && width != null && width < below;
131
+ });
132
+ // The element focused when a drawer opened, restored on close. Per side, so a
133
+ // start drawer closing never steals focus a later end drawer captured.
134
+ const drawerOpeners: Partial<Record<WorkspaceAsideSide, HTMLElement>> = {};
135
+ const captureDrawerFocus = (side: WorkspaceAsideSide) => {
136
+ if (!drawerMode()) return;
137
+ const active = document.activeElement;
138
+ if (active instanceof HTMLElement) drawerOpeners[side] = active;
139
+ };
140
+ const restoreDrawerFocus = (side: WorkspaceAsideSide) => {
141
+ const opener = drawerOpeners[side];
142
+ delete drawerOpeners[side];
143
+ if (drawerMode() && opener && opener.isConnected) opener.focus();
144
+ };
145
+
146
+ const expand = (side: WorkspaceAsideSide) => {
147
+ captureDrawerFocus(side);
148
+ setCollapsedTo(side, false);
149
+ };
150
+ const controller: WorkspaceShellController = {
151
+ toggleAside: (side) => (collapsed(side) ? expand(side) : setCollapsedTo(side, true)),
152
+ collapseAside: (side) => setCollapsedTo(side, true),
153
+ expandAside: (side) => expand(side),
154
+ };
155
+ props.controllerRef?.(controller);
156
+
157
+ // --- collapse-below-breakpoint (uncontrolled asides only) ---
158
+ // `autoCollapsed` tracks whether WE collapsed a side, so crossing back above
159
+ // the breakpoint never expands an aside the user collapsed themselves.
160
+ const autoCollapsed: Record<WorkspaceAsideSide, boolean> = { start: false, end: false };
161
+ const applyBreakpoint = (width: number) => {
162
+ const below = props.collapseBelow;
163
+ if (below == null) return;
164
+ const isBelow = width < below;
165
+ for (const side of ['start', 'end'] as const) {
166
+ // A side with no content has nothing to collapse; reporting a toggle for
167
+ // a region that does not exist would be noise, not loudness.
168
+ if (!asideContent(side)) continue;
169
+ if (isControlled(side)) continue;
170
+ if (isBelow && !collapsed(side)) {
171
+ autoCollapsed[side] = true;
172
+ setCollapsedTo(side, true);
173
+ } else if (!isBelow && collapsed(side) && autoCollapsed[side]) {
174
+ autoCollapsed[side] = false;
175
+ setCollapsedTo(side, false);
176
+ }
177
+ }
178
+ };
179
+
180
+ onMount(() => {
181
+ if (typeof ResizeObserver === 'undefined') return;
182
+ const ro = new ResizeObserver((entries) => {
183
+ const width = entries[0]?.contentRect.width ?? rootEl.clientWidth;
184
+ setHostWidth(width);
185
+ applyBreakpoint(width);
186
+ });
187
+ ro.observe(rootEl);
188
+ onCleanup(() => ro.disconnect());
189
+ });
190
+
191
+ // --- widths ---
192
+ const widths = (side: WorkspaceAsideSide) =>
193
+ side === 'start'
194
+ ? {
195
+ size: props.startWidth ?? 280,
196
+ min: props.startMinWidth ?? 200,
197
+ max: props.startMaxWidth ?? 480,
198
+ }
199
+ : {
200
+ size: props.endWidth ?? 320,
201
+ min: props.endMinWidth ?? 200,
202
+ max: props.endMaxWidth ?? 480,
203
+ };
204
+
205
+ // --- drawer Escape: scoped to the drawer (never a document listener, never
206
+ // swallowed — the kai-dock rule at page scale) ---
207
+ const onDrawerKeyDown = (side: WorkspaceAsideSide) => (event: KeyboardEvent) => {
208
+ if (event.key !== 'Escape') return;
209
+ setCollapsedTo(side, true);
210
+ };
211
+
212
+ const asideRefs: Partial<Record<WorkspaceAsideSide, HTMLElement>> = {};
213
+ const emitResize = (side: WorkspaceAsideSide) => {
214
+ const el = asideRefs[side];
215
+ if (!el) return;
216
+ props.onAsideResize?.({ side, width: el.getBoundingClientRect().width });
217
+ };
218
+
219
+ const asideContent = (side: WorkspaceAsideSide) => (side === 'start' ? props.start : props.end);
220
+
221
+ /** An aside as a resizable column (desktop mode). */
222
+ const asideColumn = (side: WorkspaceAsideSide) => {
223
+ const w = widths(side);
224
+ return (
225
+ <ResizablePanel
226
+ defaultSize={`${w.size}px`}
227
+ minSize={`${w.min}px`}
228
+ maxSize={`${w.max}px`}
229
+ part={side === 'start' ? 'aside start' : 'aside end'}
230
+ ref={(el: HTMLElement) => (asideRefs[side] = el)}
231
+ class="bg-surface"
232
+ >
233
+ {asideContent(side)}
234
+ </ResizablePanel>
235
+ );
236
+ };
237
+
238
+ /** An aside as an overlay drawer (mobile mode). */
239
+ const asideDrawer = (side: WorkspaceAsideSide) => (
240
+ <div
241
+ part={side === 'start' ? 'aside start' : 'aside end'}
242
+ data-drawer=""
243
+ class={cn(
244
+ 'absolute inset-y-0 z-20 max-w-[85%] overflow-auto border-border bg-surface shadow-lg',
245
+ side === 'start' ? 'start-0 border-e' : 'end-0 border-s',
246
+ )}
247
+ style={{ width: `${widths(side).size}px` }}
248
+ onKeyDown={onDrawerKeyDown(side)}
249
+ >
250
+ {asideContent(side)}
251
+ </div>
252
+ );
253
+
254
+ const showAside = (side: WorkspaceAsideSide) => !!asideContent(side) && !collapsed(side);
255
+
256
+ return (
257
+ <div
258
+ ref={rootEl}
259
+ class={cn('flex h-full w-full flex-col overflow-hidden bg-background', props.class)}
260
+ data-compact={props.compact ? '' : undefined}
261
+ >
262
+ <Show when={props.header}>
263
+ <div part="header" class="shrink-0 border-b border-border">{props.header}</div>
264
+ </Show>
265
+ <div class="relative min-h-0 flex-1">
266
+ <ResizablePanelGroup orientation="horizontal">
267
+ <Show when={showAside('start') && !drawerMode()}>
268
+ {asideColumn('start')}
269
+ <ResizableHandle onPanelResize={() => emitResize('start')} />
270
+ </Show>
271
+ <ResizablePanel part="main">{props.children}</ResizablePanel>
272
+ <Show when={showAside('end') && !drawerMode()}>
273
+ <ResizableHandle onPanelResize={() => emitResize('end')} />
274
+ {asideColumn('end')}
275
+ </Show>
276
+ </ResizablePanelGroup>
277
+ <Show when={showAside('start') && drawerMode()}>{asideDrawer('start')}</Show>
278
+ <Show when={showAside('end') && drawerMode()}>{asideDrawer('end')}</Show>
279
+ </div>
280
+ <Show when={props.footer}>
281
+ <div part="footer" class="shrink-0 border-t border-border">{props.footer}</div>
282
+ </Show>
283
+ </div>
284
+ );
285
+ }