@kitn.ai/ui 0.26.0 → 0.28.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 (488) hide show
  1. package/README.md +25 -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--Y8vsFal.js → Icon-DObhsRtm.js} +1 -1
  6. package/dist/action-icons-CcgNN2oT.js +1 -0
  7. package/dist/arrow-down-CjRU3JgV.js +1 -0
  8. package/dist/arrow-left-Bnpb1UEW.js +1 -0
  9. package/dist/artifact-Bwt7OgcR.js +1 -0
  10. package/dist/assets/dev-5XzXYR21.js +91 -0
  11. package/dist/attachments-Qr4kg-2b.js +1 -0
  12. package/dist/{elements/chunks/audio-visualizer-D7lB6mvR.js → audio-visualizer-DToYdfSc.js} +1 -1
  13. package/dist/badge-CdEu_jn2.js +1 -0
  14. package/dist/bash-InADTalH.js +1 -6
  15. package/dist/bell-DQAAOpDz.js +1 -0
  16. package/dist/{elements/chunks/button-flUbeufF.js → button-DkuOnGLy.js} +1 -1
  17. package/dist/card-renderer-Jx70m6_l.js +1 -0
  18. package/dist/card-routing-0YidER_3.js +1 -0
  19. package/dist/check-njkVdZu2.js +1 -0
  20. package/dist/checkbox-D7iRB3nL.js +1 -0
  21. package/dist/checkbox-group-B2_rpoPM.js +1 -0
  22. package/dist/chevron-down-B72s6PXR.js +1 -0
  23. package/dist/chevron-right-BatuHzUm.js +1 -0
  24. package/dist/choice-card-m0BYqqOK.js +1 -0
  25. package/dist/circle-check-B62pODB3.js +1 -0
  26. package/dist/circle-x-BPG0iPst.js +1 -0
  27. package/dist/circle-zx0ByzY9.js +1 -0
  28. package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
  29. package/dist/code-block-ClMLKb7C.js +1 -0
  30. package/dist/{elements/chunks/collapsible-BzbXpPzr.js → collapsible-B4DlWicL.js} +1 -1
  31. package/dist/components/attachment-types.d.ts +7 -0
  32. package/dist/components/audio-visualizer/fit-scale.d.ts +1 -1
  33. package/dist/components/card-renderer.d.ts +13 -0
  34. package/dist/components/card.d.ts +4 -0
  35. package/dist/components/chat-thread.d.ts +135 -2
  36. package/dist/components/conversation-item.d.ts +35 -7
  37. package/dist/components/conversation-list.d.ts +17 -5
  38. package/dist/components/conversation-panel.d.ts +29 -0
  39. package/dist/components/form-widgets.d.ts +67 -2
  40. package/dist/components/form.d.ts +48 -1
  41. package/dist/components/home-panel.d.ts +34 -0
  42. package/dist/components/message.d.ts +25 -3
  43. package/dist/components/reasoning.d.ts +6 -0
  44. package/dist/components/scroll-button.d.ts +11 -0
  45. package/dist/components/thread.d.ts +5 -0
  46. package/dist/components/tool-types.d.ts +9 -0
  47. package/dist/components/widget-tab-bar.d.ts +16 -0
  48. package/dist/composer-sQ0HcvyG.js +106 -0
  49. package/dist/confirm-card-rQWDQsnC.js +1 -0
  50. package/dist/construct-cli.es.js +1337 -0
  51. package/dist/{elements/chunks/context-VLIL3sJ-.js → context-0kg4ZX1J.js} +1 -1
  52. package/dist/controllable-CnGpMF6r.js +1 -0
  53. package/dist/conversation-item-BxwQLaFJ.js +1 -0
  54. package/dist/conversation-list-BCfGN1JU.js +1 -0
  55. package/dist/copy-n9u9rqpi.js +1 -0
  56. package/dist/core-AYMC6_lb.js +12 -5874
  57. package/dist/{create-tween-BFT6c-Y8.js → create-tween-C-q-JCM-.js} +1 -1
  58. package/dist/{create-tween-CJNlzoaO.js → create-tween-CRrJXxBd.js} +1 -1
  59. package/dist/{create-tween-CQED4T0g.js → create-tween-DZ3RRDHD.js} +1 -1
  60. package/dist/{elements/chunks/create-tween-c4Z8TDYz.js → create-tween-uPkArjIG.js} +2 -2
  61. package/dist/css-M7EaDHN_.js +1 -6
  62. package/dist/custom-elements.json +1433 -202
  63. package/dist/default-input-CaMrJkgH.js +1 -0
  64. package/dist/define-By-uL588.js +1 -0
  65. package/dist/define.d.ts +4 -0
  66. package/dist/define.js +510 -0
  67. package/dist/define.server.d.ts +5 -0
  68. package/dist/define.server.js +506 -0
  69. package/dist/{elements/chunks/disclosure-CcNbyLy0.js → disclosure-WUImBbDv.js} +1 -1
  70. package/dist/{elements/chunks/dropdown-BgWTthUx.js → dropdown-wZVeJOMd.js} +1 -1
  71. package/dist/elements/agent-card.js +1 -1
  72. package/dist/elements/artifact.js +1 -1
  73. package/dist/elements/attachments.js +1 -1
  74. package/dist/elements/audio-visualizer.js +1 -1
  75. package/dist/elements/autoloader.js +1 -1
  76. package/dist/elements/avatar.js +1 -1
  77. package/dist/elements/badge.js +1 -1
  78. package/dist/elements/button.js +1 -1
  79. package/dist/elements/card.js +1 -1
  80. package/dist/elements/cards.js +1 -1
  81. package/dist/elements/chain-of-thought.js +1 -1
  82. package/dist/elements/chat-scope-picker.js +1 -1
  83. package/dist/elements/chat-workspace.js +1 -1
  84. package/dist/elements/chat.js +1 -1
  85. package/dist/elements/checkbox-group.d.ts +1 -0
  86. package/dist/elements/checkbox-group.js +1 -0
  87. package/dist/elements/checkbox.d.ts +1 -0
  88. package/dist/elements/checkbox.js +1 -0
  89. package/dist/elements/checkpoint.js +1 -1
  90. package/dist/elements/choice.js +1 -1
  91. package/dist/elements/coachmark.js +1 -1
  92. package/dist/elements/code-block.js +1 -1
  93. package/dist/elements/command.js +1 -1
  94. package/dist/elements/compare.js +1 -1
  95. package/dist/elements/composer.js +1 -1
  96. package/dist/elements/confirm-card.js +1 -1
  97. package/dist/elements/context-meter.js +1 -1
  98. package/dist/elements/conversation-item.js +1 -1
  99. package/dist/elements/conversation-list.js +1 -1
  100. package/dist/elements/define-entry.d.ts +12 -0
  101. package/dist/elements/dialog.js +1 -1
  102. package/dist/elements/dock.js +102 -8
  103. package/dist/elements/dropdown.js +1 -1
  104. package/dist/elements/editable-label.js +1 -1
  105. package/dist/elements/element-data-types.d.ts +37 -0
  106. package/dist/elements/embed.js +1 -1
  107. package/dist/elements/empty.js +1 -1
  108. package/dist/elements/feedback-bar.js +1 -1
  109. package/dist/elements/file-tree.js +1 -1
  110. package/dist/elements/file-upload.js +1 -1
  111. package/dist/elements/form.js +1 -1
  112. package/dist/elements/hover-card.js +1 -1
  113. package/dist/elements/icon.js +1 -1
  114. package/dist/elements/image.js +1 -1
  115. package/dist/elements/input.js +1 -1
  116. package/dist/elements/kbd.js +1 -1
  117. package/dist/elements/link-preview.js +1 -1
  118. package/dist/elements/loader.js +1 -1
  119. package/dist/elements/markdown.js +1 -1
  120. package/dist/elements/menu.js +1 -1
  121. package/dist/elements/message-skills.js +1 -1
  122. package/dist/elements/message.js +1 -1
  123. package/dist/elements/model-switcher.js +1 -1
  124. package/dist/elements/nav.js +1 -1
  125. package/dist/elements/notice.js +1 -1
  126. package/dist/elements/pane-grid.d.ts +1 -0
  127. package/dist/elements/pane-grid.js +1 -0
  128. package/dist/elements/pane-group.js +1 -1
  129. package/dist/elements/pane.js +1 -1
  130. package/dist/elements/popover.js +1 -1
  131. package/dist/elements/progress-bar.js +1 -1
  132. package/dist/elements/prompt-dock.js +1 -1
  133. package/dist/elements/prompt-input.js +1 -1
  134. package/dist/elements/prompt-suggestions.js +1 -1
  135. package/dist/elements/radio-group.d.ts +1 -0
  136. package/dist/elements/radio-group.js +1 -0
  137. package/dist/elements/reasoning.js +1 -1
  138. package/dist/elements/remote.js +1 -1
  139. package/dist/elements/resizable.js +1 -1
  140. package/dist/elements/response-stream.js +2 -2
  141. package/dist/elements/screen.js +1 -1
  142. package/dist/elements/scroll-area.js +1 -1
  143. package/dist/elements/scroll-button.js +1 -1
  144. package/dist/elements/search.js +1 -1
  145. package/dist/elements/segmented.js +1 -1
  146. package/dist/elements/select.d.ts +1 -0
  147. package/dist/elements/select.js +1 -0
  148. package/dist/elements/separator.js +1 -1
  149. package/dist/elements/setting-item.js +1 -1
  150. package/dist/elements/settings-group.js +1 -1
  151. package/dist/elements/skeleton.js +1 -1
  152. package/dist/elements/slider.d.ts +1 -0
  153. package/dist/elements/slider.js +1 -0
  154. package/dist/elements/slots.d.ts +1 -1
  155. package/dist/elements/source.js +1 -1
  156. package/dist/elements/status.js +1 -1
  157. package/dist/elements/switch.js +1 -1
  158. package/dist/elements/tabs.js +1 -1
  159. package/dist/elements/tasks.js +1 -1
  160. package/dist/elements/text-shimmer.js +1 -1
  161. package/dist/elements/thinking-bar.js +1 -1
  162. package/dist/elements/thread.js +1 -1
  163. package/dist/elements/toast.js +1 -1
  164. package/dist/elements/tool.js +1 -1
  165. package/dist/elements/tooltip.js +1 -1
  166. package/dist/elements/voice-input.js +1 -1
  167. package/dist/elements/voice-output.js +1 -1
  168. package/dist/elements.d.ts +380 -26
  169. package/dist/{elements/chunks/ellipsis-7gU33mL2.js → ellipsis-CbOpJnU3.js} +1 -1
  170. package/dist/embed-CrfjkbUG.js +1 -0
  171. package/dist/engine-javascript-vq0WuIJl.js +14 -2516
  172. package/dist/{elements/chunks/external-link-CSOEsNlx.js → external-link-ByXAwBBQ.js} +1 -1
  173. package/dist/file-text-DMib2qwt.js +1 -0
  174. package/dist/{elements/chunks/file-tree-zvB2i7P9.js → file-tree-CJqM2aP7.js} +1 -1
  175. package/dist/folder-_BAb929C.js +1 -0
  176. package/dist/form-DSZgZQZk.js +1 -0
  177. package/dist/github-dark-dimmed-DUshB20C.js +1 -4
  178. package/dist/github-light-JYsPkUQd.js +1 -4
  179. package/dist/hover-card-BQbul9eq.js +1 -0
  180. package/dist/html-CPZ3oZQ7.js +1 -10
  181. package/dist/icon-CdcfRhYI.js +1 -0
  182. package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
  183. package/dist/index.d.ts +4 -2
  184. package/dist/index.js +7474 -5857
  185. package/dist/index.server.js +5965 -4762
  186. package/dist/info-DsFjGuI_.js +1 -0
  187. package/dist/input-DKbmkMos.js +1 -0
  188. package/dist/javascript-C25yR2R2.js +1 -6
  189. package/dist/json-DxJze_jm.js +1 -6
  190. package/dist/kai.es.js +1 -1
  191. package/dist/kbd-PvdGhX4s.js +1 -0
  192. package/dist/{elements/chunks/link-preview-OQ9IEq0f.js → link-preview-DbM1866N.js} +1 -1
  193. package/dist/link-zF8PzWfF.js +1 -0
  194. package/dist/loader-BM4CEz4G.js +1 -0
  195. package/dist/{elements/chunks/markdown-DgBFaGAz.js → markdown-LMvmAyBk.js} +7 -7
  196. package/dist/mcp.es.js +1361 -81
  197. package/dist/message-BSsgvHvf.js +1 -0
  198. package/dist/message-BYZECyek.js +1 -0
  199. package/dist/message-circle-DruwObfX.js +1 -0
  200. package/dist/message-square-BG6q6mlm.js +1 -0
  201. package/dist/{elements/chunks/minimize-2-DPIUKTrT.js → minimize-2-BQRoBV1V.js} +1 -1
  202. package/dist/{elements/chunks/model-switcher-DsT8tFB0.js → model-switcher-CMpCOYcB.js} +1 -1
  203. package/dist/{elements/chunks/overlay-DuiwdXjm.js → overlay-Cb-MkyDa.js} +1 -1
  204. package/dist/panel-right-i8warFxT.js +1 -0
  205. package/dist/paperclip-ByY3UVO6.js +1 -0
  206. package/dist/play-BvKKq3fX.js +1 -0
  207. package/dist/primitives/card-data-types.d.ts +49 -0
  208. package/dist/primitives/card-routing.d.ts +1 -33
  209. package/dist/primitives/card-validate.d.ts +2 -2
  210. package/dist/primitives/chat-config.d.ts +29 -2
  211. package/dist/primitives/conversation-store.d.ts +54 -0
  212. package/dist/primitives/field-mask.d.ts +96 -0
  213. package/dist/primitives/field-semantics.d.ts +36 -0
  214. package/dist/primitives/input-mask.d.ts +46 -0
  215. package/dist/primitives/url-scheme-policy.d.ts +34 -0
  216. package/dist/primitives/use-resize-observer.d.ts +2 -2
  217. package/dist/progress-bar-jVAHvMUf.js +1 -0
  218. package/dist/prompt-suggestion-CD-qtNgC.js +1 -0
  219. package/dist/radio-BsoBMhkF.js +1 -0
  220. package/dist/react/index.d.ts +586 -19
  221. package/dist/react.js +223 -187
  222. package/dist/reasoning-4kBPtlTx.js +1 -0
  223. package/dist/register-impl-B1hLkrI8.js +424 -0
  224. package/dist/{elements/chunks/resizable-D181b4fM.js → resizable-uPmY9VY-.js} +1 -1
  225. package/dist/rotate-ccw-B2xn93py.js +1 -0
  226. package/dist/rotate-cw-CMVLoCyH.js +1 -0
  227. package/dist/schemas/form.schema.json +21 -1
  228. package/dist/schemas/tool-defs.d.ts +11 -1
  229. package/dist/schemas.js +81 -81
  230. package/dist/scroll-area-BqLUo8ZZ.js +1 -0
  231. package/dist/scroll-button-DslXiJyL.js +1 -0
  232. package/dist/select-2cLRtPGT.js +1 -0
  233. package/dist/{elements/chunks/separator-CeJHQqLW.js → separator-BWQtNJcM.js} +1 -1
  234. package/dist/{elements/chunks/settings-CqaHgJF9.js → settings-BBR63jLg.js} +1 -1
  235. package/dist/{elements/chunks/settings-group-CjqjLCOn.js → settings-group-B2ciy-Iw.js} +1 -1
  236. package/dist/{elements/chunks/skeleton-D8akBnKS.js → skeleton-CkHI-HgC.js} +1 -1
  237. package/dist/slider-DDR9U-i0.js +1 -0
  238. package/dist/{solid-BtdqNZI7.js → solid-BzCFdL25.js} +9771 -7594
  239. package/dist/{solid-CnavwBye.js → solid-DruVOXnf.js} +7893 -6183
  240. package/dist/solid.d.ts +16 -2
  241. package/dist/solid.js +256 -243
  242. package/dist/solid.server.js +256 -243
  243. package/dist/source-GEnFF528.js +1 -0
  244. package/dist/star-CilHq8GP.js +1 -0
  245. package/dist/state/index.d.ts +1 -1
  246. package/dist/state/mock.d.ts +32 -2
  247. package/dist/state.js +133 -108
  248. package/dist/store-Ciyk6eZG.js +1 -0
  249. package/dist/svelte--5p79yCD.js +1 -15
  250. package/dist/switch-KkeQIbuH.js +1 -0
  251. package/dist/tasks-card-BrN4bggJ.js +1 -0
  252. package/dist/text-shimmer-Dcj1MHuq.js +1 -0
  253. package/dist/textarea-Dpvbngbg.js +1 -0
  254. package/dist/theme.tokens.css +110 -13
  255. package/dist/{elements/chunks/thumbs-up-DX2Ijk3H.js → thumbs-up-CsT9Wzb9.js} +1 -1
  256. package/dist/toast-store-tgK6MuMv.js +1 -0
  257. package/dist/{elements/chunks/tool-DKQ2D1DP.js → tool-DzUjIBT8.js} +1 -1
  258. package/dist/{elements/chunks/tooltip-B0G99y8w.js → tooltip-Bkkg_4t0.js} +1 -1
  259. package/dist/trash-2-jBKxjGGQ.js +1 -0
  260. package/dist/{elements/chunks/triangle-alert-F9cyUEyy.js → triangle-alert-DlQ68ppf.js} +1 -1
  261. package/dist/tsx-B8rCNbgL.js +1 -6
  262. package/dist/types.d.ts +48 -3
  263. package/dist/typescript-RycA9KXf.js +1 -6
  264. package/dist/ui/checkbox-group.d.ts +83 -0
  265. package/dist/ui/checkbox.d.ts +40 -0
  266. package/dist/ui/dock.d.ts +51 -0
  267. package/dist/ui/icon.d.ts +3 -0
  268. package/dist/ui/input.d.ts +48 -2
  269. package/dist/ui/radio.d.ts +85 -0
  270. package/dist/ui/select.d.ts +71 -0
  271. package/dist/ui/slider.d.ts +65 -0
  272. package/dist/ui/switch.d.ts +9 -2
  273. package/dist/upload-TcAHXv9W.js +1 -0
  274. package/dist/url-scheme-policy-DR5DgvkM.js +1 -0
  275. package/dist/use-card-resolution-DaZqAdWf.js +1 -0
  276. package/dist/{elements/chunks/variant-aurora-qt6Zct8M.js → variant-aurora-BhLuYxJR.js} +1 -1
  277. package/dist/{variant-aurora-B8II3-I1.js → variant-aurora-CNmfHhcx.js} +2 -2
  278. package/dist/{variant-aurora-CMNWOSJR.js → variant-aurora-D6GsdTPA.js} +2 -2
  279. package/dist/{variant-aurora-CcuDMWwQ.js → variant-aurora-DbW_OWL2.js} +1 -1
  280. package/dist/{elements/chunks/variant-custom-tJp22_BQ.js → variant-custom-BRvaNHSi.js} +1 -1
  281. package/dist/{variant-custom-L16JLLZy.js → variant-custom-BvYHR62b.js} +1 -1
  282. package/dist/{variant-custom-BdvYC7af.js → variant-custom-DMTrEO5w.js} +2 -2
  283. package/dist/{variant-custom-C714mqKl.js → variant-custom-DV2x254s.js} +2 -2
  284. package/dist/{variant-wave-ecwykeyL.js → variant-wave-BNMGrDfj.js} +2 -2
  285. package/dist/{variant-wave-2S4La2Ka.js → variant-wave-BpDd8t3Z.js} +1 -1
  286. package/dist/{variant-wave-D7ruiA1W.js → variant-wave-CyBueXj9.js} +2 -2
  287. package/dist/{elements/chunks/variant-wave-CaQ5ti39.js → variant-wave-sp-qwsdI.js} +1 -1
  288. package/dist/vue-BmIZj4XD.js +1 -33
  289. package/dist/x-Z5UntkIJ.js +1 -0
  290. package/frameworks/react/index.tsx +210 -22
  291. package/llms-full.txt +1184 -22
  292. package/llms.txt +6 -3
  293. package/package.json +17 -4
  294. package/src/agent-tooling/README.md +192 -0
  295. package/src/agent-tooling/catalog/invariants.ts +3 -3
  296. package/src/agent-tooling/catalog/surfaces.ts +72 -0
  297. package/src/agent-tooling/construct/cli-entry.ts +8 -0
  298. package/src/agent-tooling/construct/cli.ts +147 -0
  299. package/src/agent-tooling/construct/codegen.ts +1635 -0
  300. package/src/agent-tooling/construct/construct.v1.schema.json +319 -0
  301. package/src/agent-tooling/construct/dev.ts +148 -0
  302. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +7 -0
  303. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +44 -0
  304. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +32 -0
  305. package/src/agent-tooling/construct/schema.ts +452 -0
  306. package/src/agent-tooling/mcp/server.ts +2 -1
  307. package/src/agent-tooling/mcp/tools/construct.ts +130 -0
  308. package/src/agent-tooling/mcp/tools/debug.ts +126 -4
  309. package/src/agent-tooling/mcp/tools/reference.ts +104 -3
  310. package/src/agent-tooling/recipes/composed-thread.ts +714 -0
  311. package/src/agent-tooling/recipes/index.ts +22 -0
  312. package/src/agent-tooling/recipes/types.ts +32 -0
  313. package/src/agent-tooling/registry.ts +5 -0
  314. package/src/components/attachment-types.ts +7 -0
  315. package/src/components/audio-visualizer/fit-scale.ts +1 -1
  316. package/src/components/card-renderer.tsx +39 -8
  317. package/src/components/card.tsx +10 -2
  318. package/src/components/chat-scope-picker.tsx +2 -2
  319. package/src/components/chat-thread.tsx +602 -43
  320. package/src/components/choice-card.tsx +102 -37
  321. package/src/components/coachmark.tsx +1 -1
  322. package/src/components/code-block.tsx +2 -2
  323. package/src/components/composer.tsx +89 -15
  324. package/src/components/confirm-card.tsx +20 -4
  325. package/src/components/conversation-item.tsx +56 -9
  326. package/src/components/conversation-list.tsx +25 -10
  327. package/src/components/conversation-panel.tsx +118 -0
  328. package/src/components/empty.tsx +14 -3
  329. package/src/components/file-tree.tsx +5 -1
  330. package/src/components/form-widgets.tsx +257 -195
  331. package/src/components/form.tsx +297 -9
  332. package/src/components/home-panel.tsx +159 -0
  333. package/src/components/loader.tsx +12 -5
  334. package/src/components/message-skills.tsx +1 -1
  335. package/src/components/message.tsx +71 -11
  336. package/src/components/prompt-input.tsx +7 -1
  337. package/src/components/prompt-suggestion.tsx +4 -1
  338. package/src/components/reasoning.tsx +30 -3
  339. package/src/components/response-compare.tsx +2 -2
  340. package/src/components/scroll-button.tsx +56 -4
  341. package/src/components/source.tsx +6 -1
  342. package/src/components/tasks-card.tsx +58 -16
  343. package/src/components/thread.tsx +10 -1
  344. package/src/components/toast.tsx +1 -1
  345. package/src/components/tool-types.ts +9 -0
  346. package/src/components/widget-tab-bar.tsx +73 -0
  347. package/src/elements/attachments.tsx +3 -1
  348. package/src/elements/chat-workspace.tsx +1 -1
  349. package/src/elements/chat.tsx +115 -5
  350. package/src/elements/checkbox-group.tsx +164 -0
  351. package/src/elements/checkbox.tsx +134 -0
  352. package/src/elements/compiled.css +1 -1
  353. package/src/elements/composer.tsx +12 -1
  354. package/src/elements/conversation-item.tsx +62 -22
  355. package/src/elements/conversation-list.tsx +2 -2
  356. package/src/elements/define-entry.ts +12 -0
  357. package/src/elements/dock.tsx +11 -0
  358. package/src/elements/element-data-types.ts +40 -0
  359. package/src/elements/element-manifest.json +24 -0
  360. package/src/elements/element-meta.json +767 -34
  361. package/src/elements/element-nonscalar.json +17 -0
  362. package/src/elements/element-types.d.ts +380 -26
  363. package/src/elements/feedback-bar.tsx +5 -0
  364. package/src/elements/icon-names.json +29 -0
  365. package/src/elements/input.tsx +312 -23
  366. package/src/elements/message.tsx +2 -0
  367. package/src/elements/pane-grid.tsx +111 -0
  368. package/src/elements/prompt-input.tsx +8 -2
  369. package/src/elements/radio-group.tsx +129 -0
  370. package/src/elements/register-impl.ts +6 -0
  371. package/src/elements/scroll-button.tsx +54 -6
  372. package/src/elements/select.tsx +165 -0
  373. package/src/elements/slider.tsx +171 -0
  374. package/src/elements/slots.ts +1 -1
  375. package/src/elements/styles.css +89 -3
  376. package/src/elements/thread.tsx +2 -0
  377. package/src/elements/toast.tsx +14 -1
  378. package/src/index.ts +5 -2
  379. package/src/primitives/card-data-types.ts +49 -0
  380. package/src/primitives/card-host.tsx +4 -2
  381. package/src/primitives/card-routing.ts +7 -56
  382. package/src/primitives/card-schemas/form.schema.json +21 -1
  383. package/src/primitives/card-validate.ts +10 -2
  384. package/src/primitives/chat-config.tsx +33 -6
  385. package/src/primitives/conversation-store.ts +248 -0
  386. package/src/primitives/field-mask.ts +256 -0
  387. package/src/primitives/field-semantics.ts +115 -0
  388. package/src/primitives/input-mask.ts +853 -0
  389. package/src/primitives/toast-store.ts +66 -4
  390. package/src/primitives/url-scheme-policy.ts +70 -0
  391. package/src/primitives/use-resize-observer.ts +2 -2
  392. package/src/schemas/tool-defs.ts +12 -2
  393. package/src/solid.ts +20 -2
  394. package/src/state/index.ts +1 -1
  395. package/src/state/mock.ts +92 -6
  396. package/src/types.ts +45 -3
  397. package/src/ui/agent-card.tsx +2 -2
  398. package/src/ui/avatar.tsx +1 -1
  399. package/src/ui/checkbox-group.tsx +153 -0
  400. package/src/ui/checkbox.tsx +63 -0
  401. package/src/ui/dock.tsx +192 -7
  402. package/src/ui/icon.tsx +53 -0
  403. package/src/ui/input.tsx +249 -9
  404. package/src/ui/kbd.tsx +1 -1
  405. package/src/ui/nav.tsx +1 -1
  406. package/src/ui/pane-group.tsx +2 -2
  407. package/src/ui/radio.tsx +150 -0
  408. package/src/ui/select.tsx +168 -0
  409. package/src/ui/slider.tsx +178 -0
  410. package/src/ui/switch.tsx +49 -18
  411. package/src/utils/cn.ts +11 -5
  412. package/theme.css +112 -13
  413. package/dist/elements/chunks/action-icons-D9u3OzYW.js +0 -1
  414. package/dist/elements/chunks/arrow-left-CVh1TeN7.js +0 -1
  415. package/dist/elements/chunks/artifact-BkYcfIlZ.js +0 -1
  416. package/dist/elements/chunks/attachments-DwfNlubq.js +0 -1
  417. package/dist/elements/chunks/badge-Q0-Cmwzz.js +0 -1
  418. package/dist/elements/chunks/bash-InADTalH.js +0 -1
  419. package/dist/elements/chunks/card-renderer-3Dyl-FpN.js +0 -1
  420. package/dist/elements/chunks/card-routing-BK09BdmL.js +0 -1
  421. package/dist/elements/chunks/check-B5kBPKeo.js +0 -1
  422. package/dist/elements/chunks/chevron-down-DZ1GS39h.js +0 -1
  423. package/dist/elements/chunks/chevron-right-CvP7OUd2.js +0 -1
  424. package/dist/elements/chunks/choice-card-D4QUIOWZ.js +0 -1
  425. package/dist/elements/chunks/circle-CDf9g6MQ.js +0 -1
  426. package/dist/elements/chunks/circle-check-CA47Nqbz.js +0 -1
  427. package/dist/elements/chunks/circle-x-CJ_HVbC8.js +0 -1
  428. package/dist/elements/chunks/code-block-BdLiTitd.js +0 -1
  429. package/dist/elements/chunks/composer-Byve-n8K.js +0 -69
  430. package/dist/elements/chunks/confirm-card-B0flE3MN.js +0 -1
  431. package/dist/elements/chunks/controllable-DTwTIlwp.js +0 -1
  432. package/dist/elements/chunks/conversation-item-BDztezOG.js +0 -1
  433. package/dist/elements/chunks/copy-Nf-kqxXH.js +0 -1
  434. package/dist/elements/chunks/core-AYMC6_lb.js +0 -12
  435. package/dist/elements/chunks/css-M7EaDHN_.js +0 -1
  436. package/dist/elements/chunks/default-input-C2iLFq0b.js +0 -1
  437. package/dist/elements/chunks/define-wjHR1MkU.js +0 -1
  438. package/dist/elements/chunks/download-C4V7tBdb.js +0 -1
  439. package/dist/elements/chunks/embed-D3KewsIJ.js +0 -1
  440. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +0 -141
  441. package/dist/elements/chunks/file-text-D1G8khle.js +0 -1
  442. package/dist/elements/chunks/folder-WW2LGLid.js +0 -1
  443. package/dist/elements/chunks/form-CQirdC4W.js +0 -1
  444. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +0 -1
  445. package/dist/elements/chunks/github-light-JYsPkUQd.js +0 -1
  446. package/dist/elements/chunks/hover-card-CJJkfHTK.js +0 -1
  447. package/dist/elements/chunks/html-CPZ3oZQ7.js +0 -1
  448. package/dist/elements/chunks/icon-Cfgmf4fa.js +0 -1
  449. package/dist/elements/chunks/info-DX3YMOt6.js +0 -1
  450. package/dist/elements/chunks/input-BtW9U6Sq.js +0 -1
  451. package/dist/elements/chunks/javascript-C25yR2R2.js +0 -1
  452. package/dist/elements/chunks/json-DxJze_jm.js +0 -1
  453. package/dist/elements/chunks/kbd-LGXwuT_V.js +0 -1
  454. package/dist/elements/chunks/link-2My69BqT.js +0 -1
  455. package/dist/elements/chunks/loader-iOxc_Tu-.js +0 -1
  456. package/dist/elements/chunks/message-VMZohrXx.js +0 -1
  457. package/dist/elements/chunks/message-circle-CpXMM-_n.js +0 -1
  458. package/dist/elements/chunks/message-square-DxcWtETQ.js +0 -1
  459. package/dist/elements/chunks/message-tZmV2A3p.js +0 -1
  460. package/dist/elements/chunks/paperclip-CRZVp4z_.js +0 -1
  461. package/dist/elements/chunks/progress-bar-DNy66e_E.js +0 -1
  462. package/dist/elements/chunks/prompt-suggestion-Bn64Nl7L.js +0 -1
  463. package/dist/elements/chunks/reasoning-Emn9N4-T.js +0 -1
  464. package/dist/elements/chunks/rotate-cw-CPWe37D9.js +0 -1
  465. package/dist/elements/chunks/scroll-area-ClZkZxIk.js +0 -1
  466. package/dist/elements/chunks/scroll-button-DPE-t3x_.js +0 -1
  467. package/dist/elements/chunks/share-CyfnNEqp.js +0 -1
  468. package/dist/elements/chunks/source-UWCvmVak.js +0 -1
  469. package/dist/elements/chunks/star-DoHFAikQ.js +0 -1
  470. package/dist/elements/chunks/store-Cjq0qwYN.js +0 -1
  471. package/dist/elements/chunks/svelte--5p79yCD.js +0 -1
  472. package/dist/elements/chunks/tasks-card-CmZ5xh6v.js +0 -1
  473. package/dist/elements/chunks/text-shimmer-DYn09zrz.js +0 -1
  474. package/dist/elements/chunks/textarea-skiGHURM.js +0 -1
  475. package/dist/elements/chunks/toast-store-CFnQpThX.js +0 -1
  476. package/dist/elements/chunks/tsx-B8rCNbgL.js +0 -1
  477. package/dist/elements/chunks/typescript-RycA9KXf.js +0 -1
  478. package/dist/elements/chunks/use-card-resolution-DoA2f_EU.js +0 -1
  479. package/dist/elements/chunks/vue-BmIZj4XD.js +0 -1
  480. package/dist/elements/chunks/x-B6UnOiLw.js +0 -1
  481. package/dist/llms/llms-full.txt +0 -2949
  482. package/dist/llms/llms.txt +0 -156
  483. package/dist/register-impl-DpE7icIb.js +0 -293
  484. package/dist/ui/stat.d.ts +0 -16
  485. package/src/ui/stat.tsx +0 -41
  486. /package/dist/{elements/chunks/card-tags-D8lZ-C_U.js → card-tags-D8lZ-C_U.js} +0 -0
  487. /package/dist/{elements/chunks/link-preview-DNILK391.js → link-preview-DNILK391.js} +0 -0
  488. /package/dist/{elements/chunks/slots-DaNjX0rc.js → slots-DaNjX0rc.js} +0 -0
@@ -9,6 +9,14 @@
9
9
  // element, so it carries its own shadow root + the shared kit stylesheet — it's
10
10
  // viewport-positioned AND fully kit-styled, never a raw div.
11
11
  //
12
+ // ADOPTS, else creates: `ensureMounted` first
13
+ // looks for a connected `<kai-toast-region>` already in the document and binds
14
+ // the store to it, so an app that placed its own region and also calls
15
+ // `toast()` gets ONE region, not two overlapping ones. It creates a region only
16
+ // when none exists. With two or more candidate regions the choice is genuinely
17
+ // ambiguous — the first in document order wins, and a one-time console.warn
18
+ // says so (decide loudly).
19
+ //
12
20
  // SSR-safe: every DOM touch is guarded by `typeof document`. On the server,
13
21
  // raising a toast is an inert no-op (the store updates, nothing mounts).
14
22
 
@@ -156,21 +164,75 @@ function resolveDuration(item: Pick<ToastItem, 'duration' | 'action'>): number {
156
164
  }
157
165
 
158
166
  /**
159
- * Lazily create the single `<kai-toast-region>` host on `document.body` and
160
- * bind the store to its `toasts` property. Idempotent + SSR-safe. The element
161
- * upgrade (its `defineWebComponent` registration) happens when the elements
162
- * bundle loads; setting the property before/after upgrade both work because
167
+ * Lazily resolve the single `<kai-toast-region>` host for a target and bind the
168
+ * store to its `toasts` property. Idempotent + SSR-safe. The element upgrade
169
+ * (its `defineWebComponent` registration) happens when the elements bundle
170
+ * loads; setting the property before/after upgrade both work because
163
171
  * `customElement` reflects late-set properties.
172
+ *
173
+ * ADOPT-IF-PRESENT: a connected
174
+ * `<kai-toast-region>` already in the document (same `target`, not already
175
+ * claimed for another target) is adopted — the store binds to IT, and no
176
+ * second region mounts. Adoption respects the region's authored attributes
177
+ * (position/stack/…): config from `configureToasts()` is not stamped onto an
178
+ * adopted region here, though an explicit `configureToasts()` call still
179
+ * updates every region, adopted included. Binding the store REPLACES a
180
+ * `toasts` array the app set as data — after the first `toast()` call the
181
+ * imperative store owns the adopted region's list. Only when no candidate
182
+ * exists is a fresh region created on `document.body`. Two or more candidates
183
+ * for the same target are genuinely ambiguous: the first in document order
184
+ * wins, with a one-time console.warn (decide loudly).
185
+ *
186
+ * If an adopted (or created) region later leaves the DOM, the cache entry is
187
+ * dropped and the next call resolves fresh — adopt again if a region exists,
188
+ * else create — so a removed region never becomes a dead cache entry that
189
+ * swallows toasts.
164
190
  */
165
191
  // One region per distinct target (the `null` key = the global / viewport region).
166
192
  // Every region binds the SAME store proxy and filters to its own target, so a
167
193
  // toast routes to the region for its `target`.
168
194
  const regions = new Map<HTMLElement | null, HTMLElement>();
169
195
 
196
+ // Decide-loudly, once: ambiguity between 2+ candidate regions is warned about a
197
+ // single time per page, not on every toast.
198
+ let warnedAmbiguousRegions = false;
199
+
200
+ /** Read a region element's `target` property (undefined pre-upgrade → null =
201
+ * the global region). */
202
+ function regionTarget(el: HTMLElement): HTMLElement | null {
203
+ return (el as unknown as { target?: HTMLElement }).target ?? null;
204
+ }
205
+
170
206
  export function ensureMounted(target: HTMLElement | null = null): HTMLElement | undefined {
171
207
  if (typeof document === 'undefined') return undefined;
172
208
  const cached = regions.get(target);
173
209
  if (cached && cached.isConnected) return cached;
210
+ // The region this key resolved to left the DOM — drop the dead entry so it
211
+ // can never swallow toasts, and resolve fresh below.
212
+ if (cached) regions.delete(target);
213
+
214
+ // Adopt a region the app already placed. Regions this store already
215
+ // claimed for OTHER targets are not candidates.
216
+ const claimed = new Set(regions.values());
217
+ const candidates = Array.from(
218
+ document.querySelectorAll<HTMLElement>('kai-toast-region'),
219
+ ).filter((el) => el.isConnected && !claimed.has(el) && regionTarget(el) === target);
220
+ if (candidates.length > 1 && !warnedAmbiguousRegions) {
221
+ warnedAmbiguousRegions = true;
222
+ console.warn(
223
+ `[kitn] toast(): ${candidates.length} candidate <kai-toast-region> elements are in the ` +
224
+ 'document; adopting the first in document order. Keep one region per target so the ' +
225
+ 'imperative toast() API has an unambiguous home.',
226
+ );
227
+ }
228
+ const adopted = candidates[0];
229
+ if (adopted) {
230
+ (adopted as unknown as { toasts: ToastItem[] }).toasts = toasts as ToastItem[];
231
+ regions.set(target, adopted);
232
+ setMounted(true);
233
+ return adopted;
234
+ }
235
+
174
236
  const el = document.createElement('kai-toast-region') as HTMLElement;
175
237
  // Append FIRST so the element upgrades, THEN bind. Setting a property before
176
238
  // upgrade loses it: component-register resets every prop in the constructor on
@@ -0,0 +1,70 @@
1
+ // src/primitives/url-scheme-policy.ts
2
+ // The kit's one URL-scheme allow/deny policy, split out of card-routing.ts so it
3
+ // has NO DOM dependency (`URL` is a global in both the browser and Node — the
4
+ // rest of card-routing.ts uses HTMLElement/window/CustomEvent, which aren't).
5
+ // That split matters beyond tidiness: this schema.ts (agent-tooling/construct)
6
+ // runs under tsconfig.mcp.json's Node-only, no-DOM-lib pass (it's imported
7
+ // transitively via mcp/tools/construct.ts), and importing anything from
8
+ // card-routing.ts there drags its HTMLElement/window/Document/CustomEvent
9
+ // references into that compile and breaks it — even though only isSafeUrl is
10
+ // used. card-routing.ts re-exports everything here so its own public surface
11
+ // (isSafeUrl/isScriptUrl, used by markdown.tsx/artifact.tsx/source.tsx) is
12
+ // unchanged; this is a location split, not a second policy.
13
+
14
+ const SAFE_SCHEMES = ['http:', 'https:', 'mailto:'];
15
+ const SCRIPT_SCHEMES = ['javascript:', 'vbscript:'];
16
+
17
+ /** The scheme the WHATWG parser reads out of `url`, resolved against a base so a
18
+ * relative input inherits `http:`. `undefined` when it will not parse at all.
19
+ *
20
+ * ONE parser for the two questions below, deliberately: the parsing is where
21
+ * the subtlety lives (it strips embedded tabs/newlines and trims surrounding
22
+ * whitespace before reading the scheme, so `java\nscript:` and
23
+ * ` javascript:... ` are both read as `javascript:` -- which a regex over the
24
+ * raw string would miss). Two predicates over one parse cannot drift on that;
25
+ * two hand-rolled parsers would. */
26
+ function schemeOf(url: string): string | undefined {
27
+ try { return new URL(url, 'http://_invalid_base').protocol; } catch { return undefined; }
28
+ }
29
+
30
+ /** True when `url` is safe to put in an href / hand to `window.open` / an `<img src>`.
31
+ *
32
+ * Exported so the MARKDOWN renderer, the ARTIFACT viewer and the construct
33
+ * format's `widget.launcherIcon` reuse this exact guard rather than growing a
34
+ * second scheme list that can drift from this one.
35
+ *
36
+ * Resolving against a base is deliberate and is what makes it correct for
37
+ * markdown: a relative or fragment link (`/docs`, `#section`, `./rel`) is
38
+ * ordinary markdown, and resolving it inherits the base's `http:` so it
39
+ * passes, while `javascript:`/`data:`/`vbscript:` keep their own protocol and
40
+ * fail. It is correct for the artifact viewer for the same reason: a file with
41
+ * no `src` to resolve against yields a bare relative path (`docs/report.pdf`),
42
+ * which is a legitimate artifact address. Contrast `isRenderableLink` in
43
+ * `primitives/link-preview.ts`, which takes NO base and so demands an absolute
44
+ * http(s) URL: that is the right guard for a model-supplied citation -- a
45
+ * reference to a page on the public web -- this one for markdown body links,
46
+ * artifact file addresses, and a construct's launcher icon. */
47
+ export function isSafeUrl(url: string): boolean {
48
+ const scheme = schemeOf(url);
49
+ return scheme !== undefined && SAFE_SCHEMES.includes(scheme);
50
+ }
51
+
52
+ /** True when navigating to `url` would EXECUTE it as script in the initiating
53
+ * document's origin.
54
+ *
55
+ * Strictly narrower than `!isSafeUrl(url)`, and not a competing policy: it
56
+ * answers a different question, for the one sink where the allowlist is the
57
+ * wrong tool. An `<iframe src>` legitimately takes `data:` and `blob:` (a
58
+ * `data:` blob artifact is a documented `<kai-artifact>` use -- it is what
59
+ * `displayUrl` exists for) and both get an OPAQUE origin in every modern
60
+ * browser, so neither can reach the host page. `javascript:`/`vbscript:` are
61
+ * the schemes that run in the EMBEDDER's origin, and they are the whole risk:
62
+ * the default sandbox (no `allow-same-origin`) already makes the browser refuse
63
+ * them, but a consumer who sets `allow-same-origin` turns a model-supplied
64
+ * `src` into host-origin script execution. The artifact viewer used to suggest
65
+ * that setting for "an artifact you trust"; it no longer does, and this guard
66
+ * is what protects the consumers who took the old advice. */
67
+ export function isScriptUrl(url: string): boolean {
68
+ const scheme = schemeOf(url);
69
+ return scheme !== undefined && SCRIPT_SCHEMES.includes(scheme);
70
+ }
@@ -14,8 +14,8 @@ export function observeContentHeight(el: Element, onHeight: (height: number) =>
14
14
  /** Observe an element's resizes but report the FULL rendered document height (not the
15
15
  * observed element's own content-box) — `document.body.scrollHeight`.
16
16
  *
17
- * Used by the remote-card provider runtime to size the host iframe (D1, rung 5 IVP
18
- * task-10-report.md): `observeContentHeight` reports the ResizeObserver content-box of
17
+ * Used by the remote-card provider runtime to size the host iframe:
18
+ * `observeContentHeight` reports the ResizeObserver content-box of
19
19
  * the observed root, which drops that root's OWN padding/border and any ancestor
20
20
  * (e.g. `<body>`) padding above it — the host then clips the bottom of the card by
21
21
  * exactly those lost pixels. `scrollHeight` is the full padding-box height as actually
@@ -132,7 +132,7 @@ export interface CardToolOptions<P extends ToolProvider = ToolProvider> {
132
132
  */
133
133
  readonly strict?: boolean;
134
134
  /**
135
- * Narrow the DERIVED tool schema per card type, after projection (F-23).
135
+ * Narrow the DERIVED tool schema per card type, after projection.
136
136
  *
137
137
  * A card's authored schema is deliberately permissive — `files` is optional on an
138
138
  * artifact, its items need only a `path` — because the card CONTRACT must accept
@@ -147,6 +147,16 @@ export interface CardToolOptions<P extends ToolProvider = ToolProvider> {
147
147
  * unknown path or a required name the node has no property for is a TypeError
148
148
  * naming the card, the path and the field, because a tool that can never be
149
149
  * satisfied must not ship quietly.
150
+ *
151
+ * IT CANNOT REACH A FORM CARD'S FIELDS, and that is a known limitation rather
152
+ * than an oversight. The form
153
+ * card's payload IS a JSON Schema, so `form.schema.json` describes `properties`
154
+ * as a map of field definitions and there is no node at `properties.ticketId`
155
+ * for a rule to land on — `require: { form: [{ path: 'properties.ticketId' }] }`
156
+ * raises the TypeError above, correctly. So an app cannot pin
157
+ * `x-kai-format: 'custom'` onto one field of a form the model authors. The
158
+ * enum on that hint is what makes a small model pick a valid token; an app that
159
+ * needs a guarantee checks the envelope when it arrives.
150
160
  */
151
161
  readonly require?: Readonly<Record<string, readonly CardRequireRule[]>>;
152
162
  }
@@ -442,7 +452,7 @@ function relaxRootCombinators(parameters: Record<string, unknown>, cardType: str
442
452
  }
443
453
 
444
454
  /**
445
- * Apply one card's `require` rules to its PROJECTED parameters (F-23).
455
+ * Apply one card's `require` rules to its PROJECTED parameters.
446
456
  *
447
457
  * Runs after `project()` and `relaxRootCombinators()`, on the copy the projection
448
458
  * built — never on the authored schema, which `registry.validate()` keeps reading.
package/src/solid.ts CHANGED
@@ -53,8 +53,18 @@ export { Input } from './ui/input';
53
53
  export type { InputProps } from './ui/input';
54
54
  export { Kbd } from './ui/kbd';
55
55
  export type { KbdProps, KbdPlatform } from './ui/kbd';
56
+ export { Checkbox } from './ui/checkbox';
57
+ export type { CheckboxProps } from './ui/checkbox';
58
+ export { Radio, RadioGroup } from './ui/radio';
59
+ export type { RadioProps, RadioGroupProps, RadioOption } from './ui/radio';
60
+ export { CheckboxGroup } from './ui/checkbox-group';
61
+ export type { CheckboxGroupProps, CheckboxOption } from './ui/checkbox-group';
56
62
  export { Switch } from './ui/switch';
57
63
  export type { SwitchProps } from './ui/switch';
64
+ export { Slider } from './ui/slider';
65
+ export type { SliderProps } from './ui/slider';
66
+ export { Select } from './ui/select';
67
+ export type { SelectProps, SelectOption } from './ui/select';
58
68
  export { Tabs } from './ui/tabs';
59
69
  export type { TabsProps, TabsVariant, KaiTabItem } from './ui/tabs';
60
70
  export { Segmented } from './ui/segmented';
@@ -69,8 +79,8 @@ export { EditableLabel } from './ui/editable-label';
69
79
  export type { EditableLabelProps } from './ui/editable-label';
70
80
  export { Dialog } from './ui/dialog';
71
81
  export type { DialogProps, DialogController } from './ui/dialog';
72
- export { Dock, DockLauncherGlyph, DockCloseGlyph } from './ui/dock';
73
- export type { DockProps, DockController, DockPosition, DockFocusOnOpen } from './ui/dock';
82
+ export { Dock, DockLauncherGlyph, DockCloseGlyph, DockLauncherImage } from './ui/dock';
83
+ export type { DockProps, DockController, DockPosition, DockFocusOnOpen, DockLauncherImageProps } from './ui/dock';
74
84
  export { Popover } from './ui/popover';
75
85
  export type { PopoverProps, PopoverController } from './ui/popover';
76
86
  export { Nav } from './ui/nav';
@@ -136,6 +146,14 @@ export { ChatThread } from './components/chat-thread';
136
146
  export type { ChatThreadProps, ChatThreadController, ChatThreadContextUsage } from './components/chat-thread';
137
147
  export { Screen } from './components/screen';
138
148
  export type { ScreenProps, ScreenController } from './components/screen';
149
+ // HomePanel / WidgetTabBar — the widget home screen (Intercom-pattern).
150
+ // Wired into ChatThread itself behind the `home` prop (and from there into
151
+ // `<kai-chat>`'s own `home`/`onHomeLink`); exported here too for a Solid
152
+ // consumer composing either piece directly.
153
+ export { HomePanel } from './components/home-panel';
154
+ export type { HomePanelProps } from './components/home-panel';
155
+ export { WidgetTabBar } from './components/widget-tab-bar';
156
+ export type { WidgetTabBarProps } from './components/widget-tab-bar';
139
157
  export { Coachmark } from './components/coachmark';
140
158
  export type { CoachmarkProps, CoachmarkController } from './components/coachmark';
141
159
  export { Composer } from './components/composer';
@@ -23,7 +23,7 @@ export type { ParsedThread, DroppedStored, SaveScheduler, SaveSchedulerOptions }
23
23
  // so the no-backend preview runs through the SAME parser a real provider does —
24
24
  // and is deliberately impossible to mistake for one. See ./mock.
25
25
  export { createMockResponder, DEFAULT_MOCK_REPLIES, MOCK_BANNER, MOCK_MARKER, MOCK_MARKER_KEY, MOCK_MODEL_ID } from './mock';
26
- export type { MockResponder, MockResponderOptions } from './mock';
26
+ export type { MockReply, MockResponder, MockResponderOptions, MockToolCall, MockTurn } from './mock';
27
27
 
28
28
  // The content-model types every signature above mentions. Without these a
29
29
  // consumer importing only from '@kitn.ai/ui/state' cannot annotate the very
package/src/state/mock.ts CHANGED
@@ -96,9 +96,42 @@ export const DEFAULT_MOCK_REPLIES: readonly string[] = [
96
96
  'Mock again. Every frame I send is tagged `_kai_mock` and my usage reports zero tokens, so nothing here can be mistaken for a real turn.',
97
97
  ];
98
98
 
99
+ /** One scripted tool call for a mock turn. Framed exactly the way the OpenAI
100
+ * chat-completions wire frames a real one — an announce fragment carrying
101
+ * `id`/`function.name`, then the argument JSON streamed in fragments — so the
102
+ * kit's own reader (`readOpenAIStream`) reassembles it through the same path a
103
+ * real provider's call takes. */
104
+ export interface MockToolCall {
105
+ /** The tool name, e.g. `'get_weather'` or a card tool like `'kai_confirm'`. */
106
+ name: string;
107
+ /** The call's arguments. Serialized with `JSON.stringify` and streamed as
108
+ * fragments, like a real provider. Defaults to `{}`. */
109
+ arguments?: unknown;
110
+ /** Explicit tool-call id. Defaults to `call_kai-mock-<turn>-<n>`, which keeps
111
+ * the mock's naming tell (see tell 3): no provider issues ids in that shape. */
112
+ id?: string;
113
+ }
114
+
115
+ /** A scripted mock turn: optional text, then optional tool calls. A turn with
116
+ * tool calls finishes `finish_reason: 'tool_calls'`, exactly as a real
117
+ * tool-calling turn does; a turn without them finishes `'stop'`. */
118
+ export interface MockTurn {
119
+ /** Text streamed (token by token) before the tool calls. */
120
+ text?: string;
121
+ /** Tool calls announced this turn, in order. */
122
+ toolCalls?: readonly MockToolCall[];
123
+ }
124
+
125
+ /** A canned reply: plain text, or a scripted turn. A string is exactly
126
+ * `{ text }` — the pre-tool-call API unchanged. */
127
+ export type MockReply = string | MockTurn;
128
+
99
129
  export interface MockResponderOptions {
100
- /** Canned replies, cycled one per turn. Defaults to `DEFAULT_MOCK_REPLIES`. */
101
- replies?: readonly string[];
130
+ /** Canned replies, cycled one per turn. Plain strings stream as text; a
131
+ * `MockTurn` can also script tool calls (`{ text, toolCalls }`), which is
132
+ * what lets the zero-config mock exercise the kit's tool/card path without
133
+ * hand-rolled SSE framing. Defaults to `DEFAULT_MOCK_REPLIES`. */
134
+ replies?: readonly MockReply[];
102
135
  /** Delay between chunks, in ms. Defaults to 24 — fast enough to feel alive,
103
136
  * slow enough that the streaming is visible. `0` streams as fast as the
104
137
  * event loop allows, which is what tests want. */
@@ -144,6 +177,27 @@ function frame(id: string, body: Record<string, unknown>): string {
144
177
  const delay = (ms: number): Promise<void> =>
145
178
  ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
146
179
 
180
+ /** Normalize a canned reply: a plain string is exactly `{ text }`. */
181
+ const asTurn = (reply: MockReply): MockTurn => (typeof reply === 'string' ? { text: reply } : reply);
182
+
183
+ /** How many characters of argument JSON ride in each tool-call fragment. Small
184
+ * enough that any realistic argument object spans several frames, so the
185
+ * reader's fragment-reassembly path — the one a real provider exercises — is
186
+ * exercised here too, rather than a single-frame shortcut it would never see
187
+ * in production. */
188
+ const TOOL_ARG_FRAGMENT_CHARS = 16;
189
+
190
+ /** Split argument JSON into the fragments a provider streams. Never empty: an
191
+ * empty-object call still sends its `'{}'` so the reader has arguments to
192
+ * parse. */
193
+ function argumentFragments(json: string): string[] {
194
+ const out: string[] = [];
195
+ for (let i = 0; i < json.length; i += TOOL_ARG_FRAGMENT_CHARS) {
196
+ out.push(json.slice(i, i + TOOL_ARG_FRAGMENT_CHARS));
197
+ }
198
+ return out.length > 0 ? out : [''];
199
+ }
200
+
147
201
  /**
148
202
  * Build a mock responder.
149
203
  *
@@ -170,8 +224,9 @@ export function createMockResponder(options: MockResponderOptions = {}): MockRes
170
224
  let announced = false;
171
225
 
172
226
  return function respond(_prompt?: string): AsyncIterable<string> {
173
- const reply = pool[turn % pool.length];
227
+ const scripted = asTurn(pool[turn % pool.length]);
174
228
  const id = `kai-mock-${turn + 1}`;
229
+ const turnNumber = turn + 1;
175
230
  turn += 1;
176
231
 
177
232
  return {
@@ -193,15 +248,46 @@ export function createMockResponder(options: MockResponderOptions = {}): MockRes
193
248
  // exercises the reader's empty-delta path.
194
249
  yield frame(id, { choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: null }] });
195
250
 
196
- for (const token of tokenize(reply, chunkSize)) {
251
+ for (const token of tokenize(scripted.text ?? '', chunkSize)) {
197
252
  await delay(delayMs);
198
253
  yield frame(id, { choices: [{ index: 0, delta: { content: token }, finish_reason: null }] });
199
254
  }
200
255
 
256
+ // Scripted tool calls, framed the way the OpenAI wire frames real ones:
257
+ // one announce fragment per call (`index`/`id`/`function.name` + empty
258
+ // arguments), then the argument JSON in fragments. The kit's reader
259
+ // reassembles them through the exact path a real provider's call takes.
260
+ const toolCalls = scripted.toolCalls ?? [];
261
+ for (let index = 0; index < toolCalls.length; index += 1) {
262
+ const call = toolCalls[index];
263
+ // Tell 3's shape, extended to call ids: no provider issues these.
264
+ const callId = call.id ?? `call_${MOCK_MODEL_ID}-${turnNumber}-${index + 1}`;
265
+ await delay(delayMs);
266
+ yield frame(id, {
267
+ choices: [{
268
+ index: 0,
269
+ delta: { tool_calls: [{ index, id: callId, function: { name: call.name, arguments: '' } }] },
270
+ finish_reason: null,
271
+ }],
272
+ });
273
+ for (const fragment of argumentFragments(JSON.stringify(call.arguments ?? {}))) {
274
+ await delay(delayMs);
275
+ yield frame(id, {
276
+ choices: [{
277
+ index: 0,
278
+ delta: { tool_calls: [{ index, function: { arguments: fragment } }] },
279
+ finish_reason: null,
280
+ }],
281
+ });
282
+ }
283
+ }
284
+
201
285
  // Tell 4: zero usage. A real turn that produced this much text cannot
202
- // report zero completion tokens.
286
+ // report zero completion tokens. A turn that announced tool calls ends
287
+ // `tool_calls`, exactly as a real one does, so the consumer's tool loop
288
+ // branches the same way it would against a provider.
203
289
  yield frame(id, {
204
- choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
290
+ choices: [{ index: 0, delta: {}, finish_reason: toolCalls.length > 0 ? 'tool_calls' : 'stop' }],
205
291
  usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
206
292
  });
207
293
  yield 'data: [DONE]\n\n';
package/src/types.ts CHANGED
@@ -37,10 +37,29 @@ export interface ConversationSummary {
37
37
  /** Fallback for the auto relative time when updatedAt is absent. */
38
38
  lastMessageAt?: string;
39
39
  updatedAt: string;
40
- /** Right-aligned trailing text on the row a count, status, or "days ago".
41
- * When omitted, a short relative time is auto-derived from `updatedAt`
42
- * (fallback `lastMessageAt`). */
40
+ /** Trailing text describing the row's content, read differently by the two
41
+ * built-in list surfaces (both optional, same field no widened shape):
42
+ * the desktop `ConversationList`/`ConversationItem` renders it right-aligned
43
+ * as a count/status/"days ago" and auto-derives a short relative time from
44
+ * `updatedAt` (fallback `lastMessageAt`) when it's absent; the widget-box
45
+ * `ConversationPanel` list view (owner rework, 2026-08-26) renders it as the
46
+ * one-line last-message preview under the title instead — that view always
47
+ * computes its own right-aligned relative time from `updatedAt` separately,
48
+ * since a box that size has no room for a third line. `localStorageStore`
49
+ * writes it as the ~80-char truncated last-message preview on every save
50
+ * (`primitives/conversation-store.ts`). */
43
51
  trailing?: string;
52
+ /** ISO timestamp of when this conversation was last SEEN by the visitor
53
+ * (widened additively, 2026-08-26 — unread indicators; no existing field
54
+ * fit, since `trailing`/`lastMessageAt`/`updatedAt` are all content-timing
55
+ * facts, not viewing-state). Unread = `updatedAt` is later than this.
56
+ * Written by `ConversationStore.markRead` (see that doc for exactly when
57
+ * and for the decide-loudly default when a store never implements it —
58
+ * an absent `lastReadAt` reads as "not unread," never as "definitely
59
+ * unread," so a store that doesn't support the concept at all simply
60
+ * never shows an indicator rather than guessing). Round-tripped through
61
+ * `list()`/`save()`; never author this by hand. */
62
+ lastReadAt?: string;
44
63
  }
45
64
 
46
65
  export interface ConversationGroup {
@@ -51,3 +70,26 @@ export interface ConversationGroup {
51
70
  sortOrder: number;
52
71
  createdAt: string;
53
72
  }
73
+
74
+ /** One row in a `HomeConfig.links` list — a "docs" / "talk to sales" style
75
+ * entry. `href` is optional: with it, the row renders as a link (subject to
76
+ * `isSafeUrl`); without it, the row is a button that emits itself via
77
+ * `HomePanelProps.onLink`. `icon` is either a `renderIcon` name or a safe
78
+ * URL, same resolution `renderIcon` already does for every other icon prop
79
+ * in the kit. */
80
+ export interface HomeLinkEntry {
81
+ label: string;
82
+ href?: string;
83
+ description?: string;
84
+ icon?: string;
85
+ }
86
+
87
+ /** The widget home screen's JSON-shaped config (Intercom-pattern home tab).
88
+ * Consumed by `ChatThread`'s `home` prop, which wires it into `HomePanel`/
89
+ * `WidgetTabBar` and, from there, into `<kai-chat>`'s own `home` property. */
90
+ export interface HomeConfig {
91
+ greeting?: { title?: string; subtitle?: string };
92
+ recentConversation?: boolean;
93
+ newConversation?: { label?: string };
94
+ links?: HomeLinkEntry[];
95
+ }
@@ -156,7 +156,7 @@ export function AgentCard(props: AgentCardProps) {
156
156
  />
157
157
  </span>
158
158
  <Show when={local.status.label}>
159
- <span class={cn('text-[11px] font-medium leading-none', TONE_TEXT[tone()])}>
159
+ <span class={cn('text-caption font-medium leading-none', TONE_TEXT[tone()])}>
160
160
  {local.status.label}
161
161
  </span>
162
162
  </Show>
@@ -185,7 +185,7 @@ export function AgentCard(props: AgentCardProps) {
185
185
 
186
186
  <span class="flex shrink-0 items-center gap-2">
187
187
  <Show when={local.needsAttention}>
188
- <span class="inline-flex items-center gap-1 rounded-full bg-tool-amber/15 px-2 py-0.5 text-[11px] font-medium text-tool-amber">
188
+ <span class="inline-flex items-center gap-1 rounded-full bg-tool-amber/15 px-2 py-0.5 text-caption font-medium leading-none text-tool-amber">
189
189
  <Bell class="size-3" aria-hidden="true" />
190
190
  Needs you
191
191
  </span>
package/src/ui/avatar.tsx CHANGED
@@ -8,7 +8,7 @@ export interface AvatarProps extends JSX.HTMLAttributes<HTMLDivElement> {
8
8
  size?: 'sm' | 'md' | 'lg';
9
9
  }
10
10
 
11
- const sizeClasses = { sm: 'h-6 w-6 text-[10px]', md: 'h-8 w-8 text-xs', lg: 'h-10 w-10 text-sm' };
11
+ const sizeClasses = { sm: 'h-6 w-6 text-micro', md: 'h-8 w-8 text-xs', lg: 'h-10 w-10 text-sm' };
12
12
 
13
13
  export function Avatar(props: AvatarProps) {
14
14
  const [local, rest] = splitProps(props, ['src', 'alt', 'fallback', 'size', 'class']);
@@ -0,0 +1,153 @@
1
+ import { type JSX, For, Show, splitProps } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+ import { Checkbox } from './checkbox';
4
+
5
+ /** One choice in a {@link CheckboxGroup}. */
6
+ export interface CheckboxOption<T = string> {
7
+ /** The value this row selects. Compared to the group's `value` by identity. */
8
+ value: T;
9
+ /** The row's visible label. */
10
+ label: JSX.Element;
11
+ /** Optional second line under the label. */
12
+ description?: JSX.Element;
13
+ /** Disable this row alone (the group's `disabled` disables all of them). */
14
+ disabled?: boolean;
15
+ }
16
+
17
+ export interface CheckboxGroupProps<T = string>
18
+ extends Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onChange' | 'children'> {
19
+ /**
20
+ * The choices, in display order. Rendered in full: the kit never truncates,
21
+ * re-orders or de-duplicates a consumer's option list.
22
+ */
23
+ options: readonly CheckboxOption<T>[];
24
+ /** The selected values. Each option is checked when its `value` is in here (by identity). */
25
+ value?: readonly T[];
26
+ /**
27
+ * The shared form-control name every box in the group carries, so a native form
28
+ * submits the whole selection under one key and `FormData.getAll(name)` reads it back.
29
+ *
30
+ * UNLIKE {@link RadioGroup}, this has no default. A radio group needs a shared `name`
31
+ * for the browser to make the set exclusive and arrow-navigable, so one is generated
32
+ * when none is given; checkboxes are independent controls and need nothing to behave
33
+ * correctly. Generating a name here would submit the selection under a random key,
34
+ * which is worse than submitting nothing.
35
+ */
36
+ name?: string;
37
+ /** Disable every row. */
38
+ disabled?: boolean;
39
+ /** Accessible name for the group. Rendered as `aria-label` on the `group`. */
40
+ label?: string;
41
+ /**
42
+ * Fires on every toggle with the NEXT selection, plus the option that moved and
43
+ * which way it went. The next selection is a fresh array — the group holds no state
44
+ * of its own, so `value` is yours to own.
45
+ */
46
+ onChange?: (value: T[], option: CheckboxOption<T>, checked: boolean) => void;
47
+ /** Fires when a box loses focus — the commit point for a form field. */
48
+ onOptionBlur?: () => void;
49
+ /** Extra classes for each row. */
50
+ itemClass?: string;
51
+ /**
52
+ * Presentation slot. Replaces the default label/description column with whatever
53
+ * you return, so a row can carry media, a badge or a price without a second
54
+ * checkbox component existing. The control, the row chrome and the group semantics
55
+ * stay ours.
56
+ */
57
+ children?: (option: CheckboxOption<T>, state: { checked: boolean; disabled: boolean }) => JSX.Element;
58
+ }
59
+
60
+ /**
61
+ * A vertical set of checkbox rows in a bordered, divided list — the kit's standard
62
+ * "pick any number" control, and {@link RadioGroup}'s sibling: same options shape,
63
+ * same row chrome, same presentation slot, multi-value instead of single.
64
+ *
65
+ * Every row is a real `<input type="checkbox">` inside a `<label>`, so clicking
66
+ * anywhere on the row toggles it and the keyboard behaviour (a tab stop per box,
67
+ * Space to toggle) is the browser's rather than a reimplementation. The wrapper is
68
+ * `role="group"`, not `role="listbox"`: the boxes are independent controls and
69
+ * nothing here overrides what they already announce.
70
+ *
71
+ * Everything not listed in `CheckboxGroupProps` is forwarded to the group element, so
72
+ * `id`, `aria-labelledby`, any other `aria-*` and any `data-*` hook land where a form
73
+ * expects them. No validation is applied — "at least one" is your application's rule,
74
+ * not the kit's.
75
+ *
76
+ * ```tsx
77
+ * <CheckboxGroup
78
+ * label="Environments"
79
+ * name="env"
80
+ * options={[{ value: 'prod', label: 'Production' }, { value: 'staging', label: 'Staging' }]}
81
+ * value={envs()}
82
+ * onChange={setEnvs}
83
+ * />
84
+ * ```
85
+ */
86
+ export function CheckboxGroup<T = string>(props: CheckboxGroupProps<T>): JSX.Element {
87
+ const [local, rest] = splitProps(props, [
88
+ 'options', 'value', 'name', 'disabled', 'label', 'onChange', 'onOptionBlur', 'itemClass', 'children', 'class',
89
+ ]);
90
+ const selected = (): readonly T[] => local.value ?? [];
91
+ return (
92
+ <div
93
+ {...rest}
94
+ role="group"
95
+ aria-label={local.label}
96
+ class={cn('divide-y divide-border overflow-hidden rounded-lg border border-border', local.class)}
97
+ >
98
+ <For each={local.options}>
99
+ {(opt) => {
100
+ const checked = () => selected().includes(opt.value);
101
+ const rowDisabled = () => !!(local.disabled || opt.disabled);
102
+ return (
103
+ <label
104
+ class={cn(
105
+ 'flex cursor-pointer items-center gap-3 px-3 py-2.5 text-sm transition-colors',
106
+ opt.description !== undefined && 'items-start',
107
+ checked()
108
+ ? 'bg-accent font-medium text-accent-foreground'
109
+ : 'text-foreground hover:bg-muted/50',
110
+ local.itemClass,
111
+ )}
112
+ >
113
+ <Checkbox
114
+ name={local.name}
115
+ value={String(opt.value)}
116
+ checked={checked()}
117
+ disabled={rowDisabled()}
118
+ class={opt.description === undefined ? undefined : 'mt-0.5'}
119
+ onChange={(e) => {
120
+ const on = e.currentTarget.checked;
121
+ const now = selected();
122
+ // Filter-or-append rather than a toggle on the previous array, so the
123
+ // emitted selection follows the CONTROL's state. They diverge when the
124
+ // caller re-renders mid-interaction, and following the box is what a
125
+ // native form would report.
126
+ const next = on ? [...now.filter((v) => v !== opt.value), opt.value] : now.filter((v) => v !== opt.value);
127
+ local.onChange?.(next, opt, on);
128
+ }}
129
+ onBlur={() => local.onOptionBlur?.()}
130
+ />
131
+ <Show
132
+ when={local.children}
133
+ fallback={
134
+ // No description, a bare <span> — byte for byte what the form's
135
+ // hand-rolled checkbox list rendered before this component existed.
136
+ // The two-line column only appears when there is a second line.
137
+ <Show when={opt.description !== undefined} fallback={<span>{opt.label}</span>}>
138
+ <span class="flex flex-col gap-0.5">
139
+ <span>{opt.label}</span>
140
+ <span class="text-xs font-normal text-muted-foreground">{opt.description}</span>
141
+ </span>
142
+ </Show>
143
+ }
144
+ >
145
+ {(render) => render()(opt, { checked: checked(), disabled: rowDisabled() })}
146
+ </Show>
147
+ </label>
148
+ );
149
+ }}
150
+ </For>
151
+ </div>
152
+ );
153
+ }