@kitn.ai/ui 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/README.md +16 -5
  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-BnFKqqtY.js} +1 -1
  6. package/dist/action-icons-CBskfBMz.js +1 -0
  7. package/dist/arrow-down-Br0qtpsz.js +1 -0
  8. package/dist/arrow-left-Dj7xgE12.js +1 -0
  9. package/dist/artifact-CvXEm1lS.js +1 -0
  10. package/dist/assets/dev-5XzXYR21.js +91 -0
  11. package/dist/attachments-C43ZegEq.js +1 -0
  12. package/dist/{elements/chunks/audio-visualizer-D7lB6mvR.js → audio-visualizer-B-79y1Tj.js} +1 -1
  13. package/dist/badge-BpUbq8c0.js +1 -0
  14. package/dist/bash-InADTalH.js +1 -6
  15. package/dist/bell-sn17Ip9v.js +1 -0
  16. package/dist/{elements/chunks/button-flUbeufF.js → button-BH6rN0U-.js} +1 -1
  17. package/dist/card-renderer-Cb1gQmth.js +1 -0
  18. package/dist/card-routing-DifQgS7n.js +1 -0
  19. package/dist/check-DPeeMUYx.js +1 -0
  20. package/dist/checkbox-Dc9Yov_B.js +1 -0
  21. package/dist/checkbox-group-B1kDLLYk.js +1 -0
  22. package/dist/chevron-down-CHSWk7ZT.js +1 -0
  23. package/dist/chevron-right-B7l_fMgx.js +1 -0
  24. package/dist/choice-card-BT3c1S8g.js +1 -0
  25. package/dist/circle-Bb0yOpqM.js +1 -0
  26. package/dist/circle-check-BGZpCiz7.js +1 -0
  27. package/dist/circle-x-wobWkoSx.js +1 -0
  28. package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
  29. package/dist/{elements/chunks/code-block-BdLiTitd.js → code-block-CUQ5mtv0.js} +1 -1
  30. package/dist/{elements/chunks/collapsible-BzbXpPzr.js → collapsible-CPJbf6FM.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 +46 -1
  36. package/dist/components/conversation-item.d.ts +19 -7
  37. package/dist/components/conversation-list.d.ts +17 -5
  38. package/dist/components/form-widgets.d.ts +67 -2
  39. package/dist/components/form.d.ts +48 -1
  40. package/dist/components/message.d.ts +25 -3
  41. package/dist/components/reasoning.d.ts +6 -0
  42. package/dist/components/scroll-button.d.ts +11 -0
  43. package/dist/components/thread.d.ts +5 -0
  44. package/dist/components/tool-types.d.ts +9 -0
  45. package/dist/composer-CjngLzal.js +106 -0
  46. package/dist/confirm-card-DCx_A4bU.js +1 -0
  47. package/dist/construct-cli.es.js +1169 -0
  48. package/dist/{elements/chunks/context-VLIL3sJ-.js → context-BY8V-Nlp.js} +1 -1
  49. package/dist/controllable-D43BKmtX.js +1 -0
  50. package/dist/conversation-list-D4lLU_yS.js +1 -0
  51. package/dist/copy-B6DO4PqB.js +1 -0
  52. package/dist/core-AYMC6_lb.js +12 -5874
  53. package/dist/{create-tween-CQED4T0g.js → create-tween-BPzaefTp.js} +1 -1
  54. package/dist/{create-tween-BFT6c-Y8.js → create-tween-CnMHviKB.js} +1 -1
  55. package/dist/{create-tween-CJNlzoaO.js → create-tween-D1aiGZZh.js} +1 -1
  56. package/dist/{elements/chunks/create-tween-c4Z8TDYz.js → create-tween-DUSQUKpr.js} +2 -2
  57. package/dist/css-M7EaDHN_.js +1 -6
  58. package/dist/custom-elements.json +1400 -218
  59. package/dist/default-input-CucQEOqH.js +1 -0
  60. package/dist/define-CGGX-7Vg.js +1 -0
  61. package/dist/define.d.ts +4 -0
  62. package/dist/define.js +510 -0
  63. package/dist/define.server.d.ts +5 -0
  64. package/dist/define.server.js +506 -0
  65. package/dist/{elements/chunks/disclosure-CcNbyLy0.js → disclosure-CbLtajUQ.js} +1 -1
  66. package/dist/{elements/chunks/dropdown-BgWTthUx.js → dropdown-BwhViJFd.js} +1 -1
  67. package/dist/elements/agent-card.js +1 -1
  68. package/dist/elements/artifact.js +1 -1
  69. package/dist/elements/attachments.js +1 -1
  70. package/dist/elements/audio-visualizer.js +1 -1
  71. package/dist/elements/autoloader.js +1 -1
  72. package/dist/elements/avatar.js +1 -1
  73. package/dist/elements/badge.js +1 -1
  74. package/dist/elements/button.js +1 -1
  75. package/dist/elements/card.js +1 -1
  76. package/dist/elements/cards.js +1 -1
  77. package/dist/elements/chain-of-thought.js +1 -1
  78. package/dist/elements/chat-scope-picker.js +1 -1
  79. package/dist/elements/chat-workspace.js +1 -1
  80. package/dist/elements/chat.js +1 -1
  81. package/dist/elements/checkbox-group.d.ts +1 -0
  82. package/dist/elements/checkbox-group.js +1 -0
  83. package/dist/elements/checkbox.d.ts +1 -0
  84. package/dist/elements/checkbox.js +1 -0
  85. package/dist/elements/checkpoint.js +1 -1
  86. package/dist/elements/choice.js +1 -1
  87. package/dist/elements/coachmark.js +1 -1
  88. package/dist/elements/code-block.js +1 -1
  89. package/dist/elements/command.js +1 -1
  90. package/dist/elements/compare.js +1 -1
  91. package/dist/elements/composer.js +1 -1
  92. package/dist/elements/confirm-card.js +1 -1
  93. package/dist/elements/context-meter.js +1 -1
  94. package/dist/elements/conversation-item.js +1 -1
  95. package/dist/elements/conversation-list.js +1 -1
  96. package/dist/elements/define-entry.d.ts +12 -0
  97. package/dist/elements/dialog.js +1 -1
  98. package/dist/elements/dock.js +101 -7
  99. package/dist/elements/dropdown.js +1 -1
  100. package/dist/elements/editable-label.js +1 -1
  101. package/dist/elements/element-data-types.d.ts +37 -0
  102. package/dist/elements/embed.js +1 -1
  103. package/dist/elements/empty.js +1 -1
  104. package/dist/elements/feedback-bar.js +1 -1
  105. package/dist/elements/file-tree.js +1 -1
  106. package/dist/elements/file-upload.js +1 -1
  107. package/dist/elements/form.js +1 -1
  108. package/dist/elements/hover-card.js +1 -1
  109. package/dist/elements/icon.js +1 -1
  110. package/dist/elements/image.js +1 -1
  111. package/dist/elements/input.js +1 -1
  112. package/dist/elements/kbd.js +1 -1
  113. package/dist/elements/link-preview.js +1 -1
  114. package/dist/elements/loader.js +1 -1
  115. package/dist/elements/markdown.js +1 -1
  116. package/dist/elements/menu.js +1 -1
  117. package/dist/elements/message-skills.js +1 -1
  118. package/dist/elements/message.js +1 -1
  119. package/dist/elements/model-switcher.js +1 -1
  120. package/dist/elements/nav.js +1 -1
  121. package/dist/elements/notice.js +1 -1
  122. package/dist/elements/pane-grid.d.ts +1 -0
  123. package/dist/elements/pane-grid.js +1 -0
  124. package/dist/elements/pane-group.js +1 -1
  125. package/dist/elements/pane.js +1 -1
  126. package/dist/elements/popover.js +1 -1
  127. package/dist/elements/progress-bar.js +1 -1
  128. package/dist/elements/prompt-dock.js +1 -1
  129. package/dist/elements/prompt-input.js +1 -1
  130. package/dist/elements/prompt-suggestions.js +1 -1
  131. package/dist/elements/radio-group.d.ts +1 -0
  132. package/dist/elements/radio-group.js +1 -0
  133. package/dist/elements/reasoning.js +1 -1
  134. package/dist/elements/remote.js +1 -1
  135. package/dist/elements/resizable.js +1 -1
  136. package/dist/elements/response-stream.js +2 -2
  137. package/dist/elements/screen.js +1 -1
  138. package/dist/elements/scroll-area.js +1 -1
  139. package/dist/elements/scroll-button.js +1 -1
  140. package/dist/elements/search.js +1 -1
  141. package/dist/elements/segmented.js +1 -1
  142. package/dist/elements/select.d.ts +1 -0
  143. package/dist/elements/select.js +1 -0
  144. package/dist/elements/separator.js +1 -1
  145. package/dist/elements/setting-item.js +1 -1
  146. package/dist/elements/settings-group.js +1 -1
  147. package/dist/elements/skeleton.js +1 -1
  148. package/dist/elements/slider.d.ts +1 -0
  149. package/dist/elements/slider.js +1 -0
  150. package/dist/elements/slots.d.ts +1 -1
  151. package/dist/elements/source.js +1 -1
  152. package/dist/elements/status.js +1 -1
  153. package/dist/elements/switch.js +1 -1
  154. package/dist/elements/tabs.js +1 -1
  155. package/dist/elements/tasks.js +1 -1
  156. package/dist/elements/text-shimmer.js +1 -1
  157. package/dist/elements/thinking-bar.js +1 -1
  158. package/dist/elements/thread.js +1 -1
  159. package/dist/elements/toast.js +1 -1
  160. package/dist/elements/tool.js +1 -1
  161. package/dist/elements/tooltip.js +1 -1
  162. package/dist/elements/voice-input.js +1 -1
  163. package/dist/elements/voice-output.js +1 -1
  164. package/dist/elements.d.ts +362 -24
  165. package/dist/{elements/chunks/ellipsis-7gU33mL2.js → ellipsis-DFr4Vqr3.js} +1 -1
  166. package/dist/embed-5s_vU4J0.js +1 -0
  167. package/dist/engine-javascript-vq0WuIJl.js +14 -2516
  168. package/dist/{elements/chunks/external-link-CSOEsNlx.js → external-link-u9d85ORQ.js} +1 -1
  169. package/dist/file-text-CX7_x42o.js +1 -0
  170. package/dist/{elements/chunks/file-tree-zvB2i7P9.js → file-tree-D3kBjXI5.js} +1 -1
  171. package/dist/folder-V-YdIL8f.js +1 -0
  172. package/dist/form-BRNUMCN2.js +1 -0
  173. package/dist/github-dark-dimmed-DUshB20C.js +1 -4
  174. package/dist/github-light-JYsPkUQd.js +1 -4
  175. package/dist/hover-card-DaeiYrTA.js +1 -0
  176. package/dist/html-CPZ3oZQ7.js +1 -10
  177. package/dist/icon-BpixdZNq.js +1 -0
  178. package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
  179. package/dist/index.d.ts +1 -1
  180. package/dist/index.js +7330 -5852
  181. package/dist/index.server.js +5823 -4759
  182. package/dist/info-8mFsIjS3.js +1 -0
  183. package/dist/input-d1Yuu-hz.js +1 -0
  184. package/dist/javascript-C25yR2R2.js +1 -6
  185. package/dist/json-DxJze_jm.js +1 -6
  186. package/dist/kai.es.js +1 -1
  187. package/dist/kbd-961zMPFB.js +1 -0
  188. package/dist/link-DlZZBGaR.js +1 -0
  189. package/dist/{elements/chunks/link-preview-OQ9IEq0f.js → link-preview-DIIbV2Sd.js} +1 -1
  190. package/dist/loader-YQszHFaW.js +1 -0
  191. package/dist/{elements/chunks/markdown-DgBFaGAz.js → markdown-CVk1MPsj.js} +1 -1
  192. package/dist/mcp.es.js +1285 -79
  193. package/dist/message-CwKVEdZg.js +1 -0
  194. package/dist/message-DS8lzHc1.js +1 -0
  195. package/dist/message-circle-Xf8Q-vZp.js +1 -0
  196. package/dist/message-square-Cxnwgkns.js +1 -0
  197. package/dist/{elements/chunks/minimize-2-DPIUKTrT.js → minimize-2-UEZmP2QT.js} +1 -1
  198. package/dist/{elements/chunks/model-switcher-DsT8tFB0.js → model-switcher-Kk1jEQ5t.js} +1 -1
  199. package/dist/{elements/chunks/overlay-DuiwdXjm.js → overlay-C1od6LOz.js} +1 -1
  200. package/dist/panel-right-ryET0yyn.js +1 -0
  201. package/dist/paperclip-BDNoZFJP.js +1 -0
  202. package/dist/play-OFIvYXUQ.js +1 -0
  203. package/dist/primitives/card-data-types.d.ts +49 -0
  204. package/dist/primitives/card-routing.d.ts +1 -33
  205. package/dist/primitives/card-validate.d.ts +2 -2
  206. package/dist/primitives/chat-config.d.ts +29 -2
  207. package/dist/primitives/field-mask.d.ts +96 -0
  208. package/dist/primitives/field-semantics.d.ts +36 -0
  209. package/dist/primitives/input-mask.d.ts +46 -0
  210. package/dist/primitives/url-scheme-policy.d.ts +34 -0
  211. package/dist/primitives/use-resize-observer.d.ts +2 -2
  212. package/dist/progress-bar-BvYX0aoy.js +1 -0
  213. package/dist/prompt-suggestion-BRFu2lcu.js +1 -0
  214. package/dist/radio-DcL23cNJ.js +1 -0
  215. package/dist/react/index.d.ts +193 -19
  216. package/dist/react.js +216 -180
  217. package/dist/reasoning-Cackq4Rh.js +1 -0
  218. package/dist/register-impl-C7rENvPK.js +424 -0
  219. package/dist/{elements/chunks/resizable-D181b4fM.js → resizable-Cp9HUYih.js} +1 -1
  220. package/dist/rotate-ccw-0DLRDHH5.js +1 -0
  221. package/dist/rotate-cw-DOB9IGcU.js +1 -0
  222. package/dist/schemas/form.schema.json +21 -1
  223. package/dist/schemas/tool-defs.d.ts +11 -1
  224. package/dist/schemas.js +81 -81
  225. package/dist/scroll-area-emVmWP4K.js +1 -0
  226. package/dist/scroll-button-CeP_gv1x.js +1 -0
  227. package/dist/select-CWmu00tS.js +1 -0
  228. package/dist/{elements/chunks/separator-CeJHQqLW.js → separator-CE6vly6z.js} +1 -1
  229. package/dist/{elements/chunks/settings-CqaHgJF9.js → settings-CE-78SvZ.js} +1 -1
  230. package/dist/{elements/chunks/settings-group-CjqjLCOn.js → settings-group-ZFWNxFQR.js} +1 -1
  231. package/dist/{elements/chunks/skeleton-D8akBnKS.js → skeleton-BebdP4mf.js} +1 -1
  232. package/dist/slider-BAKRYUlz.js +1 -0
  233. package/dist/{solid-BtdqNZI7.js → solid-BE5ui6sy.js} +8727 -7212
  234. package/dist/{solid-CnavwBye.js → solid-DFhsce3O.js} +7015 -5886
  235. package/dist/solid.d.ts +10 -0
  236. package/dist/solid.js +240 -234
  237. package/dist/solid.server.js +240 -234
  238. package/dist/source-B0FlguSx.js +1 -0
  239. package/dist/star-CBMZvLuN.js +1 -0
  240. package/dist/state/index.d.ts +1 -1
  241. package/dist/state/mock.d.ts +32 -2
  242. package/dist/state.js +133 -108
  243. package/dist/{elements/chunks/store-Cjq0qwYN.js → store-7eoadydQ.js} +1 -1
  244. package/dist/svelte--5p79yCD.js +1 -15
  245. package/dist/switch-CViARZCy.js +1 -0
  246. package/dist/tasks-card-MjCcOhR_.js +1 -0
  247. package/dist/text-shimmer-DKlwRF2G.js +1 -0
  248. package/dist/textarea-3ay2Me2-.js +1 -0
  249. package/dist/theme.tokens.css +82 -13
  250. package/dist/{elements/chunks/thumbs-up-DX2Ijk3H.js → thumbs-up-DQcu2HMO.js} +1 -1
  251. package/dist/toast-store-C6yKOhbk.js +1 -0
  252. package/dist/{elements/chunks/tool-DKQ2D1DP.js → tool-BCYNlfKy.js} +1 -1
  253. package/dist/{elements/chunks/tooltip-B0G99y8w.js → tooltip-05zI4hjs.js} +1 -1
  254. package/dist/trash-2-BR1RxGky.js +1 -0
  255. package/dist/{elements/chunks/triangle-alert-F9cyUEyy.js → triangle-alert-DHohDOCy.js} +1 -1
  256. package/dist/tsx-B8rCNbgL.js +1 -6
  257. package/dist/typescript-RycA9KXf.js +1 -6
  258. package/dist/ui/checkbox-group.d.ts +83 -0
  259. package/dist/ui/checkbox.d.ts +40 -0
  260. package/dist/ui/dock.d.ts +22 -0
  261. package/dist/ui/icon.d.ts +3 -0
  262. package/dist/ui/input.d.ts +48 -2
  263. package/dist/ui/radio.d.ts +85 -0
  264. package/dist/ui/select.d.ts +71 -0
  265. package/dist/ui/slider.d.ts +65 -0
  266. package/dist/ui/switch.d.ts +9 -2
  267. package/dist/upload-BbGW6A_u.js +1 -0
  268. package/dist/url-scheme-policy-DHpoTJwB.js +1 -0
  269. package/dist/use-card-resolution-CJW4grFa.js +1 -0
  270. package/dist/{variant-aurora-B8II3-I1.js → variant-aurora-BUOVgPOB.js} +2 -2
  271. package/dist/{variant-aurora-CMNWOSJR.js → variant-aurora-CUsUJvFI.js} +2 -2
  272. package/dist/{variant-aurora-CcuDMWwQ.js → variant-aurora-CW2i4bfr.js} +1 -1
  273. package/dist/{elements/chunks/variant-aurora-qt6Zct8M.js → variant-aurora-xiMPty8f.js} +1 -1
  274. package/dist/{variant-custom-L16JLLZy.js → variant-custom-BQtOnmeO.js} +1 -1
  275. package/dist/{variant-custom-C714mqKl.js → variant-custom-CAm9L-E5.js} +2 -2
  276. package/dist/{variant-custom-BdvYC7af.js → variant-custom-CAnzzppW.js} +2 -2
  277. package/dist/{elements/chunks/variant-custom-tJp22_BQ.js → variant-custom-D91HjAKz.js} +1 -1
  278. package/dist/{variant-wave-ecwykeyL.js → variant-wave-Ca1Ls4_y.js} +2 -2
  279. package/dist/{variant-wave-D7ruiA1W.js → variant-wave-CxX39DKd.js} +2 -2
  280. package/dist/{elements/chunks/variant-wave-CaQ5ti39.js → variant-wave-DYyzIIZv.js} +1 -1
  281. package/dist/{variant-wave-2S4La2Ka.js → variant-wave-uY5crU_K.js} +1 -1
  282. package/dist/vue-BmIZj4XD.js +1 -33
  283. package/dist/x-C5m1JFgi.js +1 -0
  284. package/frameworks/react/index.tsx +198 -20
  285. package/llms-full.txt +1178 -21
  286. package/llms.txt +6 -3
  287. package/package.json +17 -4
  288. package/src/agent-tooling/catalog/invariants.ts +1 -1
  289. package/src/agent-tooling/catalog/surfaces.ts +72 -0
  290. package/src/agent-tooling/construct/cli-entry.ts +8 -0
  291. package/src/agent-tooling/construct/cli.ts +131 -0
  292. package/src/agent-tooling/construct/codegen.ts +1353 -0
  293. package/src/agent-tooling/construct/construct.v1.schema.json +250 -0
  294. package/src/agent-tooling/construct/dev.ts +148 -0
  295. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +7 -0
  296. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +44 -0
  297. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +23 -0
  298. package/src/agent-tooling/construct/schema.ts +371 -0
  299. package/src/agent-tooling/mcp/server.ts +2 -1
  300. package/src/agent-tooling/mcp/tools/construct.ts +130 -0
  301. package/src/agent-tooling/mcp/tools/debug.ts +126 -4
  302. package/src/agent-tooling/mcp/tools/reference.ts +104 -3
  303. package/src/agent-tooling/recipes/composed-thread.ts +714 -0
  304. package/src/agent-tooling/recipes/index.ts +22 -0
  305. package/src/agent-tooling/recipes/types.ts +32 -0
  306. package/src/agent-tooling/registry.ts +5 -0
  307. package/src/components/attachment-types.ts +7 -0
  308. package/src/components/audio-visualizer/fit-scale.ts +1 -1
  309. package/src/components/card-renderer.tsx +39 -8
  310. package/src/components/card.tsx +10 -2
  311. package/src/components/chat-scope-picker.tsx +2 -2
  312. package/src/components/chat-thread.tsx +72 -9
  313. package/src/components/choice-card.tsx +102 -37
  314. package/src/components/coachmark.tsx +1 -1
  315. package/src/components/code-block.tsx +2 -2
  316. package/src/components/composer.tsx +89 -15
  317. package/src/components/confirm-card.tsx +20 -4
  318. package/src/components/conversation-item.tsx +33 -9
  319. package/src/components/conversation-list.tsx +25 -10
  320. package/src/components/empty.tsx +14 -3
  321. package/src/components/file-tree.tsx +5 -1
  322. package/src/components/form-widgets.tsx +257 -195
  323. package/src/components/form.tsx +297 -9
  324. package/src/components/loader.tsx +12 -5
  325. package/src/components/message-skills.tsx +1 -1
  326. package/src/components/message.tsx +71 -11
  327. package/src/components/prompt-input.tsx +7 -1
  328. package/src/components/prompt-suggestion.tsx +4 -1
  329. package/src/components/reasoning.tsx +30 -3
  330. package/src/components/response-compare.tsx +2 -2
  331. package/src/components/scroll-button.tsx +56 -4
  332. package/src/components/source.tsx +6 -1
  333. package/src/components/tasks-card.tsx +58 -16
  334. package/src/components/thread.tsx +10 -1
  335. package/src/components/toast.tsx +1 -1
  336. package/src/components/tool-types.ts +9 -0
  337. package/src/elements/attachments.tsx +3 -1
  338. package/src/elements/chat-workspace.tsx +1 -1
  339. package/src/elements/chat.tsx +22 -4
  340. package/src/elements/checkbox-group.tsx +164 -0
  341. package/src/elements/checkbox.tsx +134 -0
  342. package/src/elements/compiled.css +1 -1
  343. package/src/elements/composer.tsx +12 -1
  344. package/src/elements/conversation-item.tsx +62 -22
  345. package/src/elements/conversation-list.tsx +2 -2
  346. package/src/elements/define-entry.ts +12 -0
  347. package/src/elements/dock.tsx +11 -0
  348. package/src/elements/element-data-types.ts +40 -0
  349. package/src/elements/element-manifest.json +24 -0
  350. package/src/elements/element-meta.json +736 -46
  351. package/src/elements/element-nonscalar.json +15 -0
  352. package/src/elements/element-types.d.ts +362 -24
  353. package/src/elements/feedback-bar.tsx +5 -0
  354. package/src/elements/icon-names.json +29 -0
  355. package/src/elements/input.tsx +312 -23
  356. package/src/elements/message.tsx +2 -0
  357. package/src/elements/pane-grid.tsx +111 -0
  358. package/src/elements/prompt-input.tsx +8 -2
  359. package/src/elements/radio-group.tsx +129 -0
  360. package/src/elements/register-impl.ts +6 -0
  361. package/src/elements/scroll-button.tsx +54 -6
  362. package/src/elements/select.tsx +165 -0
  363. package/src/elements/slider.tsx +171 -0
  364. package/src/elements/slots.ts +1 -1
  365. package/src/elements/styles.css +89 -3
  366. package/src/elements/thread.tsx +2 -0
  367. package/src/elements/toast.tsx +14 -1
  368. package/src/index.ts +2 -1
  369. package/src/primitives/card-data-types.ts +49 -0
  370. package/src/primitives/card-host.tsx +4 -2
  371. package/src/primitives/card-routing.ts +7 -56
  372. package/src/primitives/card-schemas/form.schema.json +21 -1
  373. package/src/primitives/card-validate.ts +10 -2
  374. package/src/primitives/chat-config.tsx +33 -6
  375. package/src/primitives/field-mask.ts +256 -0
  376. package/src/primitives/field-semantics.ts +115 -0
  377. package/src/primitives/input-mask.ts +853 -0
  378. package/src/primitives/toast-store.ts +66 -4
  379. package/src/primitives/url-scheme-policy.ts +70 -0
  380. package/src/primitives/use-resize-observer.ts +2 -2
  381. package/src/schemas/tool-defs.ts +12 -2
  382. package/src/solid.ts +10 -0
  383. package/src/state/index.ts +1 -1
  384. package/src/state/mock.ts +92 -6
  385. package/src/ui/agent-card.tsx +2 -2
  386. package/src/ui/avatar.tsx +1 -1
  387. package/src/ui/checkbox-group.tsx +153 -0
  388. package/src/ui/checkbox.tsx +63 -0
  389. package/src/ui/dock.tsx +145 -6
  390. package/src/ui/icon.tsx +53 -0
  391. package/src/ui/input.tsx +249 -9
  392. package/src/ui/kbd.tsx +1 -1
  393. package/src/ui/nav.tsx +1 -1
  394. package/src/ui/pane-group.tsx +2 -2
  395. package/src/ui/radio.tsx +150 -0
  396. package/src/ui/select.tsx +168 -0
  397. package/src/ui/slider.tsx +178 -0
  398. package/src/ui/switch.tsx +49 -18
  399. package/src/utils/cn.ts +11 -5
  400. package/theme.css +84 -13
  401. package/dist/elements/chunks/action-icons-D9u3OzYW.js +0 -1
  402. package/dist/elements/chunks/arrow-left-CVh1TeN7.js +0 -1
  403. package/dist/elements/chunks/artifact-BkYcfIlZ.js +0 -1
  404. package/dist/elements/chunks/attachments-DwfNlubq.js +0 -1
  405. package/dist/elements/chunks/badge-Q0-Cmwzz.js +0 -1
  406. package/dist/elements/chunks/bash-InADTalH.js +0 -1
  407. package/dist/elements/chunks/card-renderer-3Dyl-FpN.js +0 -1
  408. package/dist/elements/chunks/card-routing-BK09BdmL.js +0 -1
  409. package/dist/elements/chunks/check-B5kBPKeo.js +0 -1
  410. package/dist/elements/chunks/chevron-down-DZ1GS39h.js +0 -1
  411. package/dist/elements/chunks/chevron-right-CvP7OUd2.js +0 -1
  412. package/dist/elements/chunks/choice-card-D4QUIOWZ.js +0 -1
  413. package/dist/elements/chunks/circle-CDf9g6MQ.js +0 -1
  414. package/dist/elements/chunks/circle-check-CA47Nqbz.js +0 -1
  415. package/dist/elements/chunks/circle-x-CJ_HVbC8.js +0 -1
  416. package/dist/elements/chunks/composer-Byve-n8K.js +0 -69
  417. package/dist/elements/chunks/confirm-card-B0flE3MN.js +0 -1
  418. package/dist/elements/chunks/controllable-DTwTIlwp.js +0 -1
  419. package/dist/elements/chunks/conversation-item-BDztezOG.js +0 -1
  420. package/dist/elements/chunks/copy-Nf-kqxXH.js +0 -1
  421. package/dist/elements/chunks/core-AYMC6_lb.js +0 -12
  422. package/dist/elements/chunks/css-M7EaDHN_.js +0 -1
  423. package/dist/elements/chunks/default-input-C2iLFq0b.js +0 -1
  424. package/dist/elements/chunks/define-wjHR1MkU.js +0 -1
  425. package/dist/elements/chunks/download-C4V7tBdb.js +0 -1
  426. package/dist/elements/chunks/embed-D3KewsIJ.js +0 -1
  427. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +0 -141
  428. package/dist/elements/chunks/file-text-D1G8khle.js +0 -1
  429. package/dist/elements/chunks/folder-WW2LGLid.js +0 -1
  430. package/dist/elements/chunks/form-CQirdC4W.js +0 -1
  431. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +0 -1
  432. package/dist/elements/chunks/github-light-JYsPkUQd.js +0 -1
  433. package/dist/elements/chunks/hover-card-CJJkfHTK.js +0 -1
  434. package/dist/elements/chunks/html-CPZ3oZQ7.js +0 -1
  435. package/dist/elements/chunks/icon-Cfgmf4fa.js +0 -1
  436. package/dist/elements/chunks/info-DX3YMOt6.js +0 -1
  437. package/dist/elements/chunks/input-BtW9U6Sq.js +0 -1
  438. package/dist/elements/chunks/javascript-C25yR2R2.js +0 -1
  439. package/dist/elements/chunks/json-DxJze_jm.js +0 -1
  440. package/dist/elements/chunks/kbd-LGXwuT_V.js +0 -1
  441. package/dist/elements/chunks/link-2My69BqT.js +0 -1
  442. package/dist/elements/chunks/loader-iOxc_Tu-.js +0 -1
  443. package/dist/elements/chunks/message-VMZohrXx.js +0 -1
  444. package/dist/elements/chunks/message-circle-CpXMM-_n.js +0 -1
  445. package/dist/elements/chunks/message-square-DxcWtETQ.js +0 -1
  446. package/dist/elements/chunks/message-tZmV2A3p.js +0 -1
  447. package/dist/elements/chunks/paperclip-CRZVp4z_.js +0 -1
  448. package/dist/elements/chunks/progress-bar-DNy66e_E.js +0 -1
  449. package/dist/elements/chunks/prompt-suggestion-Bn64Nl7L.js +0 -1
  450. package/dist/elements/chunks/reasoning-Emn9N4-T.js +0 -1
  451. package/dist/elements/chunks/rotate-cw-CPWe37D9.js +0 -1
  452. package/dist/elements/chunks/scroll-area-ClZkZxIk.js +0 -1
  453. package/dist/elements/chunks/scroll-button-DPE-t3x_.js +0 -1
  454. package/dist/elements/chunks/share-CyfnNEqp.js +0 -1
  455. package/dist/elements/chunks/source-UWCvmVak.js +0 -1
  456. package/dist/elements/chunks/star-DoHFAikQ.js +0 -1
  457. package/dist/elements/chunks/svelte--5p79yCD.js +0 -1
  458. package/dist/elements/chunks/tasks-card-CmZ5xh6v.js +0 -1
  459. package/dist/elements/chunks/text-shimmer-DYn09zrz.js +0 -1
  460. package/dist/elements/chunks/textarea-skiGHURM.js +0 -1
  461. package/dist/elements/chunks/toast-store-CFnQpThX.js +0 -1
  462. package/dist/elements/chunks/tsx-B8rCNbgL.js +0 -1
  463. package/dist/elements/chunks/typescript-RycA9KXf.js +0 -1
  464. package/dist/elements/chunks/use-card-resolution-DoA2f_EU.js +0 -1
  465. package/dist/elements/chunks/vue-BmIZj4XD.js +0 -1
  466. package/dist/elements/chunks/x-B6UnOiLw.js +0 -1
  467. package/dist/llms/llms-full.txt +0 -2949
  468. package/dist/llms/llms.txt +0 -156
  469. package/dist/register-impl-DpE7icIb.js +0 -293
  470. package/dist/ui/stat.d.ts +0 -16
  471. package/src/ui/stat.tsx +0 -41
  472. /package/dist/{elements/chunks/card-tags-D8lZ-C_U.js → card-tags-D8lZ-C_U.js} +0 -0
  473. /package/dist/{elements/chunks/link-preview-DNILK391.js → link-preview-DNILK391.js} +0 -0
  474. /package/dist/{elements/chunks/slots-DaNjX0rc.js → slots-DaNjX0rc.js} +0 -0
@@ -0,0 +1,129 @@
1
+ import { createEffect, createSignal, untrack } from 'solid-js';
2
+ import { defineWebComponent } from './define';
3
+ import { RadioGroup } from '../ui/radio';
4
+ // Public shape of the `options` prop; lives in ./element-data-types so the ROOT
5
+ // entry can re-export it (see that file's header).
6
+ import type { KaiRadioOption } from './element-data-types';
7
+
8
+ interface Props extends Record<string, unknown> {
9
+ /** The choices, top to bottom. Set as a JS PROPERTY (array), never an attribute. */
10
+ options: KaiRadioOption[];
11
+ /** Controlled selected `value`. Settable and reflected to the `value` attribute.
12
+ * `el.value = 'degraded'` drives it; choosing a row updates it and fires
13
+ * `kai-change`. Read `el.value` for live state. */
14
+ value?: string;
15
+ /** Shared form-control name for every radio in the group. Defaults to a generated
16
+ * id, so the group is exclusive and keyboard-navigable even when nothing is
17
+ * submitted. */
18
+ name?: string;
19
+ /** Disable every row. Individual rows carry their own `disabled`. */
20
+ disabled?: boolean;
21
+ /** Accessible name for the group. */
22
+ label?: string;
23
+ }
24
+
25
+ /** Events fired by `<kai-radio-group>`. */
26
+ interface Events {
27
+ /** A row was chosen. */
28
+ 'kai-change': { value: string };
29
+ }
30
+
31
+ /**
32
+ * `<kai-radio-group>` — the kit's "pick exactly one" control: a bordered, divided
33
+ * list over real `<input type="radio">`s that share a `name`, so one tab stop, arrow
34
+ * keys, mutual exclusion and form participation are the browser's and not a
35
+ * reimplementation.
36
+ *
37
+ * Feed it `options` (a JS-property array of `{ value, label, description?, disabled? }`),
38
+ * drive/read the selection with the `value` property (settable + reflected to the
39
+ * `value` attribute, so `:host([value])` and `el.value` see live state), and listen
40
+ * for `kai-change`.
41
+ *
42
+ * Re-rendering follows the kit's reactivity contract: hand it a NEW array reference,
43
+ * and a new object for any option whose content changed — the rows are a
44
+ * reference-keyed list, so mutating an option in place changes nothing on screen.
45
+ *
46
+ * ```html
47
+ * <kai-radio-group value="degraded" label="Severity"></kai-radio-group>
48
+ * <script type="module">
49
+ * import '@kitn.ai/ui/elements';
50
+ * const group = document.querySelector('kai-radio-group');
51
+ * group.options = [
52
+ * { value: 'blocking', label: 'Blocking', description: 'Pages the on-call' },
53
+ * { value: 'degraded', label: 'Degraded' },
54
+ * { value: 'cosmetic', label: 'Cosmetic' },
55
+ * ];
56
+ * group.addEventListener('kai-change', (e) => console.log(e.detail.value));
57
+ * group.value = 'cosmetic'; // drive it (no kai-change — the host already knows)
58
+ * </script>
59
+ * ```
60
+ */
61
+ defineWebComponent<Props, Events>('kai-radio-group', {
62
+ options: [],
63
+ value: undefined,
64
+ name: undefined,
65
+ disabled: undefined,
66
+ label: undefined,
67
+ }, (props, ctx) => {
68
+ const { element, dispatch, flag, expose } = ctx;
69
+
70
+ // Lift the selection into the facade and drive RadioGroup CONTROLLED so the host can
71
+ // read it (`el.value` / `:host([value])`) and set it after mount. Seed from the
72
+ // `value` property/attribute present on mount; nothing is auto-selected, because
73
+ // `options` normally arrives as a property AFTER upgrade (kai-segmented's structure).
74
+ const [value, setValue] = createSignal(
75
+ (props.value as string | undefined) ?? element.getAttribute('value') ?? '',
76
+ );
77
+
78
+ // Coerce anything arriving through the `value` setter (a JS assignment, or the
79
+ // attributeChangedCallback write-back) to a string, falling back to the live
80
+ // attribute when nullish so the reflect write-back equals the signal and the guards
81
+ // below absorb it — no attr⇄prop feedback loop.
82
+ const coerce = (v: unknown): string =>
83
+ v == null ? (element.getAttribute('value') ?? '') : String(v);
84
+
85
+ // Apply a new value and fire kai-change once (the user-choice path).
86
+ const apply = (next: string) => {
87
+ if (untrack(value) === next) return;
88
+ setValue(next);
89
+ dispatch('kai-change', { value: next });
90
+ };
91
+
92
+ Object.defineProperty(element, 'value', {
93
+ get: () => value(),
94
+ set: (v: unknown) => { const next = coerce(v); if (untrack(value) !== next) setValue(next); },
95
+ configurable: true,
96
+ enumerable: true,
97
+ });
98
+
99
+ // Reflect internal value → the `[value]` host attribute (for `:host([value])`).
100
+ createEffect(() => {
101
+ const v = value();
102
+ if (v) {
103
+ if (element.getAttribute('value') !== v) element.setAttribute('value', v);
104
+ } else if (element.hasAttribute('value')) {
105
+ element.removeAttribute('value');
106
+ }
107
+ });
108
+
109
+ expose({
110
+ /** Focus the group's tab stop. That is the selected radio, or the first row when
111
+ * nothing is selected yet. */
112
+ focus: (options?: FocusOptions) => {
113
+ const root = element.shadowRoot;
114
+ const radios = [...(root?.querySelectorAll<HTMLInputElement>('input[type="radio"]') ?? [])];
115
+ (radios.find((r) => r.checked) ?? radios[0])?.focus(options);
116
+ },
117
+ });
118
+
119
+ return (
120
+ <RadioGroup
121
+ options={(props.options ?? []) as KaiRadioOption[]}
122
+ value={value()}
123
+ name={props.name as string | undefined}
124
+ disabled={flag('disabled')}
125
+ label={props.label as string | undefined}
126
+ onChange={(next) => apply(next)}
127
+ />
128
+ );
129
+ });
@@ -54,6 +54,11 @@ import './artifact';
54
54
  import './scroll-button';
55
55
  import './popover';
56
56
  import './switch';
57
+ import './checkbox';
58
+ import './checkbox-group';
59
+ import './radio-group';
60
+ import './slider';
61
+ import './select';
57
62
  import './button';
58
63
  import './avatar';
59
64
  import './badge';
@@ -94,6 +99,7 @@ import './setting-item';
94
99
  // Multi-agent workspace primitives (graduated from SolidJS prototypes)
95
100
  import './pane';
96
101
  import './pane-group';
102
+ import './pane-grid';
97
103
  import './agent-card';
98
104
  import './dialog';
99
105
  import './dock';
@@ -1,7 +1,7 @@
1
- import { createSignal, onCleanup, onMount } from 'solid-js';
1
+ import { Show, createSignal, onCleanup, onMount } from 'solid-js';
2
2
  import { cn } from '../utils/cn';
3
3
  import { Button } from '../ui/button';
4
- import { ChevronDown } from 'lucide-solid';
4
+ import { ArrowDown } from 'lucide-solid';
5
5
  import { defineWebComponent } from './define';
6
6
 
7
7
  interface Props extends Record<string, unknown> {
@@ -14,6 +14,14 @@ interface Props extends Record<string, unknown> {
14
14
  variant?: 'outline' | 'ghost' | 'default';
15
15
  /** Button size token. Defaults to `'icon'` (square). */
16
16
  size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm';
17
+ /** The button's accessible name. It is announced whether or not the label is
18
+ * visible, so the text is always localisable. Defaults to
19
+ * `'Scroll to bottom'`. */
20
+ label?: string;
21
+ /** Also render `label` visibly beside the icon. Defaults to `false`, which
22
+ * is the icon-only button. When the text is visible it IS the accessible
23
+ * name, so nothing gets announced twice. */
24
+ showLabel?: boolean;
17
25
  }
18
26
 
19
27
  /** Events fired by `<kai-scroll-button>`. */
@@ -26,6 +34,27 @@ interface Events {
26
34
 
27
35
  const SCROLL_THRESHOLD = 50;
28
36
 
37
+ /** The accessible name used when the consumer does not supply one. */
38
+ const DEFAULT_LABEL = 'Scroll to bottom';
39
+
40
+ /**
41
+ * The floating chip surface. Mirrors `surfaceClasses` in
42
+ * `components/scroll-button.tsx`: the shared `outline` button variant is
43
+ * `bg-muted/50`, half-transparent, so message text showed through a control
44
+ * floating over scrolling content and its edge measured about 1.05:1 against
45
+ * the thread behind it (SC 1.4.11 wants 3:1). `bg-card` is the opaque surface
46
+ * popovers and dropdowns already use, `border-input` is the kit's control edge
47
+ * and clears 3:1 in both themes, and `kai-elevation` is the shared themeable
48
+ * float shadow that keeps an opaque button reading as ABOVE the content.
49
+ * `ghost` and `default` are explicit consumer choices about the fill, so they
50
+ * keep theirs.
51
+ */
52
+ function surfaceClasses(variant: Props['variant']) {
53
+ return (variant ?? 'outline') === 'outline'
54
+ ? 'border border-input bg-card text-card-foreground kai-elevation'
55
+ : 'kai-elevation';
56
+ }
57
+
29
58
  /** Walk the composed tree upwards from `startEl` (outside shadow roots) to
30
59
  * find the nearest scrollable ancestor. */
31
60
  function findScrollableAncestor(startEl: HTMLElement): HTMLElement | null {
@@ -63,7 +92,9 @@ defineWebComponent<Props, Events>('kai-scroll-button', {
63
92
  for: undefined,
64
93
  variant: 'outline',
65
94
  size: 'icon',
66
- }, (props, { element, dispatch }) => {
95
+ label: undefined,
96
+ showLabel: undefined,
97
+ }, (props, { element, dispatch, flag }) => {
67
98
  const [isAtBottom, setIsAtBottom] = createSignal(true);
68
99
  let containerEl: HTMLElement | null = null;
69
100
  let cleanupFns: (() => void)[] = [];
@@ -104,18 +135,25 @@ defineWebComponent<Props, Events>('kai-scroll-button', {
104
135
 
105
136
  onCleanup(detach);
106
137
 
138
+ const label = () => (props.label as string | undefined) ?? DEFAULT_LABEL;
139
+ const showLabel = () => flag('showLabel');
140
+
107
141
  return (
108
142
  <Button
109
143
  variant={props.variant ?? 'outline'}
110
144
  size={props.size ?? 'icon'}
111
- aria-label="Scroll to bottom"
145
+ // When the label is visible it IS the accessible name; an aria-label on
146
+ // top of it would override the visible text and the two could disagree.
147
+ aria-label={showLabel() ? undefined : label()}
112
148
  // While at the bottom this button is fully transparent and pointer-inert,
113
149
  // but it stayed in the tab order — a keyboard user landed on a control
114
150
  // they could not see. Mirrors ScrollButton in components/scroll-button.tsx.
115
151
  tabindex={isAtBottom() ? -1 : 0}
116
152
  aria-hidden={isAtBottom() ? 'true' : undefined}
117
153
  class={cn(
118
- 'rounded-full transition-all duration-150 ease-out',
154
+ 'rounded-lg transition-all duration-150 ease-out',
155
+ surfaceClasses(props.variant),
156
+ showLabel() ? 'h-9 w-auto gap-1.5 px-3 text-sm' : '',
119
157
  !isAtBottom()
120
158
  ? 'translate-y-0 scale-100 opacity-100'
121
159
  : 'pointer-events-none translate-y-4 scale-95 opacity-0',
@@ -125,7 +163,17 @@ defineWebComponent<Props, Events>('kai-scroll-button', {
125
163
  dispatch('kai-scroll');
126
164
  }}
127
165
  >
128
- <ChevronDown class="h-5 w-5" />
166
+ {/* h-4, not the h-5 the chevron used, and the same reasoning as
167
+ components/scroll-button.tsx: a ChevronDown only inks the lower part of
168
+ its 24px viewBox, so at a 20px box its visible mass is small, while
169
+ ArrowDown inks the full height (stem + head) and at 20px crowded the
170
+ padding. Re-checked here because this button box is 36px (size="icon")
171
+ rather than the Solid component's 40px, and 16px balances both. It is
172
+ also the size lucide pairs with text-sm, which the label renders at. */}
173
+ <ArrowDown class="h-4 w-4 shrink-0" aria-hidden="true" />
174
+ <Show when={showLabel()}>
175
+ <span>{label()}</span>
176
+ </Show>
129
177
  </Button>
130
178
  );
131
179
  });
@@ -0,0 +1,165 @@
1
+ import { createEffect, createSignal, untrack } from 'solid-js';
2
+ import { defineWebComponent } from './define';
3
+ import { Select } from '../ui/select';
4
+ // Public shape of the `options` prop; lives in ./element-data-types so the ROOT
5
+ // entry can re-export it (see that file's header).
6
+ import type { KaiSelectOption } from './element-data-types';
7
+
8
+ interface Props extends Record<string, unknown> {
9
+ /** The choices, in display order. Set as a JS PROPERTY (array), never an attribute.
10
+ * Rendered in full: the kit never truncates, re-orders or de-duplicates them. */
11
+ options: KaiSelectOption[];
12
+ /** Controlled selected value. Settable and reflected to the `value` attribute.
13
+ * `el.value = 'high'` drives it; choosing an option updates it and fires
14
+ * `kai-change`. Read `el.value` for live state; for a `multiple` select read
15
+ * `el.values` instead. */
16
+ value?: string;
17
+ /** Text for a leading, disabled, empty option: the "nothing chosen yet" row.
18
+ * Omitted means no such row at all; there is no default wording, because inventing
19
+ * one would put words in your UI. */
20
+ placeholder?: string;
21
+ /** Allow more than one selection. Turns the control into the platform's list box, so
22
+ * the kit's chevron is not drawn. */
23
+ multiple?: boolean;
24
+ /** Force the invalid (destructive-border) state. */
25
+ invalid?: boolean;
26
+ /** Disable interaction. */
27
+ disabled?: boolean;
28
+ /** Set the native `required` attribute, and nothing more. Whether an empty select is
29
+ * an error is the application's rule, not the kit's. */
30
+ required?: boolean;
31
+ /** Accessible label for the control. */
32
+ label?: string;
33
+ /** Form-control name, for a native form submit. */
34
+ name?: string;
35
+ }
36
+
37
+ /** Events fired by `<kai-select>`. */
38
+ interface Events {
39
+ /** A choice was made. `value` is the first selected option (empty when nothing is
40
+ * selected); `values` is every selected option, which is what a `multiple` select
41
+ * needs. Both are always present, so neither shape silently loses the other. */
42
+ 'kai-change': { value: string; values: string[] };
43
+ }
44
+
45
+ /**
46
+ * `<kai-select>` — a select over a REAL native `<select>`, in the kit's field box with
47
+ * the kit's own chevron. Native, not a hand-built listbox: that is what keeps the
48
+ * platform picker on mobile, type-ahead on desktop, form participation and the OS
49
+ * accessibility tree.
50
+ *
51
+ * The dropdown list itself stays OS chrome — it renders outside the page and no
52
+ * stylesheet reaches it. What the element does do is make it follow the kit's
53
+ * light/dark mode rather than the OS's.
54
+ *
55
+ * Feed it `options` (a JS-property array of `{ value, label?, disabled? }`), drive and
56
+ * read the selection with the `value` property (settable + reflected to the `value`
57
+ * attribute, so `:host([value])` and `el.value` see live state), and listen for
58
+ * `kai-change`.
59
+ *
60
+ * Re-rendering follows the kit's reactivity contract: hand it a NEW array reference,
61
+ * and a new object for any option whose content changed — the list is
62
+ * reference-keyed, so mutating an option in place changes nothing on screen.
63
+ *
64
+ * ```html
65
+ * <kai-select value="high" placeholder="Choose a severity…" label="Severity"></kai-select>
66
+ * <script type="module">
67
+ * import '@kitn.ai/ui/elements';
68
+ * const select = document.querySelector('kai-select');
69
+ * // ARRAY prop — a JS property, never an attribute.
70
+ * select.options = [
71
+ * { value: 'low', label: 'Low' },
72
+ * { value: 'high', label: 'High priority' },
73
+ * { value: 'legacy', label: 'Legacy', disabled: true },
74
+ * ];
75
+ * select.addEventListener('kai-change', (e) => console.log(e.detail.value, e.detail.values));
76
+ * select.value = 'low'; // drive it (no kai-change — the host already knows)
77
+ * </script>
78
+ * ```
79
+ */
80
+ defineWebComponent<Props, Events>('kai-select', {
81
+ options: [],
82
+ value: undefined,
83
+ placeholder: undefined,
84
+ multiple: undefined,
85
+ invalid: undefined,
86
+ disabled: undefined,
87
+ required: undefined,
88
+ label: undefined,
89
+ name: undefined,
90
+ }, (props, ctx) => {
91
+ const { element, dispatch, flag, expose } = ctx;
92
+
93
+ // Lift the selection into the facade and drive Select CONTROLLED so the host can read
94
+ // it (`el.value` / `:host([value])`) and set it after mount. Seed from the `value`
95
+ // property/attribute present on mount; nothing is auto-selected, because `options`
96
+ // normally arrives as a property AFTER upgrade (kai-radio-group's structure).
97
+ const seed = (props.value as string | undefined) ?? element.getAttribute('value') ?? '';
98
+ const [values, setValues] = createSignal<string[]>(seed ? [seed] : []);
99
+ const first = () => values()[0] ?? '';
100
+
101
+ // Coerce anything arriving through the `value` setter (a JS assignment, or the
102
+ // attributeChangedCallback write-back the reflect effect below triggers) to a string,
103
+ // falling back to the live attribute when nullish so the write-back equals the signal
104
+ // and the guards absorb it — no attr⇄prop feedback loop.
105
+ const coerce = (v: unknown): string =>
106
+ v == null ? (element.getAttribute('value') ?? '') : String(v);
107
+
108
+ // Apply a new selection and fire kai-change once (the user-choice path).
109
+ const apply = (next: string[]) => {
110
+ const prev = untrack(values);
111
+ if (prev.length === next.length && prev.every((v, i) => v === next[i])) return;
112
+ setValues(next);
113
+ dispatch('kai-change', { value: next[0] ?? '', values: next });
114
+ };
115
+
116
+ Object.defineProperty(element, 'value', {
117
+ get: () => first(),
118
+ set: (v: unknown) => {
119
+ const next = coerce(v);
120
+ if (untrack(first) !== next) setValues(next ? [next] : []);
121
+ },
122
+ configurable: true,
123
+ enumerable: true,
124
+ });
125
+
126
+ // The multi-selection read/write side. `value` alone cannot represent it, and
127
+ // returning only the first option from a `multiple` select would be a silent drop.
128
+ Object.defineProperty(element, 'values', {
129
+ get: () => values(),
130
+ set: (v: unknown) => setValues(Array.isArray(v) ? v.map(String) : []),
131
+ configurable: true,
132
+ enumerable: true,
133
+ });
134
+
135
+ // Reflect the primary value → the `[value]` host attribute (for `:host([value])`).
136
+ createEffect(() => {
137
+ const v = first();
138
+ if (v) {
139
+ if (element.getAttribute('value') !== v) element.setAttribute('value', v);
140
+ } else if (element.hasAttribute('value')) {
141
+ element.removeAttribute('value');
142
+ }
143
+ });
144
+
145
+ expose({
146
+ /** Focus the inner select (the host element can't reach it). */
147
+ focus: (options?: FocusOptions) =>
148
+ element.shadowRoot?.querySelector<HTMLSelectElement>('select')?.focus(options),
149
+ });
150
+
151
+ return (
152
+ <Select
153
+ options={(props.options ?? []) as KaiSelectOption[]}
154
+ value={values()}
155
+ placeholder={props.placeholder as string | undefined}
156
+ multiple={flag('multiple')}
157
+ invalid={flag('invalid')}
158
+ disabled={flag('disabled')}
159
+ required={flag('required')}
160
+ aria-label={props.label as string | undefined}
161
+ name={props.name as string | undefined}
162
+ onChange={(e) => apply([...e.currentTarget.selectedOptions].map((o) => o.value))}
163
+ />
164
+ );
165
+ });
@@ -0,0 +1,171 @@
1
+ import { createEffect, createSignal, untrack } from 'solid-js';
2
+ import { defineWebComponent } from './define';
3
+ import { Slider } from '../ui/slider';
4
+
5
+ interface Props extends Record<string, unknown> {
6
+ /** Lowest selectable value. Required: a range with no bounds is a guess, and the
7
+ * guess belongs to whoever knows what the number means. */
8
+ min?: number;
9
+ /** Highest selectable value. Required, for the same reason as `min`. */
10
+ max?: number;
11
+ /** Granularity. Omitted means the native default of 1; `any` means continuous. */
12
+ step?: number | 'any';
13
+ /** Controlled value. Settable and reflected to the `value` attribute. `el.value = 40`
14
+ * drives it; dragging updates it and fires `kai-input` per step, `kai-change` on
15
+ * release. Read `el.value` for live state. */
16
+ value?: number;
17
+ /** Disable interaction. */
18
+ disabled?: boolean;
19
+ /** Accessible label for the slider. */
20
+ label?: string;
21
+ /** Form-control name, for a native form submit. */
22
+ name?: string;
23
+ /** Show the current value beside the track. Off by default.
24
+ *
25
+ * Two ways in, because one of them is not a scalar. As a bare ATTRIBUTE
26
+ * (`<kai-slider value-label>`) it renders the raw number. As a JS PROPERTY it also
27
+ * accepts a formatter function (`el.valueLabel = (v) => v + '%'`), for a slider that
28
+ * is not counting bare numbers. A function cannot survive an attribute, so that half
29
+ * is property-only.
30
+ *
31
+ * The readout is hidden from assistive tech: the slider already reports the same
32
+ * number, and an exposed copy would be announced twice. */
33
+ valueLabel?: boolean | ((value: number) => string);
34
+ }
35
+
36
+ /** Events fired by `<kai-slider>`. */
37
+ interface Events {
38
+ /** The thumb moved. Fires per step during a drag or a key press. */
39
+ 'kai-input': { value: number };
40
+ /** The value was committed: pointer released, or a key press finished. */
41
+ 'kai-change': { value: number };
42
+ }
43
+
44
+ /**
45
+ * `<kai-slider>` — a slider over a real `<input type="range">`, with the kit's filled
46
+ * track and thumb. Arrows, Home, End and PageUp/PageDown all work because the control
47
+ * underneath is the browser's, not a reimplementation.
48
+ *
49
+ * Give it `min` and `max` (there are no defaults on purpose), drive and read the
50
+ * position with the `value` property, and listen for `kai-input` while dragging or
51
+ * `kai-change` on release.
52
+ *
53
+ * ```html
54
+ * <kai-slider min="0" max="100" step="5" value="40" label="Temperature"></kai-slider>
55
+ * <script type="module">
56
+ * import '@kitn.ai/ui/elements';
57
+ * const slider = document.querySelector('kai-slider');
58
+ * slider.addEventListener('kai-input', (e) => console.log(e.detail.value));
59
+ * slider.value = 70; // drive it (no event — the host already knows)
60
+ * </script>
61
+ * ```
62
+ */
63
+ defineWebComponent<Props, Events>('kai-slider', {
64
+ min: undefined,
65
+ max: undefined,
66
+ step: undefined,
67
+ value: undefined,
68
+ disabled: undefined,
69
+ label: undefined,
70
+ name: undefined,
71
+ valueLabel: undefined,
72
+ }, (props, ctx) => {
73
+ const { element, dispatch, flag, expose } = ctx;
74
+
75
+ // `Number(null)` is 0 and `Number('')` is 0, both FINITE, so a naive
76
+ // `Number.isFinite(Number(v))` treats a missing attribute as a real zero and the
77
+ // fallbacks below never fire. Reject the empty cases before coercing.
78
+ const num = (v: unknown, fallback: number): number => {
79
+ if (v == null || (typeof v === 'string' && v.trim() === '')) return fallback;
80
+ const n = Number(v);
81
+ return Number.isFinite(n) ? n : fallback;
82
+ };
83
+ // No invented bounds: these mirror the native `<input type="range">` defaults, which
84
+ // is what an author who wrote neither attribute is already going to get.
85
+ const min = () => num(props.min, 0);
86
+ const max = () => num(props.max, 100);
87
+
88
+ // Lift the position into the facade and drive Slider CONTROLLED so the host can read
89
+ // it (`el.value` / `:host([value])`) and set it after mount. Seed from the `value`
90
+ // property/attribute present on mount, falling back to the midpoint — HTML's own rule
91
+ // for a range with no value. kai-checkbox / kai-radio-group's structure, deliberately.
92
+ const [value, setValue] = createSignal(
93
+ props.value != null
94
+ ? num(props.value, (min() + max()) / 2)
95
+ : num(element.getAttribute('value'), (min() + max()) / 2),
96
+ );
97
+
98
+ // Coerce anything arriving through the `value` setter (a JS assignment, or the
99
+ // write-back `attributeChangedCallback` fires when the reflect effect below writes the
100
+ // attribute) to a number, falling back to the live attribute when nullish so the
101
+ // write-back equals the signal and the guard absorbs it — no attr⇄prop feedback loop.
102
+ const coerce = (v: unknown): number =>
103
+ v == null ? num(element.getAttribute('value'), untrack(value)) : num(v, untrack(value));
104
+
105
+ // Apply a new value and fire once (the user path). No-op while disabled.
106
+ const apply = (next: number, type: 'kai-input' | 'kai-change') => {
107
+ if (flag('disabled')) return;
108
+ // kai-change fires on commit EVEN IF the value equals the last one this element
109
+ // reported, because "you let go here" is information the host cannot derive; the
110
+ // equality guard applies to the per-step stream only.
111
+ if (type === 'kai-input' && untrack(value) === next) return;
112
+ setValue(next);
113
+ dispatch(type, { value: next });
114
+ };
115
+
116
+ // Reads return LIVE state; host writes drive it WITHOUT firing an event — the host
117
+ // already knows what it set. The equality guard kills the reflect write-back.
118
+ Object.defineProperty(element, 'value', {
119
+ get: () => value(),
120
+ set: (v: unknown) => { const next = coerce(v); if (untrack(value) !== next) setValue(next); },
121
+ configurable: true,
122
+ enumerable: true,
123
+ });
124
+
125
+ // Reflect internal value → the `[value]` host attribute (for `:host([value])`).
126
+ createEffect(() => {
127
+ const v = String(value());
128
+ if (element.getAttribute('value') !== v) element.setAttribute('value', v);
129
+ });
130
+
131
+ expose({
132
+ /** Focus the inner range input (the host element can't reach it). */
133
+ focus: (options?: FocusOptions) =>
134
+ element.shadowRoot?.querySelector<HTMLInputElement>('input[type="range"]')?.focus(options),
135
+ });
136
+
137
+ // `step` arrives as a string when it came from an attribute. Resolve the one literal
138
+ // the spec allows (`any`, continuous) and treat everything else as a number.
139
+ const step = (): number | 'any' | undefined => {
140
+ const raw = props.step;
141
+ if (raw == null) return undefined;
142
+ if (String(raw) === 'any') return 'any';
143
+ const n = Number(raw);
144
+ return Number.isFinite(n) ? n : undefined;
145
+ };
146
+
147
+ // `valueLabel` arrives either as a bare attribute (the boolean half) or as a property
148
+ // holding a formatter (the half an attribute cannot carry). `flag` handles the first,
149
+ // a typeof check the second.
150
+ const valueLabel = (): boolean | ((v: number) => string) => {
151
+ const raw = props.valueLabel;
152
+ if (typeof raw === 'function') return raw as (v: number) => string;
153
+ return flag('valueLabel');
154
+ };
155
+
156
+ // Base sets `:host{display:block}`, which is right for a full-width track.
157
+ return (
158
+ <Slider
159
+ min={min()}
160
+ max={max()}
161
+ step={step()}
162
+ value={value()}
163
+ disabled={flag('disabled')}
164
+ aria-label={props.label as string | undefined}
165
+ name={props.name as string | undefined}
166
+ valueLabel={valueLabel()}
167
+ onInput={(e) => apply(e.currentTarget.valueAsNumber, 'kai-input')}
168
+ onChange={(e) => apply(e.currentTarget.valueAsNumber, 'kai-change')}
169
+ />
170
+ );
171
+ });
@@ -2,7 +2,7 @@
2
2
  * Composition slots for `kai-*` elements. A slot is a named region a consumer
3
3
  * fills with their own markup. This registry is the SINGLE SOURCE OF TRUTH:
4
4
  * the facade derives its detection from it (and, later, docs are generated
5
- * from it). See docs/superpowers/specs/2026-06-23-kai-chat-composition-seams-design.md.
5
+ * from it).
6
6
  */
7
7
 
8
8
  /** `inject` = additive (the built-in region still renders, your markup is added