@kitn.ai/ui 0.16.0 → 0.18.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 (493) hide show
  1. package/README.md +55 -17
  2. package/dist/components/artifact.d.ts +39 -1
  3. package/dist/components/attachment-types.d.ts +12 -0
  4. package/dist/components/attachments.d.ts +2 -10
  5. package/dist/components/card.d.ts +27 -0
  6. package/dist/components/chain-of-thought.d.ts +78 -2
  7. package/dist/components/chat-scope-picker.d.ts +7 -0
  8. package/dist/components/chat-thread.d.ts +39 -16
  9. package/dist/components/choice-card.d.ts +27 -0
  10. package/dist/components/coachmark.d.ts +49 -0
  11. package/dist/components/composer-dom.d.ts +35 -0
  12. package/dist/components/composer-highlight.d.ts +48 -0
  13. package/dist/components/composer-history.d.ts +34 -0
  14. package/dist/components/composer.d.ts +86 -0
  15. package/dist/components/confirm-card.d.ts +17 -0
  16. package/dist/components/conversation-item.d.ts +9 -0
  17. package/dist/components/conversation-list.d.ts +36 -1
  18. package/dist/components/dismissed-stub.d.ts +22 -0
  19. package/dist/components/file-tree.d.ts +15 -0
  20. package/dist/components/form.d.ts +34 -0
  21. package/dist/components/markdown.d.ts +3 -0
  22. package/dist/components/message.d.ts +71 -2
  23. package/dist/components/model-switcher.d.ts +7 -0
  24. package/dist/components/prompt-input.d.ts +13 -4
  25. package/dist/components/prompt-suggestion.d.ts +9 -1
  26. package/dist/components/reasoning.d.ts +14 -1
  27. package/dist/components/response-compare-types.d.ts +66 -0
  28. package/dist/components/response-compare.d.ts +65 -0
  29. package/dist/components/screen.d.ts +46 -0
  30. package/dist/components/tasks-card.d.ts +50 -1
  31. package/dist/components/toast.d.ts +58 -0
  32. package/dist/components/tool-types.d.ts +10 -0
  33. package/dist/components/tool.d.ts +8 -8
  34. package/dist/components/use-card-resolution.d.ts +10 -6
  35. package/dist/components/voice-input.d.ts +27 -0
  36. package/dist/components/voice-output.d.ts +33 -0
  37. package/dist/core-C8fzo39E.js +12 -0
  38. package/dist/custom-elements.json +8472 -1691
  39. package/dist/elements/agent-card.d.ts +1 -0
  40. package/dist/elements/agent-card.js +1 -0
  41. package/dist/elements/artifact.js +1 -0
  42. package/dist/elements/attachments.js +1 -0
  43. package/dist/elements/autoloader.d.ts +2 -0
  44. package/dist/elements/autoloader.js +1 -0
  45. package/dist/elements/avatar.d.ts +1 -0
  46. package/dist/elements/avatar.js +1 -0
  47. package/dist/elements/badge.d.ts +1 -0
  48. package/dist/elements/badge.js +1 -0
  49. package/dist/elements/button.d.ts +1 -0
  50. package/dist/elements/button.js +1 -0
  51. package/dist/elements/card.js +1 -0
  52. package/dist/elements/cards.d.ts +1 -0
  53. package/dist/elements/cards.js +1 -0
  54. package/dist/elements/chain-of-thought.d.ts +0 -12
  55. package/dist/elements/chain-of-thought.js +1 -0
  56. package/dist/elements/chat-scope-picker.js +1 -0
  57. package/dist/elements/chat-types.d.ts +8 -2
  58. package/dist/elements/chat-workspace.js +1 -0
  59. package/dist/elements/chat.js +1 -0
  60. package/dist/elements/checkpoint.js +1 -0
  61. package/dist/elements/choice.js +1 -0
  62. package/dist/elements/chunks/Icon-DLLBeLG5.js +1 -0
  63. package/dist/elements/chunks/action-icons-UB4V5FNq.js +1 -0
  64. package/dist/elements/chunks/arrow-left-C-gwlUmx.js +1 -0
  65. package/dist/elements/chunks/attachments-CgbErf1r.js +1 -0
  66. package/dist/elements/chunks/badge-DTQpgB6N.js +1 -0
  67. package/dist/elements/chunks/bash-InADTalH.js +1 -0
  68. package/dist/elements/chunks/button-XDUM6lXs.js +1 -0
  69. package/dist/elements/chunks/card-CBlFZ-7I.js +1 -0
  70. package/dist/elements/chunks/card-routing-C986KuUA.js +1 -0
  71. package/dist/elements/chunks/chat-thread-BIhmqwNL.js +1 -0
  72. package/dist/elements/chunks/check-hGUoVIKb.js +1 -0
  73. package/dist/elements/chunks/chevron-down-DDoaSUtO.js +1 -0
  74. package/dist/elements/chunks/chevron-right-CdGEUakB.js +1 -0
  75. package/dist/elements/chunks/circle-BKAZdmzb.js +1 -0
  76. package/dist/elements/chunks/circle-check-CmYG-y3I.js +1 -0
  77. package/dist/elements/chunks/circle-x-DewgAEym.js +1 -0
  78. package/dist/elements/chunks/cn-CU7UAWzk.js +1 -0
  79. package/dist/elements/chunks/code-block-DedwxwFI.js +1 -0
  80. package/dist/elements/chunks/collapsible-FfgmTHtM.js +1 -0
  81. package/dist/elements/chunks/composer-Cfbw5HKT.js +65 -0
  82. package/dist/elements/chunks/context-DwJmxlRE.js +1 -0
  83. package/dist/elements/chunks/conversation-list-DsCQMfPq.js +1 -0
  84. package/dist/elements/chunks/core-AYMC6_lb.js +12 -0
  85. package/dist/elements/chunks/css-M7EaDHN_.js +1 -0
  86. package/dist/elements/chunks/default-input-BuqsoENC.js +1 -0
  87. package/dist/elements/chunks/define-rGsaWsLL.js +1 -0
  88. package/dist/elements/chunks/disclosure-CwoAkpMH.js +1 -0
  89. package/dist/elements/chunks/download-DJ06G3Bz.js +1 -0
  90. package/dist/elements/chunks/dropdown-BFg8ri_R.js +1 -0
  91. package/dist/elements/chunks/ellipsis-dlN6YvGw.js +1 -0
  92. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +141 -0
  93. package/dist/elements/chunks/external-link-DBX2lc_R.js +1 -0
  94. package/dist/elements/chunks/file-text-HfwUx4F9.js +1 -0
  95. package/dist/elements/chunks/file-tree-C6Toj71u.js +1 -0
  96. package/dist/elements/chunks/folder-MYuqa9Xp.js +1 -0
  97. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +1 -0
  98. package/dist/elements/chunks/github-light-JYsPkUQd.js +1 -0
  99. package/dist/elements/chunks/hover-card-gE7iyXdW.js +1 -0
  100. package/dist/elements/chunks/html-CPZ3oZQ7.js +1 -0
  101. package/dist/elements/chunks/icon-Vqz4ivrY.js +1 -0
  102. package/dist/elements/chunks/index-BCRhi84a.js +1 -0
  103. package/dist/elements/chunks/info-B_toFS0l.js +1 -0
  104. package/dist/elements/chunks/input-gzbHYZvQ.js +1 -0
  105. package/dist/elements/chunks/javascript-C25yR2R2.js +1 -0
  106. package/dist/elements/chunks/json-DxJze_jm.js +1 -0
  107. package/dist/elements/chunks/kbd-n49c4KZm.js +1 -0
  108. package/dist/elements/chunks/link-CKPs9yM0.js +1 -0
  109. package/dist/elements/chunks/loader-q6MamGus.js +1 -0
  110. package/dist/elements/chunks/markdown-DBrcqq4S.js +64 -0
  111. package/dist/elements/chunks/message-CTqaYI0s.js +1 -0
  112. package/dist/elements/chunks/message-feedback-Cvzm03w0.js +1 -0
  113. package/dist/elements/chunks/message-square-CUg9eRCi.js +1 -0
  114. package/dist/elements/chunks/minimize-2-l2klxP6K.js +1 -0
  115. package/dist/elements/chunks/model-switcher-CmyjiHO1.js +1 -0
  116. package/dist/elements/chunks/overlay-C8sOYqjT.js +1 -0
  117. package/dist/elements/chunks/paperclip-42Ia7xHD.js +1 -0
  118. package/dist/elements/chunks/progress-bar-tzKMWAwO.js +1 -0
  119. package/dist/elements/chunks/prompt-suggestion-D3F6vP_A.js +1 -0
  120. package/dist/elements/chunks/reasoning-BJNmmyov.js +1 -0
  121. package/dist/elements/chunks/resizable-BRsk19qI.js +1 -0
  122. package/dist/elements/chunks/rotate-cw-BgcggLHC.js +1 -0
  123. package/dist/elements/chunks/scroll-area-CgGbFFbT.js +1 -0
  124. package/dist/elements/chunks/separator-8hM4GdHE.js +1 -0
  125. package/dist/elements/chunks/settings-CaUIYOg-.js +1 -0
  126. package/dist/elements/chunks/settings-group-CYjOVIZQ.js +1 -0
  127. package/dist/elements/chunks/share-BTYM3Ddt.js +1 -0
  128. package/dist/elements/chunks/skeleton-DsTYrPUe.js +1 -0
  129. package/dist/elements/chunks/slots-DkZ-Cmha.js +1 -0
  130. package/dist/elements/chunks/star-9R3LBQ9n.js +1 -0
  131. package/dist/elements/chunks/store-CYl5Y0_u.js +1 -0
  132. package/dist/elements/chunks/svelte--5p79yCD.js +1 -0
  133. package/dist/elements/chunks/text-shimmer-CaYGv8Se.js +1 -0
  134. package/dist/elements/chunks/textarea-NEr2SLcD.js +1 -0
  135. package/dist/elements/chunks/thumbs-up-DZYZkFfc.js +1 -0
  136. package/dist/elements/chunks/toast-store-ClxVZRcc.js +1 -0
  137. package/dist/elements/chunks/tool-CHZOmoe9.js +1 -0
  138. package/dist/elements/chunks/tooltip-WpHElELC.js +1 -0
  139. package/dist/elements/chunks/triangle-alert-BlS80PUg.js +1 -0
  140. package/dist/elements/chunks/tsx-B8rCNbgL.js +1 -0
  141. package/dist/elements/chunks/typescript-RycA9KXf.js +1 -0
  142. package/dist/elements/chunks/use-card-resolution-VSVoxDeJ.js +1 -0
  143. package/dist/elements/chunks/video-DxTPZqT8.js +1 -0
  144. package/dist/elements/chunks/vue-BmIZj4XD.js +1 -0
  145. package/dist/elements/chunks/x-C1qtndxn.js +1 -0
  146. package/dist/elements/coachmark.d.ts +1 -0
  147. package/dist/elements/coachmark.js +1 -0
  148. package/dist/elements/code-block.js +1 -0
  149. package/dist/elements/command.d.ts +1 -0
  150. package/dist/elements/command.js +1 -0
  151. package/dist/elements/compare.d.ts +1 -0
  152. package/dist/elements/compare.js +1 -0
  153. package/dist/elements/composer.d.ts +1 -0
  154. package/dist/elements/composer.js +1 -0
  155. package/dist/elements/confirm-card.js +1 -0
  156. package/dist/elements/context-meter.js +1 -0
  157. package/dist/elements/conversation-list.d.ts +1 -11
  158. package/dist/elements/conversation-list.js +1 -0
  159. package/dist/elements/default-input.d.ts +20 -9
  160. package/dist/elements/define.d.ts +11 -0
  161. package/dist/elements/dialog.d.ts +1 -0
  162. package/dist/elements/dialog.js +1 -0
  163. package/dist/elements/disclosure.d.ts +37 -0
  164. package/dist/elements/editable-label.d.ts +1 -0
  165. package/dist/elements/editable-label.js +1 -0
  166. package/dist/elements/embed.js +1 -0
  167. package/dist/elements/empty.js +1 -0
  168. package/dist/elements/feedback-bar.js +1 -0
  169. package/dist/elements/file-tree.js +1 -0
  170. package/dist/elements/file-upload.js +1 -0
  171. package/dist/elements/form.js +1 -0
  172. package/dist/elements/hover-card.d.ts +1 -0
  173. package/dist/elements/hover-card.js +1 -0
  174. package/dist/elements/icon.d.ts +1 -0
  175. package/dist/elements/icon.js +1 -0
  176. package/dist/elements/image.js +1 -0
  177. package/dist/elements/input.d.ts +1 -0
  178. package/dist/elements/input.js +1 -0
  179. package/dist/elements/kbd.d.ts +1 -0
  180. package/dist/elements/kbd.js +1 -0
  181. package/dist/elements/link-preview.js +1 -0
  182. package/dist/elements/loader.js +1 -0
  183. package/dist/elements/markdown.js +1 -0
  184. package/dist/elements/menu.d.ts +1 -0
  185. package/dist/elements/menu.js +1 -0
  186. package/dist/elements/message-skills.d.ts +0 -16
  187. package/dist/elements/message-skills.js +1 -0
  188. package/dist/elements/message.js +1 -0
  189. package/dist/elements/model-switcher.d.ts +1 -11
  190. package/dist/elements/model-switcher.js +1 -0
  191. package/dist/elements/nav.d.ts +1 -0
  192. package/dist/elements/nav.js +1 -0
  193. package/dist/elements/notice.d.ts +1 -0
  194. package/dist/elements/notice.js +1 -0
  195. package/dist/elements/pane-group.d.ts +1 -0
  196. package/dist/elements/pane-group.js +1 -0
  197. package/dist/elements/pane.d.ts +1 -0
  198. package/dist/elements/pane.js +1 -0
  199. package/dist/elements/popover.js +1 -0
  200. package/dist/elements/progress-bar.d.ts +1 -0
  201. package/dist/elements/progress-bar.js +1 -0
  202. package/dist/elements/prompt-dock.d.ts +1 -0
  203. package/dist/elements/prompt-dock.js +1 -0
  204. package/dist/elements/prompt-input.d.ts +1 -9
  205. package/dist/elements/prompt-input.js +1 -0
  206. package/dist/elements/prompt-suggestions.d.ts +0 -6
  207. package/dist/elements/prompt-suggestions.js +1 -0
  208. package/dist/elements/reasoning.js +1 -0
  209. package/dist/elements/register.d.ts +10 -0
  210. package/dist/elements/remote.js +1 -0
  211. package/dist/elements/resizable.d.ts +1 -17
  212. package/dist/elements/resizable.js +1 -0
  213. package/dist/elements/response-stream.js +14 -0
  214. package/dist/elements/screen.d.ts +1 -0
  215. package/dist/elements/screen.js +1 -0
  216. package/dist/elements/scroll-area.d.ts +1 -0
  217. package/dist/elements/scroll-area.js +1 -0
  218. package/dist/elements/scroll-button.js +1 -0
  219. package/dist/elements/search.d.ts +1 -0
  220. package/dist/elements/search.js +1 -0
  221. package/dist/elements/segmented.d.ts +1 -0
  222. package/dist/elements/segmented.js +1 -0
  223. package/dist/elements/separator.d.ts +1 -0
  224. package/dist/elements/separator.js +1 -0
  225. package/dist/elements/setting-item.d.ts +1 -0
  226. package/dist/elements/setting-item.js +1 -0
  227. package/dist/elements/settings-group.d.ts +1 -0
  228. package/dist/elements/settings-group.js +1 -0
  229. package/dist/elements/skeleton.d.ts +1 -0
  230. package/dist/elements/skeleton.js +1 -0
  231. package/dist/elements/slots.d.ts +176 -0
  232. package/dist/elements/source.d.ts +0 -17
  233. package/dist/elements/source.js +1 -0
  234. package/dist/elements/status.d.ts +1 -0
  235. package/dist/elements/status.js +1 -0
  236. package/dist/elements/switch.js +1 -0
  237. package/dist/elements/tabs.d.ts +1 -0
  238. package/dist/elements/tabs.js +1 -0
  239. package/dist/elements/tasks.js +1 -0
  240. package/dist/elements/text-shimmer.js +1 -0
  241. package/dist/elements/thinking-bar.js +1 -0
  242. package/dist/elements/toast.d.ts +1 -0
  243. package/dist/elements/toast.js +1 -0
  244. package/dist/elements/tool.js +1 -0
  245. package/dist/elements/tooltip.d.ts +1 -0
  246. package/dist/elements/tooltip.js +1 -0
  247. package/dist/elements/voice-input.js +1 -0
  248. package/dist/elements/voice-output.d.ts +1 -0
  249. package/dist/elements/voice-output.js +1 -0
  250. package/dist/elements.d.ts +772 -61
  251. package/dist/engine-javascript-C1x7zo1_.js +141 -0
  252. package/dist/index.d.ts +15 -2
  253. package/dist/index.js +14405 -9810
  254. package/dist/kai.es.js +1 -0
  255. package/dist/llms/llms-full.txt +1234 -107
  256. package/dist/llms/llms.txt +4 -4
  257. package/dist/mcp.es.js +217 -6
  258. package/dist/primitives/card-contract.d.ts +20 -4
  259. package/dist/primitives/card-recovery.d.ts +55 -0
  260. package/dist/primitives/composer-model.d.ts +22 -0
  261. package/dist/primitives/composer-triggers.d.ts +19 -0
  262. package/dist/primitives/create-kai-chat.d.ts +38 -0
  263. package/dist/primitives/message-feedback.d.ts +35 -0
  264. package/dist/primitives/toast-store.d.ts +118 -0
  265. package/dist/primitives/use-speech-recognition.d.ts +26 -0
  266. package/dist/react/index.d.ts +1166 -98
  267. package/dist/react/runtime.d.ts +9 -2
  268. package/dist/react/use-kai-chat.d.ts +41 -0
  269. package/dist/react.js +575 -186
  270. package/dist/register-impl-CYWGkgbI.js +141 -0
  271. package/dist/schemas/choice.schema.json +5 -0
  272. package/dist/schemas/tasks.schema.json +7 -2
  273. package/dist/state/index.d.ts +4 -0
  274. package/dist/state/messages.d.ts +11 -0
  275. package/dist/state/stream.d.ts +22 -0
  276. package/dist/state/suggestions.d.ts +4 -0
  277. package/dist/state.js +117 -0
  278. package/dist/theme.tokens.css +87 -0
  279. package/dist/types.d.ts +4 -0
  280. package/dist/ui/agent-card.d.ts +61 -0
  281. package/dist/ui/button.d.ts +5 -2
  282. package/dist/ui/card.d.ts +61 -0
  283. package/dist/ui/collapsible.d.ts +15 -1
  284. package/dist/ui/command.d.ts +33 -0
  285. package/dist/ui/dialog.d.ts +42 -0
  286. package/dist/ui/dropdown.d.ts +95 -1
  287. package/dist/ui/editable-label.d.ts +26 -0
  288. package/dist/ui/hover-card.d.ts +13 -1
  289. package/dist/ui/icon.d.ts +18 -0
  290. package/dist/ui/input.d.ts +33 -0
  291. package/dist/ui/kbd.d.ts +21 -0
  292. package/dist/ui/nav.d.ts +62 -0
  293. package/dist/ui/notice.d.ts +31 -0
  294. package/dist/ui/overlay.d.ts +12 -0
  295. package/dist/ui/pane-grid.d.ts +58 -0
  296. package/dist/ui/pane-group.d.ts +75 -0
  297. package/dist/ui/pane.d.ts +78 -0
  298. package/dist/ui/popover.d.ts +11 -1
  299. package/dist/ui/progress-bar.d.ts +23 -0
  300. package/dist/ui/prompt-dock.d.ts +96 -0
  301. package/dist/ui/resizable.d.ts +12 -0
  302. package/dist/ui/scroll-area.d.ts +4 -0
  303. package/dist/ui/segmented.d.ts +28 -0
  304. package/dist/ui/settings-group.d.ts +32 -0
  305. package/dist/ui/skeleton.d.ts +12 -0
  306. package/dist/ui/stat.d.ts +16 -0
  307. package/dist/ui/status.d.ts +13 -0
  308. package/dist/ui/switch.d.ts +8 -1
  309. package/dist/ui/tabs.d.ts +35 -0
  310. package/dist/ui/tooltip.d.ts +20 -1
  311. package/frameworks/react/index.tsx +1100 -163
  312. package/frameworks/react/runtime.tsx +30 -1
  313. package/frameworks/react/use-kai-chat.tsx +87 -0
  314. package/llms-full.txt +1234 -107
  315. package/llms.txt +4 -4
  316. package/package.json +36 -12
  317. package/src/agent-tooling/mcp/manifest.ts +14 -0
  318. package/src/agent-tooling/mcp/tools/debug.ts +194 -6
  319. package/src/agent-tooling/mcp/tools/reference.ts +32 -0
  320. package/src/agent-tooling/mcp/tools/scaffold.ts +162 -1
  321. package/src/components/artifact.tsx +98 -13
  322. package/src/components/attachment-types.ts +13 -0
  323. package/src/components/attachments.tsx +4 -14
  324. package/src/components/card.tsx +109 -15
  325. package/src/components/chain-of-thought.tsx +210 -1
  326. package/src/components/chat-scope-picker.tsx +13 -3
  327. package/src/components/chat-thread.tsx +238 -137
  328. package/src/components/choice-card.tsx +136 -15
  329. package/src/components/coachmark.tsx +245 -0
  330. package/src/components/component-meta.json +921 -108
  331. package/src/components/composer-dom.ts +159 -0
  332. package/src/components/composer-highlight.ts +242 -0
  333. package/src/components/composer-history.ts +82 -0
  334. package/src/components/composer.tsx +979 -0
  335. package/src/components/confirm-card.tsx +69 -2
  336. package/src/components/conversation-item.tsx +69 -7
  337. package/src/components/conversation-list.tsx +134 -37
  338. package/src/components/dismissed-stub.tsx +80 -0
  339. package/src/components/file-tree.tsx +197 -47
  340. package/src/components/form-widgets.tsx +9 -7
  341. package/src/components/form.tsx +161 -20
  342. package/src/components/image.tsx +3 -5
  343. package/src/components/link-preview.tsx +5 -4
  344. package/src/components/markdown.tsx +5 -2
  345. package/src/components/message.tsx +174 -14
  346. package/src/components/model-switcher.tsx +19 -4
  347. package/src/components/prompt-input.tsx +46 -75
  348. package/src/components/prompt-suggestion.tsx +48 -16
  349. package/src/components/reasoning.tsx +29 -6
  350. package/src/components/response-compare-types.ts +127 -0
  351. package/src/components/response-compare.tsx +460 -0
  352. package/src/components/screen.tsx +176 -0
  353. package/src/components/tasks-card.tsx +374 -23
  354. package/src/components/toast.tsx +486 -0
  355. package/src/components/tool-types.ts +10 -0
  356. package/src/components/tool.tsx +22 -16
  357. package/src/components/use-card-resolution.ts +24 -8
  358. package/src/components/voice-input.tsx +102 -19
  359. package/src/components/voice-output.tsx +202 -0
  360. package/src/elements/agent-card.tsx +67 -0
  361. package/src/elements/artifact.tsx +53 -4
  362. package/src/elements/attachments.tsx +1 -0
  363. package/src/elements/autoloader.ts +89 -0
  364. package/src/elements/avatar.tsx +33 -0
  365. package/src/elements/badge.tsx +32 -0
  366. package/src/elements/button.tsx +126 -0
  367. package/src/elements/card-media.jpg +0 -0
  368. package/src/elements/card.tsx +124 -31
  369. package/src/elements/cards.tsx +81 -7
  370. package/src/elements/chain-of-thought.tsx +77 -32
  371. package/src/elements/chat-scope-picker.tsx +35 -8
  372. package/src/elements/chat-types.ts +9 -2
  373. package/src/elements/chat-workspace.tsx +157 -43
  374. package/src/elements/chat.tsx +49 -25
  375. package/src/elements/choice.tsx +71 -19
  376. package/src/elements/coachmark.tsx +90 -0
  377. package/src/elements/command.tsx +209 -0
  378. package/src/elements/compare.tsx +110 -0
  379. package/src/elements/compiled.css +1 -1
  380. package/src/elements/composer.tsx +151 -0
  381. package/src/elements/confirm-card.tsx +36 -11
  382. package/src/elements/conversation-list.tsx +80 -11
  383. package/src/elements/default-input.tsx +75 -53
  384. package/src/elements/define.tsx +47 -6
  385. package/src/elements/dialog.tsx +98 -0
  386. package/src/elements/disclosure.ts +73 -0
  387. package/src/elements/editable-label.tsx +125 -0
  388. package/src/elements/element-manifest.json +310 -0
  389. package/src/elements/element-meta.json +5954 -1605
  390. package/src/elements/element-types.d.ts +771 -60
  391. package/src/elements/file-tree.tsx +12 -2
  392. package/src/elements/file-upload.tsx +1 -1
  393. package/src/elements/form.tsx +54 -11
  394. package/src/elements/hover-card.tsx +80 -0
  395. package/src/elements/icon-names.json +48 -0
  396. package/src/elements/icon.tsx +41 -0
  397. package/src/elements/input.tsx +203 -0
  398. package/src/elements/kbd.tsx +45 -0
  399. package/src/elements/menu.tsx +217 -0
  400. package/src/elements/message.tsx +84 -55
  401. package/src/elements/model-switcher.tsx +27 -1
  402. package/src/elements/nav.tsx +87 -0
  403. package/src/elements/notice.tsx +53 -0
  404. package/src/elements/pane-group.tsx +119 -0
  405. package/src/elements/pane.tsx +129 -0
  406. package/src/elements/popover.tsx +39 -17
  407. package/src/elements/progress-bar.tsx +39 -0
  408. package/src/elements/prompt-dock.tsx +81 -0
  409. package/src/elements/prompt-input.tsx +112 -63
  410. package/src/elements/prompt-suggestions.tsx +25 -9
  411. package/src/elements/reasoning.tsx +30 -9
  412. package/src/elements/register-impl.ts +41 -3
  413. package/src/elements/register.ts +20 -3
  414. package/src/elements/remote.tsx +4 -0
  415. package/src/elements/resizable.tsx +48 -11
  416. package/src/elements/screen.tsx +90 -0
  417. package/src/elements/scroll-area.tsx +33 -0
  418. package/src/elements/search.tsx +180 -0
  419. package/src/elements/segmented.tsx +135 -0
  420. package/src/elements/separator.tsx +36 -0
  421. package/src/elements/setting-item.tsx +50 -0
  422. package/src/elements/settings-group.tsx +37 -0
  423. package/src/elements/skeleton.tsx +45 -0
  424. package/src/elements/slots.ts +607 -0
  425. package/src/elements/source.tsx +13 -6
  426. package/src/elements/status.tsx +42 -0
  427. package/src/elements/styles.css +19 -0
  428. package/src/elements/switch.tsx +89 -14
  429. package/src/elements/tabs.tsx +95 -0
  430. package/src/elements/tasks.tsx +70 -14
  431. package/src/elements/toast.tsx +89 -0
  432. package/src/elements/tool.tsx +43 -9
  433. package/src/elements/tooltip.tsx +85 -0
  434. package/src/elements/video-poster.jpg +0 -0
  435. package/src/elements/voice-input.tsx +66 -16
  436. package/src/elements/voice-output.tsx +69 -0
  437. package/src/index.ts +35 -2
  438. package/src/primitives/card-contract.ts +14 -5
  439. package/src/primitives/card-recovery.ts +149 -0
  440. package/src/primitives/card-routing.ts +3 -0
  441. package/src/primitives/card-schemas/choice.schema.json +5 -0
  442. package/src/primitives/card-schemas/tasks.schema.json +7 -2
  443. package/src/primitives/composer-model.ts +49 -0
  444. package/src/primitives/composer-triggers.ts +35 -0
  445. package/src/primitives/create-kai-chat.ts +66 -0
  446. package/src/primitives/message-feedback.ts +123 -0
  447. package/src/primitives/toast-store.ts +276 -0
  448. package/src/primitives/use-speech-recognition.ts +135 -0
  449. package/src/state/index.ts +6 -0
  450. package/src/state/messages.ts +36 -0
  451. package/src/state/stream.ts +92 -0
  452. package/src/state/suggestions.ts +9 -0
  453. package/src/types.ts +4 -0
  454. package/src/ui/agent-card.tsx +212 -0
  455. package/src/ui/button.tsx +16 -5
  456. package/src/ui/card.tsx +245 -0
  457. package/src/ui/collapsible.tsx +30 -4
  458. package/src/ui/command.tsx +99 -0
  459. package/src/ui/dialog.tsx +217 -0
  460. package/src/ui/dropdown.tsx +375 -13
  461. package/src/ui/editable-label.tsx +112 -0
  462. package/src/ui/hover-card.tsx +28 -5
  463. package/src/ui/icon.tsx +110 -0
  464. package/src/ui/input.tsx +132 -0
  465. package/src/ui/kbd.tsx +118 -0
  466. package/src/ui/nav.tsx +282 -0
  467. package/src/ui/notice.tsx +84 -0
  468. package/src/ui/overlay.tsx +108 -2
  469. package/src/ui/pane-grid.tsx +116 -0
  470. package/src/ui/pane-group.tsx +280 -0
  471. package/src/ui/pane.tsx +193 -0
  472. package/src/ui/popover.tsx +16 -2
  473. package/src/ui/progress-bar.tsx +83 -0
  474. package/src/ui/prompt-dock.tsx +145 -0
  475. package/src/ui/resizable.tsx +69 -1
  476. package/src/ui/scroll-area.tsx +14 -3
  477. package/src/ui/segmented.tsx +109 -0
  478. package/src/ui/settings-group.tsx +67 -0
  479. package/src/ui/skeleton.tsx +62 -5
  480. package/src/ui/stat.tsx +41 -0
  481. package/src/ui/status.tsx +48 -0
  482. package/src/ui/switch.tsx +49 -23
  483. package/src/ui/tabs.tsx +168 -0
  484. package/src/ui/tooltip.tsx +57 -7
  485. package/theme.css +113 -0
  486. package/dist/components/slash-command.d.ts +0 -15
  487. package/dist/kitn-chat.es.js +0 -1
  488. package/dist/register-impl-wCsFPoh9.js +0 -77
  489. package/dist/register-ujhr1nBS.js +0 -1
  490. package/src/components/slash-command.tsx +0 -232
  491. package/src/elements/framework-usage.json +0 -563
  492. /package/dist/{kitn-chat-provider.d.ts → kai-provider.d.ts} +0 -0
  493. /package/dist/{kitn-chat-provider.es.js → kai-provider.es.js} +0 -0
@@ -10,6 +10,7 @@ import {
10
10
  Show,
11
11
  } from 'solid-js';
12
12
  import { cn } from '../utils/cn';
13
+ import { Skeleton } from '../ui/skeleton';
13
14
  import { Link as LinkIcon } from 'lucide-solid';
14
15
  import type { CardEvent } from '../primitives/card-contract';
15
16
  import {
@@ -153,11 +154,11 @@ export function LinkPreview(props: LinkPreviewProps): JSX.Element {
153
154
  >
154
155
  <Show when={loading()}>
155
156
  {/* Skeleton while the bare-URL fetcher resolves. */}
156
- <div class="aspect-[16/9] w-full animate-pulse bg-muted" />
157
+ <Skeleton class="aspect-[16/9] w-full rounded-none" />
157
158
  <div class="space-y-2 p-3">
158
- <div class="h-3 w-1/3 animate-pulse rounded bg-muted" />
159
- <div class="h-4 w-2/3 animate-pulse rounded bg-muted" />
160
- <div class="h-3 w-full animate-pulse rounded bg-muted" />
159
+ <Skeleton class="h-3 w-1/3" />
160
+ <Skeleton class="h-4 w-2/3" />
161
+ <Skeleton class="h-3 w-full" />
161
162
  </div>
162
163
  </Show>
163
164
  <Show when={!loading()}>
@@ -11,6 +11,9 @@ export interface MarkdownProps {
11
11
  id?: string;
12
12
  class?: string;
13
13
  codeTheme?: string;
14
+ /** Optional `::part` name(s) to expose on the rendered root. Lets callers
15
+ * (e.g. the message bubble) surface a styleable part through the shadow. */
16
+ part?: string;
14
17
  }
15
18
 
16
19
  interface ParsedBlock {
@@ -49,13 +52,13 @@ function MarkdownBlock(props: { content: string }) {
49
52
  }
50
53
 
51
54
  function Markdown(props: MarkdownProps) {
52
- const [local] = splitProps(props, ['content', 'id', 'class', 'codeTheme']);
55
+ const [local] = splitProps(props, ['content', 'id', 'class', 'codeTheme', 'part']);
53
56
  const config = useChatConfig();
54
57
  const blockId = () => local.id ?? createUniqueId();
55
58
  const blocks = createMemo(() => parseMarkdownIntoBlocks(local.content));
56
59
 
57
60
  return (
58
- <div class={cn('chat-markdown max-w-none break-words whitespace-normal [&>div:first-child>p:first-child]:mt-0 [&>div:last-child>p:last-child]:mb-0', textClass(config.proseSize()), local.class)}>
61
+ <div part={local.part} class={cn('chat-markdown max-w-none break-words whitespace-normal [&>div:first-child>p:first-child]:mt-0 [&>div:last-child>p:last-child]:mb-0', textClass(config.proseSize()), local.class)}>
59
62
  <For each={blocks()}>
60
63
  {(block) => (
61
64
  <Switch>
@@ -5,8 +5,11 @@ import { cn } from "../utils/cn";
5
5
  import { Markdown } from "./markdown";
6
6
  import { Button } from "../ui/button";
7
7
  import { actionIcon, BUILTIN_ACTION_LABEL } from "../ui/action-icons";
8
- import type { ChatMessageAction, CustomAction } from "../elements/chat-types";
8
+ import type { ChatMessageAction, CustomAction, FeedbackVote } from "../elements/chat-types";
9
9
  import { useChatConfig, textClass } from "../primitives/chat-config";
10
+ import { Reasoning, ReasoningTrigger, ReasoningContent } from "./reasoning";
11
+ import { Tool, type ToolPart } from "./tool";
12
+ import { Attachments, Attachment, AttachmentPreview, AttachmentInfo, type AttachmentData } from "./attachments";
10
13
 
11
14
  // --- Message ---
12
15
 
@@ -17,7 +20,7 @@ export interface MessageProps extends JSX.HTMLAttributes<HTMLDivElement> {
17
20
  function Message(props: MessageProps) {
18
21
  const [local, rest] = splitProps(props, ["children", "class"]);
19
22
  return (
20
- <div class={cn("flex items-start gap-3", local.class)} {...rest}>
23
+ <div part="row" class={cn("flex items-start gap-3", local.class)} {...rest}>
21
24
  {local.children}
22
25
  </div>
23
26
  );
@@ -35,6 +38,7 @@ export interface MessageAvatarProps {
35
38
  function MessageAvatar(props: MessageAvatarProps) {
36
39
  return (
37
40
  <div
41
+ part="avatar"
38
42
  class={cn("h-8 w-8 shrink-0 overflow-hidden rounded-full", props.class)}
39
43
  >
40
44
  <Show
@@ -62,10 +66,14 @@ function MessageAvatar(props: MessageAvatarProps) {
62
66
  export interface MessageContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
63
67
  children: JSX.Element | string;
64
68
  markdown?: boolean;
69
+ /** `::part` name(s) exposed on the content node. The body passes
70
+ * `"bubble content"` so consumers can target either the rounded bubble or the
71
+ * text region from outside the shadow boundary. */
72
+ part?: string;
65
73
  }
66
74
 
67
75
  function MessageContent(props: MessageContentProps) {
68
- const [local, rest] = splitProps(props, ["children", "markdown", "class"]);
76
+ const [local, rest] = splitProps(props, ["children", "markdown", "class", "part"]);
69
77
  const config = useChatConfig();
70
78
  const classNames = () =>
71
79
  cn(
@@ -78,12 +86,12 @@ function MessageContent(props: MessageContentProps) {
78
86
  <Show
79
87
  when={local.markdown}
80
88
  fallback={
81
- <div class={classNames()} {...rest}>
89
+ <div part={local.part} class={classNames()} {...rest}>
82
90
  {local.children}
83
91
  </div>
84
92
  }
85
93
  >
86
- <Markdown content={local.children as string} class={classNames()} />
94
+ <Markdown part={local.part} content={local.children as string} class={classNames()} />
87
95
  </Show>
88
96
  );
89
97
  }
@@ -119,6 +127,13 @@ export interface MessageActionBarProps {
119
127
  reveal?: 'always' | 'hover';
120
128
  /** Fired with the built-in name or the custom action id when a button is clicked. */
121
129
  onAction: (id: string) => void;
130
+ /** The active feedback vote. When `'like'`/`'dislike'` is set, that button is
131
+ * marked pressed (filled) and the OTHER vote button animates out. `undefined`
132
+ * shows both. Pure/prop-driven so the bar survives re-renders. */
133
+ activeFeedback?: FeedbackVote;
134
+ /** When true, the `copy` button shows its emerald check icon instead of the
135
+ * copy glyph (cleared by the owner after ~2s). */
136
+ copied?: boolean;
122
137
  class?: string;
123
138
  }
124
139
 
@@ -130,16 +145,30 @@ function normalizeAction(a: ChatMessageAction | CustomAction) {
130
145
  return { id: a.id, label: a.label, Icon: actionIcon(a.icon) };
131
146
  }
132
147
 
148
+ /** Is this entry one of the two feedback votes? */
149
+ function feedbackVoteOf(a: ChatMessageAction | CustomAction): FeedbackVote | undefined {
150
+ return a === 'like' || a === 'dislike' ? a : undefined;
151
+ }
152
+
133
153
  /**
134
154
  * The shared message action toolbar. Renders one ghost icon button per entry —
135
155
  * built-in names pull their label+icon from the curated registry; custom
136
156
  * descriptors use their `label` plus `actionIcon(icon)` (label-only when the
137
157
  * icon is unknown or absent). `reveal="hover"` makes the bar fade in on the
138
158
  * parent `.group`'s hover.
159
+ *
160
+ * Pure/prop-driven: feedback (`activeFeedback`) and copy (`copied`) state are
161
+ * owned by the parent facade and passed in — the bar holds no internal signals,
162
+ * so it survives the new-array-per-chunk re-renders of a streaming thread. With
163
+ * a vote active, the chosen `like`/`dislike` button is marked `aria-pressed` +
164
+ * filled and the other vote button collapses its width (sliding the active thumb
165
+ * into its place) via a 0fr↔1fr grid transition. The
166
+ * `copy` button swaps to an emerald check while `copied`.
139
167
  */
140
168
  function MessageActionBar(props: MessageActionBarProps) {
141
169
  return (
142
170
  <MessageActions
171
+ part="actions"
143
172
  class={cn(
144
173
  'mt-1 flex gap-0',
145
174
  props.reveal === 'hover' && 'opacity-0 transition-opacity group-hover:opacity-100',
@@ -150,38 +179,169 @@ function MessageActionBar(props: MessageActionBarProps) {
150
179
  {(a) => {
151
180
  const item = normalizeAction(a);
152
181
  const tooltipText = () => (typeof a !== 'string' && a.tooltip) ? a.tooltip : item.label;
182
+ const vote = feedbackVoteOf(a);
183
+ // A vote button is the active one when it matches the resolved vote.
184
+ const isActiveVote = () => vote !== undefined && props.activeFeedback === vote;
185
+ // The COPY button reflects the copied check.
186
+ const isCopy = item.id === 'copy';
187
+ const showCheck = () => isCopy && props.copied === true;
188
+
153
189
  // Factory (not a shared node): each Show branch gets its own Button so
154
190
  // the eager DOM node is never referenced from two places.
155
191
  const button = () => (
156
192
  <Button
157
193
  variant="ghost"
158
194
  size="icon-sm"
159
- class="rounded-full"
195
+ class={cn('rounded-full', isActiveVote() && 'text-primary')}
160
196
  data-action={item.id}
161
- aria-label={item.label}
197
+ aria-label={showCheck() ? 'Copied' : item.label}
198
+ aria-pressed={vote !== undefined ? isActiveVote() : undefined}
162
199
  onClick={() => props.onAction(item.id)}
163
200
  >
164
- <Show when={item.Icon} fallback={<span class="px-1 text-xs">{item.label}</span>}>
165
- {(Icon) => {
166
- const I = Icon();
167
- return <I class="size-3.5" />;
168
- }}
201
+ <Show
202
+ when={showCheck()}
203
+ fallback={
204
+ <Show when={item.Icon} fallback={<span class="px-1 text-xs">{item.label}</span>}>
205
+ {(Icon) => {
206
+ const I = Icon();
207
+ return <I class={cn('size-3.5', isActiveVote() && 'fill-current')} />;
208
+ }}
209
+ </Show>
210
+ }
211
+ >
212
+ <Check class="size-3.5 text-emerald-400" />
169
213
  </Show>
170
214
  </Button>
171
215
  );
172
216
  // Icon-only buttons get a tooltip; label-only buttons (text already
173
217
  // visible) don't.
174
- return (
218
+ const rendered = () => (
175
219
  <Show when={item.Icon} fallback={button()}>
176
220
  <Tooltip content={tooltipText()}>{button()}</Tooltip>
177
221
  </Show>
178
222
  );
223
+
224
+ // When the OTHER vote is active, this vote button collapses — its WIDTH
225
+ // animates to zero via a 0fr↔1fr grid so the remaining thumb slides into
226
+ // its place, while it fades out; it slides + fades back on un-vote. (Kept
227
+ // mounted-but-collapsed, not unmounted, so the sibling can slide.) No
228
+ // vote active → both shown; non-vote entries always render.
229
+ return (
230
+ <Show when={vote !== undefined} fallback={rendered()}>
231
+ {(() => {
232
+ const show = () => props.activeFeedback === undefined || props.activeFeedback === vote;
233
+ return (
234
+ <span
235
+ data-feedback-collapsed={show() ? undefined : ''}
236
+ class={cn(
237
+ 'grid transition-[grid-template-columns,opacity] duration-300 ease-out',
238
+ show() ? 'grid-cols-[1fr] opacity-100' : 'grid-cols-[0fr] opacity-0',
239
+ )}
240
+ >
241
+ <span class="min-w-0 overflow-hidden">{rendered()}</span>
242
+ </span>
243
+ );
244
+ })()}
245
+ </Show>
246
+ );
179
247
  }}
180
248
  </For>
181
249
  </MessageActions>
182
250
  );
183
251
  }
184
252
 
253
+ // --- MessageBody ---
254
+
255
+ export interface MessageBodyProps {
256
+ /** The message text/markdown. */
257
+ content: string;
258
+ /** Optional collapsible reasoning block, rendered above the content. */
259
+ reasoning?: { text: string; label?: string };
260
+ /** Tool-call parts, rendered above the content. */
261
+ tools?: ToolPart[];
262
+ /** Inline attachment previews, rendered above the content. */
263
+ attachments?: AttachmentData[];
264
+ /** Whether this is a user message (right-aligned bubble) vs an assistant
265
+ * message (full-width transparent). */
266
+ isUser: boolean;
267
+ /** Whether the content renders as markdown. */
268
+ markdown: boolean;
269
+ /** Action-bar entries — built-in names and/or custom descriptors. When empty
270
+ * the bar is not rendered. */
271
+ actions?: (ChatMessageAction | CustomAction)[];
272
+ /** `'always'` (default) keeps the bar visible; `'hover'` reveals it on parent
273
+ * `.group` hover. */
274
+ actionsReveal?: 'always' | 'hover';
275
+ /** Fired with the built-in name or custom id when an action is clicked. */
276
+ onAction?: (id: string) => void;
277
+ /** The currently-active feedback vote, so the bar can mark like/dislike and
278
+ * hide the other. */
279
+ activeFeedback?: FeedbackVote;
280
+ /** When true, the copy button shows its "copied" check icon. */
281
+ copied?: boolean;
282
+ /** INJECT slot projected at the TOP of the body — above the reasoning / tools /
283
+ * content. A per-message header: a model-name label, a role + timestamp line.
284
+ * In the `<kai-message>` shadow this is `<slot name="before-body" />`. */
285
+ beforeBody?: JSX.Element;
286
+ /** INJECT slot projected at the BOTTOM of the body — below the action bar. A
287
+ * citation / sources row, a token-cost / latency line. In the `<kai-message>`
288
+ * shadow this is `<slot name="after-body" />`. */
289
+ afterBody?: JSX.Element;
290
+ }
291
+
292
+ /**
293
+ * The shared message body: an optional reasoning block, tool calls, inline
294
+ * attachments, the content bubble, and the action bar — in that order. This is
295
+ * the single source of truth for how a message renders, consumed by `ChatThread`
296
+ * (the `<For>` over `messages`), the standalone `<kai-message>` facade, and (in
297
+ * future) `kai-compare` for each candidate. Pure/prop-driven: all interaction
298
+ * state (copied, feedback vote) is owned above and passed in.
299
+ */
300
+ function MessageBody(props: MessageBodyProps) {
301
+ return (
302
+ <>
303
+ {/* before-body (inject): a per-message header above everything else. */}
304
+ <Show when={props.beforeBody}>{props.beforeBody}</Show>
305
+ <Show when={props.reasoning}>
306
+ {(r) => (
307
+ <Reasoning class="mb-2 w-full">
308
+ <ReasoningTrigger>{r().label ?? 'Reasoning'}</ReasoningTrigger>
309
+ <ReasoningContent markdown>{r().text}</ReasoningContent>
310
+ </Reasoning>
311
+ )}
312
+ </Show>
313
+ <For each={props.tools ?? []}>{(tp) => <Tool toolPart={tp} class="mb-2 w-full" />}</For>
314
+ <Show when={props.attachments?.length}>
315
+ <Attachments variant="inline" class={props.isUser ? 'mb-2 justify-end' : 'mb-2'}>
316
+ <For each={props.attachments!}>
317
+ {(att) => (<Attachment data={att}><AttachmentPreview /><AttachmentInfo /></Attachment>)}
318
+ </For>
319
+ </Attachments>
320
+ </Show>
321
+ <MessageContent
322
+ part="bubble content"
323
+ markdown={props.markdown}
324
+ class={props.isUser
325
+ ? 'bg-muted text-primary max-w-[85%] rounded-2xl px-4 py-2'
326
+ : 'bg-transparent p-0'}
327
+ >
328
+ {props.content}
329
+ </MessageContent>
330
+ <Show when={(props.actions?.length ?? 0) > 0}>
331
+ <MessageActionBar
332
+ actions={props.actions!}
333
+ reveal={props.actionsReveal === 'hover' ? 'hover' : 'always'}
334
+ activeFeedback={props.activeFeedback}
335
+ copied={props.copied}
336
+ onAction={(id) => props.onAction?.(id)}
337
+ />
338
+ </Show>
339
+ {/* after-body (inject): a citation row / cost line below the action bar. */}
340
+ <Show when={props.afterBody}>{props.afterBody}</Show>
341
+ </>
342
+ );
343
+ }
344
+
185
345
  // --- MessageAction ---
186
346
 
187
347
  export interface MessageActionProps {
@@ -224,4 +384,4 @@ function MessageCopyButton(props: MessageCopyButtonProps) {
224
384
  );
225
385
  }
226
386
 
227
- export { Message, MessageAvatar, MessageContent, MessageActions, MessageActionBar, MessageAction, MessageCopyButton };
387
+ export { Message, MessageAvatar, MessageContent, MessageActions, MessageActionBar, MessageBody, MessageAction, MessageCopyButton };
@@ -1,10 +1,21 @@
1
1
  import { splitProps, For, Show, createSignal } from 'solid-js';
2
2
  import { cn } from '../utils/cn';
3
- import { Dropdown, DropdownTrigger, DropdownContent, DropdownItem } from '../ui/dropdown';
3
+ import { Dropdown, DropdownTrigger, DropdownContent, DropdownItem, type DropdownController } from '../ui/dropdown';
4
4
  import { Button } from '../ui/button';
5
5
  import type { ModelOption } from '../types';
6
6
 
7
- export interface ModelSwitcherProps { models: ModelOption[]; currentModelId: string; onModelChange: (modelId: string) => void; class?: string; }
7
+ export interface ModelSwitcherProps {
8
+ models: ModelOption[];
9
+ currentModelId: string;
10
+ onModelChange: (modelId: string) => void;
11
+ class?: string;
12
+ /** Initial open state of the dropdown (uncontrolled seed). */
13
+ defaultOpen?: boolean;
14
+ /** Disable the trigger — click/keyboard no longer open the dropdown. */
15
+ disabled?: boolean;
16
+ /** Receive the dropdown's open controller (forwarded from the inner Dropdown). */
17
+ controllerRef?: (api: DropdownController) => void;
18
+ }
8
19
 
9
20
  const Chevron = (props: { class?: string }) => (
10
21
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class={props.class}>
@@ -25,7 +36,7 @@ function ModelRow(props: { model: ModelOption; currentModelId: string; onModelCh
25
36
  }
26
37
 
27
38
  export function ModelSwitcher(props: ModelSwitcherProps) {
28
- const [local] = splitProps(props, ['models', 'currentModelId', 'onModelChange', 'class']);
39
+ const [local] = splitProps(props, ['models', 'currentModelId', 'onModelChange', 'class', 'defaultOpen', 'disabled', 'controllerRef']);
29
40
  const currentModel = () => local.models.find((m) => m.id === local.currentModelId);
30
41
 
31
42
  // Ungrouped models list first; grouped models collect under their group name,
@@ -44,7 +55,11 @@ export function ModelSwitcher(props: ModelSwitcherProps) {
44
55
 
45
56
  return (
46
57
  <Show when={local.models.length > 1}>
47
- <Dropdown>
58
+ <Dropdown
59
+ defaultOpen={local.defaultOpen}
60
+ disabled={local.disabled}
61
+ controllerRef={local.controllerRef}
62
+ >
48
63
  <DropdownTrigger as={(triggerProps: any) => (
49
64
  <Button variant="ghost" size="sm" class={cn('gap-1 text-meta text-muted-foreground', local.class)} {...triggerProps}>
50
65
  {currentModel()?.name ?? local.currentModelId}
@@ -1,18 +1,21 @@
1
- import { type JSX, splitProps, createSignal, createContext, useContext, createEffect, on } from 'solid-js';
1
+ import { type JSX, splitProps, createSignal, createContext, useContext } from 'solid-js';
2
2
  import { cn } from '../utils/cn';
3
3
  import { useChatConfig, textClass } from '../primitives/chat-config';
4
+ import { Composer, type TriggerDef, type ComposerChange } from './composer';
5
+ import type { ComposerDoc } from '../primitives/composer-model';
4
6
 
5
7
  // --- Context ---
6
8
 
7
9
  interface PromptInputContextType {
8
10
  isLoading: boolean;
9
- value: () => string;
11
+ // A string (controlled text) or a ComposerDoc (a seed that pre-populates pills).
12
+ value: () => string | ComposerDoc;
10
13
  setValue: (value: string) => void;
11
14
  maxHeight: number | string;
12
15
  onSubmit?: () => void;
13
16
  disabled?: boolean;
14
- textareaRef: HTMLTextAreaElement | undefined;
15
- setTextareaRef: (el: HTMLTextAreaElement) => void;
17
+ textareaRef: HTMLElement | undefined;
18
+ setTextareaRef: (el: HTMLElement) => void;
16
19
  }
17
20
 
18
21
  const PromptInputContext = createContext<PromptInputContextType>();
@@ -27,7 +30,8 @@ function usePromptInput() {
27
30
 
28
31
  export interface PromptInputProps extends JSX.HTMLAttributes<HTMLDivElement> {
29
32
  isLoading?: boolean;
30
- value?: string;
33
+ /** String = controlled text; ComposerDoc = a seed that pre-populates pills. */
34
+ value?: string | ComposerDoc;
31
35
  onValueChange?: (value: string) => void;
32
36
  maxHeight?: number | string;
33
37
  onSubmit?: () => void;
@@ -41,8 +45,8 @@ function PromptInput(props: PromptInputProps) {
41
45
  'children', 'disabled', 'class', 'onClick',
42
46
  ]);
43
47
 
44
- const [internalValue, setInternalValue] = createSignal(local.value ?? '');
45
- let textareaRef: HTMLTextAreaElement | undefined;
48
+ const [internalValue, setInternalValue] = createSignal<string | ComposerDoc>(local.value ?? '');
49
+ let textareaRef: HTMLElement | undefined;
46
50
 
47
51
  const handleChange = (newValue: string) => {
48
52
  setInternalValue(newValue);
@@ -70,13 +74,14 @@ function PromptInput(props: PromptInputProps) {
70
74
  }}
71
75
  >
72
76
  <div
77
+ data-prompt-input
73
78
  onClick={handleClick}
74
79
  class={cn(
75
80
  // The inner textarea neutralizes its own ring (focus-visible:ring-0),
76
81
  // so the FRAME owns the focus affordance: a blue ring whenever a
77
82
  // control inside it is focused. Without this the composer had no
78
83
  // visible keyboard-focus state.
79
- 'bg-muted/40 cursor-text rounded-xl p-2 shadow-xs',
84
+ 'bg-surface cursor-text rounded-xl p-2 shadow-xs',
80
85
  'focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-0',
81
86
  local.disabled && 'cursor-not-allowed opacity-60',
82
87
  local.class
@@ -93,83 +98,49 @@ function PromptInput(props: PromptInputProps) {
93
98
 
94
99
  export interface PromptInputTextareaProps extends JSX.TextareaHTMLAttributes<HTMLTextAreaElement> {
95
100
  disableAutosize?: boolean;
101
+ /** Rich entity triggers (`/`, `@`) forwarded to the composer. */
102
+ triggers?: TriggerDef[];
103
+ /** Default icon per entity kind (kind → image src), forwarded to the composer. */
104
+ kindIcons?: Record<string, string>;
105
+ /** Structured change (doc + entities) from the composer, on every edit. */
106
+ onComposerChange?: (change: ComposerChange) => void;
96
107
  }
97
108
 
98
109
  function PromptInputTextarea(props: PromptInputTextareaProps) {
99
- const [local, rest] = splitProps(props, ['class', 'onKeyDown', 'disableAutosize']);
110
+ const [local] = splitProps(props, ['class', 'placeholder', 'aria-label', 'triggers', 'kindIcons', 'onComposerChange']);
100
111
  const ctx = usePromptInput();
101
112
  const config = useChatConfig();
102
113
 
103
- function adjustHeight(el: HTMLTextAreaElement | undefined) {
104
- if (!el || local.disableAutosize) return;
105
- el.style.height = 'auto';
106
- const maxH = ctx.maxHeight;
107
- if (typeof maxH === 'number') {
108
- el.style.height = `${Math.min(el.scrollHeight, maxH)}px`;
109
- } else {
110
- el.style.height = `min(${el.scrollHeight}px, ${maxH})`;
111
- }
112
- }
113
-
114
- function handleRef(el: HTMLTextAreaElement) {
115
- ctx.setTextareaRef(el);
116
- adjustHeight(el);
117
- }
118
-
119
- createEffect(on(
120
- () => [ctx.value(), ctx.maxHeight, local.disableAutosize],
121
- () => {
122
- if (ctx.textareaRef && !local.disableAutosize) {
123
- adjustHeight(ctx.textareaRef);
124
- }
125
- }
126
- ));
127
-
128
- function handleInput(e: InputEvent & { currentTarget: HTMLTextAreaElement }) {
129
- const el = e.currentTarget;
130
- let value = el.value;
131
- // Disallow leading whitespace — a prompt can't start with a space or blank
132
- // line. Strip it (covers typing a space at the start AND pasting) and keep
133
- // the caret in the right place.
134
- if (/^\s/.test(value)) {
135
- const stripped = value.replace(/^\s+/, '');
136
- const removed = value.length - stripped.length;
137
- const caret = Math.max(0, (el.selectionStart ?? 0) - removed);
138
- el.value = stripped;
139
- el.setSelectionRange(caret, caret);
140
- value = stripped;
141
- }
142
- adjustHeight(el);
143
- ctx.setValue(value);
144
- }
145
-
146
- function handleKeyDown(e: KeyboardEvent & { currentTarget: HTMLTextAreaElement }) {
147
- if (e.key === 'Enter' && !e.shiftKey) {
148
- e.preventDefault();
149
- // A disabled composer is non-interactive: Enter must not submit.
150
- if (!ctx.disabled) ctx.onSubmit?.();
151
- }
152
- if (typeof local.onKeyDown === 'function') {
153
- (local.onKeyDown as (e: KeyboardEvent & { currentTarget: HTMLTextAreaElement }) => void)(e);
154
- }
155
- }
114
+ // The editable mirrors the original <textarea>'s classes EXACTLY so swapping in
115
+ // the contenteditable composer is visually identical. The frame (PromptInput
116
+ // root) still owns radius/bg/padding/focus-ring; auto-grow is native to a
117
+ // contenteditable block (capped via max-height on the editable).
118
+ const editableClass = () =>
119
+ cn(
120
+ 'text-foreground min-h-[44px] w-full bg-transparent shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0 overflow-y-auto whitespace-pre-wrap break-words',
121
+ textClass(config.proseSize()),
122
+ local.class,
123
+ );
156
124
 
157
125
  return (
158
- <textarea
159
- ref={handleRef}
126
+ <Composer
127
+ bare
160
128
  value={ctx.value()}
161
- onInput={handleInput}
162
- onKeyDown={handleKeyDown}
163
- class={cn(
164
- // Typed text uses the normal foreground (light/dark), NOT the brand/accent
165
- // color — the composer should read as plain text, not tinted.
166
- 'text-foreground min-h-[44px] w-full resize-none border-none bg-transparent shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0',
167
- textClass(config.proseSize()),
168
- local.class
169
- )}
170
- rows={1}
129
+ placeholder={local.placeholder as string | undefined}
130
+ ariaLabel={local['aria-label'] as string | undefined}
171
131
  disabled={ctx.disabled}
172
- {...rest}
132
+ maxHeight={ctx.maxHeight}
133
+ editableClass={editableClass()}
134
+ editableRef={(el) => ctx.setTextareaRef(el)}
135
+ triggers={local.triggers}
136
+ kindIcons={local.kindIcons}
137
+ // Surface the structured change (doc/entities) BEFORE the string value, so a
138
+ // consumer that enriches its events has the latest doc when value-change fires.
139
+ // A prompt can't start with whitespace — strip leading whitespace from the
140
+ // string value (parity with the old textarea); the controlled round-trip
141
+ // re-renders the editable so the stripped value shows too.
142
+ onChange={(c) => { local.onComposerChange?.(c); ctx.setValue(c.text.replace(/^\s+/, '')); }}
143
+ onSubmit={() => { if (!ctx.disabled) ctx.onSubmit?.(); }}
173
144
  />
174
145
  );
175
146
  }