@kitn.ai/ui 0.25.1 → 0.26.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 (409) hide show
  1. package/README.md +5 -7
  2. package/dist/components/attachment-types.d.ts +46 -1
  3. package/dist/components/attachments.d.ts +31 -0
  4. package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
  5. package/dist/components/audio-visualizer/index.d.ts +15 -1
  6. package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
  7. package/dist/components/chat-thread.d.ts +3 -41
  8. package/dist/components/code-block.d.ts +22 -0
  9. package/dist/components/conversation-item.d.ts +54 -1
  10. package/dist/components/conversation-list.d.ts +65 -0
  11. package/dist/components/form.d.ts +42 -0
  12. package/dist/components/message.d.ts +7 -0
  13. package/dist/components/toast.d.ts +2 -1
  14. package/dist/components/voice-input.d.ts +8 -0
  15. package/dist/components/voice-output.d.ts +12 -1
  16. package/dist/components/workspace-shell.d.ts +87 -0
  17. package/dist/{create-tween-B5Y4Im8G.js → create-tween-BFT6c-Y8.js} +1 -1
  18. package/dist/{create-tween-4z1XYLkR.js → create-tween-CJNlzoaO.js} +1 -1
  19. package/dist/{create-tween-CUzOlaMS.js → create-tween-CQED4T0g.js} +1 -1
  20. package/dist/custom-elements.json +2931 -2572
  21. package/dist/diagnostics/hook.d.ts +81 -0
  22. package/dist/diagnostics/index.d.ts +7 -0
  23. package/dist/diagnostics/report-request.d.ts +61 -0
  24. package/dist/diagnostics.d.ts +5 -0
  25. package/dist/diagnostics.js +209 -0
  26. package/dist/elements/agent-card.js +1 -1
  27. package/dist/elements/artifact.js +1 -1
  28. package/dist/elements/attachments.js +1 -1
  29. package/dist/elements/audio-visualizer.js +1 -1
  30. package/dist/elements/autoloader.js +1 -1
  31. package/dist/elements/avatar.js +1 -1
  32. package/dist/elements/badge.js +1 -1
  33. package/dist/elements/button.js +1 -1
  34. package/dist/elements/card.js +1 -1
  35. package/dist/elements/cards.js +1 -1
  36. package/dist/elements/chain-of-thought.js +1 -1
  37. package/dist/elements/chat-scope-picker.js +1 -1
  38. package/dist/elements/chat-workspace.js +1 -1
  39. package/dist/elements/chat.js +1 -1
  40. package/dist/elements/checkpoint.js +1 -1
  41. package/dist/elements/choice.js +1 -1
  42. package/dist/elements/chunks/{Icon-b5A8hlHs.js → Icon--Y8vsFal.js} +1 -1
  43. package/dist/elements/chunks/action-icons-D9u3OzYW.js +1 -0
  44. package/dist/elements/chunks/arrow-left-CVh1TeN7.js +1 -0
  45. package/dist/elements/chunks/{artifact-BSuPYFHY.js → artifact-BkYcfIlZ.js} +1 -1
  46. package/dist/elements/chunks/attachments-DwfNlubq.js +1 -0
  47. package/dist/elements/chunks/audio-visualizer-D7lB6mvR.js +1 -0
  48. package/dist/elements/chunks/{badge-TIMiJFKc.js → badge-Q0-Cmwzz.js} +1 -1
  49. package/dist/elements/chunks/{button-B9okqG73.js → button-flUbeufF.js} +1 -1
  50. package/dist/elements/chunks/{card-renderer-CSoTL_e1.js → card-renderer-3Dyl-FpN.js} +1 -1
  51. package/dist/elements/chunks/check-B5kBPKeo.js +1 -0
  52. package/dist/elements/chunks/chevron-down-DZ1GS39h.js +1 -0
  53. package/dist/elements/chunks/chevron-right-CvP7OUd2.js +1 -0
  54. package/dist/elements/chunks/{choice-card-CGMc3kek.js → choice-card-D4QUIOWZ.js} +1 -1
  55. package/dist/elements/chunks/circle-CDf9g6MQ.js +1 -0
  56. package/dist/elements/chunks/circle-check-CA47Nqbz.js +1 -0
  57. package/dist/elements/chunks/{circle-x-mhH7pOK2.js → circle-x-CJ_HVbC8.js} +1 -1
  58. package/dist/elements/chunks/code-block-BdLiTitd.js +1 -0
  59. package/dist/elements/chunks/collapsible-BzbXpPzr.js +1 -0
  60. package/dist/elements/chunks/{composer-CZZmtYOT.js → composer-Byve-n8K.js} +2 -2
  61. package/dist/elements/chunks/{confirm-card-BWjf2-xo.js → confirm-card-B0flE3MN.js} +1 -1
  62. package/dist/elements/chunks/context-VLIL3sJ-.js +1 -0
  63. package/dist/elements/chunks/controllable-DTwTIlwp.js +1 -0
  64. package/dist/elements/chunks/conversation-item-BDztezOG.js +1 -0
  65. package/dist/elements/chunks/copy-Nf-kqxXH.js +1 -0
  66. package/dist/elements/chunks/{create-tween-CUrM6D_o.js → create-tween-c4Z8TDYz.js} +2 -2
  67. package/dist/elements/chunks/default-input-C2iLFq0b.js +1 -0
  68. package/dist/elements/chunks/define-wjHR1MkU.js +1 -0
  69. package/dist/elements/chunks/disclosure-CcNbyLy0.js +1 -0
  70. package/dist/elements/chunks/{download-DnCyWFJE.js → download-C4V7tBdb.js} +1 -1
  71. package/dist/elements/chunks/dropdown-BgWTthUx.js +1 -0
  72. package/dist/elements/chunks/{ellipsis-CVxqfsDZ.js → ellipsis-7gU33mL2.js} +1 -1
  73. package/dist/elements/chunks/embed-D3KewsIJ.js +1 -0
  74. package/dist/elements/chunks/{external-link-DG2sRdQL.js → external-link-CSOEsNlx.js} +1 -1
  75. package/dist/elements/chunks/{file-text-D0J9F6M7.js → file-text-D1G8khle.js} +1 -1
  76. package/dist/elements/chunks/{file-tree-yGWNK0Js.js → file-tree-zvB2i7P9.js} +1 -1
  77. package/dist/elements/chunks/{folder-NV1pi_ud.js → folder-WW2LGLid.js} +1 -1
  78. package/dist/elements/chunks/form-CQirdC4W.js +1 -0
  79. package/dist/elements/chunks/hover-card-CJJkfHTK.js +1 -0
  80. package/dist/elements/chunks/icon-Cfgmf4fa.js +1 -0
  81. package/dist/elements/chunks/{info-C8r5Q6cc.js → info-DX3YMOt6.js} +1 -1
  82. package/dist/elements/chunks/input-BtW9U6Sq.js +1 -0
  83. package/dist/elements/chunks/{kbd-OGJDUT-O.js → kbd-LGXwuT_V.js} +1 -1
  84. package/dist/elements/chunks/{link-BdsfLB7b.js → link-2My69BqT.js} +1 -1
  85. package/dist/elements/chunks/{link-preview-Cf5KOP-i.js → link-preview-OQ9IEq0f.js} +1 -1
  86. package/dist/elements/chunks/{loader-B5QzK_8_.js → loader-iOxc_Tu-.js} +1 -1
  87. package/dist/elements/chunks/{markdown-DbozyYqa.js → markdown-DgBFaGAz.js} +1 -1
  88. package/dist/elements/chunks/{message-DtXKPqiR.js → message-VMZohrXx.js} +1 -1
  89. package/dist/elements/chunks/message-circle-CpXMM-_n.js +1 -0
  90. package/dist/elements/chunks/message-square-DxcWtETQ.js +1 -0
  91. package/dist/elements/chunks/message-tZmV2A3p.js +1 -0
  92. package/dist/elements/chunks/{minimize-2-SegJ_oku.js → minimize-2-DPIUKTrT.js} +1 -1
  93. package/dist/elements/chunks/{model-switcher-D7JrFFHp.js → model-switcher-DsT8tFB0.js} +1 -1
  94. package/dist/elements/chunks/{overlay-CV7z2YuX.js → overlay-DuiwdXjm.js} +1 -1
  95. package/dist/elements/chunks/{paperclip-Bg0ITU3b.js → paperclip-CRZVp4z_.js} +1 -1
  96. package/dist/elements/chunks/{progress-bar-D1_rzt6N.js → progress-bar-DNy66e_E.js} +1 -1
  97. package/dist/elements/chunks/{prompt-suggestion-D5Sm8xF1.js → prompt-suggestion-Bn64Nl7L.js} +1 -1
  98. package/dist/elements/chunks/reasoning-Emn9N4-T.js +1 -0
  99. package/dist/elements/chunks/{resizable-Dpzjlvp4.js → resizable-D181b4fM.js} +1 -1
  100. package/dist/elements/chunks/{rotate-cw-DzVDJOA1.js → rotate-cw-CPWe37D9.js} +1 -1
  101. package/dist/elements/chunks/{scroll-area-93A1bbeG.js → scroll-area-ClZkZxIk.js} +1 -1
  102. package/dist/elements/chunks/scroll-button-DPE-t3x_.js +1 -0
  103. package/dist/elements/chunks/{separator-DH2r_ZSZ.js → separator-CeJHQqLW.js} +1 -1
  104. package/dist/elements/chunks/{settings-BFMOd6yB.js → settings-CqaHgJF9.js} +1 -1
  105. package/dist/elements/chunks/{settings-group-B3VZKIcz.js → settings-group-CjqjLCOn.js} +1 -1
  106. package/dist/elements/chunks/share-CyfnNEqp.js +1 -0
  107. package/dist/elements/chunks/{skeleton-N8BuWYmf.js → skeleton-D8akBnKS.js} +1 -1
  108. package/dist/elements/chunks/slots-DaNjX0rc.js +1 -0
  109. package/dist/elements/chunks/{source-w9V0NnBn.js → source-UWCvmVak.js} +1 -1
  110. package/dist/elements/chunks/{star-DHvACxtm.js → star-DoHFAikQ.js} +1 -1
  111. package/dist/elements/chunks/store-Cjq0qwYN.js +1 -0
  112. package/dist/elements/chunks/{tasks-card-lPxoodlA.js → tasks-card-CmZ5xh6v.js} +1 -1
  113. package/dist/elements/chunks/{text-shimmer-BY_rI3s5.js → text-shimmer-DYn09zrz.js} +1 -1
  114. package/dist/elements/chunks/textarea-skiGHURM.js +1 -0
  115. package/dist/elements/chunks/{thumbs-up-DQe0hvkZ.js → thumbs-up-DX2Ijk3H.js} +1 -1
  116. package/dist/elements/chunks/{toast-store-VUZZBhzH.js → toast-store-CFnQpThX.js} +1 -1
  117. package/dist/elements/chunks/{tool-C1YtxwPu.js → tool-DKQ2D1DP.js} +1 -1
  118. package/dist/elements/chunks/{tooltip-BqwnFWhn.js → tooltip-B0G99y8w.js} +1 -1
  119. package/dist/elements/chunks/{triangle-alert-B6or7F5u.js → triangle-alert-F9cyUEyy.js} +1 -1
  120. package/dist/elements/chunks/{use-card-resolution-DJg32jGH.js → use-card-resolution-DoA2f_EU.js} +1 -1
  121. package/dist/elements/chunks/{variant-aurora-QXTZPyST.js → variant-aurora-qt6Zct8M.js} +2 -2
  122. package/dist/elements/chunks/variant-custom-tJp22_BQ.js +1 -0
  123. package/dist/{variant-wave-BwzrHD1s.js → elements/chunks/variant-wave-CaQ5ti39.js} +2 -2
  124. package/dist/elements/chunks/x-B6UnOiLw.js +1 -0
  125. package/dist/elements/coachmark.js +1 -1
  126. package/dist/elements/code-block.js +1 -1
  127. package/dist/elements/command.js +1 -1
  128. package/dist/elements/compare.js +1 -1
  129. package/dist/elements/composer.js +1 -1
  130. package/dist/elements/confirm-card.js +1 -1
  131. package/dist/elements/context-meter.js +1 -1
  132. package/dist/elements/conversation-item.d.ts +1 -0
  133. package/dist/elements/conversation-item.js +1 -0
  134. package/dist/elements/conversation-list.js +1 -1
  135. package/dist/elements/default-input.d.ts +3 -3
  136. package/dist/elements/define.d.ts +27 -0
  137. package/dist/elements/diagnostic-events.d.ts +74 -0
  138. package/dist/elements/dialog.js +1 -1
  139. package/dist/elements/dock.d.ts +1 -0
  140. package/dist/elements/dock.js +149 -0
  141. package/dist/elements/dropdown.d.ts +1 -0
  142. package/dist/elements/dropdown.js +1 -0
  143. package/dist/elements/editable-label.js +1 -1
  144. package/dist/elements/element-diagnostics.d.ts +81 -0
  145. package/dist/elements/embed.js +1 -1
  146. package/dist/elements/empty.js +1 -1
  147. package/dist/elements/feedback-bar.js +1 -1
  148. package/dist/elements/file-tree.js +1 -1
  149. package/dist/elements/file-upload.js +1 -1
  150. package/dist/elements/form.js +1 -1
  151. package/dist/elements/hover-card.js +1 -1
  152. package/dist/elements/icon.js +1 -1
  153. package/dist/elements/image.js +1 -1
  154. package/dist/elements/input.js +1 -1
  155. package/dist/elements/kbd.js +1 -1
  156. package/dist/elements/link-preview.js +1 -1
  157. package/dist/elements/loader.js +1 -1
  158. package/dist/elements/markdown.js +1 -1
  159. package/dist/elements/menu.js +1 -1
  160. package/dist/elements/message-skills.js +1 -1
  161. package/dist/elements/message.js +1 -1
  162. package/dist/elements/model-switcher.js +1 -1
  163. package/dist/elements/nav.js +1 -1
  164. package/dist/elements/notice.js +1 -1
  165. package/dist/elements/pane-group.js +1 -1
  166. package/dist/elements/pane.js +1 -1
  167. package/dist/elements/popover.js +1 -1
  168. package/dist/elements/progress-bar.js +1 -1
  169. package/dist/elements/prompt-dock.js +1 -1
  170. package/dist/elements/prompt-input.js +1 -1
  171. package/dist/elements/prompt-suggestions.js +1 -1
  172. package/dist/elements/reasoning.js +1 -1
  173. package/dist/elements/register.d.ts +2 -0
  174. package/dist/elements/remote.js +1 -1
  175. package/dist/elements/resizable.js +1 -1
  176. package/dist/elements/response-stream.js +3 -3
  177. package/dist/elements/screen.js +1 -1
  178. package/dist/elements/scroll-area.js +1 -1
  179. package/dist/elements/scroll-button.js +1 -1
  180. package/dist/elements/search.js +1 -1
  181. package/dist/elements/segmented.js +1 -1
  182. package/dist/elements/separator.js +1 -1
  183. package/dist/elements/setting-item.js +1 -1
  184. package/dist/elements/settings-group.js +1 -1
  185. package/dist/elements/skeleton.js +1 -1
  186. package/dist/elements/slots.d.ts +35 -3
  187. package/dist/elements/source.js +1 -1
  188. package/dist/elements/status.js +1 -1
  189. package/dist/elements/switch.js +1 -1
  190. package/dist/elements/tabs.js +1 -1
  191. package/dist/elements/tasks.js +1 -1
  192. package/dist/elements/text-shimmer.js +1 -1
  193. package/dist/elements/thinking-bar.js +1 -1
  194. package/dist/elements/thread.js +1 -1
  195. package/dist/elements/toast.js +1 -1
  196. package/dist/elements/tool.js +1 -1
  197. package/dist/elements/tooltip.js +1 -1
  198. package/dist/elements/voice-input.js +1 -1
  199. package/dist/elements/voice-output.js +1 -1
  200. package/dist/elements.d.ts +232 -148
  201. package/dist/index.d.ts +2 -0
  202. package/dist/index.js +5456 -4931
  203. package/dist/index.server.js +4142 -3669
  204. package/dist/kai-provider.es.js +40 -40
  205. package/dist/kai.es.js +1 -1
  206. package/dist/llms/llms-full.txt +184 -69
  207. package/dist/llms/llms.txt +3 -3
  208. package/dist/mcp.es.js +2669 -546
  209. package/dist/primitives/use-resize-observer.d.ts +23 -0
  210. package/dist/primitives/use-speech-recognition.d.ts +8 -0
  211. package/dist/primitives/use-text-stream.d.ts +5 -0
  212. package/dist/react/index.d.ts +115 -266
  213. package/dist/react.js +247 -221
  214. package/dist/register-impl-DpE7icIb.js +293 -0
  215. package/dist/schemas/index.d.ts +1 -1
  216. package/dist/schemas/tool-defs.d.ts +35 -0
  217. package/dist/schemas.js +403 -310
  218. package/dist/{solid-BMuDmFo_.js → solid-BtdqNZI7.js} +8435 -7394
  219. package/dist/{solid-CGYNdLRR.js → solid-CnavwBye.js} +5987 -5035
  220. package/dist/solid.d.ts +5 -0
  221. package/dist/solid.js +210 -203
  222. package/dist/solid.server.js +210 -203
  223. package/dist/state/index.d.ts +4 -0
  224. package/dist/state/persistence.d.ts +51 -0
  225. package/dist/state/stream.d.ts +50 -0
  226. package/dist/state/threads.d.ts +71 -0
  227. package/dist/state.js +269 -169
  228. package/dist/types.d.ts +4 -2
  229. package/dist/ui/dock.d.ts +109 -0
  230. package/dist/ui/hover-card.d.ts +44 -0
  231. package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-B8II3-I1.js} +27 -27
  232. package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CMNWOSJR.js} +28 -28
  233. package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CcuDMWwQ.js} +2 -2
  234. package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-BdvYC7af.js} +40 -40
  235. package/dist/{variant-custom-BhS9wIpU.js → variant-custom-C714mqKl.js} +65 -65
  236. package/dist/variant-custom-L16JLLZy.js +1 -0
  237. package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-2S4La2Ka.js} +2 -2
  238. package/dist/{variant-wave-CbVsmgwT.js → variant-wave-D7ruiA1W.js} +25 -25
  239. package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-ecwykeyL.js} +21 -21
  240. package/dist/wire/chunk.d.ts +47 -0
  241. package/dist/wire/diagnostics.d.ts +575 -0
  242. package/dist/wire/encode-probe.d.ts +70 -0
  243. package/dist/wire/encode.d.ts +24 -0
  244. package/dist/wire/index.d.ts +2 -0
  245. package/dist/wire/sse.d.ts +10 -2
  246. package/dist/wire.js +917 -480
  247. package/frameworks/react/index.tsx +132 -93
  248. package/llms-full.txt +184 -69
  249. package/llms.txt +3 -3
  250. package/package.json +27 -7
  251. package/src/agent-tooling/archetypes.ts +41 -9
  252. package/src/agent-tooling/catalog/README.md +676 -0
  253. package/src/agent-tooling/catalog/catalog-types.ts +231 -0
  254. package/src/agent-tooling/catalog/fabrications.ts +96 -0
  255. package/src/agent-tooling/catalog/invariants.ts +284 -0
  256. package/src/agent-tooling/catalog/labs-titles.ts +114 -0
  257. package/src/agent-tooling/catalog/scenarios.ts +87 -0
  258. package/src/agent-tooling/catalog/surfaces.ts +275 -0
  259. package/src/agent-tooling/integrations/anthropic.ts +7 -1
  260. package/src/agent-tooling/integrations/cloudflare.ts +43 -2
  261. package/src/agent-tooling/integrations/langgraph.ts +7 -1
  262. package/src/agent-tooling/integrations/mastra.ts +7 -1
  263. package/src/agent-tooling/integrations/mock.ts +92 -17
  264. package/src/agent-tooling/integrations/ollama.ts +7 -1
  265. package/src/agent-tooling/integrations/openai.ts +7 -1
  266. package/src/agent-tooling/integrations/openrouter.ts +7 -1
  267. package/src/agent-tooling/integrations/pi.ts +56 -1
  268. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
  269. package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
  270. package/src/agent-tooling/mcp/manifest.ts +100 -1
  271. package/src/agent-tooling/mcp/server.ts +61 -5
  272. package/src/agent-tooling/mcp/tools/reference.ts +440 -4
  273. package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
  274. package/src/agent-tooling/mcp/tools/theme.ts +146 -30
  275. package/src/agent-tooling/mcp/validate-args.ts +141 -0
  276. package/src/agent-tooling/registry.ts +19 -7
  277. package/src/agent-tooling/route-emit.ts +26 -2
  278. package/src/components/attachment-types.ts +47 -1
  279. package/src/components/attachments.tsx +169 -22
  280. package/src/components/audio-visualizer/fit-scale.ts +112 -0
  281. package/src/components/audio-visualizer/index.tsx +80 -3
  282. package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
  283. package/src/components/audio-visualizer/variant-bar.tsx +38 -6
  284. package/src/components/audio-visualizer/variant-custom.tsx +8 -2
  285. package/src/components/audio-visualizer/variant-grid.tsx +16 -11
  286. package/src/components/audio-visualizer/variant-radial.tsx +15 -10
  287. package/src/components/audio-visualizer/variant-wave.tsx +7 -2
  288. package/src/components/chat-container.tsx +4 -1
  289. package/src/components/chat-thread.tsx +36 -7
  290. package/src/components/code-block.tsx +79 -3
  291. package/src/components/composer.tsx +8 -1
  292. package/src/components/conversation-item.tsx +114 -4
  293. package/src/components/conversation-list.tsx +212 -12
  294. package/src/components/embed.tsx +4 -0
  295. package/src/components/form.tsx +41 -14
  296. package/src/components/message.tsx +75 -5
  297. package/src/components/response-stream.tsx +13 -7
  298. package/src/components/screen.tsx +5 -0
  299. package/src/components/scroll-button.tsx +7 -0
  300. package/src/components/toast.tsx +11 -5
  301. package/src/components/voice-input.tsx +35 -3
  302. package/src/components/voice-output.tsx +46 -6
  303. package/src/components/workspace-shell.tsx +285 -0
  304. package/src/diagnostics/hook.ts +352 -0
  305. package/src/diagnostics/index.ts +82 -0
  306. package/src/diagnostics/report-request.ts +296 -0
  307. package/src/elements/attachments.tsx +6 -7
  308. package/src/elements/audio-visualizer.tsx +48 -4
  309. package/src/elements/chat-workspace.tsx +159 -287
  310. package/src/elements/chat.tsx +12 -9
  311. package/src/elements/code-block.tsx +19 -1
  312. package/src/elements/command.tsx +4 -1
  313. package/src/elements/compiled.css +1 -1
  314. package/src/elements/conversation-item.tsx +80 -0
  315. package/src/elements/conversation-list.tsx +60 -11
  316. package/src/elements/default-input.tsx +5 -5
  317. package/src/elements/define.tsx +168 -8
  318. package/src/elements/diagnostic-events.ts +114 -0
  319. package/src/elements/dialog.tsx +47 -0
  320. package/src/elements/disclosure.ts +34 -12
  321. package/src/elements/dock.tsx +204 -0
  322. package/src/elements/dropdown.tsx +123 -0
  323. package/src/elements/element-diagnostics.ts +392 -0
  324. package/src/elements/element-manifest.json +12 -0
  325. package/src/elements/element-meta.json +672 -439
  326. package/src/elements/element-nonscalar.json +170 -0
  327. package/src/elements/element-types.d.ts +232 -148
  328. package/src/elements/menu.tsx +13 -0
  329. package/src/elements/prompt-dock.tsx +2 -1
  330. package/src/elements/prompt-input.tsx +8 -8
  331. package/src/elements/register-impl.ts +46 -0
  332. package/src/elements/register.ts +28 -0
  333. package/src/elements/resizable.tsx +52 -25
  334. package/src/elements/scroll-button.tsx +5 -0
  335. package/src/elements/slots.ts +107 -12
  336. package/src/elements/styles.css +30 -6
  337. package/src/elements/thinking-bar.tsx +16 -7
  338. package/src/elements/toast.tsx +3 -0
  339. package/src/elements/tool.tsx +26 -5
  340. package/src/elements/voice-input.tsx +11 -2
  341. package/src/elements/voice-output.tsx +16 -3
  342. package/src/index.ts +2 -0
  343. package/src/primitives/use-resize-observer.ts +33 -0
  344. package/src/primitives/use-speech-recognition.ts +12 -1
  345. package/src/primitives/use-stick-to-bottom.ts +34 -2
  346. package/src/primitives/use-text-stream.ts +33 -2
  347. package/src/remote/provider-runtime.ts +6 -3
  348. package/src/schemas/index.ts +1 -0
  349. package/src/schemas/tool-defs.ts +297 -6
  350. package/src/solid.ts +5 -0
  351. package/src/state/index.ts +12 -0
  352. package/src/state/persistence.ts +180 -0
  353. package/src/state/stream.ts +71 -4
  354. package/src/state/threads.ts +142 -0
  355. package/src/types.ts +4 -2
  356. package/src/ui/dialog.tsx +6 -0
  357. package/src/ui/dock.tsx +445 -0
  358. package/src/ui/dropdown.tsx +67 -23
  359. package/src/ui/hover-card.tsx +156 -6
  360. package/src/ui/input.tsx +29 -10
  361. package/src/ui/resizable.tsx +11 -0
  362. package/src/wire/chunk.ts +47 -0
  363. package/src/wire/consume.ts +185 -26
  364. package/src/wire/diagnostics.ts +727 -0
  365. package/src/wire/encode-probe.ts +214 -0
  366. package/src/wire/encode.ts +267 -14
  367. package/src/wire/formats/anthropic.ts +8 -1
  368. package/src/wire/formats/openai.ts +5 -0
  369. package/src/wire/index.ts +22 -0
  370. package/src/wire/read.ts +250 -6
  371. package/src/wire/sse.ts +16 -3
  372. package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
  373. package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
  374. package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
  375. package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
  376. package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
  377. package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
  378. package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
  379. package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
  380. package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
  381. package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
  382. package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
  383. package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
  384. package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
  385. package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
  386. package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
  387. package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
  388. package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
  389. package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
  390. package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
  391. package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
  392. package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
  393. package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
  394. package/dist/elements/chunks/input-DrseEjhp.js +0 -1
  395. package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
  396. package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
  397. package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
  398. package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
  399. package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
  400. package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
  401. package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
  402. package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
  403. package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
  404. package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
  405. package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
  406. package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
  407. package/dist/register-impl-DGmGzrEM.js +0 -145
  408. package/dist/variant-custom-MkTNTycd.js +0 -1
  409. package/src/primitives/card-validate-generator.testlib.ts +0 -38
@@ -1,148 +1,125 @@
1
- import { Show, createSignal, onMount, onCleanup } from 'solid-js';
1
+ import { createSignal, onMount, onCleanup, untrack } from 'solid-js';
2
2
  import { defineWebComponent } from './define';
3
- import { createControllableSignal } from '../primitives/controllable';
4
3
  import { readSlots, WORKSPACE_SLOTS } from './slots';
5
- import { ChatThread, type ChatThreadContextUsage, type ChatThreadController } from '../components/chat-thread';
6
- import { ConversationList, CollapsedRail } from '../components/conversation-list';
7
- import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '../ui/resizable';
8
- import { cardComponentsFromTags } from './message';
9
- import { createMessagesGuard } from './validate-messages';
10
- import type { AttachmentData } from '../components/attachments';
11
- import type { TriggerDef } from '../components/composer';
12
- import type { ChatMessage } from './chat-types';
13
- import type { ProseSize } from '../primitives/chat-config';
14
- import type { ModelOption, ConversationGroup, ConversationSummary } from '../types';
4
+ import {
5
+ WorkspaceShell,
6
+ type WorkspaceShellController,
7
+ type WorkspaceAsideSide,
8
+ type WorkspaceAsideToggleDetail,
9
+ type WorkspaceAsideResizeDetail,
10
+ } from '../components/workspace-shell';
15
11
 
16
12
  interface Props extends Record<string, unknown> {
17
- /** The sidebar's section headers, rendered in array order. A group carries no
18
- * conversations of its own; it is matched against `conversations` by id, so
19
- * the two props are complementary rather than alternatives. Omit for an
20
- * ungrouped sidebar. Set as a JS property. */
21
- groups?: ConversationGroup[];
22
- /** Every conversation in the sidebar, flat. Each one is filed under the group
23
- * whose `id` equals its `groupId`; one with no `groupId`, or with a `groupId`
24
- * matching no entry in `groups`, falls into a trailing ungrouped section
25
- * (headerless when `compact`), so nothing you pass in is ever dropped. There is
26
- * no recency bucketing. Set as a JS property. Omit for an empty sidebar, or
27
- * when `no-conversations` replaces the built-in list with your own
28
- * `sidebar-header` content. */
29
- conversations?: ConversationSummary[];
30
- /** Id of the open conversation, highlighted in the sidebar. */
31
- activeId?: string;
32
- /** The active conversation's message thread, newest last. Set as a JS property
33
- * (`el.messages = [...]`); a NEW array reference per streaming chunk
34
- * re-renders (mutating in place does not). Omit for an empty thread. */
35
- messages?: ChatMessage[];
36
- value?: string;
37
- placeholder?: string;
38
- loading?: boolean;
39
- suggestions?: string[];
40
- suggestionMode?: 'submit' | 'fill';
41
- proseSize?: ProseSize;
42
- codeTheme?: string;
43
- codeHighlight?: boolean;
44
- chatTitle?: string;
45
- models?: ModelOption[];
46
- currentModel?: string;
47
- context?: ChatThreadContextUsage;
48
- scrollButton?: boolean;
49
- search?: boolean;
50
- voice?: boolean;
51
- /** Rich entity triggers (`/` skills, `@` agents/plugins) forwarded to the input. */
52
- triggers?: TriggerDef[];
53
- /** Default icon per entity kind (kind → image src) forwarded to the input. */
54
- kindIcons?: Record<string, string>;
55
- /** Sidebar default width as a percent of the workspace (default 26). */
56
- sidebarWidth?: number;
57
- /** Sidebar min width in px (default 240). */
58
- sidebarMinWidth?: number;
59
- /** Sidebar max width in px (default 420). */
60
- sidebarMaxWidth?: number;
61
- /** Controlled collapsed state. Set this as a JS property (`el.sidebarCollapsed
62
- * = true`) to drive the sidebar from your app, updating it in response to the
63
- * `kai-sidebar-toggle` event. Omit for uncontrolled (the element manages it). */
64
- sidebarCollapsed?: boolean;
65
- /** Initial collapsed state when uncontrolled (default false). Use the
66
- * `default-sidebar-collapsed` attribute to start collapsed in plain HTML. */
67
- defaultSidebarCollapsed?: boolean;
68
- /** Auto-collapse the rail when the workspace's own width drops below this many
69
- * px, and re-expand when it grows back above. Uncontrolled only (it never
70
- * fights an app-driven `sidebarCollapsed`); omit to disable. Fires
71
- * `kai-sidebar-toggle`. Attribute: `collapse-below`. */
13
+ /** Controlled collapsed state of the start aside. Set this as a JS property
14
+ * (`el.startCollapsed = true`) to drive the aside from your app, updating it
15
+ * in response to the `kai-aside-toggle` event. Omit for uncontrolled (the
16
+ * element manages it). */
17
+ startCollapsed?: boolean;
18
+ /** Initial collapsed state of the start aside when uncontrolled (default
19
+ * false). Use the `default-start-collapsed` attribute to start collapsed in
20
+ * plain HTML. */
21
+ defaultStartCollapsed?: boolean;
22
+ /** Controlled collapsed state of the end aside. Set this as a JS property
23
+ * (`el.endCollapsed = true`) to drive the aside from your app, updating it
24
+ * in response to the `kai-aside-toggle` event. Omit for uncontrolled (the
25
+ * element manages it). */
26
+ endCollapsed?: boolean;
27
+ /** Initial collapsed state of the end aside when uncontrolled (default
28
+ * false). Use the `default-end-collapsed` attribute to start collapsed in
29
+ * plain HTML. */
30
+ defaultEndCollapsed?: boolean;
31
+ /** Auto-collapse both asides when the shell's own width drops below this many
32
+ * px, and re-expand when it grows back above. Applies to uncontrolled asides
33
+ * only (it never fights an app-driven collapsed prop); omit to disable.
34
+ * Fires `kai-aside-toggle`. Attribute: `collapse-below`. */
72
35
  collapseBelow?: number;
73
- /** Render Recents as dense single-line rows (a leading dot + title, no count). */
36
+ /** Below this shell width in px, an expanded aside renders as an overlay
37
+ * drawer over the main region instead of a column beside it. Escape inside
38
+ * the drawer closes it and returns focus to the element focused before it
39
+ * opened. Omit to disable. Attribute: `drawer-below`. */
40
+ drawerBelow?: number;
41
+ /** Density hint. Reflected as a `data-compact` hook on the root (and as the
42
+ * `compact` attribute on the element) for your CSS and slotted content; the
43
+ * shell itself keeps no other opinion about density. */
74
44
  compact?: boolean;
75
- /** Suppress the built-in ConversationList so the `sidebar-header` slot owns the
76
- * whole rail flex region (for apps that supply their own rail nav). Default
77
- * false. Attribute: `no-conversations`. */
78
- noConversations?: boolean;
79
- /** Optional card type -> custom-element tag overrides/additions for `card`
80
- * parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a
81
- * plain string map (not the `CardTagMap` alias) so the generated React
82
- * wrapper inlines it instead of emitting an unresolved named type. */
83
- cardTypes?: Record<string, string>;
84
- /** JSON Schemas for the card types this app renders, keyed by envelope type. The
85
- * companion of `cardTypes`, which says what DRAWS a card while this says what a
86
- * VALID one looks like. An OBJECT, so it is a JS property only: `el.cardSchemas
87
- * = { 'pricing-table': pricingSchema }`, never an attribute.
88
- * `createCardRegistry(...).validationSchemas` is exactly this shape.
89
- *
90
- * Without it the kit validates its own seven built-ins and leaves your own card
91
- * type, the one your app actually cares about, as the only unchecked thing on
92
- * screen. A schema here WINS over a built-in of the same name.
93
- *
94
- * Typed `Record<string, object>` rather than `Record<string, JsonSchema>`
95
- * deliberately: an imported `.json` schema widens `"type"` to `string`, and an
96
- * authored one carries `$schema`/`title`/`description`/`additionalProperties`,
97
- * so the tighter type would reject both of the normal ways to supply one. */
98
- cardSchemas?: Record<string, object>;
99
45
  }
100
46
 
47
+ /** Events fired by `<kai-workspace>`. Layout only: every chat event now belongs
48
+ * to the part that fires it, inside your own markup. */
101
49
  interface Events {
102
- /** A conversation was selected in the sidebar. */
103
- 'kai-conversation-select': { id: string };
104
- /** The "New chat" button was clicked. */
105
- 'kai-new-chat': Record<string, never>;
106
- /** The sidebar was collapsed or expanded. */
107
- 'kai-sidebar-toggle': { collapsed: boolean };
108
- /** User submitted a message. */
109
- 'kai-submit': { value: string; attachments: AttachmentData[] };
110
- /** Fired on every input change. */
111
- 'kai-value-change': { value: string };
112
- /** The header model switcher changed. */
113
- 'kai-model-change': { modelId: string };
114
- /** An action button on a message was clicked. `state` is present only for the
115
- * toggleable feedback votes: `'on'` when a like/dislike is set, `'off'` when
116
- * re-tapped to clear. */
117
- 'kai-message-action': { messageId: string; action: string; state?: 'on' | 'off' };
118
- /** The Search button was clicked. */
119
- 'kai-search': Record<string, never>;
120
- /** The Mic / voice button was clicked. */
121
- 'kai-voice': Record<string, never>;
122
- /** A suggestion chip was clicked (only in `suggestion-mode="fill"`). */
123
- 'kai-suggestion-click': { value: string };
50
+ /** An aside collapsed or expanded (a method, the breakpoint, the drawer's Escape). */
51
+ 'kai-aside-toggle': WorkspaceAsideToggleDetail;
52
+ /** The aside was resized (fires per drag step, keyboard nudge, or a handle double-click reset), width in px. */
53
+ 'kai-aside-resize': WorkspaceAsideResizeDetail;
124
54
  }
125
55
 
56
+ /**
57
+ * `<kai-workspace>` — the chat-agnostic layout shell: five slots (`header` ·
58
+ * `start` · `main` · `end` · `footer`), resize handles between the columns,
59
+ * per-aside collapse, collapse-below-breakpoint, and a mobile drawer mode for
60
+ * the asides. It knows nothing about chat: a file tree in `start` is as valid
61
+ * as `<kai-conversations>`, and the workspace app slots `<kai-conversations>`
62
+ * and `<kai-chat>` into it.
63
+ *
64
+ * ```html
65
+ * <kai-workspace collapse-below="720" drawer-below="640">
66
+ * <kai-conversations slot="start"></kai-conversations>
67
+ * <kai-chat></kai-chat>
68
+ * </kai-workspace>
69
+ * ```
70
+ *
71
+ * Aside geometry is CSS custom properties, not props (the `kai-dock` rule):
72
+ * `--kai-workspace-start-width` (280px) · `--kai-workspace-start-min-width`
73
+ * (200px) · `--kai-workspace-start-max-width` (480px) · `--kai-workspace-end-width`
74
+ * (320px) · `--kai-workspace-end-min-width` (200px) · `--kai-workspace-end-max-width`
75
+ * (480px). Read once at upgrade. Parts: `header` · `start` · `main` · `end` ·
76
+ * `footer` (the asides also match `::part(aside)`).
77
+ *
78
+ * **BREAKING (the 0.24 re-cast, spec 2026-08-20):** this element was a chat
79
+ * preset; it is now a layout shell, and everything chat-shaped is gone from its
80
+ * surface. Where each removed prop went:
81
+ *
82
+ * - `conversations` / `activeId` / `groups` / `noConversations`: set them on your
83
+ * own `<kai-conversations slot="start">`.
84
+ * - `messages` / `loading` / `proseSize` / `codeTheme` / `codeHighlight` /
85
+ * `chatTitle` / `scrollButton` / `cardTypes` / `cardSchemas`: set them on your
86
+ * own `<kai-chat>` in the main region.
87
+ * - `value` / `placeholder` / `suggestions` / `suggestionMode` / `voice` /
88
+ * `triggers` / `kindIcons` and the renamed `webSearch` (was `search`): the
89
+ * composer surface on `<kai-chat>` or `<kai-prompt-input>`.
90
+ * - `models` / `currentModel` / `context`: header chrome you slot (a model
91
+ * picker is a part in a slot, not three orchestrator props).
92
+ * - `sidebarWidth` / `sidebarMinWidth` / `sidebarMaxWidth`: the
93
+ * `--kai-workspace-start-*` custom properties above.
94
+ * - `sidebarCollapsed` / `defaultSidebarCollapsed`: `startCollapsed` /
95
+ * `defaultStartCollapsed` (per-aside; the end aside has its own pair).
96
+ * - `collapseBelow`: kept, now collapsing both asides.
97
+ * - `compact`: kept as a reflected density hint; the rail's row density is
98
+ * `<kai-conversations>`' own `compact`.
99
+ * - Chat events (`kai-submit`, `kai-conversation-select`, `kai-message-action`,
100
+ * `kai-model-change`, `kai-search`, `kai-voice`, `kai-value-change`,
101
+ * `kai-suggestion-click`): listen on the part that fires them.
102
+ * `kai-sidebar-toggle` is now `kai-aside-toggle` with a `side`.
103
+ * - Methods: `toggleSidebar()` / `collapseSidebar()` / `expandSidebar()` are now
104
+ * `toggleAside(side)` / `collapseAside(side)` / `expandAside(side)`; the
105
+ * thread methods (`focus`/`clear`/`send`/`scrollToBottom`) live on your
106
+ * `<kai-chat>`.
107
+ *
108
+ * The migration path for a full app is the `workspace` block scaffold (the kai
109
+ * MCP `scaffold` tool), which emits this composition wired.
110
+ */
126
111
  defineWebComponent<Props, Events>('kai-workspace', {
127
- groups: [], conversations: [], activeId: undefined, messages: [],
128
- value: undefined, placeholder: 'Send a message...', loading: false,
129
- suggestions: undefined, suggestionMode: 'submit', proseSize: 'sm',
130
- codeTheme: 'github-dark-dimmed', codeHighlight: true, chatTitle: undefined,
131
- models: undefined, currentModel: undefined, context: undefined, scrollButton: true,
132
- search: false, voice: false, triggers: undefined, kindIcons: undefined,
133
- sidebarWidth: 26, sidebarMinWidth: 240, sidebarMaxWidth: 420,
134
- sidebarCollapsed: undefined, defaultSidebarCollapsed: undefined, collapseBelow: undefined, compact: undefined,
135
- noConversations: undefined, cardTypes: undefined, cardSchemas: undefined,
136
- }, (props, { dispatch, flag, expose, element }) => {
137
- // `messages` is an untyped boundary: a consumer can hand it anything at
138
- // runtime (a pre-0.20.0 `{ id, role, content }` array, in particular). Skip
139
- // the invalid entries rather than let `groupMessageParts` throw deep inside a
140
- // render pass, which would blank the whole workspace instead of one message.
141
- const validMessages = createMessagesGuard('kai-workspace');
142
-
143
- // Which injection slots the consumer has filled. A bare <slot> is always a
144
- // truthy JSX node, so we render each region wrapper ONLY when readSlots reports
145
- // projected light-DOM content (re-read on childList mutation).
112
+ startCollapsed: undefined,
113
+ defaultStartCollapsed: undefined,
114
+ endCollapsed: undefined,
115
+ defaultEndCollapsed: undefined,
116
+ collapseBelow: undefined,
117
+ drawerBelow: undefined,
118
+ compact: undefined,
119
+ }, (props, { dispatch, flag, expose, element, reflectFlag }) => {
120
+ // Which slots the consumer has filled. A bare <slot> is always a truthy JSX
121
+ // node, so each optional region renders ONLY when readSlots reports projected
122
+ // light-DOM content (re-read on childList mutation). Main always renders.
146
123
  const [slots, setSlots] = createSignal<Record<string, boolean>>({});
147
124
  onMount(() => {
148
125
  const read = () => setSlots(readSlots(element, WORKSPACE_SLOTS));
@@ -152,165 +129,60 @@ defineWebComponent<Props, Events>('kai-workspace', {
152
129
  onCleanup(() => observer.disconnect());
153
130
  });
154
131
 
155
- // Controlled/uncontrolled collapse: `sidebarCollapsed` (when set) wins;
156
- // otherwise the element manages its own state, seeded from
157
- // `defaultSidebarCollapsed`. `setCollapsedTo` always writes the internal value (a
158
- // no-op visually while controlled) and emits `kai-sidebar-toggle` so a controlling
159
- // app can update its own state.
160
- const [collapsed, setCollapsed] = createControllableSignal(
161
- () => props.sidebarCollapsed as boolean | undefined,
162
- flag('defaultSidebarCollapsed'),
163
- );
164
- const setCollapsedTo = (next: boolean) => { setCollapsed(next); dispatch('kai-sidebar-toggle', { collapsed: next }); };
165
- const toggle = () => setCollapsedTo(!collapsed());
132
+ // Reflect the read-back flags (the G-05 rule: a bare attribute parses to
133
+ // `undefined`, so without this the property would contradict the attribute).
134
+ reflectFlag('compact');
135
+ reflectFlag('defaultStartCollapsed');
136
+ reflectFlag('defaultEndCollapsed');
166
137
 
167
- // Responsive auto-collapse. When `collapseBelow` is set, a ResizeObserver on the
168
- // workspace root collapses the rail once the workspace's own width drops below
169
- // it and re-expands above it. Uncontrolled only — bail while `sidebarCollapsed`
170
- // is set so we never fight an app-driven collapse. `autoCollapsed` tracks whether
171
- // WE collapsed it, so a user's manual expand isn't auto-undone on the next tick.
172
- let rootEl!: HTMLDivElement;
173
- let autoCollapsed = false;
174
- onMount(() => {
175
- if (typeof ResizeObserver === 'undefined') return;
176
- const ro = new ResizeObserver((entries) => {
177
- const below = props.collapseBelow as number | undefined;
178
- if (below == null || props.sidebarCollapsed !== undefined) return;
179
- const width = entries[0]?.contentRect.width ?? rootEl.clientWidth;
180
- const isBelow = width < below;
181
- if (isBelow && !collapsed()) {
182
- autoCollapsed = true;
183
- setCollapsedTo(true);
184
- } else if (!isBelow && collapsed() && autoCollapsed) {
185
- autoCollapsed = false;
186
- setCollapsedTo(false);
187
- }
188
- });
189
- ro.observe(rootEl);
190
- onCleanup(() => ro.disconnect());
191
- });
138
+ // Aside geometry from the element's CSS custom properties, read once at
139
+ // upgrade (inline style first so jsdom, which does not resolve custom
140
+ // properties through getComputedStyle, still honors a direct set).
141
+ const readVar = (name: string, fallback: number): number => {
142
+ const inline = element.style.getPropertyValue(name).trim();
143
+ const computed = inline || getComputedStyle(element).getPropertyValue(name).trim();
144
+ const n = parseFloat(computed);
145
+ return Number.isFinite(n) ? n : fallback;
146
+ };
147
+ const geometry = untrack(() => ({
148
+ startWidth: readVar('--kai-workspace-start-width', 280),
149
+ startMinWidth: readVar('--kai-workspace-start-min-width', 200),
150
+ startMaxWidth: readVar('--kai-workspace-start-max-width', 480),
151
+ endWidth: readVar('--kai-workspace-end-width', 320),
152
+ endMinWidth: readVar('--kai-workspace-end-min-width', 200),
153
+ endMaxWidth: readVar('--kai-workspace-end-max-width', 480),
154
+ }));
192
155
 
193
- // Imperative method API. The sidebar methods drive the same collapse path as the
194
- // in-UI toggle (named *Sidebar to avoid the `sidebarCollapsed` prop accessor).
195
- // The thread methods delegate to the embedded ChatThread's controller, captured
196
- // once below — the thread node is shared across both <Show> branches, so a single
197
- // controllerRef is stable across collapse/expand.
198
- let controller: ChatThreadController | undefined;
156
+ let controller: WorkspaceShellController | undefined;
199
157
  expose({
200
- /** Collapse/expand the conversation sidebar and fire `kai-sidebar-toggle`. */
201
- toggleSidebar: () => toggle(),
202
- /** Force the conversation sidebar collapsed (fires `kai-sidebar-toggle`). */
203
- collapseSidebar: () => setCollapsedTo(true),
204
- /** Force the conversation sidebar expanded (fires `kai-sidebar-toggle`). */
205
- expandSidebar: () => setCollapsedTo(false),
206
- /** Focus the thread's composer. */
207
- focus: (options?: FocusOptions) => controller?.focus(options),
208
- /** Clear the thread draft + staged attachments. */
209
- clear: () => controller?.clear(),
210
- /** Submit the current thread draft programmatically (fires `kai-submit`). */
211
- send: () => controller?.send(),
212
- /** Scroll the thread to the newest message. */
213
- scrollToBottom: (behavior?: ScrollBehavior) => controller?.scrollToBottom(behavior),
158
+ /** Collapse/expand one aside (fires `kai-aside-toggle`). */
159
+ toggleAside: (side: WorkspaceAsideSide) => controller?.toggleAside(side),
160
+ /** Force one aside collapsed (fires `kai-aside-toggle`). */
161
+ collapseAside: (side: WorkspaceAsideSide) => controller?.collapseAside(side),
162
+ /** Force one aside expanded (fires `kai-aside-toggle`). */
163
+ expandAside: (side: WorkspaceAsideSide) => controller?.expandAside(side),
214
164
  });
215
165
 
216
- // Create the thread ONCE and reference the same node in both <Show> branches.
217
- // It's owned by this component root (not by a Show branch), so toggling the
218
- // sidebar moves the node between branches without disposing it — the thread's
219
- // own state (e.g. an uncontrolled draft) survives the collapse/expand.
220
- const threadEl = (
221
- <ChatThread
222
- messages={validMessages(props.messages)} value={props.value as string | undefined} placeholder={props.placeholder as string}
223
- loading={flag('loading')} suggestions={props.suggestions as string[] | undefined}
224
- suggestionMode={props.suggestionMode as 'submit' | 'fill'} proseSize={props.proseSize as ProseSize}
225
- codeTheme={props.codeTheme as string} codeHighlight={flag('codeHighlight')}
226
- chatTitle={props.chatTitle as string | undefined} models={props.models as ModelOption[] | undefined}
227
- currentModel={props.currentModel as string | undefined} context={props.context as ChatThreadContextUsage | undefined}
228
- scrollButton={props.scrollButton !== false} search={flag('search')} voice={flag('voice')}
229
- triggers={props.triggers as TriggerDef[] | undefined}
230
- kindIcons={props.kindIcons as Record<string, string> | undefined}
231
- cardTypes={cardComponentsFromTags(props.cardTypes as Record<string, string> | undefined, (props as { theme?: string }).theme)}
232
- cardSchemas={props.cardSchemas as Record<string, object> | undefined}
233
- onValueChange={(value) => dispatch('kai-value-change', { value })}
234
- onSubmit={(detail) => dispatch('kai-submit', detail)}
235
- onSuggestionClick={(value) => dispatch('kai-suggestion-click', { value })}
236
- onModelChange={(modelId) => dispatch('kai-model-change', { modelId })}
237
- onMessageAction={(detail) => dispatch('kai-message-action', detail)}
238
- onSearch={() => dispatch('kai-search', {})}
239
- onVoice={() => dispatch('kai-voice', {})}
240
- controllerRef={(c) => (controller = c)}
241
- />
242
- );
243
-
244
- // The main region: an optional top-placed `main-header` band above the thread.
245
- // Shared across both collapse branches (the thread node is the same instance).
246
- const mainRegion = (
247
- <div class="flex h-full flex-col overflow-hidden">
248
- <Show when={slots()['main-header']}>
249
- <div class="shrink-0 border-b border-border"><slot name="main-header" /></div>
250
- </Show>
251
- <div class="min-h-0 flex-1">
252
- <Show when={slots()['main']} fallback={threadEl}>
253
- <slot name="main" />
254
- </Show>
255
- </div>
256
- </div>
257
- );
258
-
259
166
  return (
260
- <div ref={rootEl} class="h-full w-full overflow-hidden bg-background">
261
- <Show
262
- when={!collapsed()}
263
- fallback={
264
- <div class="flex h-full">
265
- {/* Collapsed: the reopen control lives in its OWN thin rail column
266
- (matching the sidebar's bg-surface) rather than floating over the
267
- content. The previous `absolute` overlay sat on top of a
268
- `main-header`'s leading title — see the t3code/codex/chatgpt apps,
269
- all of which fill that slot — so it both overlapped the header and
270
- competed with any in-header toggle. A dedicated column never
271
- overlaps and reads as a collapsed sidebar. */}
272
- <div class="flex w-11 shrink-0 flex-col items-center border-r border-border bg-surface pt-2.5">
273
- <CollapsedRail onExpand={toggle} class="bg-transparent shadow-none backdrop-blur-none" />
274
- </div>
275
- <div class="min-w-0 flex-1">{mainRegion}</div>
276
- </div>
277
- }
278
- >
279
- <ResizablePanelGroup orientation="horizontal">
280
- <ResizablePanel defaultSize={props.sidebarWidth as number} data-min-size={String(props.sidebarMinWidth)} data-max-size={String(props.sidebarMaxWidth)}>
281
- {/* The rail: an optional injected header above the list and footer
282
- below it (upgrade card / Design trigger / user menu). Carries a
283
- subtle, theme-aware default background (bg-surface), exposed as
284
- ::part(sidebar) so a consumer can override it. The inner list is
285
- made transparent so the part background reads uniformly. */}
286
- <div part="sidebar" class="flex h-full flex-col overflow-hidden bg-surface">
287
- <Show when={slots()['sidebar-header']}>
288
- {/* When `no-conversations` suppresses the built-in list, the
289
- projected header owns the whole rail flex region; otherwise it
290
- sits as a fixed band above the list. */}
291
- <div class={flag('noConversations') ? 'min-h-0 flex-1' : 'shrink-0'}><slot name="sidebar-header" /></div>
292
- </Show>
293
- <Show when={!flag('noConversations')}>
294
- <div class="min-h-0 flex-1">
295
- <ConversationList
296
- class="bg-transparent"
297
- groups={props.groups ?? []} conversations={props.conversations ?? []} activeId={props.activeId as string | undefined}
298
- compact={flag('compact')}
299
- onSelect={(id) => dispatch('kai-conversation-select', { id })}
300
- onNewChat={() => dispatch('kai-new-chat', {})}
301
- onToggleSidebar={toggle}
302
- />
303
- </div>
304
- </Show>
305
- <Show when={slots()['sidebar-footer']}>
306
- <div class="shrink-0"><slot name="sidebar-footer" /></div>
307
- </Show>
308
- </div>
309
- </ResizablePanel>
310
- <ResizableHandle />
311
- <ResizablePanel>{mainRegion}</ResizablePanel>
312
- </ResizablePanelGroup>
313
- </Show>
314
- </div>
167
+ <WorkspaceShell
168
+ header={slots()['header'] ? <slot name="header" /> : undefined}
169
+ start={slots()['start'] ? <slot name="start" /> : undefined}
170
+ end={slots()['end'] ? <slot name="end" /> : undefined}
171
+ footer={slots()['footer'] ? <slot name="footer" /> : undefined}
172
+ startCollapsed={props.startCollapsed as boolean | undefined}
173
+ defaultStartCollapsed={flag('defaultStartCollapsed')}
174
+ endCollapsed={props.endCollapsed as boolean | undefined}
175
+ defaultEndCollapsed={flag('defaultEndCollapsed')}
176
+ collapseBelow={props.collapseBelow as number | undefined}
177
+ drawerBelow={props.drawerBelow as number | undefined}
178
+ compact={flag('compact')}
179
+ {...geometry}
180
+ onAsideToggle={(detail) => dispatch('kai-aside-toggle', detail)}
181
+ onAsideResize={(detail) => dispatch('kai-aside-resize', detail)}
182
+ controllerRef={(c) => (controller = c)}
183
+ >
184
+ <slot name="main" />
185
+ <slot />
186
+ </WorkspaceShell>
315
187
  );
316
188
  });
@@ -1,4 +1,4 @@
1
- import { createEffect, createSignal, onCleanup, onMount } from 'solid-js';
1
+ import { createSignal, onCleanup, onMount } from 'solid-js';
2
2
  import { defineWebComponent } from './define';
3
3
  import { CHAT_SLOTS, readSlots } from './slots';
4
4
  import { ChatThread, type ChatThreadProps, type ChatThreadContextUsage, type ChatThreadController } from '../components/chat-thread';
@@ -14,7 +14,7 @@ import type { ModelOption } from '../types';
14
14
 
15
15
  type Props = Omit<ChatThreadProps,
16
16
  'class' | 'onValueChange' | 'onSubmit' | 'onAttachmentsChange' | 'onSuggestionClick' | 'onModelChange'
17
- | 'onMessageAction' | 'onSearch' | 'onVoice' | 'controllerRef' | 'cardTypes' | 'cardSchemas' | 'messages'
17
+ | 'onMessageAction' | 'onWebSearch' | 'onVoice' | 'controllerRef' | 'cardTypes' | 'cardSchemas' | 'messages'
18
18
  | 'accept' | 'onAttachmentsRejected'> & Record<string, unknown> & {
19
19
  /** Which attachment media types the user may stage, in HTML `accept` syntax:
20
20
  * `<kai-chat accept="image/*,application/pdf">`. A plain string, so unlike
@@ -86,8 +86,8 @@ interface Events {
86
86
  'kai-message-action': { messageId: string; action: string; state?: 'on' | 'off' };
87
87
  /** The header model switcher changed. */
88
88
  'kai-model-change': { modelId: string };
89
- /** The Search button was clicked. */
90
- 'kai-search': Record<string, never>;
89
+ /** The web-search (Globe) toolbar button was clicked. */
90
+ 'kai-web-search': Record<string, never>;
91
91
  /** The Mic / voice button was clicked. */
92
92
  'kai-voice': Record<string, never>;
93
93
  }
@@ -97,9 +97,9 @@ defineWebComponent<Props, Events>('kai-chat', {
97
97
  suggestions: undefined, suggestionMode: 'submit', persistSuggestions: false, proseSize: 'sm',
98
98
  codeTheme: 'github-dark-dimmed', codeHighlight: true, chatTitle: undefined,
99
99
  models: undefined, currentModel: undefined, context: undefined, scrollButton: true,
100
- search: false, voice: false, triggers: undefined, kindIcons: undefined,
100
+ webSearch: false, voice: false, triggers: undefined, kindIcons: undefined,
101
101
  actionsReveal: 'always', cardTypes: undefined, cardSchemas: undefined, accept: undefined,
102
- }, (props, { dispatch, flag, element, expose }) => {
102
+ }, (props, { dispatch, flag, reflectFlag, element, expose }) => {
103
103
  // `messages` is an untyped boundary: a consumer can hand it anything at
104
104
  // runtime (a pre-0.20.0 `{ id, role, content }` array, in particular). Skip
105
105
  // the invalid entries rather than let `groupMessageParts` throw deep inside a
@@ -120,7 +120,10 @@ defineWebComponent<Props, Events>('kai-chat', {
120
120
 
121
121
  // Reflect streaming state to a host attribute so slotted composer/notice CSS
122
122
  // can react without reading internals (e.g. :host([loading]) ::slotted(...)).
123
- createEffect(() => { element.toggleAttribute('loading', flag('loading')); });
123
+ // reflectFlag, not a hand-rolled toggleAttribute effect: the reflection is what
124
+ // makes the property read back `undefined`, so the two belong in one call. See
125
+ // WebComponentContext.reflectFlag.
126
+ reflectFlag('loading');
124
127
 
125
128
  // Imperative method API — forward the chat-thread controller onto the host
126
129
  // (focus the composer, clear it, send programmatically, scroll the thread).
@@ -161,7 +164,7 @@ defineWebComponent<Props, Events>('kai-chat', {
161
164
  codeTheme={props.codeTheme as string} codeHighlight={flag('codeHighlight')}
162
165
  chatTitle={props.chatTitle as string | undefined} models={props.models as ModelOption[] | undefined}
163
166
  currentModel={props.currentModel as string | undefined} context={props.context as ChatThreadContextUsage | undefined}
164
- scrollButton={props.scrollButton !== false} search={flag('search')} voice={flag('voice')}
167
+ scrollButton={props.scrollButton !== false} webSearch={flag('webSearch')} voice={flag('voice')}
165
168
  triggers={props.triggers as TriggerDef[] | undefined}
166
169
  kindIcons={props.kindIcons as Record<string, string> | undefined}
167
170
  actionsReveal={props.actionsReveal as 'always' | 'hover'}
@@ -175,7 +178,7 @@ defineWebComponent<Props, Events>('kai-chat', {
175
178
  onSuggestionClick={(value) => dispatch('kai-suggestion-click', { value })}
176
179
  onModelChange={(modelId) => dispatch('kai-model-change', { modelId })}
177
180
  onMessageAction={(detail) => dispatch('kai-message-action', detail)}
178
- onSearch={() => dispatch('kai-search', {})}
181
+ onWebSearch={() => dispatch('kai-web-search', {})}
179
182
  onVoice={() => dispatch('kai-voice', {})}
180
183
  controllerRef={(c) => (controller = c)}
181
184
  headerStart={slot('header-start')}
@@ -11,6 +11,11 @@ interface Props extends Record<string, unknown> {
11
11
  codeTheme?: string;
12
12
  /** Disable syntax highlighting (renders plain text, no Shiki). */
13
13
  codeHighlight?: boolean;
14
+ /**
15
+ * Show the copy button. **Defaults to ON**, because this element is documented
16
+ * as shipping one. Opt out with `copy="false"` or `el.copy = false`.
17
+ */
18
+ copy?: boolean;
14
19
  /** Code text sizing. */
15
20
  proseSize?: ProseSize;
16
21
  }
@@ -18,12 +23,17 @@ interface Props extends Record<string, unknown> {
18
23
  /**
19
24
  * `<kai-code-block>` — one syntax-highlighted code block (with a copy button).
20
25
  * Code via the `code` property; `language`/`code-theme` via attributes.
26
+ *
27
+ * The copy button is ON by default and writes the raw `code` property, never the
28
+ * highlighted markup. Turn it off with `copy="false"`. Style it via
29
+ * `kai-code-block::part(copy)`.
21
30
  */
22
31
  defineWebComponent<Props>('kai-code-block', {
23
32
  code: '',
24
33
  language: undefined,
25
34
  codeTheme: 'github-dark-dimmed',
26
35
  codeHighlight: true,
36
+ copy: true,
27
37
  proseSize: 'sm',
28
38
  }, (props, { flag }) => {
29
39
  const outer = useChatConfig();
@@ -34,7 +44,15 @@ defineWebComponent<Props>('kai-code-block', {
34
44
  codeHighlight={flag('codeHighlight')}
35
45
  portalMount={outer.portalMount()}
36
46
  >
37
- <CodeBlock>
47
+ {/* The ELEMENT opts in; the Solid `CodeBlock` default stays OFF so markdown and
48
+ artifact are untouched. `copyText` is the raw property — the button must
49
+ never copy what Shiki produced. `copy` is NOT reflected, matching
50
+ `codeHighlight` beside it: reflecting a default-ON flag would stamp `copy=""`
51
+ on every instance for no benefit, and the runtime-attribute hazard that
52
+ earned `reflectFlag` elsewhere needs a BARE attribute, which is a no-op on a
53
+ flag that is already on. The meaningful spelling, `copy="false"`, is a real
54
+ prop change and is pinned as one. */}
55
+ <CodeBlock copy={flag('copy')} copyText={props.code}>
38
56
  <CodeBlockCode code={props.code} language={props.language} />
39
57
  </CodeBlock>
40
58
  </ChatConfig>
@@ -174,7 +174,10 @@ defineWebComponent<Props, Events>('kai-command', {
174
174
  aria-controls={listboxId}
175
175
  value={query()}
176
176
  placeholder={(props.placeholder as string | undefined) ?? 'Search…'}
177
- class="w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none"
177
+ // No `focus-visible:outline-none` here. It used to suppress the base
178
+ // focus ring and put nothing in its place, so keyboard users had no
179
+ // indication of where they were in the palette.
180
+ class="w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground"
178
181
  onInput={handleInput}
179
182
  onKeyDown={handleKeyDown}
180
183
  />