@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
@@ -8,6 +8,7 @@ import {
8
8
  createMemo,
9
9
  createEffect,
10
10
  on,
11
+ onMount,
11
12
  ErrorBoundary,
12
13
  createUniqueId,
13
14
  } from 'solid-js';
@@ -15,11 +16,12 @@ import { cn } from '../utils/cn';
15
16
  import { Button } from '../ui/button';
16
17
  import { HoverCard } from '../ui/hover-card';
17
18
  import { Card } from './card';
19
+ import { DismissedStub } from './dismissed-stub';
18
20
  import type { CardEnvelope, CardEvent, CardHost, CardResolution } from '../primitives/card-contract';
19
21
  import { useCardResolution } from './use-card-resolution';
20
22
  import { emitCardEvent } from '../primitives/card-routing';
21
23
  import { useCardHost } from '../primitives/card-host';
22
- import { Check } from 'lucide-solid';
24
+ import { Check, X } from 'lucide-solid';
23
25
 
24
26
  // ─────────────────────────────────────────────────────────────────────────────
25
27
  // Types (choice.schema.json) — see src/primitives/card-schemas/choice.schema.json
@@ -49,6 +51,7 @@ export interface ChoiceCardData {
49
51
  options: ChoiceOption[]; // 1..N
50
52
  allowOther?: ChoiceAllowOther; // free-text escape
51
53
  submitLabel?: string; // label for the Submit button (default 'Submit')
54
+ dismissible?: boolean; // show a close affordance that emits `dismiss`
52
55
  }
53
56
 
54
57
  export type ChoiceCardEnvelope = CardEnvelope<'choice', ChoiceCardData>;
@@ -149,6 +152,22 @@ export function firstEnabledIndex(options: ChoiceOption[]): number {
149
152
  // The <ChoiceCard> component.
150
153
  // ─────────────────────────────────────────────────────────────────────────────
151
154
 
155
+ /** Imperative handle exposed via `controllerRef` — surfaces the choice card's
156
+ * latent selection/submit/dismiss capabilities so the `<kai-choice>` facade can
157
+ * forward them as instance methods (focus/select/send/dismiss/reopen). */
158
+ export interface ChoiceController {
159
+ /** Focus the radiogroup roving tab stop (or the Other input when selected). */
160
+ focus(options?: FocusOptions): void;
161
+ /** Select an option by id locally (no emit) — same as a row click. */
162
+ select(optionId: string): void;
163
+ /** Submit the current selection — emits the `action` verb + resolves single-shot. */
164
+ send(): void;
165
+ /** Dismiss the card — emits `dismiss` + optimistically collapses to the stub. */
166
+ dismiss(): void;
167
+ /** Re-open a dismissed card from its stub — emits `reopen`. */
168
+ reopen(): void;
169
+ }
170
+
152
171
  export interface ChoiceCardProps {
153
172
  /** The choice definition (CardEnvelope.data). */
154
173
  data?: ChoiceCardData;
@@ -164,6 +183,17 @@ export interface ChoiceCardProps {
164
183
  class?: string;
165
184
  /** When set, render the chromed read-only view instead of the interactive radiogroup. */
166
185
  resolution?: CardResolution;
186
+ /** Controlled selection (option id). When set, the consumer owns the current pick. */
187
+ value?: string;
188
+ /** Option id to pre-select on mount (uncontrolled seed). */
189
+ defaultValue?: string;
190
+ /** Disable the whole radiogroup + Submit (e.g. while the agent is busy). */
191
+ disabled?: boolean;
192
+ /** Fires when the selection changes BEFORE submit (row click or select()). */
193
+ onValueChange?: (value: string) => void;
194
+ /** Receive the imperative controller once mounted. The `<kai-choice>` facade
195
+ * forwards these as element methods (focus/select/send/dismiss/reopen). */
196
+ controllerRef?: (controller: ChoiceController) => void;
167
197
  }
168
198
 
169
199
  /**
@@ -180,7 +210,20 @@ export interface ChoiceCardProps {
180
210
  */
181
211
  export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
182
212
  const merged = mergeProps({ cardId: 'kai-choice' }, props);
183
- const [local] = splitProps(merged, ['data', 'cardId', 'heading', 'host', 'hostElement', 'class', 'resolution']);
213
+ const [local] = splitProps(merged, [
214
+ 'data',
215
+ 'cardId',
216
+ 'heading',
217
+ 'host',
218
+ 'hostElement',
219
+ 'class',
220
+ 'resolution',
221
+ 'value',
222
+ 'defaultValue',
223
+ 'disabled',
224
+ 'onValueChange',
225
+ 'controllerRef',
226
+ ]);
184
227
 
185
228
  const ctxHost = useCardHost();
186
229
  const uid = createUniqueId();
@@ -212,20 +255,33 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
212
255
  });
213
256
 
214
257
  const [focusIndex, setFocusIndex] = createSignal(0);
215
- const [selectedId, setSelectedId] = createSignal<string | undefined>(undefined);
258
+ // Uncontrolled selection store, seeded from `defaultValue`. When the consumer
259
+ // sets the `value` prop the controlled path wins (same pattern as the composer):
260
+ // `selectedId()` reads value ?? internal, and `setSelection()` writes the
261
+ // internal store + fires onValueChange so a controlled host can mirror it.
262
+ const [internalId, setInternalId] = createSignal<string | undefined>(props.defaultValue);
263
+ const selectedId = createMemo<string | undefined>(() => local.value ?? internalId());
264
+ const setSelection = (id: string): void => {
265
+ if (selectedId() === id) return;
266
+ setInternalId(id);
267
+ local.onValueChange?.(id);
268
+ };
216
269
  const [otherText, setOtherText] = createSignal('');
217
270
 
271
+ const isDisabled = (): boolean => local.disabled === true;
272
+
218
273
  let groupRef: HTMLDivElement | undefined;
219
274
  let otherInputRef: HTMLInputElement | undefined;
220
275
 
221
276
  const otherSelected = createMemo(() => selectedId() === OTHER_ACTION);
222
277
 
223
- // Reset all transient state whenever a NEW definition arrives.
278
+ // Reset all transient state whenever a NEW definition arrives (re-seed the
279
+ // uncontrolled selection from `defaultValue`).
224
280
  createEffect(
225
281
  on(
226
282
  () => local.data,
227
283
  () => {
228
- setSelectedId(undefined);
284
+ setInternalId(local.defaultValue);
229
285
  setOtherText('');
230
286
  setFocusIndex(Math.max(0, firstEnabledIndex(options())));
231
287
  },
@@ -262,11 +318,12 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
262
318
 
263
319
  const isOther = (opt: ChoiceOption): boolean => opt.id === OTHER_ACTION;
264
320
 
265
- // Select (do NOT emit) the given option locally.
321
+ // Select (do NOT emit) the given option locally; fires onValueChange.
266
322
  const select = (opt: ChoiceOption): void => {
267
323
  if (res.isResolved()) return; // single-shot
324
+ if (isDisabled()) return; // group-level freeze
268
325
  if (opt.disabled) return;
269
- setSelectedId(opt.id);
326
+ setSelection(opt.id);
270
327
  if (isOther(opt)) {
271
328
  queueMicrotask(() => otherInputRef?.focus());
272
329
  }
@@ -275,6 +332,7 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
275
332
  // Whether the current selection is submittable.
276
333
  const canSubmit = createMemo(() => {
277
334
  if (res.isResolved()) return false;
335
+ if (isDisabled()) return false; // group-level freeze
278
336
  const id = selectedId();
279
337
  if (id === undefined) return false;
280
338
  if (id === OTHER_ACTION) return otherText().trim().length > 0;
@@ -286,6 +344,7 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
286
344
  // Submit the current selection: emit `action` then resolve (single-shot).
287
345
  const submit = (): void => {
288
346
  if (res.isResolved()) return;
347
+ if (isDisabled()) return; // group-level freeze
289
348
  const id = selectedId();
290
349
  if (id === undefined) return;
291
350
  if (id === OTHER_ACTION) {
@@ -310,13 +369,52 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
310
369
  });
311
370
  };
312
371
 
313
- const focusRadio = (index: number): void => {
372
+ // Dismiss: emit `dismiss` AND optimistically flip to a `dismissed` resolution so
373
+ // the card collapses to its re-openable stub immediately.
374
+ const onDismiss = (): void => {
375
+ if (res.isResolved()) return;
376
+ emit({ kind: 'dismiss', cardId: local.cardId });
377
+ res.setLocal({ kind: 'dismissed' });
378
+ };
379
+ const onReopen = (): void => emit({ kind: 'reopen', cardId: local.cardId });
380
+
381
+ const focusRadio = (index: number, options?: FocusOptions): void => {
314
382
  const radios = groupRef?.querySelectorAll<HTMLElement>('[role="radio"]');
315
- radios?.[index]?.focus();
383
+ radios?.[index]?.focus(options);
316
384
  };
317
385
 
386
+ // ── Imperative controller (Pattern C): hand the facade a handle over the
387
+ // card's latent selection/submit/dismiss capabilities. Every method drives
388
+ // the SAME internal path the buttons/keyboard drive, so the same kai-card
389
+ // events fire (and onValueChange for select()). ─────────────────────────────
390
+ onMount(() => {
391
+ local.controllerRef?.({
392
+ // Focus the roving radiogroup tab stop, or the Other input when it's selected.
393
+ focus: (options) => {
394
+ if (otherSelected() && otherInputRef) otherInputRef.focus(options);
395
+ else focusRadio(focusIndex(), options);
396
+ },
397
+ // Select an option by id locally (no emit) — same as a row click; respects
398
+ // disabled/resolved gating and moves the roving focus to the picked row.
399
+ select: (optionId) => {
400
+ const opts = options();
401
+ const idx = opts.findIndex((o) => o.id === optionId);
402
+ if (idx === -1) return;
403
+ setFocusIndex(idx);
404
+ select(opts[idx]);
405
+ },
406
+ // Submit the current selection (the Submit-button path).
407
+ send: () => submit(),
408
+ // Trigger the dismiss path (the X-button path).
409
+ dismiss: () => onDismiss(),
410
+ // Re-open a dismissed card (the stub's affordance path).
411
+ reopen: () => onReopen(),
412
+ });
413
+ });
414
+
318
415
  const onGroupKeyDown = (e: KeyboardEvent): void => {
319
416
  if (res.isResolved()) return;
417
+ if (isDisabled()) return; // group-level freeze
320
418
  const opts = options();
321
419
  const i = focusIndex();
322
420
  if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
@@ -353,6 +451,10 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
353
451
  return <Card heading={local.heading} errorMessage="The card failed to render." />;
354
452
  }}
355
453
  >
454
+ <Show
455
+ when={!res.isDeferred()}
456
+ fallback={<DismissedStub type={CHOICE_CARD_TYPE} title={local.heading} onReopen={onReopen} />}
457
+ >
356
458
  <Card heading={local.heading}>
357
459
  <div class={cn('flex flex-col gap-3', local.class)}>
358
460
  <Show when={local.data?.prompt}>
@@ -370,14 +472,18 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
370
472
  role="radiogroup"
371
473
  aria-label={groupLabel()}
372
474
  aria-describedby={local.data?.prompt ? promptId : undefined}
373
- class="divide-y divide-border overflow-hidden rounded-lg border border-input"
475
+ aria-disabled={isDisabled() ? 'true' : undefined}
476
+ class={cn(
477
+ 'divide-y divide-border overflow-hidden rounded-lg border border-input',
478
+ isDisabled() && 'cursor-not-allowed opacity-60',
479
+ )}
374
480
  onKeyDown={onGroupKeyDown}
375
481
  >
376
482
  <For each={options()}>
377
483
  {(opt, index) => {
378
484
  const checked = () => selectedId() === opt.id;
379
485
  const tabStop = () =>
380
- !opt.disabled && index() === focusIndex() && !res.isResolved();
486
+ !opt.disabled && !isDisabled() && index() === focusIndex() && !res.isResolved();
381
487
  const descId = `kai-choice-desc-${uid}-${opt.id}`;
382
488
  const hasDesc = () => Boolean(opt.description);
383
489
  return (
@@ -407,7 +513,8 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
407
513
  id={otherInputId}
408
514
  ref={otherInputRef}
409
515
  type="text"
410
- class="w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
516
+ disabled={isDisabled()}
517
+ class="w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-60"
411
518
  placeholder={otherCfg()?.placeholder}
412
519
  value={otherText()}
413
520
  onInput={(e) => setOtherText(e.currentTarget.value)}
@@ -421,12 +528,26 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
421
528
  </div>
422
529
  </Show>
423
530
 
424
- <Button type="button" class="self-end" disabled={!canSubmit()} onClick={submit}>
425
- {submitLabel()}
426
- </Button>
531
+ <div class="flex w-full flex-wrap items-center justify-between gap-2">
532
+ <Show when={local.data?.dismissible === true}>
533
+ <Button
534
+ type="button"
535
+ variant="ghost"
536
+ size="icon-sm"
537
+ aria-label="Dismiss"
538
+ onClick={onDismiss}
539
+ >
540
+ <X size={16} aria-hidden="true" />
541
+ </Button>
542
+ </Show>
543
+ <Button type="button" class="ml-auto" disabled={!canSubmit()} onClick={submit}>
544
+ {submitLabel()}
545
+ </Button>
546
+ </div>
427
547
  </Show>
428
548
  </div>
429
549
  </Card>
550
+ </Show>
430
551
  </ErrorBoundary>
431
552
  </Show>
432
553
  );
@@ -0,0 +1,245 @@
1
+ import { createSignal, createUniqueId, createEffect, onCleanup, Show, type Accessor, type JSX } from 'solid-js';
2
+ import { Portal } from 'solid-js/web';
3
+ import { X } from 'lucide-solid';
4
+ import type { Placement } from '@floating-ui/dom';
5
+ import { cn } from '../utils/cn';
6
+ import { useChatConfig } from '../primitives/chat-config';
7
+ import { createPresence, usePosition } from '../ui/overlay';
8
+
9
+ /** Imperative open controller, handed to the kai-coachmark facade via
10
+ * `controllerRef` so it can drive/observe open state with `wireDisclosure`. */
11
+ export interface CoachmarkController {
12
+ open: Accessor<boolean>;
13
+ setOpen: (v: boolean) => void;
14
+ }
15
+
16
+ export interface CoachmarkProps {
17
+ /** The anchor/trigger (the default slot). The bubble is positioned against it. */
18
+ children?: JSX.Element;
19
+ /** The bubble body text (the `content` slot). */
20
+ content?: JSX.Element;
21
+ /** The bold title. NOT `title` — that is a reserved IDL attr (see define.tsx). */
22
+ headline?: JSX.Element;
23
+ /** A small badge pill (e.g. "New"). */
24
+ badge?: JSX.Element;
25
+ /** Floating placement relative to the anchor. Defaults to `'bottom'`. */
26
+ placement?: Placement;
27
+ /** Color tone: `primary` (default, theme accent), `info` (blue), `success`
28
+ * (green), `warning` (amber), or `error` (red) — reusing the kit's tool hues. */
29
+ tone?: 'primary' | 'info' | 'success' | 'warning' | 'error';
30
+ /** Controlled open state. When set, the component never changes it itself;
31
+ * drive it from `onOpenChange`. Omit for uncontrolled (internal) state. */
32
+ open?: boolean;
33
+ /** Initial open state when uncontrolled. */
34
+ defaultOpen?: boolean;
35
+ /** Fires whenever open wants to change (the × button / a method). */
36
+ onOpenChange?: (open: boolean) => void;
37
+ /** Dismiss intent: the × button. */
38
+ onDismiss?: () => void;
39
+ /** Render the arrow that points at the anchor. Defaults to `true`; set `false`
40
+ * for a plain bubble with no pointer. */
41
+ arrow?: boolean;
42
+ /** Receive the open controller once mounted. */
43
+ controllerRef?: (api: CoachmarkController) => void;
44
+ }
45
+
46
+ /** Map a (post flip/shift) placement side to the bubble edge the arrow sits on. */
47
+ const STATIC_SIDE: Record<string, 'top' | 'bottom' | 'left' | 'right'> = {
48
+ top: 'bottom',
49
+ bottom: 'top',
50
+ left: 'right',
51
+ right: 'left',
52
+ };
53
+
54
+ /** DEFAULT bubble colors per `tone`. `info` is the blue onboarding look (white
55
+ * text). These seed the `--kai-coachmark-bg` / `--kai-coachmark-fg` custom
56
+ * properties. A consumer can override those in CSS (e.g. `kai-coachmark {
57
+ * --kai-coachmark-bg: var(--color-tool-blue) }`) to recolor the bubble, arrow,
58
+ * and text in one place, persistently, without touching `tone`. */
59
+ const TONE_DEFAULTS = {
60
+ primary: { bg: 'var(--color-primary)', fg: 'var(--color-primary-foreground)' },
61
+ info: { bg: 'var(--color-tool-blue)', fg: '#fff' },
62
+ success: { bg: 'var(--color-tool-green)', fg: '#fff' },
63
+ // Amber is light, so it carries a fixed dark foreground for contrast (a theme
64
+ // var would flip to white in dark mode and fail against the amber).
65
+ warning: { bg: 'var(--color-tool-amber)', fg: '#1a1a1a' },
66
+ error: { bg: 'var(--color-tool-red)', fg: '#fff' },
67
+ } as const;
68
+
69
+ // The dismiss/badge/content sub-elements derive from the bubble's foreground via
70
+ // `currentColor` (which inherits the resolved `--kai-coachmark-fg`), so they
71
+ // follow the tone default OR any CSS override automatically (no per-tone class).
72
+ const DISMISS_CLS = 'text-current/70 hover:bg-current/15 hover:text-current';
73
+ const BADGE_CLS = 'bg-current/20';
74
+ const CONTENT_CLS = 'text-current/85';
75
+
76
+ /**
77
+ * Coachmark is the presentational onboarding hint: a tinted bubble with an arrow,
78
+ * anchored to a trigger. It wraps the anchor (the default slot) and positions the
79
+ * bubble against it with `usePosition` (the shared Floating UI primitive) so the
80
+ * arrow tracks the anchor on scroll/resize AND when a sibling reflows it,
81
+ * flipping/shifting to stay on screen. The developer owns when it shows
82
+ * (`open`/`default-open`); the ×, Escape, OR clicking the trigger fires
83
+ * `onDismiss`. Color comes from `--kai-coachmark-bg` / `--kai-coachmark-fg` (each
84
+ * defaulting to the `tone` palette), so a consumer can recolor purely in CSS; the
85
+ * arrow reads the same bg var so it always continues the bubble.
86
+ */
87
+ export function Coachmark(props: CoachmarkProps) {
88
+ const config = useChatConfig();
89
+ const tone = () => TONE_DEFAULTS[props.tone ?? 'primary'];
90
+ const titleId = createUniqueId();
91
+ const [internalOpen, setInternalOpen] = createSignal(props.defaultOpen ?? false);
92
+ const [anchor, setAnchor] = createSignal<HTMLElement>();
93
+ const [bubble, setBubble] = createSignal<HTMLElement>();
94
+ const [arrowEl, setArrowEl] = createSignal<HTMLElement>();
95
+
96
+ const isControlled = () => props.open !== undefined;
97
+ const isOpen = () => (isControlled() ? !!props.open : internalOpen());
98
+ const setOpen = (v: boolean) => {
99
+ if (!isControlled()) setInternalOpen(v);
100
+ props.onOpenChange?.(v);
101
+ };
102
+
103
+ props.controllerRef?.({ open: isOpen, setOpen });
104
+
105
+ const presence = createPresence(isOpen);
106
+ const position = usePosition(anchor, bubble, {
107
+ placement: props.placement ?? 'bottom',
108
+ gutter: 10,
109
+ arrowEl,
110
+ // Anchor removed from the DOM -> close so the bubble portal doesn't orphan.
111
+ onDisconnect: () => setOpen(false),
112
+ });
113
+
114
+ // The bubble edge the arrow sits on, derived from the resolved (post flip/shift)
115
+ // placement.
116
+ const arrowSide = () => STATIC_SIDE[position.pos().placement.split('-')[0]] ?? 'top';
117
+
118
+ const dismiss = () => {
119
+ setOpen(false);
120
+ props.onDismiss?.();
121
+ };
122
+
123
+ // Clicking the trigger dismisses the hint (alongside the × and Escape): the
124
+ // consumer engaged with what the hint points at, so it has done its job. A
125
+ // native listener (not preventing default or stopping propagation) so the
126
+ // trigger's own click still fires, and so it works for a slotted trigger in the
127
+ // web component. Only acts while open so it never re-fires onDismiss when shut.
128
+ createEffect(() => {
129
+ const el = anchor();
130
+ if (!el) return;
131
+ const onClick = () => { if (isOpen()) dismiss(); };
132
+ el.addEventListener('click', onClick);
133
+ onCleanup(() => el.removeEventListener('click', onClick));
134
+ });
135
+
136
+ return (
137
+ <>
138
+ {/* The anchor wrapper. inline-FLEX (not inline-block) so it hugs the trigger
139
+ it positions against WITHOUT the baseline strut: an inline-block sits on
140
+ the host line box's baseline, which reserves descender space below it
141
+ (line-height 24px), making the host ~6px taller than the trigger and
142
+ anchoring the trigger to the top, so in a flex toolbar (items-center) the
143
+ trigger renders a few px above the other controls. inline-flex +
144
+ vertical-align:middle decouples from that baseline so the wrapper hugs the
145
+ trigger's true height and stays centered. The trigger-click dismissal is
146
+ wired as a NATIVE listener on this element (see the effect below) rather
147
+ than a Solid-delegated onClick, so it also fires for a trigger projected
148
+ through the <slot/> in the web component (Solid's delegation walks
149
+ parentNode, which for slotted content stays in the light DOM and never
150
+ reaches this shadow ancestor). */}
151
+ <span ref={setAnchor} style={{ display: 'inline-flex', 'vertical-align': 'middle' }}>
152
+ {props.children}
153
+ </span>
154
+ <Show when={presence.present()}>
155
+ <Portal mount={config.portalMount()}>
156
+ <div
157
+ ref={(el) => { setBubble(el); presence.setRef(el); }}
158
+ part="bubble"
159
+ role="dialog"
160
+ aria-labelledby={titleId}
161
+ data-expanded={presence.state() === 'open' ? '' : undefined}
162
+ data-closed={presence.state() === 'closed' ? '' : undefined}
163
+ style={{
164
+ position: 'fixed',
165
+ left: `${position.pos().x}px`,
166
+ top: `${position.pos().y}px`,
167
+ visibility: position.hidden() ? 'hidden' : 'visible',
168
+ // Color via CSS vars: defaults to the `tone` palette, overridable in
169
+ // CSS. The text color is inherited by every child (dismiss/badge/
170
+ // content read it through currentColor).
171
+ background: `var(--kai-coachmark-bg, ${tone().bg})`,
172
+ color: `var(--kai-coachmark-fg, ${tone().fg})`,
173
+ }}
174
+ class={cn(
175
+ 'z-50 w-64 rounded-lg p-3 pr-8 text-sm kai-elevation',
176
+ 'animate-in fade-in-0 zoom-in-95 data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95',
177
+ )}
178
+ >
179
+ {/* The arrow: a rotated square inheriting the bubble bg. usePosition
180
+ writes its offset-axis position (x for top/bottom placements, y for
181
+ left/right); the static side (`-4px`) pins it to the bubble edge
182
+ facing the anchor so it pokes out as a pointer.
183
+
184
+ Each side is an EXPLICIT, static key. Do NOT collapse this to a
185
+ computed `[arrowSide()]: '-4px'` key: Solid compiles `style={{…}}`
186
+ object literals to per-property setStyleProperty calls at build time
187
+ and silently DROPS dynamic/computed keys, so the offset never
188
+ reaches the DOM and the arrow sinks into the bubble's padding box
189
+ (invisible against the matching background). */}
190
+ <Show when={props.arrow !== false}>
191
+ <div
192
+ ref={setArrowEl}
193
+ part="arrow"
194
+ aria-hidden="true"
195
+ class="absolute size-2 rotate-45"
196
+ style={{
197
+ left: arrowSide() === 'left' ? '-4px'
198
+ : position.arrowPos().x != null ? `${position.arrowPos().x}px` : '',
199
+ top: arrowSide() === 'top' ? '-4px'
200
+ : position.arrowPos().y != null ? `${position.arrowPos().y}px` : '',
201
+ right: arrowSide() === 'right' ? '-4px' : '',
202
+ bottom: arrowSide() === 'bottom' ? '-4px' : '',
203
+ // Same bg var as the bubble so the arrow always reads as part of it.
204
+ background: `var(--kai-coachmark-bg, ${tone().bg})`,
205
+ }}
206
+ />
207
+ </Show>
208
+
209
+ <button
210
+ type="button"
211
+ part="dismiss"
212
+ aria-label="Dismiss"
213
+ onClick={dismiss}
214
+ class={cn('absolute right-2 top-2 inline-flex size-5 items-center justify-center rounded transition-colors', DISMISS_CLS)}
215
+ >
216
+ <X class="size-3.5" aria-hidden="true" />
217
+ </button>
218
+
219
+ <Show when={props.headline}>
220
+ <div class="flex items-center gap-2">
221
+ <span id={titleId} part="title" class="font-semibold leading-tight">
222
+ {props.headline}
223
+ </span>
224
+ <Show when={props.badge}>
225
+ <span
226
+ part="badge"
227
+ class={cn('inline-flex items-center rounded-full px-1.5 py-0.5 text-[0.625rem] font-medium uppercase leading-none tracking-wide', BADGE_CLS)}
228
+ >
229
+ {props.badge}
230
+ </span>
231
+ </Show>
232
+ </div>
233
+ </Show>
234
+
235
+ <Show when={props.content}>
236
+ <div class={cn(CONTENT_CLS, props.headline ? 'mt-1.5' : '')}>
237
+ {props.content}
238
+ </div>
239
+ </Show>
240
+ </div>
241
+ </Portal>
242
+ </Show>
243
+ </>
244
+ );
245
+ }