@kitn.ai/ui 0.17.0 → 0.18.1

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 (494) hide show
  1. package/README.md +28 -5
  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 +37 -12
  9. package/dist/components/choice-card.d.ts +26 -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/file-tree.d.ts +15 -0
  19. package/dist/components/form.d.ts +34 -0
  20. package/dist/components/markdown.d.ts +3 -0
  21. package/dist/components/message.d.ts +12 -0
  22. package/dist/components/model-switcher.d.ts +7 -0
  23. package/dist/components/prompt-input.d.ts +13 -4
  24. package/dist/components/prompt-suggestion.d.ts +9 -1
  25. package/dist/components/reasoning.d.ts +14 -1
  26. package/dist/components/response-compare.d.ts +14 -0
  27. package/dist/components/screen.d.ts +46 -0
  28. package/dist/components/tasks-card.d.ts +49 -1
  29. package/dist/components/toast.d.ts +12 -2
  30. package/dist/components/tool-types.d.ts +10 -0
  31. package/dist/components/tool.d.ts +8 -8
  32. package/dist/components/voice-input.d.ts +27 -0
  33. package/dist/components/voice-output.d.ts +33 -0
  34. package/dist/core-C8fzo39E.js +12 -0
  35. package/dist/custom-elements.json +8392 -1860
  36. package/dist/elements/agent-card.d.ts +1 -0
  37. package/dist/elements/agent-card.js +1 -0
  38. package/dist/elements/artifact.js +1 -1
  39. package/dist/elements/attachments.js +1 -1
  40. package/dist/elements/autoloader.js +1 -1
  41. package/dist/elements/avatar.d.ts +1 -0
  42. package/dist/elements/avatar.js +1 -0
  43. package/dist/elements/badge.d.ts +1 -0
  44. package/dist/elements/badge.js +1 -0
  45. package/dist/elements/button.d.ts +1 -0
  46. package/dist/elements/button.js +1 -0
  47. package/dist/elements/card.js +1 -1
  48. package/dist/elements/cards.js +1 -1
  49. package/dist/elements/chain-of-thought.js +1 -1
  50. package/dist/elements/chat-scope-picker.js +1 -1
  51. package/dist/elements/chat-types.d.ts +47 -0
  52. package/dist/elements/chat-workspace.js +1 -1
  53. package/dist/elements/chat.js +1 -1
  54. package/dist/elements/checkpoint.js +1 -1
  55. package/dist/elements/choice.js +1 -1
  56. package/dist/elements/chunks/Icon-C2LTALdM.js +1 -0
  57. package/dist/elements/chunks/action-icons-Dtl8Ox5D.js +1 -0
  58. package/dist/elements/chunks/arrow-left-GbBceGBE.js +1 -0
  59. package/dist/elements/chunks/attachments-DLJcWCDV.js +1 -0
  60. package/dist/elements/chunks/badge-BTNZTODL.js +1 -0
  61. package/dist/elements/chunks/button-DBDZebGr.js +1 -0
  62. package/dist/elements/chunks/card-YfCyI4fH.js +1 -0
  63. package/dist/elements/chunks/chat-thread-Dz2qbzvI.js +1 -0
  64. package/dist/elements/chunks/check-fhmRksq_.js +1 -0
  65. package/dist/elements/chunks/chevron-down-DcRg1aRU.js +1 -0
  66. package/dist/elements/chunks/chevron-right-ChzBcNJ-.js +1 -0
  67. package/dist/elements/chunks/circle-B4fTe9v6.js +1 -0
  68. package/dist/elements/chunks/circle-check-CJV-3Rem.js +1 -0
  69. package/dist/elements/chunks/circle-x-pDtzlghQ.js +1 -0
  70. package/dist/elements/chunks/cn-CU7UAWzk.js +1 -0
  71. package/dist/elements/chunks/code-block-DZ6mz6vs.js +1 -0
  72. package/dist/elements/chunks/collapsible-CWUAShMC.js +1 -0
  73. package/dist/elements/chunks/composer-BZyPU6gD.js +65 -0
  74. package/dist/elements/chunks/context-Cx6eQA_G.js +1 -0
  75. package/dist/elements/chunks/conversation-list-C4xTQSpT.js +1 -0
  76. package/dist/elements/chunks/default-input-BZcwyKcP.js +1 -0
  77. package/dist/elements/chunks/define-CNSKFZCd.js +1 -0
  78. package/dist/elements/chunks/disclosure-BcBs--Jf.js +1 -0
  79. package/dist/elements/chunks/download-BDQwcFav.js +1 -0
  80. package/dist/elements/chunks/dropdown-wgs8xCJZ.js +1 -0
  81. package/dist/elements/chunks/ellipsis-CUiEJ6R1.js +1 -0
  82. package/dist/elements/chunks/external-link-DJUmC7g2.js +1 -0
  83. package/dist/elements/chunks/file-text-DOYfYvrh.js +1 -0
  84. package/dist/elements/chunks/file-tree-DQzNQrbr.js +1 -0
  85. package/dist/elements/chunks/folder-DBDUBANz.js +1 -0
  86. package/dist/elements/chunks/hover-card-BQGdGAQ3.js +1 -0
  87. package/dist/elements/chunks/icon-CAyRqVrg.js +1 -0
  88. package/dist/elements/chunks/{index-CTBEJeo6.js → index-BCRhi84a.js} +1 -1
  89. package/dist/elements/chunks/info-DBQl-m6e.js +1 -0
  90. package/dist/elements/chunks/input-DZD67EvW.js +1 -0
  91. package/dist/elements/chunks/kbd-DbyVrlnU.js +1 -0
  92. package/dist/elements/chunks/link-vko7AttA.js +1 -0
  93. package/dist/elements/chunks/loader-Cg6rO8tQ.js +1 -0
  94. package/dist/elements/chunks/{markdown-CMTGx9r8.js → markdown-n3Xbj3PR.js} +9 -9
  95. package/dist/elements/chunks/message-CgKytiSB.js +1 -0
  96. package/dist/elements/chunks/message-feedback-Ds3-US0e.js +1 -0
  97. package/dist/elements/chunks/message-square-MmRkvc-M.js +1 -0
  98. package/dist/elements/chunks/minimize-2-3kCdE0q3.js +1 -0
  99. package/dist/elements/chunks/model-switcher-BMtXGwLU.js +1 -0
  100. package/dist/elements/chunks/overlay-B1-MBI6h.js +1 -0
  101. package/dist/elements/chunks/paperclip-DDEfmkHc.js +1 -0
  102. package/dist/elements/chunks/progress-bar-Dwbiyv3s.js +1 -0
  103. package/dist/elements/chunks/prompt-suggestion-9lvgFcEa.js +1 -0
  104. package/dist/elements/chunks/reasoning-DzHWsdAJ.js +1 -0
  105. package/dist/elements/chunks/resizable-Bq_eHxUm.js +1 -0
  106. package/dist/elements/chunks/rotate-cw-jpisOdOV.js +1 -0
  107. package/dist/elements/chunks/scroll-area-g5Ze3K6f.js +1 -0
  108. package/dist/elements/chunks/separator-Bmjc5XyX.js +1 -0
  109. package/dist/elements/chunks/settings-Cz1bMJP9.js +1 -0
  110. package/dist/elements/chunks/settings-group-9imY3kRA.js +1 -0
  111. package/dist/elements/chunks/share-BdenF0wQ.js +1 -0
  112. package/dist/elements/chunks/skeleton-Pjc5jiaY.js +1 -0
  113. package/dist/elements/chunks/slots-DkZ-Cmha.js +1 -0
  114. package/dist/elements/chunks/star-DY6z1UUH.js +1 -0
  115. package/dist/elements/chunks/store-th4VyJXM.js +1 -0
  116. package/dist/elements/chunks/text-shimmer-CEi-tlXr.js +1 -0
  117. package/dist/elements/chunks/textarea-CboigarR.js +1 -0
  118. package/dist/elements/chunks/thumbs-up-BR33KLfA.js +1 -0
  119. package/dist/elements/chunks/toast-store-BEMda0fv.js +1 -0
  120. package/dist/elements/chunks/tool-TMued3WO.js +1 -0
  121. package/dist/elements/chunks/tooltip-Cft_Rxiw.js +1 -0
  122. package/dist/elements/chunks/triangle-alert-B07fX3Q8.js +1 -0
  123. package/dist/elements/chunks/use-card-resolution-M36zVciB.js +1 -0
  124. package/dist/elements/chunks/video-MKp3LmNT.js +1 -0
  125. package/dist/elements/chunks/x-Ckwicf6g.js +1 -0
  126. package/dist/elements/coachmark.d.ts +1 -0
  127. package/dist/elements/coachmark.js +1 -0
  128. package/dist/elements/code-block.js +1 -1
  129. package/dist/elements/command.d.ts +1 -0
  130. package/dist/elements/command.js +1 -0
  131. package/dist/elements/compare.js +1 -1
  132. package/dist/elements/composer.d.ts +1 -0
  133. package/dist/elements/composer.js +1 -0
  134. package/dist/elements/confirm-card.js +1 -1
  135. package/dist/elements/context-meter.js +1 -1
  136. package/dist/elements/conversation-list.js +1 -1
  137. package/dist/elements/css.d.ts +1 -0
  138. package/dist/elements/default-input.d.ts +51 -0
  139. package/dist/elements/define.d.ts +53 -0
  140. package/dist/elements/dialog.d.ts +1 -0
  141. package/dist/elements/dialog.js +1 -0
  142. package/dist/elements/disclosure.d.ts +37 -0
  143. package/dist/elements/editable-label.d.ts +1 -0
  144. package/dist/elements/editable-label.js +1 -0
  145. package/dist/elements/embed.js +1 -1
  146. package/dist/elements/empty.js +1 -1
  147. package/dist/elements/feedback-bar.js +1 -1
  148. package/dist/elements/file-tree.js +1 -1
  149. package/dist/elements/file-upload.js +1 -1
  150. package/dist/elements/form.js +1 -1
  151. package/dist/elements/hover-card.d.ts +1 -0
  152. package/dist/elements/hover-card.js +1 -0
  153. package/dist/elements/icon.d.ts +1 -0
  154. package/dist/elements/icon.js +1 -0
  155. package/dist/elements/image.js +1 -1
  156. package/dist/elements/input.d.ts +1 -0
  157. package/dist/elements/input.js +1 -0
  158. package/dist/elements/kbd.d.ts +1 -0
  159. package/dist/elements/kbd.js +1 -0
  160. package/dist/elements/link-preview.js +1 -1
  161. package/dist/elements/loader.js +1 -1
  162. package/dist/elements/markdown.js +1 -1
  163. package/dist/elements/menu.d.ts +1 -0
  164. package/dist/elements/menu.js +1 -0
  165. package/dist/elements/message-skills.js +1 -1
  166. package/dist/elements/message.js +1 -1
  167. package/dist/elements/model-switcher.js +1 -1
  168. package/dist/elements/nav.d.ts +1 -0
  169. package/dist/elements/nav.js +1 -0
  170. package/dist/elements/notice.d.ts +1 -0
  171. package/dist/elements/notice.js +1 -0
  172. package/dist/elements/pane-group.d.ts +1 -0
  173. package/dist/elements/pane-group.js +1 -0
  174. package/dist/elements/pane.d.ts +1 -0
  175. package/dist/elements/pane.js +1 -0
  176. package/dist/elements/popover.js +1 -1
  177. package/dist/elements/progress-bar.d.ts +1 -0
  178. package/dist/elements/progress-bar.js +1 -0
  179. package/dist/elements/prompt-dock.d.ts +1 -0
  180. package/dist/elements/prompt-dock.js +1 -0
  181. package/dist/elements/prompt-input.js +1 -1
  182. package/dist/elements/prompt-suggestions.js +1 -1
  183. package/dist/elements/reasoning.js +1 -1
  184. package/dist/elements/register-impl.d.ts +0 -0
  185. package/dist/elements/register.d.ts +13 -0
  186. package/dist/elements/remote.d.ts +1 -0
  187. package/dist/elements/remote.js +1 -0
  188. package/dist/elements/resizable.js +1 -1
  189. package/dist/elements/response-stream.js +2 -2
  190. package/dist/elements/screen.d.ts +1 -0
  191. package/dist/elements/screen.js +1 -0
  192. package/dist/elements/scroll-area.d.ts +1 -0
  193. package/dist/elements/scroll-area.js +1 -0
  194. package/dist/elements/scroll-button.js +1 -1
  195. package/dist/elements/search.d.ts +1 -0
  196. package/dist/elements/search.js +1 -0
  197. package/dist/elements/segmented.d.ts +1 -0
  198. package/dist/elements/segmented.js +1 -0
  199. package/dist/elements/separator.d.ts +1 -0
  200. package/dist/elements/separator.js +1 -0
  201. package/dist/elements/setting-item.d.ts +1 -0
  202. package/dist/elements/setting-item.js +1 -0
  203. package/dist/elements/settings-group.d.ts +1 -0
  204. package/dist/elements/settings-group.js +1 -0
  205. package/dist/elements/skeleton.d.ts +1 -0
  206. package/dist/elements/skeleton.js +1 -0
  207. package/dist/elements/slots.d.ts +176 -0
  208. package/dist/elements/source.js +1 -1
  209. package/dist/elements/status.d.ts +1 -0
  210. package/dist/elements/status.js +1 -0
  211. package/dist/elements/switch.js +1 -1
  212. package/dist/elements/tabs.d.ts +1 -0
  213. package/dist/elements/tabs.js +1 -0
  214. package/dist/elements/tasks.js +1 -1
  215. package/dist/elements/text-shimmer.js +1 -1
  216. package/dist/elements/thinking-bar.js +1 -1
  217. package/dist/elements/toast.js +1 -1
  218. package/dist/elements/tool.js +1 -1
  219. package/dist/elements/tooltip.d.ts +1 -0
  220. package/dist/elements/tooltip.js +1 -0
  221. package/dist/elements/voice-input.js +1 -1
  222. package/dist/elements/voice-output.d.ts +1 -0
  223. package/dist/elements/voice-output.js +1 -0
  224. package/dist/elements.d.ts +733 -58
  225. package/dist/engine-javascript-C1x7zo1_.js +141 -0
  226. package/dist/index.d.ts +3 -2
  227. package/dist/index.js +8255 -5703
  228. package/dist/kai.es.js +1 -0
  229. package/dist/llms/llms-full.txt +1146 -64
  230. package/dist/llms/llms.txt +4 -4
  231. package/dist/mcp.es.js +62 -7
  232. package/dist/primitives/composer-model.d.ts +22 -0
  233. package/dist/primitives/composer-triggers.d.ts +19 -0
  234. package/dist/primitives/create-kai-chat.d.ts +38 -0
  235. package/dist/primitives/toast-store.d.ts +32 -2
  236. package/dist/primitives/use-speech-recognition.d.ts +26 -0
  237. package/dist/react/index.d.ts +1085 -97
  238. package/dist/react/runtime.d.ts +9 -2
  239. package/dist/react/use-kai-chat.d.ts +41 -0
  240. package/dist/react.js +572 -193
  241. package/dist/register-impl-cKscku5m.js +141 -0
  242. package/dist/schemas/tasks.schema.json +2 -2
  243. package/dist/state/index.d.ts +4 -0
  244. package/dist/state/messages.d.ts +11 -0
  245. package/dist/state/stream.d.ts +22 -0
  246. package/dist/state/suggestions.d.ts +4 -0
  247. package/dist/state.js +117 -0
  248. package/dist/theme.tokens.css +87 -0
  249. package/dist/types.d.ts +4 -0
  250. package/dist/ui/agent-card.d.ts +61 -0
  251. package/dist/ui/button.d.ts +5 -2
  252. package/dist/ui/card.d.ts +61 -0
  253. package/dist/ui/collapsible.d.ts +15 -1
  254. package/dist/ui/command.d.ts +33 -0
  255. package/dist/ui/dialog.d.ts +42 -0
  256. package/dist/ui/dropdown.d.ts +95 -1
  257. package/dist/ui/editable-label.d.ts +26 -0
  258. package/dist/ui/hover-card.d.ts +13 -1
  259. package/dist/ui/icon.d.ts +18 -0
  260. package/dist/ui/input.d.ts +33 -0
  261. package/dist/ui/kbd.d.ts +21 -0
  262. package/dist/ui/nav.d.ts +62 -0
  263. package/dist/ui/notice.d.ts +31 -0
  264. package/dist/ui/overlay.d.ts +12 -0
  265. package/dist/ui/pane-grid.d.ts +58 -0
  266. package/dist/ui/pane-group.d.ts +77 -0
  267. package/dist/ui/pane.d.ts +78 -0
  268. package/dist/ui/popover.d.ts +11 -1
  269. package/dist/ui/progress-bar.d.ts +23 -0
  270. package/dist/ui/prompt-dock.d.ts +96 -0
  271. package/dist/ui/resizable.d.ts +12 -0
  272. package/dist/ui/scroll-area.d.ts +4 -0
  273. package/dist/ui/segmented.d.ts +28 -0
  274. package/dist/ui/settings-group.d.ts +32 -0
  275. package/dist/ui/skeleton.d.ts +12 -0
  276. package/dist/ui/stat.d.ts +16 -0
  277. package/dist/ui/status.d.ts +13 -0
  278. package/dist/ui/switch.d.ts +8 -1
  279. package/dist/ui/tabs.d.ts +35 -0
  280. package/dist/ui/tooltip.d.ts +19 -1
  281. package/frameworks/react/index.tsx +1042 -157
  282. package/frameworks/react/runtime.tsx +30 -1
  283. package/frameworks/react/use-kai-chat.tsx +87 -0
  284. package/llms-full.txt +1146 -64
  285. package/llms.txt +4 -4
  286. package/package.json +31 -10
  287. package/src/agent-tooling/mcp/manifest.ts +14 -0
  288. package/src/agent-tooling/mcp/tools/debug.ts +11 -7
  289. package/src/agent-tooling/mcp/tools/reference.ts +32 -0
  290. package/src/agent-tooling/mcp/tools/scaffold.ts +30 -1
  291. package/src/components/artifact.tsx +98 -13
  292. package/src/components/attachment-types.ts +13 -0
  293. package/src/components/attachments.tsx +4 -14
  294. package/src/components/card.tsx +109 -15
  295. package/src/components/chain-of-thought.tsx +210 -1
  296. package/src/components/chat-scope-picker.tsx +13 -3
  297. package/src/components/chat-thread.tsx +225 -116
  298. package/src/components/choice-card.tsx +103 -11
  299. package/src/components/coachmark.tsx +249 -0
  300. package/src/components/component-meta.json +699 -110
  301. package/src/components/composer-dom.ts +159 -0
  302. package/src/components/composer-highlight.ts +242 -0
  303. package/src/components/composer-history.ts +82 -0
  304. package/src/components/composer.tsx +979 -0
  305. package/src/components/confirm-card.tsx +54 -1
  306. package/src/components/conversation-item.tsx +69 -7
  307. package/src/components/conversation-list.tsx +134 -37
  308. package/src/components/file-tree.tsx +197 -47
  309. package/src/components/form-widgets.tsx +9 -7
  310. package/src/components/form.tsx +140 -17
  311. package/src/components/image.tsx +3 -5
  312. package/src/components/link-preview.tsx +5 -4
  313. package/src/components/markdown.tsx +5 -2
  314. package/src/components/message.tsx +23 -4
  315. package/src/components/model-switcher.tsx +19 -4
  316. package/src/components/prompt-input.tsx +46 -75
  317. package/src/components/prompt-suggestion.tsx +48 -16
  318. package/src/components/reasoning.tsx +29 -6
  319. package/src/components/response-compare.tsx +31 -2
  320. package/src/components/screen.tsx +176 -0
  321. package/src/components/tasks-card.tsx +341 -21
  322. package/src/components/toast.tsx +119 -31
  323. package/src/components/tool-types.ts +10 -0
  324. package/src/components/tool.tsx +22 -16
  325. package/src/components/voice-input.tsx +102 -19
  326. package/src/components/voice-output.tsx +202 -0
  327. package/src/elements/agent-card.tsx +67 -0
  328. package/src/elements/artifact.tsx +53 -4
  329. package/src/elements/attachments.tsx +1 -0
  330. package/src/elements/avatar.tsx +33 -0
  331. package/src/elements/badge.tsx +32 -0
  332. package/src/elements/button.tsx +126 -0
  333. package/src/elements/card-media.jpg +0 -0
  334. package/src/elements/card.tsx +124 -31
  335. package/src/elements/cards.tsx +81 -7
  336. package/src/elements/chain-of-thought.tsx +77 -32
  337. package/src/elements/chat-scope-picker.tsx +35 -8
  338. package/src/elements/chat-types.ts +2 -2
  339. package/src/elements/chat-workspace.tsx +153 -41
  340. package/src/elements/chat.tsx +45 -23
  341. package/src/elements/choice.tsx +71 -19
  342. package/src/elements/coachmark.tsx +90 -0
  343. package/src/elements/command.tsx +209 -0
  344. package/src/elements/compare.tsx +24 -2
  345. package/src/elements/compiled.css +2 -2
  346. package/src/elements/composer.tsx +151 -0
  347. package/src/elements/confirm-card.tsx +36 -11
  348. package/src/elements/conversation-list.tsx +80 -11
  349. package/src/elements/default-input.tsx +75 -53
  350. package/src/elements/define.tsx +87 -7
  351. package/src/elements/dialog.tsx +98 -0
  352. package/src/elements/disclosure.ts +73 -0
  353. package/src/elements/editable-label.tsx +125 -0
  354. package/src/elements/element-manifest.json +129 -1
  355. package/src/elements/element-meta.json +5562 -1336
  356. package/src/elements/element-types.d.ts +732 -57
  357. package/src/elements/file-tree.tsx +12 -2
  358. package/src/elements/file-upload.tsx +1 -1
  359. package/src/elements/form.tsx +54 -11
  360. package/src/elements/hover-card.tsx +80 -0
  361. package/src/elements/icon-names.json +48 -0
  362. package/src/elements/icon.tsx +41 -0
  363. package/src/elements/input.tsx +203 -0
  364. package/src/elements/kbd.tsx +45 -0
  365. package/src/elements/menu.tsx +217 -0
  366. package/src/elements/message.tsx +58 -11
  367. package/src/elements/model-switcher.tsx +27 -1
  368. package/src/elements/nav.tsx +87 -0
  369. package/src/elements/notice.tsx +53 -0
  370. package/src/elements/pane-group.tsx +119 -0
  371. package/src/elements/pane.tsx +129 -0
  372. package/src/elements/popover.tsx +39 -17
  373. package/src/elements/progress-bar.tsx +39 -0
  374. package/src/elements/prompt-dock.tsx +81 -0
  375. package/src/elements/prompt-input.tsx +112 -63
  376. package/src/elements/prompt-suggestions.tsx +25 -9
  377. package/src/elements/reasoning.tsx +30 -9
  378. package/src/elements/register-impl.ts +38 -3
  379. package/src/elements/register.ts +11 -3
  380. package/src/elements/resizable.tsx +48 -11
  381. package/src/elements/screen.tsx +90 -0
  382. package/src/elements/scroll-area.tsx +33 -0
  383. package/src/elements/search.tsx +180 -0
  384. package/src/elements/segmented.tsx +135 -0
  385. package/src/elements/separator.tsx +36 -0
  386. package/src/elements/setting-item.tsx +50 -0
  387. package/src/elements/settings-group.tsx +37 -0
  388. package/src/elements/skeleton.tsx +45 -0
  389. package/src/elements/slots.ts +607 -0
  390. package/src/elements/source.tsx +13 -6
  391. package/src/elements/status.tsx +42 -0
  392. package/src/elements/switch.tsx +89 -14
  393. package/src/elements/tabs.tsx +95 -0
  394. package/src/elements/tasks.tsx +70 -14
  395. package/src/elements/toast.tsx +17 -2
  396. package/src/elements/tool.tsx +43 -9
  397. package/src/elements/tooltip.tsx +85 -0
  398. package/src/elements/video-poster.jpg +0 -0
  399. package/src/elements/voice-input.tsx +66 -16
  400. package/src/elements/voice-output.tsx +69 -0
  401. package/src/index.ts +5 -2
  402. package/src/primitives/card-schemas/tasks.schema.json +2 -2
  403. package/src/primitives/composer-model.ts +49 -0
  404. package/src/primitives/composer-triggers.ts +35 -0
  405. package/src/primitives/create-kai-chat.ts +66 -0
  406. package/src/primitives/toast-store.ts +43 -2
  407. package/src/primitives/use-speech-recognition.ts +135 -0
  408. package/src/state/index.ts +6 -0
  409. package/src/state/messages.ts +36 -0
  410. package/src/state/stream.ts +92 -0
  411. package/src/state/suggestions.ts +9 -0
  412. package/src/types.ts +4 -0
  413. package/src/ui/agent-card.tsx +218 -0
  414. package/src/ui/button.tsx +16 -5
  415. package/src/ui/card.tsx +245 -0
  416. package/src/ui/collapsible.tsx +30 -4
  417. package/src/ui/command.tsx +99 -0
  418. package/src/ui/dialog.tsx +217 -0
  419. package/src/ui/dropdown.tsx +375 -13
  420. package/src/ui/editable-label.tsx +112 -0
  421. package/src/ui/hover-card.tsx +28 -5
  422. package/src/ui/icon.tsx +110 -0
  423. package/src/ui/input.tsx +132 -0
  424. package/src/ui/kbd.tsx +118 -0
  425. package/src/ui/nav.tsx +282 -0
  426. package/src/ui/notice.tsx +84 -0
  427. package/src/ui/overlay.tsx +108 -2
  428. package/src/ui/pane-grid.tsx +117 -0
  429. package/src/ui/pane-group.tsx +291 -0
  430. package/src/ui/pane.tsx +193 -0
  431. package/src/ui/popover.tsx +16 -2
  432. package/src/ui/progress-bar.tsx +83 -0
  433. package/src/ui/prompt-dock.tsx +145 -0
  434. package/src/ui/resizable.tsx +69 -1
  435. package/src/ui/scroll-area.tsx +14 -3
  436. package/src/ui/segmented.tsx +109 -0
  437. package/src/ui/settings-group.tsx +67 -0
  438. package/src/ui/skeleton.tsx +62 -5
  439. package/src/ui/stat.tsx +41 -0
  440. package/src/ui/status.tsx +48 -0
  441. package/src/ui/switch.tsx +49 -23
  442. package/src/ui/tabs.tsx +168 -0
  443. package/src/ui/tooltip.tsx +52 -8
  444. package/theme.css +113 -0
  445. package/dist/components/slash-command.d.ts +0 -15
  446. package/dist/elements/chunks/Icon-D_Zmwmwh.js +0 -1
  447. package/dist/elements/chunks/action-icons-PVfIvcuG.js +0 -1
  448. package/dist/elements/chunks/attachments-Dp6vqtrc.js +0 -1
  449. package/dist/elements/chunks/button-CKyYD9G4.js +0 -1
  450. package/dist/elements/chunks/card-Cjho-PYk.js +0 -1
  451. package/dist/elements/chunks/chat-thread-RT808iPf.js +0 -1
  452. package/dist/elements/chunks/check-CDYaMYrM.js +0 -1
  453. package/dist/elements/chunks/chevron-down-3SBiq8HF.js +0 -1
  454. package/dist/elements/chunks/chevron-right-k2UFlIVK.js +0 -1
  455. package/dist/elements/chunks/cn-Dogu6PbV.js +0 -1
  456. package/dist/elements/chunks/code-block-BYUmfFSd.js +0 -1
  457. package/dist/elements/chunks/collapsible-BiNwdOn6.js +0 -1
  458. package/dist/elements/chunks/context-18g6oi4J.js +0 -1
  459. package/dist/elements/chunks/conversation-list-D9Y0NVdo.js +0 -1
  460. package/dist/elements/chunks/default-input-CSAoYYMd.js +0 -1
  461. package/dist/elements/chunks/download-D8K0Je8r.js +0 -1
  462. package/dist/elements/chunks/dropdown-bUs2iTia.js +0 -1
  463. package/dist/elements/chunks/external-link-6mA0wxab.js +0 -1
  464. package/dist/elements/chunks/file-text-DnaYk3Dm.js +0 -1
  465. package/dist/elements/chunks/file-tree-CgtRLlxK.js +0 -1
  466. package/dist/elements/chunks/hover-card-CIGw62gP.js +0 -1
  467. package/dist/elements/chunks/link-BlGSeoPy.js +0 -1
  468. package/dist/elements/chunks/loader-DvhR4qa0.js +0 -1
  469. package/dist/elements/chunks/message-CDZwXjhL.js +0 -1
  470. package/dist/elements/chunks/message-feedback-CYx81QiA.js +0 -1
  471. package/dist/elements/chunks/model-switcher-O_tUhoOT.js +0 -1
  472. package/dist/elements/chunks/overlay-DydXYMQW.js +0 -1
  473. package/dist/elements/chunks/prompt-suggestion-ldlWV5kd.js +0 -1
  474. package/dist/elements/chunks/reasoning-2PUnHlmr.js +0 -1
  475. package/dist/elements/chunks/resizable-B5WNbSeb.js +0 -1
  476. package/dist/elements/chunks/star-t9tI-P0q.js +0 -1
  477. package/dist/elements/chunks/store-C6rIooZz.js +0 -1
  478. package/dist/elements/chunks/text-shimmer-uh9V_KUh.js +0 -1
  479. package/dist/elements/chunks/textarea-3nm6Ia9U.js +0 -1
  480. package/dist/elements/chunks/thumbs-up-BBtguGld.js +0 -1
  481. package/dist/elements/chunks/toast-store-D8cL3fWl.js +0 -1
  482. package/dist/elements/chunks/tool-GJ3vwp1U.js +0 -1
  483. package/dist/elements/chunks/tooltip-DwKAz4XR.js +0 -1
  484. package/dist/elements/chunks/triangle-alert-Ct1zt2JL.js +0 -1
  485. package/dist/elements/chunks/use-card-resolution-D-_0Alzy.js +0 -1
  486. package/dist/elements/chunks/video-mVYiM99-.js +0 -1
  487. package/dist/elements/chunks/x-CzxL27Ch.js +0 -1
  488. package/dist/kitn-chat.es.js +0 -1
  489. package/dist/register-JyTglR6m.js +0 -1
  490. package/dist/register-impl-C6wlSsts.js +0 -77
  491. package/src/components/slash-command.tsx +0 -232
  492. package/src/elements/framework-usage.json +0 -588
  493. /package/dist/{kitn-chat-provider.d.ts → kai-provider.d.ts} +0 -0
  494. /package/dist/{kitn-chat-provider.es.js → kai-provider.es.js} +0 -0
@@ -0,0 +1,110 @@
1
+ import type { Component, JSX } from 'solid-js';
2
+ import {
3
+ Plus, Paperclip, Github, Globe, Sparkles, Settings,
4
+ FileText, Folder, Image, Monitor, MessageCircle, MessageSquare, Search,
5
+ Mic, AudioLines, X, ChevronDown, ChevronLeft,
6
+ Pencil, BookOpen, Code, Smile,
7
+ Share, ArrowLeft, MoreHorizontal,
8
+ House, Clock, Lock, Box, Briefcase, PanelLeft, Circle, SlidersHorizontal, Workflow, SquarePen,
9
+ Archive, ArrowUp, Bookmark, Copy, Ellipsis, ExternalLink, Flag,
10
+ GitBranch, GitPullRequest, ListFilter, RotateCw,
11
+ } from 'lucide-solid';
12
+
13
+ type IconComponent = Component<{ class?: string }>;
14
+
15
+ /** Curated name → lucide-solid component map for item icons in kai-menu /
16
+ * kai-command. Extend here when new named icons are needed. */
17
+ const NAMED_ICONS: Record<string, IconComponent> = {
18
+ plus: Plus,
19
+ paperclip: Paperclip,
20
+ github: Github,
21
+ globe: Globe,
22
+ sparkles: Sparkles,
23
+ settings: Settings,
24
+ 'file-text': FileText,
25
+ folder: Folder,
26
+ image: Image,
27
+ monitor: Monitor,
28
+ 'message-circle': MessageCircle,
29
+ 'message-square': MessageSquare,
30
+ search: Search,
31
+ mic: Mic,
32
+ 'audio-lines': AudioLines,
33
+ x: X,
34
+ 'chevron-down': ChevronDown,
35
+ pencil: Pencil,
36
+ 'book-open': BookOpen,
37
+ code: Code,
38
+ smile: Smile,
39
+ // Header / chrome glyphs.
40
+ share: Share,
41
+ 'arrow-left': ArrowLeft,
42
+ 'more-horizontal': MoreHorizontal,
43
+ 'chevron-left': ChevronLeft,
44
+ // App-shell glyphs (sidebar nav, recents, filters).
45
+ home: House,
46
+ clock: Clock,
47
+ lock: Lock,
48
+ box: Box,
49
+ briefcase: Briefcase,
50
+ 'panel-left': PanelLeft,
51
+ circle: Circle,
52
+ 'sliders-horizontal': SlidersHorizontal,
53
+ workflow: Workflow,
54
+ 'square-pen': SquarePen,
55
+ // Code-app / dev-tool glyphs (git, filters, list actions).
56
+ archive: Archive,
57
+ 'arrow-up': ArrowUp,
58
+ bookmark: Bookmark,
59
+ copy: Copy,
60
+ ellipsis: Ellipsis,
61
+ 'external-link': ExternalLink,
62
+ flag: Flag,
63
+ 'git-branch': GitBranch,
64
+ 'git-pull-request': GitPullRequest,
65
+ 'list-filter': ListFilter,
66
+ 'rotate-cw': RotateCw,
67
+ };
68
+
69
+ /** Render an item icon.
70
+ *
71
+ * Resolution order:
72
+ * 1. Known icon name (e.g. `"paperclip"`) → lucide-solid component.
73
+ * 2. URL / absolute path / data-URI → `<img>`.
74
+ * 3. Anything else → `<span>` text fallback.
75
+ * Returns `null` when `icon` is undefined/empty.
76
+ *
77
+ * The img and span branches render different markup, so they accept different
78
+ * class options: `imgClass` for the `<img>`, `spanClass` for the `<span>`.
79
+ * `class` is a shared fallback. Pass `ariaHidden` to mark the span decorative. */
80
+ export function renderIcon(
81
+ icon: string | undefined,
82
+ opts?: { class?: string; imgClass?: string; spanClass?: string; ariaHidden?: boolean },
83
+ ): JSX.Element {
84
+ if (!icon) return null;
85
+ const Named = NAMED_ICONS[icon];
86
+ if (Named) {
87
+ return <Named class={opts?.imgClass ?? opts?.class ?? 'mr-2 size-4 shrink-0'} />;
88
+ }
89
+ const isUrl = /^(https?:|\/|data:)/.test(icon);
90
+ // DEV footgun guard: a kebab/identifier-shaped string that isn't a URL and
91
+ // isn't a known name is almost certainly a typo'd/unregistered icon (e.g.
92
+ // `icon="share"` before it was added) — it would silently paint as literal
93
+ // text. Warn in dev only; emoji/arbitrary text passes through untouched.
94
+ if (import.meta.env.DEV && !isUrl && /^[a-z][a-z0-9-]*$/.test(icon)) {
95
+ console.warn(
96
+ `[kai-icon] unknown icon name "${icon}" — rendering as text. ` +
97
+ 'Add it to NAMED_ICONS in src/ui/icon.tsx, or pass a URL / an inline SVG via slot="icon".',
98
+ );
99
+ }
100
+ return isUrl
101
+ ? <img src={icon} alt="" class={opts?.imgClass ?? opts?.class ?? 'size-4 shrink-0'} />
102
+ : (
103
+ <span
104
+ class={opts?.spanClass ?? opts?.class ?? 'size-4 shrink-0'}
105
+ aria-hidden={opts?.ariaHidden ? 'true' : undefined}
106
+ >
107
+ {icon}
108
+ </span>
109
+ );
110
+ }
@@ -0,0 +1,132 @@
1
+ import { type JSX, Show, splitProps, createUniqueId } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+
4
+ export interface InputProps extends Omit<JSX.InputHTMLAttributes<HTMLInputElement>, 'onInput' | 'onChange' | 'size'> {
5
+ /** Field label rendered above the control and linked via `for`/`id`. */
6
+ label?: string;
7
+ /** Helper text rendered below the control. */
8
+ hint?: string;
9
+ /** Error text; rendered below the control and flips the field invalid. */
10
+ error?: string;
11
+ /** Control density. Defaults to `md`. */
12
+ size?: 'sm' | 'md';
13
+ /** Force the invalid (destructive-border) state without an `error` string. */
14
+ invalid?: boolean;
15
+ /** Leading affix (icon, unit). Rendered inside the field row, before the input. */
16
+ leading?: JSX.Element;
17
+ /** Trailing affix (icon, inline button). Rendered inside the field row, after the input. */
18
+ trailing?: JSX.Element;
19
+ /** Fires per keystroke with the current value. */
20
+ onValueInput?: (value: string) => void;
21
+ /** Fires on commit (blur) with the current value. */
22
+ onValueChange?: (value: string) => void;
23
+ }
24
+
25
+ // The single source of the field shell styling — lifted verbatim from the
26
+ // `inputBase` constant that used to live in `src/components/form-widgets.tsx`.
27
+ // `Input` now owns it; the form widgets render `Input` rather than re-pasting it.
28
+ export const FIELD_BASE =
29
+ '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:opacity-50 disabled:pointer-events-none';
30
+
31
+ // When a leading/trailing affix is present the border + padding wrap the whole
32
+ // row and the focus ring is driven off the row (`focus-within`), so the input
33
+ // itself sits borderless and transparent inside it.
34
+ const FIELD_ROW =
35
+ 'flex w-full items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm focus-within:outline-none focus-within:ring-2 focus-within:ring-ring';
36
+ const ROW_INPUT =
37
+ 'min-w-0 flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed';
38
+
39
+ const SIZE_SM = 'px-2.5 py-1';
40
+ const INVALID = 'border-destructive dark:border-red-400/70';
41
+
42
+ // Suppress the native search affordances Chrome/WebKit render for `type="search"`.
43
+ // Without this the browser's `::-webkit-search-cancel-button` (×) stacks on top of
44
+ // a custom clear control (e.g. kai-search's `part="clear"`) — a double ×. Applied
45
+ // to the inner `<input>` in BOTH layouts (the field can be `type="search"` either
46
+ // way; kai-search uses the affix layout for its leading icon).
47
+ const SEARCH_RESET =
48
+ '[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-cancel-button]:hidden';
49
+
50
+ /**
51
+ * `Input`: the token-themed single-line text field shell. A label/hint/error
52
+ * stack around a field row that holds an optional `leading` affix, the
53
+ * `<input>`, and an optional `trailing` affix. The shared border/background/ring
54
+ * styling lives here (the single field source the form widgets build on).
55
+ *
56
+ * Parts: `field` (the bordered control), `input`, `label`, `hint`.
57
+ * a11y: a generated id links `<label for>`; `invalid`/`error` set `aria-invalid`;
58
+ * the hint/error text is linked via `aria-describedby`.
59
+ */
60
+ export function Input(props: InputProps): JSX.Element {
61
+ const [local, rest] = splitProps(props, [
62
+ 'class', 'label', 'hint', 'error', 'size', 'invalid',
63
+ 'leading', 'trailing', 'onValueInput', 'onValueChange', 'onBlur', 'id', 'disabled',
64
+ ]);
65
+
66
+ const id = local.id ?? createUniqueId();
67
+ const hintId = `${id}-hint`;
68
+ const isInvalid = () => !!local.invalid || !!local.error;
69
+ const hasHint = () => !!local.error || !!local.hint;
70
+ const hasAffix = () => local.leading != null || local.trailing != null;
71
+
72
+ const inputEl = (cls: string, part: string): JSX.Element => (
73
+ <input
74
+ {...rest}
75
+ id={id}
76
+ part={part}
77
+ disabled={local.disabled}
78
+ aria-invalid={isInvalid() ? 'true' : undefined}
79
+ aria-describedby={hasHint() ? hintId : undefined}
80
+ class={cn(SEARCH_RESET, cls)}
81
+ onInput={(e) => local.onValueInput?.(e.currentTarget.value)}
82
+ onBlur={(e) => {
83
+ const handler = local.onBlur;
84
+ if (typeof handler === 'function') handler(e);
85
+ local.onValueChange?.(e.currentTarget.value);
86
+ }}
87
+ />
88
+ );
89
+
90
+ return (
91
+ <div class="flex w-full flex-col gap-1.5">
92
+ <Show when={local.label}>
93
+ <label part="label" for={id} class="text-sm font-medium text-foreground">
94
+ {local.label}
95
+ </label>
96
+ </Show>
97
+
98
+ <Show
99
+ when={hasAffix()}
100
+ fallback={inputEl(
101
+ cn(FIELD_BASE, local.size === 'sm' && SIZE_SM, isInvalid() && INVALID, local.class),
102
+ 'field input',
103
+ )}
104
+ >
105
+ <div
106
+ part="field"
107
+ class={cn(
108
+ FIELD_ROW,
109
+ local.size === 'sm' && SIZE_SM,
110
+ isInvalid() && INVALID,
111
+ local.disabled && 'opacity-50 pointer-events-none',
112
+ local.class,
113
+ )}
114
+ >
115
+ <Show when={local.leading}>
116
+ <span class="flex shrink-0 items-center text-muted-foreground">{local.leading}</span>
117
+ </Show>
118
+ {inputEl(ROW_INPUT, 'input')}
119
+ <Show when={local.trailing}>
120
+ <span class="flex shrink-0 items-center text-muted-foreground">{local.trailing}</span>
121
+ </Show>
122
+ </div>
123
+ </Show>
124
+
125
+ <Show when={hasHint()}>
126
+ <p part="hint" id={hintId} class={cn('text-xs', local.error ? 'text-destructive' : 'text-muted-foreground')}>
127
+ {local.error ?? local.hint}
128
+ </p>
129
+ </Show>
130
+ </div>
131
+ );
132
+ }
package/src/ui/kbd.tsx ADDED
@@ -0,0 +1,118 @@
1
+ import { type JSX, For, Show, splitProps } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+
4
+ export type KbdPlatform = 'auto' | 'mac' | 'other';
5
+
6
+ export interface KbdProps extends JSX.HTMLAttributes<HTMLElement> {
7
+ /** Shortcut spec — tokens joined by `+` (e.g. `Mod+Shift+K`). When omitted,
8
+ * children render verbatim. Display only; `kai-kbd` does not bind keys. */
9
+ keys?: string;
10
+ /** Glyph platform. `mac` uses ⌘/⌥; `other` uses Ctrl. `auto` (default for the
11
+ * facade) sniffs the OS, SSR-safe (falls back to `other` on the server). */
12
+ platform?: KbdPlatform;
13
+ /** Cap size. Defaults to `md`. */
14
+ size?: 'sm' | 'md';
15
+ }
16
+
17
+ /** SSR-safe mac check: prefers UA-CH `platform`, falls back to `navigator.platform`. */
18
+ function isMacOS(): boolean {
19
+ if (typeof navigator === 'undefined') return false;
20
+ const nav = navigator as Navigator & { userAgentData?: { platform?: string } };
21
+ const platform = nav.userAgentData?.platform ?? nav.platform ?? '';
22
+ return /mac/i.test(platform);
23
+ }
24
+
25
+ /** Resolve `auto` to a concrete platform; `mac`/`other` pass through. */
26
+ function resolvePlatform(platform: KbdPlatform): 'mac' | 'other' {
27
+ if (platform === 'mac') return 'mac';
28
+ if (platform === 'other') return 'other';
29
+ return isMacOS() ? 'mac' : 'other';
30
+ }
31
+
32
+ /** Map a single token to its display glyph. Unknown tokens uppercase (so `k` → `K`). */
33
+ function glyph(token: string, mac: boolean): string {
34
+ const t = token.trim();
35
+ switch (t.toLowerCase()) {
36
+ case 'mod':
37
+ return mac ? '⌘' : 'Ctrl';
38
+ case 'cmd':
39
+ case 'command':
40
+ case 'meta':
41
+ return '⌘';
42
+ case 'ctrl':
43
+ case 'control':
44
+ return 'Ctrl';
45
+ case 'shift':
46
+ return '⇧';
47
+ case 'alt':
48
+ case 'opt':
49
+ case 'option':
50
+ return '⌥';
51
+ case 'arrowup':
52
+ return '↑';
53
+ case 'arrowdown':
54
+ return '↓';
55
+ case 'arrowleft':
56
+ return '←';
57
+ case 'arrowright':
58
+ return '→';
59
+ case 'enter':
60
+ case 'return':
61
+ return '⏎';
62
+ case 'esc':
63
+ case 'escape':
64
+ return 'Esc';
65
+ case 'space':
66
+ return 'Space';
67
+ default:
68
+ return t.toUpperCase();
69
+ }
70
+ }
71
+
72
+ const SIZE: Record<'sm' | 'md', string> = {
73
+ sm: 'h-5 min-w-5 px-1 text-[11px]',
74
+ md: 'h-6 min-w-6 px-1.5 text-xs',
75
+ };
76
+
77
+ /**
78
+ * `Kbd`: a keyboard-shortcut display. Splits `keys` on `+`, maps each token to a
79
+ * platform glyph (`Mod` → ⌘ on mac else Ctrl, `Shift` → ⇧, `ArrowUp` → ↑, …) and
80
+ * renders each as an inset `part="key"` cap, with an empty `part="separator"` hook
81
+ * between caps. Token-driven, light + dark. Display only — it does not bind keys.
82
+ *
83
+ * When `keys` is omitted, the children render verbatim (use any custom content).
84
+ */
85
+ export function Kbd(props: KbdProps): JSX.Element {
86
+ const [local, rest] = splitProps(props, ['keys', 'platform', 'size', 'class', 'children']);
87
+ const mac = () => resolvePlatform(local.platform ?? 'other') === 'mac';
88
+ const tokens = () =>
89
+ (local.keys ?? '')
90
+ .split('+')
91
+ .map((t) => t.trim())
92
+ .filter(Boolean);
93
+
94
+ return (
95
+ <kbd {...rest} class={cn('inline-flex items-center gap-0.5 align-middle font-sans', local.class)}>
96
+ <Show when={local.keys != null} fallback={local.children}>
97
+ <For each={tokens()}>
98
+ {(token, i) => (
99
+ <>
100
+ <Show when={i() > 0}>
101
+ <span part="separator" aria-hidden="true" class="text-muted-foreground" />
102
+ </Show>
103
+ <span
104
+ part="key"
105
+ class={cn(
106
+ 'inline-flex items-center justify-center rounded border border-border bg-muted font-medium leading-none tabular-nums text-muted-foreground',
107
+ SIZE[local.size ?? 'md'],
108
+ )}
109
+ >
110
+ {glyph(token, mac())}
111
+ </span>
112
+ </>
113
+ )}
114
+ </For>
115
+ </Show>
116
+ </kbd>
117
+ );
118
+ }
package/src/ui/nav.tsx ADDED
@@ -0,0 +1,282 @@
1
+ import { For, Show, type JSX, createSignal, splitProps } from 'solid-js';
2
+ import { ChevronRight } from 'lucide-solid';
3
+ import { cn } from '../utils/cn';
4
+ import { renderIcon } from './icon';
5
+ import { Badge } from './badge';
6
+
7
+ /** Status tone for a nav item's dot. `primary` is the theme accent; the rest use
8
+ * the kit's tool hues (the same colors as kai-status / kai-progress-bar). Generic
9
+ * enough for thread/onboarding states (success = "Completed", info = "Working")
10
+ * and task-run states (neutral = queued, info = running, success = done,
11
+ * error = failed). */
12
+ export type NavStatusTone = 'primary' | 'info' | 'success' | 'warning' | 'error' | 'neutral';
13
+
14
+ /** Dot hue per `tone`, mirroring the kai-status / kai-progress-bar token map. */
15
+ const STATUS_DOT_BG: Record<NavStatusTone, string> = {
16
+ primary: 'bg-primary',
17
+ info: 'bg-tool-blue',
18
+ success: 'bg-tool-green',
19
+ warning: 'bg-tool-amber',
20
+ error: 'bg-tool-red',
21
+ neutral: 'bg-muted-foreground',
22
+ };
23
+
24
+ /** A small colored status indicator on a nav row. The `label` (when set) is folded
25
+ * into the row's accessible name; `pulse` adds an animated ping ring. */
26
+ export interface NavItemStatus {
27
+ tone: NavStatusTone;
28
+ /** Short status word ("Working", "Failed"). Shown as muted text and announced. */
29
+ label?: string;
30
+ /** Animated ping ring (disabled under prefers-reduced-motion). */
31
+ pulse?: boolean;
32
+ }
33
+
34
+ export interface KaiNavItem {
35
+ id: string;
36
+ label?: string;
37
+ /** Leading icon: a named icon, an image URL / data-URI, or plain text. */
38
+ icon?: string;
39
+ /** Trailing text pill (e.g. "Beta"). */
40
+ badge?: string;
41
+ /** Trailing icon (a named icon), e.g. an edit/compose affordance. */
42
+ trailing?: string;
43
+ disabled?: boolean;
44
+ /** Nested items. When present, the row becomes a collapsible group: a parent
45
+ * row with a disclosure chevron over an indented child list. Recurses for
46
+ * arbitrary depth (primarily 2 levels, e.g. project -> threads). */
47
+ children?: KaiNavItem[];
48
+ /** A small colored status dot (+ optional label) on the row. */
49
+ status?: NavItemStatus;
50
+ /** Right-aligned muted trailing text (e.g. a relative time, "24d ago").
51
+ * Distinct from `trailing` (a hover icon). */
52
+ meta?: string;
53
+ /** An interactive trailing action button (`icon` is a named icon; `label` is
54
+ * its accessible name). Activating it fires `onItemAction` and does NOT select
55
+ * the row. Distinct from `trailing` (a decorative hover icon, not a button). */
56
+ action?: { icon: string; label: string };
57
+ /** Render an interactive trailing close (×) button. Activating it fires
58
+ * `onItemClose` and does NOT select the row. */
59
+ closable?: boolean;
60
+ }
61
+
62
+ export interface NavProps extends JSX.HTMLAttributes<HTMLElement> {
63
+ items?: KaiNavItem[];
64
+ /** Active item id (drives aria-current + the selected look). */
65
+ value?: string;
66
+ onItemSelect?: (id: string) => void;
67
+ /** A row's trailing `action` button was activated (not a select). */
68
+ onItemAction?: (id: string, action?: { icon: string; label: string }) => void;
69
+ /** A `closable` row's trailing close button was activated (not a select). */
70
+ onItemClose?: (id: string) => void;
71
+ /** Ids of group items collapsed on first render. Groups default to expanded. */
72
+ defaultCollapsed?: string[];
73
+ }
74
+
75
+ export function Nav(props: NavProps) {
76
+ const [local, rest] = splitProps(props, [
77
+ 'items', 'value', 'onItemSelect', 'onItemAction', 'onItemClose', 'defaultCollapsed', 'class',
78
+ ]);
79
+
80
+ // Groups default to expanded; `defaultCollapsed` seeds the closed set. Using a
81
+ // Set keyed by item id keeps expand state independent of the items reference,
82
+ // so late-arriving items (the element sets `items` post-construction) are open.
83
+ const [collapsed, setCollapsed] = createSignal<Set<string>>(
84
+ new Set(local.defaultCollapsed ?? []),
85
+ { equals: false },
86
+ );
87
+ const isExpanded = (id: string) => !collapsed().has(id);
88
+ const toggle = (id: string) =>
89
+ setCollapsed((prev) => {
90
+ const next = new Set(prev);
91
+ if (next.has(id)) next.delete(id);
92
+ else next.add(id);
93
+ return next;
94
+ });
95
+
96
+ return (
97
+ <nav {...rest} class={cn('flex flex-col gap-0.5', local.class)}>
98
+ <NavRows
99
+ items={local.items ?? []}
100
+ depth={0}
101
+ value={local.value}
102
+ onItemSelect={local.onItemSelect}
103
+ onItemAction={local.onItemAction}
104
+ onItemClose={local.onItemClose}
105
+ isExpanded={isExpanded}
106
+ toggle={toggle}
107
+ />
108
+ </nav>
109
+ );
110
+ }
111
+
112
+ interface NavRowsProps {
113
+ items: KaiNavItem[];
114
+ depth: number;
115
+ value?: string;
116
+ onItemSelect?: (id: string) => void;
117
+ onItemAction?: (id: string, action?: { icon: string; label: string }) => void;
118
+ onItemClose?: (id: string) => void;
119
+ isExpanded: (id: string) => boolean;
120
+ toggle: (id: string) => void;
121
+ }
122
+
123
+ /** Render one nesting level. Recurses through `children`. */
124
+ function NavRows(props: NavRowsProps) {
125
+ // When a level has any groups, every row reserves a chevron-width disclosure
126
+ // column (chevron for groups, a spacer for leaves) so icons/labels line up.
127
+ const hasGroups = () => props.items.some((i) => (i.children?.length ?? 0) > 0);
128
+ return (
129
+ <For each={props.items}>
130
+ {(item) => {
131
+ const isGroup = () => (item.children?.length ?? 0) > 0;
132
+ const expanded = () => props.isExpanded(item.id);
133
+ const active = () => props.value === item.id;
134
+ const tone = () => item.status?.tone ?? 'neutral';
135
+ // Fold the status word + meta into the accessible name so AT gets them
136
+ // (the dot/label/meta nodes are aria-hidden to avoid double-announcing).
137
+ const ariaLabel = () => {
138
+ if (!item.status?.label && !item.meta) return undefined;
139
+ return [item.label, item.status?.label, item.meta].filter(Boolean).join(', ');
140
+ };
141
+ const onClick = () => {
142
+ if (item.disabled) return;
143
+ if (isGroup()) props.toggle(item.id);
144
+ else props.onItemSelect?.(item.id);
145
+ };
146
+ // Native buttons handle Enter/Space; add ArrowRight/Left for tree parity.
147
+ const onKeyDown = (e: KeyboardEvent) => {
148
+ if (!isGroup() || item.disabled) return;
149
+ if (e.key === 'ArrowRight' && !expanded()) { e.preventDefault(); props.toggle(item.id); }
150
+ else if (e.key === 'ArrowLeft' && expanded()) { e.preventDefault(); props.toggle(item.id); }
151
+ };
152
+ return (
153
+ <>
154
+ {/* The row is a non-interactive wrapper: the selectable item button
155
+ and the trailing action/close button are SIBLINGS, never nested
156
+ (a button inside a button is invalid HTML — browsers hoist it out
157
+ — and trips axe's nested-interactive). Each control owns its own
158
+ click, so a trailing-button click never reaches the item button's
159
+ select handler; no event-delegation discrimination is needed. */}
160
+ <div
161
+ class={cn(
162
+ 'group relative flex items-center rounded-md transition-colors',
163
+ 'hover:bg-accent',
164
+ active() && 'bg-accent',
165
+ item.disabled && 'pointer-events-none opacity-50',
166
+ )}
167
+ >
168
+ <button
169
+ part="item"
170
+ type="button"
171
+ disabled={item.disabled}
172
+ aria-current={active() ? 'page' : undefined}
173
+ aria-expanded={isGroup() ? expanded() : undefined}
174
+ aria-label={ariaLabel()}
175
+ onClick={onClick}
176
+ onKeyDown={onKeyDown}
177
+ style={props.depth > 0 ? { 'padding-left': `${10 + props.depth * 16}px` } : undefined}
178
+ class={cn(
179
+ 'flex min-w-0 flex-1 items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors',
180
+ 'text-muted-foreground group-hover:text-foreground',
181
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
182
+ 'disabled:pointer-events-none',
183
+ active() && 'font-medium text-foreground',
184
+ )}
185
+ >
186
+ <Show
187
+ when={isGroup()}
188
+ fallback={hasGroups() ? <span class="size-4 shrink-0" aria-hidden="true" /> : null}
189
+ >
190
+ <span
191
+ part="chevron"
192
+ aria-hidden="true"
193
+ class={cn('flex shrink-0 items-center opacity-60 transition-transform', expanded() && 'rotate-90')}
194
+ >
195
+ <ChevronRight size={16} />
196
+ </span>
197
+ </Show>
198
+ <Show when={item.icon}>{renderIcon(item.icon, { class: 'size-4 shrink-0' })}</Show>
199
+ <span class="min-w-0 flex-1 truncate">{item.label}</span>
200
+ <Show when={item.status}>
201
+ <span part="status" class="flex shrink-0 items-center gap-1.5" aria-hidden="true">
202
+ <span class="relative inline-flex">
203
+ <Show when={item.status!.pulse}>
204
+ <span
205
+ class={cn(
206
+ 'absolute inline-flex h-full w-full animate-ping rounded-full opacity-75 motion-reduce:hidden',
207
+ STATUS_DOT_BG[tone()],
208
+ )}
209
+ />
210
+ </Show>
211
+ <span class={cn('relative inline-block size-2 rounded-full', STATUS_DOT_BG[tone()])} />
212
+ </span>
213
+ <Show when={item.status!.label}>
214
+ <span class="text-xs text-muted-foreground">{item.status!.label}</span>
215
+ </Show>
216
+ </span>
217
+ </Show>
218
+ <Show when={item.meta}>
219
+ <span part="meta" class="shrink-0 text-xs tabular-nums text-muted-foreground" aria-hidden="true">
220
+ {item.meta}
221
+ </span>
222
+ </Show>
223
+ <Show when={item.badge}>
224
+ <Badge variant="default" class="px-1.5 py-0 text-[0.625rem] font-medium uppercase tracking-wide">{item.badge}</Badge>
225
+ </Show>
226
+ <Show when={item.trailing}>{renderIcon(item.trailing, { class: 'size-4 shrink-0 opacity-0 transition-opacity group-hover:opacity-60' })}</Show>
227
+ </button>
228
+ <Show when={item.action}>
229
+ <button
230
+ part="item-action"
231
+ data-nav-action="action"
232
+ type="button"
233
+ aria-label={item.action!.label}
234
+ onClick={() => props.onItemAction?.(item.id, item.action)}
235
+ class={cn(
236
+ 'mr-1 flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground',
237
+ 'opacity-0 transition-opacity hover:bg-background/70 hover:text-foreground',
238
+ 'focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
239
+ 'group-hover:opacity-100',
240
+ )}
241
+ >
242
+ {renderIcon(item.action!.icon, { class: 'size-4' })}
243
+ </button>
244
+ </Show>
245
+ <Show when={item.closable}>
246
+ <button
247
+ part="item-action"
248
+ data-nav-action="close"
249
+ type="button"
250
+ aria-label={item.label ? `Remove ${item.label}` : 'Remove'}
251
+ onClick={() => props.onItemClose?.(item.id)}
252
+ class={cn(
253
+ 'mr-1 flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground',
254
+ 'opacity-0 transition-opacity hover:bg-background/70 hover:text-foreground',
255
+ 'focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
256
+ 'group-hover:opacity-100',
257
+ )}
258
+ >
259
+ {renderIcon('x', { class: 'size-4' })}
260
+ </button>
261
+ </Show>
262
+ </div>
263
+ <Show when={isGroup() && expanded()}>
264
+ <div part="group" role="group">
265
+ <NavRows
266
+ items={item.children!}
267
+ depth={props.depth + 1}
268
+ value={props.value}
269
+ onItemSelect={props.onItemSelect}
270
+ onItemAction={props.onItemAction}
271
+ onItemClose={props.onItemClose}
272
+ isExpanded={props.isExpanded}
273
+ toggle={props.toggle}
274
+ />
275
+ </div>
276
+ </Show>
277
+ </>
278
+ );
279
+ }}
280
+ </For>
281
+ );
282
+ }