@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
@@ -7,11 +7,13 @@ import {
7
7
  createMemo,
8
8
  createEffect,
9
9
  on,
10
+ onMount,
10
11
  ErrorBoundary,
11
12
  } from 'solid-js';
12
13
  import { cn } from '../utils/cn';
13
14
  import { Button } from '../ui/button';
14
15
  import { Card } from './card';
16
+ import { DismissedStub } from './dismissed-stub';
15
17
  import type { CardEnvelope, CardEvent, CardHost, CardResolution } from '../primitives/card-contract';
16
18
  import { useCardResolution } from './use-card-resolution';
17
19
  import { emitCardEvent } from '../primitives/card-routing';
@@ -120,6 +122,21 @@ export function defaultActionId(actions: ConfirmAction[]): string | undefined {
120
122
  // The <ConfirmCard> component.
121
123
  // ─────────────────────────────────────────────────────────────────────────────
122
124
 
125
+ /** Imperative handle exposed via `controllerRef` — surfaces the confirm card's
126
+ * latent action/dismiss capabilities so the `<kai-confirm>` facade can forward
127
+ * them as instance methods (focus/confirm/dismiss/reopen). */
128
+ export interface ConfirmController {
129
+ /** Focus the default action button (or the first action if none default). */
130
+ focus(options?: FocusOptions): void;
131
+ /** Activate an action by id (or the default action when omitted) — emits the
132
+ * `action` verb + resolves single-shot. */
133
+ confirm(actionId?: string): void;
134
+ /** Dismiss the card — emits `dismiss` + optimistically collapses to the stub. */
135
+ dismiss(): void;
136
+ /** Re-open a dismissed card from its stub — emits `reopen`. */
137
+ reopen(): void;
138
+ }
139
+
123
140
  export interface ConfirmCardProps {
124
141
  /** The confirm definition (CardEnvelope.data). */
125
142
  data?: ConfirmCardData;
@@ -137,6 +154,9 @@ export interface ConfirmCardProps {
137
154
  class?: string;
138
155
  /** When set, render the chromed read-only view instead of the buttons. */
139
156
  resolution?: CardResolution;
157
+ /** Receive the imperative controller once mounted. The `<kai-confirm>` facade
158
+ * forwards these as element methods (focus/confirm/dismiss/reopen). */
159
+ controllerRef?: (controller: ConfirmController) => void;
140
160
  }
141
161
 
142
162
  /**
@@ -158,6 +178,7 @@ export function ConfirmCard(props: ConfirmCardProps): JSX.Element {
158
178
  'autofocus',
159
179
  'class',
160
180
  'resolution',
181
+ 'controllerRef',
161
182
  ]);
162
183
 
163
184
  const ctxHost = useCardHost();
@@ -208,9 +229,50 @@ export function ConfirmCard(props: ConfirmCardProps): JSX.Element {
208
229
  });
209
230
  };
210
231
 
211
- const onDismiss = (): void => emit({ kind: 'dismiss', cardId: local.cardId });
232
+ // Dismiss: emit the contract `dismiss` AND optimistically flip to a `dismissed`
233
+ // resolution so the card collapses to its re-openable stub immediately (the host
234
+ // confirms/overrides via the resolution prop on the next render).
235
+ const onDismiss = (): void => {
236
+ if (res.isResolved()) return;
237
+ emit({ kind: 'dismiss', cardId: local.cardId });
238
+ res.setLocal({ kind: 'dismissed' });
239
+ };
240
+ const onReopen = (): void => emit({ kind: 'reopen', cardId: local.cardId });
212
241
 
213
242
  let bodyRef: HTMLDivElement | undefined;
243
+ let actionsRef: HTMLDivElement | undefined;
244
+
245
+ // The default action button (or the first action button if none is marked
246
+ // default) — the same target the `autofocus` prop focuses on mount.
247
+ const focusTarget = (): HTMLButtonElement | null => {
248
+ if (!actionsRef) return null;
249
+ return (
250
+ actionsRef.querySelector<HTMLButtonElement>('button[data-kai-default="true"]') ??
251
+ actionsRef.querySelector<HTMLButtonElement>('button[data-action-id]')
252
+ );
253
+ };
254
+
255
+ // ── Imperative controller (Pattern C): hand the facade a handle over the
256
+ // card's latent action/dismiss capabilities. Every method drives the SAME
257
+ // internal path the buttons drive, so the same kai-card events fire. ──────────
258
+ onMount(() => {
259
+ local.controllerRef?.({
260
+ // Focus the default (or first) action button on demand.
261
+ focus: (options) => focusTarget()?.focus(options),
262
+ // Activate an action by id (or the default action with no id) — the button path.
263
+ confirm: (actionId) => {
264
+ const list = actions();
265
+ const id = actionId ?? defaultId();
266
+ if (id === undefined) return;
267
+ const action = list.find((a) => a.id === id);
268
+ if (action) onAction(action);
269
+ },
270
+ // Trigger the dismiss path (the X-button path).
271
+ dismiss: () => onDismiss(),
272
+ // Re-open a dismissed card (the stub's affordance path).
273
+ reopen: () => onReopen(),
274
+ });
275
+ });
214
276
 
215
277
  // Surface the resolved action id for host styling.
216
278
  createEffect(() => {
@@ -229,6 +291,10 @@ export function ConfirmCard(props: ConfirmCardProps): JSX.Element {
229
291
  return <Card heading={local.heading} errorMessage="The card failed to render." />;
230
292
  }}
231
293
  >
294
+ <Show
295
+ when={!res.isDeferred()}
296
+ fallback={<DismissedStub type={CONFIRM_CARD_TYPE} title={local.heading} onReopen={onReopen} />}
297
+ >
232
298
  <Card
233
299
  heading={local.heading}
234
300
  actions={
@@ -256,7 +322,7 @@ export function ConfirmCard(props: ConfirmCardProps): JSX.Element {
256
322
  <X size={16} aria-hidden="true" />
257
323
  </Button>
258
324
  </Show>
259
- <div class="ml-auto flex flex-wrap items-center gap-2">
325
+ <div ref={actionsRef} class="ml-auto flex flex-wrap items-center gap-2">
260
326
  <For each={actions()}>
261
327
  {(action) => (
262
328
  <Button
@@ -309,6 +375,7 @@ export function ConfirmCard(props: ConfirmCardProps): JSX.Element {
309
375
  </Show>
310
376
  </div>
311
377
  </Card>
378
+ </Show>
312
379
  </ErrorBoundary>
313
380
  </Show>
314
381
  );
@@ -1,16 +1,78 @@
1
- import { splitProps } from 'solid-js';
1
+ import { Show, splitProps, createMemo } from 'solid-js';
2
+ import { MessageSquare } from 'lucide-solid';
2
3
  import { cn } from '../utils/cn';
3
4
  import type { ConversationSummary } from '../types';
4
5
 
5
- export interface ConversationItemProps { conversation: ConversationSummary; isActive: boolean; onSelect: (id: string) => void; class?: string; }
6
+ export interface ConversationItemProps {
7
+ conversation: ConversationSummary;
8
+ isActive: boolean;
9
+ onSelect: (id: string) => void;
10
+ /** Dense single-line row: a leading dot + title, no message count. */
11
+ compact?: boolean;
12
+ class?: string;
13
+ }
14
+
15
+ /**
16
+ * Short relative time from an ISO date string: "just now", "5m ago", "3h ago",
17
+ * "2d ago", "24d ago". Pure — it snapshots `now` (defaults to `Date.now()`) at
18
+ * call time, so it re-derives whenever the list re-renders; there is no internal
19
+ * ticking clock. Returns '' for a missing or unparseable date.
20
+ */
21
+ export function relativeTimeShort(iso?: string, now: number = Date.now()): string {
22
+ if (!iso) return '';
23
+ const then = Date.parse(iso);
24
+ if (Number.isNaN(then)) return '';
25
+ const secs = Math.max(0, Math.round((now - then) / 1000));
26
+ if (secs < 60) return 'just now';
27
+ const mins = Math.floor(secs / 60);
28
+ if (mins < 60) return `${mins}m ago`;
29
+ const hours = Math.floor(mins / 60);
30
+ if (hours < 24) return `${hours}h ago`;
31
+ return `${Math.floor(hours / 24)}d ago`;
32
+ }
6
33
 
7
34
  export function ConversationItem(props: ConversationItemProps) {
8
- const [local] = splitProps(props, ['conversation', 'isActive', 'onSelect', 'class']);
35
+ const [local] = splitProps(props, ['conversation', 'isActive', 'onSelect', 'compact', 'class']);
36
+ // The trailing text: the consumer's own `trailing` field, else an auto relative
37
+ // time from updatedAt (fallback lastMessageAt). REACTIVITY: this (and the auto
38
+ // time) update when the consumer re-assigns the `conversations` array with a new
39
+ // reference — the kit's standard reactivity model — not via an internal clock.
40
+ const trailing = createMemo(
41
+ () => local.conversation.trailing ?? relativeTimeShort(local.conversation.updatedAt ?? local.conversation.lastMessageAt),
42
+ );
9
43
  return (
10
- <button data-conversation-id={local.conversation.id} onClick={() => local.onSelect(local.conversation.id)}
11
- class={cn('w-full text-left rounded-lg px-2.5 py-2 transition-colors', local.isActive ? 'bg-muted' : 'hover:bg-muted/50', local.class)}>
12
- <div class={cn('truncate text-sm', local.isActive ? 'text-foreground font-medium' : 'text-foreground/80')}>{local.conversation.title}</div>
13
- <div class={cn('truncate mt-0.5 text-xs', local.isActive ? 'text-foreground/70' : 'text-muted-foreground')}>{local.conversation.messageCount} messages</div>
44
+ <button
45
+ data-conversation-id={local.conversation.id}
46
+ onClick={() => local.onSelect(local.conversation.id)}
47
+ class={cn(
48
+ 'w-full rounded-lg text-left transition-colors',
49
+ local.compact ? 'px-2.5 py-1.5' : 'px-2.5 py-2',
50
+ local.isActive ? 'bg-muted' : 'hover:bg-muted/50',
51
+ local.class,
52
+ )}
53
+ >
54
+ <Show
55
+ when={local.compact}
56
+ fallback={
57
+ <>
58
+ <div class="flex items-center gap-2">
59
+ <div class={cn('min-w-0 flex-1 truncate text-sm', local.isActive ? 'font-medium text-foreground' : 'text-foreground/80')}>{local.conversation.title}</div>
60
+ <Show when={trailing()}>
61
+ <span part="trailing" class="ml-auto shrink-0 text-xs text-muted-foreground">{trailing()}</span>
62
+ </Show>
63
+ </div>
64
+ <div class={cn('mt-0.5 truncate text-xs', local.isActive ? 'text-foreground/70' : 'text-muted-foreground')}>{local.conversation.messageCount} messages</div>
65
+ </>
66
+ }
67
+ >
68
+ <div class="flex items-center gap-2.5">
69
+ <MessageSquare class={cn('size-3.5 shrink-0', local.isActive ? 'text-foreground' : 'text-muted-foreground')} />
70
+ <span class={cn('min-w-0 flex-1 truncate text-sm', local.isActive ? 'font-medium text-foreground' : 'text-foreground/80')}>{local.conversation.title}</span>
71
+ <Show when={trailing()}>
72
+ <span part="trailing" class="ml-auto shrink-0 text-xs text-muted-foreground">{trailing()}</span>
73
+ </Show>
74
+ </div>
75
+ </Show>
14
76
  </button>
15
77
  );
16
78
  }
@@ -1,4 +1,5 @@
1
- import { splitProps, For, Show, createSignal, createMemo } from 'solid-js';
1
+ import { splitProps, For, Show, createSignal, createMemo, onMount, type JSX } from 'solid-js';
2
+ import { PanelLeftOpen } from 'lucide-solid';
2
3
  import { cn } from '../utils/cn';
3
4
  import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '../ui/collapsible';
4
5
  import { Button } from '../ui/button';
@@ -7,6 +8,26 @@ import { ScrollArea } from '../ui/scroll-area';
7
8
  import { ConversationItem } from './conversation-item';
8
9
  import type { ConversationSummary, ConversationGroup } from '../types';
9
10
 
11
+ /**
12
+ * The collapsed-rail fallback: a floating reopen button shown when the
13
+ * conversation sidebar is collapsed. Shared by `kai-workspace` (its collapsed
14
+ * branch) and the standalone `kai-conversations` (collapsed mode), so the rail
15
+ * collapses identically in both. Renders only the button; the host owns the
16
+ * surrounding region (the workspace puts the thread beside it, the standalone
17
+ * element stands alone). `onExpand` reopens the rail.
18
+ */
19
+ export function CollapsedRail(props: { onExpand: () => void; class?: string }) {
20
+ return (
21
+ <Button
22
+ variant="ghost" size="icon-sm" aria-label="Open sidebar"
23
+ class={cn('rounded-full bg-card/80 shadow-sm backdrop-blur', props.class)}
24
+ onClick={props.onExpand}
25
+ >
26
+ <PanelLeftOpen class="size-4" />
27
+ </Button>
28
+ );
29
+ }
30
+
10
31
  export interface ConversationListProps {
11
32
  groups: ConversationGroup[];
12
33
  conversations: ConversationSummary[];
@@ -14,12 +35,48 @@ export interface ConversationListProps {
14
35
  onSelect: (id: string) => void;
15
36
  onNewChat: () => void;
16
37
  onToggleSidebar?: () => void;
38
+ /** Replaces the built-in title bar (toggle / "Chats" / New chat). */
39
+ header?: JSX.Element;
40
+ /** A row below the list (e.g. account / settings / usage). */
41
+ footer?: JSX.Element;
42
+ /** Replaces the built-in "no conversations yet" state. */
43
+ empty?: JSX.Element;
44
+ /** Dense single-line rows (a leading dot + title, no message count). */
45
+ compact?: boolean;
46
+ /** Fired whenever the built-in search box query changes (typing or a
47
+ * programmatic `clear()`). Lets the facade surface a `kai-search` event. */
48
+ onSearchChange?: (query: string) => void;
49
+ /** Receive the imperative controller once mounted. The `kai-conversations`
50
+ * facade uses it to focus / clear the internal search input. */
51
+ controllerRef?: (controller: ConversationListController) => void;
17
52
  class?: string;
18
53
  }
19
54
 
55
+ /** Imperative handle exposed via `controllerRef` — surfaces the internal search
56
+ * box to the `kai-conversations` facade (the searchQuery signal lives here). */
57
+ export interface ConversationListController {
58
+ /** Focus the built-in search `<input>`. */
59
+ focus(options?: FocusOptions): void;
60
+ /** Clear the internal search query (resets the list filter). */
61
+ clearSearch(): void;
62
+ }
63
+
20
64
  export function ConversationList(props: ConversationListProps) {
21
- const [local] = splitProps(props, ['groups', 'conversations', 'activeId', 'onSelect', 'onNewChat', 'onToggleSidebar', 'class']);
65
+ const [local] = splitProps(props, ['groups', 'conversations', 'activeId', 'onSelect', 'onNewChat', 'onToggleSidebar', 'header', 'footer', 'empty', 'compact', 'onSearchChange', 'controllerRef', 'class']);
22
66
  const [searchQuery, setSearchQuery] = createSignal('');
67
+ const isEmpty = createMemo(() => local.conversations.length === 0);
68
+ // The search query is owned here; setQuery is the single mutation point so both
69
+ // typing and the imperative clearSearch() notify the facade (→ kai-search).
70
+ let searchInput: HTMLInputElement | undefined;
71
+ const setQuery = (q: string) => { setSearchQuery(q); local.onSearchChange?.(q); };
72
+
73
+ // Hand the imperative controller (focus / clear the search box) to the facade.
74
+ onMount(() => {
75
+ local.controllerRef?.({
76
+ focus: (options) => searchInput?.focus(options),
77
+ clearSearch: () => setQuery(''),
78
+ });
79
+ });
23
80
 
24
81
  const filteredConversations = createMemo(() => {
25
82
  const q = searchQuery().toLowerCase();
@@ -41,45 +98,85 @@ export function ConversationList(props: ConversationListProps) {
41
98
 
42
99
  return (
43
100
  <div class={cn('flex flex-col h-full bg-sidebar', local.class)}>
44
- <div class="flex items-center justify-between p-3 pb-2">
45
- <div class="flex items-center gap-2">
46
- <Button variant="ghost" size="icon-sm" aria-label="Toggle sidebar" onClick={local.onToggleSidebar}>
47
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
48
- </Button>
49
- <span class="text-sm font-semibold text-foreground">Chats</span>
101
+ {/* header (replace): the consumer's own title bar, else the built-in one. */}
102
+ <Show
103
+ when={local.header}
104
+ fallback={
105
+ <div class="flex items-center justify-between p-3 pb-2">
106
+ <div class="flex items-center gap-2">
107
+ <Button variant="ghost" size="icon-sm" aria-label="Toggle sidebar" onClick={local.onToggleSidebar}>
108
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
109
+ </Button>
110
+ <span class="text-sm font-semibold text-foreground">Chats</span>
111
+ </div>
112
+ <Button variant="ghost" size="icon-sm" aria-label="New chat" onClick={local.onNewChat}>
113
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
114
+ </Button>
115
+ </div>
116
+ }
117
+ >
118
+ {local.header}
119
+ </Show>
120
+ <Show when={!isEmpty()}>
121
+ <div class="px-3 pb-2">
122
+ <div class="flex items-center gap-2 rounded-md bg-surface-strong px-2.5 py-1.5">
123
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-muted-foreground"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
124
+ <input ref={searchInput} type="text" value={searchQuery()} onInput={(e) => setQuery(e.currentTarget.value)} placeholder="Search chats..."
125
+ aria-label="Search chats"
126
+ class="bg-transparent text-[13px] text-foreground placeholder:text-muted-foreground rounded-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring w-full" />
127
+ </div>
50
128
  </div>
51
- <Button variant="ghost" size="icon-sm" aria-label="New chat" onClick={local.onNewChat}>
52
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
53
- </Button>
54
- </div>
55
- <div class="px-3 pb-2">
56
- <div class="flex items-center gap-2 rounded-md bg-muted/40 px-2.5 py-1.5">
57
- <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-muted-foreground"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
58
- <input type="text" value={searchQuery()} onInput={(e) => setSearchQuery(e.currentTarget.value)} placeholder="Search chats..."
59
- aria-label="Search chats"
60
- class="bg-transparent text-[13px] text-foreground placeholder:text-muted-foreground rounded-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring w-full" />
61
- </div>
62
- </div>
63
- <ScrollArea class="flex-1 px-2">
64
- <For each={local.groups}>
65
- {(group) => {
66
- const convs = createMemo(() => groupedConversations().get(group.id) ?? []);
67
- return (
68
- <Show when={convs().length > 0}>
69
- <GroupSection name={group.name} count={convs().length} conversations={convs()} activeId={local.activeId} onSelect={local.onSelect} />
70
- </Show>
71
- );
72
- }}
73
- </For>
74
- <Show when={ungrouped().length > 0}>
75
- <GroupSection name="Ungrouped" count={ungrouped().length} conversations={ungrouped()} activeId={local.activeId} onSelect={local.onSelect} />
76
- </Show>
77
- </ScrollArea>
129
+ </Show>
130
+ {/* list, or the empty state (replace) when there are no conversations. */}
131
+ <Show
132
+ when={!isEmpty()}
133
+ fallback={
134
+ <Show
135
+ when={local.empty}
136
+ fallback={
137
+ <div class="flex flex-1 flex-col items-center justify-center gap-1 p-6 text-center text-sm text-muted-foreground">
138
+ No conversations yet
139
+ </div>
140
+ }
141
+ >
142
+ {local.empty}
143
+ </Show>
144
+ }
145
+ >
146
+ <ScrollArea class="flex-1 px-2">
147
+ <For each={local.groups}>
148
+ {(group) => {
149
+ const convs = createMemo(() => groupedConversations().get(group.id) ?? []);
150
+ return (
151
+ <Show when={convs().length > 0}>
152
+ <GroupSection name={group.name} count={convs().length} conversations={convs()} activeId={local.activeId} onSelect={local.onSelect} compact={local.compact} />
153
+ </Show>
154
+ );
155
+ }}
156
+ </For>
157
+ <Show when={ungrouped().length > 0}>
158
+ <Show
159
+ when={local.compact}
160
+ fallback={<GroupSection name="Ungrouped" count={ungrouped().length} conversations={ungrouped()} activeId={local.activeId} onSelect={local.onSelect} />}
161
+ >
162
+ <div class="space-y-0.5 py-1">
163
+ <For each={ungrouped()}>
164
+ {(conv) => <ConversationItem conversation={conv} isActive={conv.id === local.activeId} onSelect={local.onSelect} compact />}
165
+ </For>
166
+ </div>
167
+ </Show>
168
+ </Show>
169
+ </ScrollArea>
170
+ </Show>
171
+ {/* footer (inject): a row below the list (account / settings / …). */}
172
+ <Show when={local.footer}>
173
+ <div class="shrink-0 border-t border-border">{local.footer}</div>
174
+ </Show>
78
175
  </div>
79
176
  );
80
177
  }
81
178
 
82
- function GroupSection(props: { name: string; count: number; conversations: ConversationSummary[]; activeId?: string; onSelect: (id: string) => void }) {
179
+ function GroupSection(props: { name: string; count: number; conversations: ConversationSummary[]; activeId?: string; onSelect: (id: string) => void; compact?: boolean }) {
83
180
  const [open, setOpen] = createSignal(true);
84
181
  return (
85
182
  <Collapsible open={open()} onOpenChange={setOpen}>
@@ -92,7 +189,7 @@ function GroupSection(props: { name: string; count: number; conversations: Conve
92
189
  <CollapsibleContent>
93
190
  <div class="pl-2 mt-0.5 space-y-0.5">
94
191
  <For each={props.conversations}>
95
- {(conv) => <ConversationItem conversation={conv} isActive={conv.id === props.activeId} onSelect={props.onSelect} />}
192
+ {(conv) => <ConversationItem conversation={conv} isActive={conv.id === props.activeId} onSelect={props.onSelect} compact={props.compact} />}
96
193
  </For>
97
194
  </div>
98
195
  </CollapsibleContent>
@@ -0,0 +1,80 @@
1
+ import { type JSX, Show } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+ import { Button } from '../ui/button';
4
+ import { Card } from './card';
5
+ import { RotateCcw } from 'lucide-solid';
6
+
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ // DismissedStub — the collapsed, re-openable read-only view a card renders once
9
+ // the user has dismissed it (a DEFERRED resolution, not a terminal one). It's a
10
+ // dense `Card` showing "<intent>: <title> — dismissed" + a ghost Reopen button.
11
+ // Pure/presentational: the owning card wires `onReopen` to emit `{kind:'reopen'}`.
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+
14
+ /** The card types that render a dismissed stub, mapped to their intent verb. */
15
+ export type DismissedCardType = 'confirm' | 'choice' | 'form' | 'tasks';
16
+
17
+ const INTENT_LABELS: Record<DismissedCardType, string> = {
18
+ confirm: 'Proposed',
19
+ choice: 'Choose',
20
+ form: 'Form',
21
+ tasks: 'Tasks',
22
+ };
23
+
24
+ /** Map a card `type` to its dismissed-stub intent label ("Proposed:", "Choose:",
25
+ * "Form:", "Tasks:"). Falls back to a capitalized type for unknown card types. */
26
+ export function stubIntent(type: string): string {
27
+ const known = INTENT_LABELS[type as DismissedCardType];
28
+ if (known) return `${known}:`;
29
+ if (type.length === 0) return 'Card:';
30
+ return `${type.charAt(0).toUpperCase()}${type.slice(1)}:`;
31
+ }
32
+
33
+ export interface DismissedStubProps {
34
+ /** The card type, used to derive the intent label ("Proposed:" etc.). */
35
+ type: string;
36
+ /** The card's title/heading; shown after the intent label. Falls back gracefully. */
37
+ title?: string;
38
+ /** Re-open handler. The owning card emits `{ kind:'reopen', cardId }`. */
39
+ onReopen: () => void;
40
+ /** The Reopen button label. Default 'Reopen'. */
41
+ reopenLabel?: string;
42
+ class?: string;
43
+ }
44
+
45
+ /**
46
+ * `DismissedStub` — the compact stub a card collapses to after it's been dismissed.
47
+ * Group-labelled for a11y; the Reopen affordance is a ghost button.
48
+ */
49
+ export function DismissedStub(props: DismissedStubProps): JSX.Element {
50
+ const intent = (): string => stubIntent(props.type);
51
+ const title = (): string => props.title ?? 'this card';
52
+ const label = (): string => props.reopenLabel ?? 'Reopen';
53
+ const groupLabel = (): string => `${intent()} ${title()} — dismissed`;
54
+
55
+ return (
56
+ <Card dense class={props.class}>
57
+ <div
58
+ role="group"
59
+ aria-label={groupLabel()}
60
+ class="flex flex-wrap items-center gap-x-2 gap-y-1"
61
+ >
62
+ <span class="min-w-0 text-sm text-muted-foreground">
63
+ <span class="font-medium text-foreground">{intent()}</span>{' '}
64
+ <span class="text-foreground">{title()}</span>
65
+ <span class="text-muted-foreground"> — dismissed</span>
66
+ </span>
67
+ <Button
68
+ type="button"
69
+ variant="ghost"
70
+ size="sm"
71
+ class={cn('ml-auto gap-1.5')}
72
+ onClick={() => props.onReopen()}
73
+ >
74
+ <RotateCcw size={14} aria-hidden="true" />
75
+ <Show when={label()}>{label()}</Show>
76
+ </Button>
77
+ </div>
78
+ </Card>
79
+ );
80
+ }