@keboola/design 11.9.0 → 13.0.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 (367) hide show
  1. package/AGENTS.md +45 -14
  2. package/CHANGELOG.md +246 -0
  3. package/COMPONENT-API.md +352 -294
  4. package/README.md +11 -1
  5. package/dist/{AIKit-ELIsegJz.js → AIKit-DcOlHIBr.js} +420 -247
  6. package/dist/AIKit-DcOlHIBr.js.map +1 -0
  7. package/dist/{Alert-BsIM_oFK.js → Alert-UI-CIs-1.js} +16 -16
  8. package/dist/Alert-UI-CIs-1.js.map +1 -0
  9. package/dist/{AlertModal-BGJdaRRj.js → AlertModal-mbSnuuka.js} +8 -8
  10. package/dist/AlertModal-mbSnuuka.js.map +1 -0
  11. package/dist/{AppShell-DRgJxpXW.js → AppShell-WSQabK8r.js} +15 -52
  12. package/dist/AppShell-WSQabK8r.js.map +1 -0
  13. package/dist/{AutoSaveTextarea-BeuWuszr.js → AutoSaveTextarea-C5iIr2Ts.js} +2 -2
  14. package/dist/{AutoSaveTextarea-BeuWuszr.js.map → AutoSaveTextarea-C5iIr2Ts.js.map} +1 -1
  15. package/dist/{Badge-o5jURtpB.js → Badge-DIjhwPvh.js} +4 -9
  16. package/dist/Badge-DIjhwPvh.js.map +1 -0
  17. package/dist/{BrandEditor-BsTBM3s6.js → BrandEditor-Bkd6dhb8.js} +7 -7
  18. package/dist/{BrandEditor-BsTBM3s6.js.map → BrandEditor-Bkd6dhb8.js.map} +1 -1
  19. package/dist/{BrandLogo-CoKBk4MI.js → BrandLogo-CKpVSZb6.js} +4 -4
  20. package/dist/BrandLogo-CKpVSZb6.js.map +1 -0
  21. package/dist/{Breadcrumb-xqEUxfwn.js → Breadcrumb-BUoAZyhO.js} +8 -14
  22. package/dist/Breadcrumb-BUoAZyhO.js.map +1 -0
  23. package/dist/{Button-DsRrU5F1.js → Button-B21oVcPU.js} +31 -14
  24. package/dist/Button-B21oVcPU.js.map +1 -0
  25. package/dist/{Calendar-cG5NIJ4O.js → Calendar-DFtmZzbF.js} +99 -99
  26. package/dist/Calendar-DFtmZzbF.js.map +1 -0
  27. package/dist/{Card-Dyo7055Q.js → Card-BQmBWHXl.js} +35 -7
  28. package/dist/Card-BQmBWHXl.js.map +1 -0
  29. package/dist/{Checkbox-Dq5fPNrO.js → Checkbox-8yIIgY7g.js} +4 -4
  30. package/dist/Checkbox-8yIIgY7g.js.map +1 -0
  31. package/dist/{Clipboard-BZitKiSA.js → Clipboard-_xf6hMDD.js} +2 -2
  32. package/dist/{Clipboard-BZitKiSA.js.map → Clipboard-_xf6hMDD.js.map} +1 -1
  33. package/dist/{ClipboardButton-KHQnPEh_.js → ClipboardButton-srUgWgwH.js} +4 -4
  34. package/dist/{ClipboardButton-KHQnPEh_.js.map → ClipboardButton-srUgWgwH.js.map} +1 -1
  35. package/dist/{CodeEditor-BJKJXA0c.js → CodeEditor-jZMw1ioj.js} +55 -165
  36. package/dist/CodeEditor-jZMw1ioj.js.map +1 -0
  37. package/dist/{ColorInput-Bj3dfPFA.js → ColorInput-B3gBZ8Ya.js} +12 -27
  38. package/dist/ColorInput-B3gBZ8Ya.js.map +1 -0
  39. package/dist/{ColorModeSwitch-CWt-DZGC.js → ColorModeSwitch-Bw9wMyoL.js} +7 -17
  40. package/dist/ColorModeSwitch-Bw9wMyoL.js.map +1 -0
  41. package/dist/Combobox-C_MTpQ1l.js +1081 -0
  42. package/dist/Combobox-C_MTpQ1l.js.map +1 -0
  43. package/dist/{Command-BDU3C1gg.js → Command-BQPiZCNz.js} +4 -4
  44. package/dist/{Command-BDU3C1gg.js.map → Command-BQPiZCNz.js.map} +1 -1
  45. package/dist/{CopyField-BHRQUyuF.js → CopyField-Ba7h8MqZ.js} +19 -55
  46. package/dist/{CopyField-BHRQUyuF.js.map → CopyField-Ba7h8MqZ.js.map} +1 -1
  47. package/dist/{DateInput-B6N8g0ra.js → DateInput-D0nWUasV.js} +6 -6
  48. package/dist/{DateInput-B6N8g0ra.js.map → DateInput-D0nWUasV.js.map} +1 -1
  49. package/dist/{DatePicker-nA8YdE84.js → DatePicker-CtSa9b3V.js} +3 -3
  50. package/dist/{DatePicker-nA8YdE84.js.map → DatePicker-CtSa9b3V.js.map} +1 -1
  51. package/dist/{DateRangeInput-7M9Z4cVO.js → DateRangeInput-i6wE45-V.js} +10 -14
  52. package/dist/DateRangeInput-i6wE45-V.js.map +1 -0
  53. package/dist/{DateRangePicker-DjSUFjh4.js → DateRangePicker-BAnuz6vO.js} +9 -13
  54. package/dist/DateRangePicker-BAnuz6vO.js.map +1 -0
  55. package/dist/DiffView-ppTIzh2h.js.map +1 -1
  56. package/dist/{DropdownMenu-CgVwhDvl.js → DropdownMenu-gRM-A8qU.js} +3 -3
  57. package/dist/DropdownMenu-gRM-A8qU.js.map +1 -0
  58. package/dist/{FieldItem-DZGIdiXE.js → FieldItem-BHViBJS3.js} +12 -25
  59. package/dist/FieldItem-BHViBJS3.js.map +1 -0
  60. package/dist/{FieldItem-vhIJW4pj.d.ts → FieldItem-CPN1xjN5.d.ts} +11 -25
  61. package/dist/{FileUpload-DYQCTGjU.js → FileUpload-Ch2hrIWq.js} +11 -16
  62. package/dist/{FileUpload-DYQCTGjU.js.map → FileUpload-Ch2hrIWq.js.map} +1 -1
  63. package/dist/{Form-CcK7dRrC.js → Form-VkzfVPyT.js} +15 -23
  64. package/dist/{Form-CcK7dRrC.js.map → Form-VkzfVPyT.js.map} +1 -1
  65. package/dist/{FormGroup-DR_gZ25-.js → FormGroup-CmCppxOn.js} +11 -17
  66. package/dist/FormGroup-CmCppxOn.js.map +1 -0
  67. package/dist/{FullScreenModal-CN9nAs72.js → FullScreenModal-Cwnm8WdM.js} +15 -29
  68. package/dist/FullScreenModal-Cwnm8WdM.js.map +1 -0
  69. package/dist/{HoverCard-B9ZMJxjn.js → HoverCard-B8OKu9XZ.js} +2 -2
  70. package/dist/{HoverCard-B9ZMJxjn.js.map → HoverCard-B8OKu9XZ.js.map} +1 -1
  71. package/dist/{InputGroup-Do8K2hvD.js → InputGroup-CrlMRz4X.js} +11 -13
  72. package/dist/InputGroup-CrlMRz4X.js.map +1 -0
  73. package/dist/{Kbd-LCFMumoq.js → Kbd-J4n1J6i4.js} +23 -13
  74. package/dist/Kbd-J4n1J6i4.js.map +1 -0
  75. package/dist/{MarkedText-DNGEUVWc.js → MarkedText-DlmlJO0u.js} +2 -2
  76. package/dist/{MarkedText-DNGEUVWc.js.map → MarkedText-DlmlJO0u.js.map} +1 -1
  77. package/dist/{MermaidDiagram-ZG0iiDV_.js → MermaidDiagram-B30x2yV7.js} +16 -34
  78. package/dist/MermaidDiagram-B30x2yV7.js.map +1 -0
  79. package/dist/{Modal-I3VWk2oo.js → Modal-BkSuS9Yz.js} +17 -34
  80. package/dist/Modal-BkSuS9Yz.js.map +1 -0
  81. package/dist/{ModalTitle-CeLM_mX9.js → ModalTitle-BxopkIoQ.js} +3 -3
  82. package/dist/ModalTitle-BxopkIoQ.js.map +1 -0
  83. package/dist/{MultiActionButtonGroup-CYZ8IgSf.js → MultiActionButtonGroup-AWLj1Tg7.js} +3 -3
  84. package/dist/{MultiActionButtonGroup-CYZ8IgSf.js.map → MultiActionButtonGroup-AWLj1Tg7.js.map} +1 -1
  85. package/dist/{Navbar-Liy1TPI7.js → Navbar-B_DNpv5s.js} +9 -13
  86. package/dist/{Navbar-Liy1TPI7.js.map → Navbar-B_DNpv5s.js.map} +1 -1
  87. package/dist/{OtpInput-CbbymqYs.js → OtpInput-I8B8Vjdl.js} +2 -2
  88. package/dist/{OtpInput-CbbymqYs.js.map → OtpInput-I8B8Vjdl.js.map} +1 -1
  89. package/dist/{PageHeader-Q3IpCnQd.js → PageHeader-CgfyXpf1.js} +13 -26
  90. package/dist/PageHeader-CgfyXpf1.js.map +1 -0
  91. package/dist/{PageLayout-DSVM-ASz.js → PageLayout-DiAAILuS.js} +12 -20
  92. package/dist/PageLayout-DiAAILuS.js.map +1 -0
  93. package/dist/{PageToolbar-BKVJiDAL.js → PageToolbar-CIW0VmKe.js} +12 -42
  94. package/dist/PageToolbar-CIW0VmKe.js.map +1 -0
  95. package/dist/{Pagination-C_b7Xhey.js → Pagination-DH3f6LN0.js} +3 -3
  96. package/dist/{Pagination-C_b7Xhey.js.map → Pagination-DH3f6LN0.js.map} +1 -1
  97. package/dist/{Popconfirm-5pBzT_HQ.js → Popconfirm-Dhzn7sy6.js} +3 -3
  98. package/dist/{Popconfirm-5pBzT_HQ.js.map → Popconfirm-Dhzn7sy6.js.map} +1 -1
  99. package/dist/{Popover-BEbQrC_P.js → Popover-CEk915I1.js} +4 -3
  100. package/dist/Popover-CEk915I1.js.map +1 -0
  101. package/dist/{RadioGroup-Byc2mym4.js → RadioGroup-CBPRLRN0.js} +4 -4
  102. package/dist/{RadioGroup-Byc2mym4.js.map → RadioGroup-CBPRLRN0.js.map} +1 -1
  103. package/dist/{Search-Dhig_hPD.js → Search-DmkQo716.js} +3 -3
  104. package/dist/{Search-Dhig_hPD.js.map → Search-DmkQo716.js.map} +1 -1
  105. package/dist/{SearchInput-92r2bkA7.js → SearchInput-D3qMkHaW.js} +8 -13
  106. package/dist/SearchInput-D3qMkHaW.js.map +1 -0
  107. package/dist/{SectionNav-CNLJsiVG.js → SectionNav-TU5i3JeM.js} +10 -42
  108. package/dist/SectionNav-TU5i3JeM.js.map +1 -0
  109. package/dist/{Select-DV3Hak_l.js → Select-B4uK0QVm.js} +10 -12
  110. package/dist/Select-B4uK0QVm.js.map +1 -0
  111. package/dist/{Sheet-OparjIi-.js → Sheet-D0fAE14T.js} +5 -9
  112. package/dist/Sheet-D0fAE14T.js.map +1 -0
  113. package/dist/{Sidebar-CLEbBtNY.js → Sidebar-ZLMNIbg5.js} +16 -33
  114. package/dist/Sidebar-ZLMNIbg5.js.map +1 -0
  115. package/dist/{Slider-DLclvRws.js → Slider-B0ou9r0B.js} +3 -3
  116. package/dist/{Slider-DLclvRws.js.map → Slider-B0ou9r0B.js.map} +1 -1
  117. package/dist/{Stepper-C8sm9ZFM.js → Stepper-BLkLhfnd.js} +6 -9
  118. package/dist/{Stepper-C8sm9ZFM.js.map → Stepper-BLkLhfnd.js.map} +1 -1
  119. package/dist/{Switch-B0WdGSjz.js → Switch-DZvNePOm.js} +2 -2
  120. package/dist/{Switch-B0WdGSjz.js.map → Switch-DZvNePOm.js.map} +1 -1
  121. package/dist/{Table-CvYuTuNU.js → Table-BaVT5Tkn.js} +6 -11
  122. package/dist/{Table-CvYuTuNU.js.map → Table-BaVT5Tkn.js.map} +1 -1
  123. package/dist/{Tabs-eB974xJf.js → Tabs-CTDmye1N.js} +3 -3
  124. package/dist/{Tabs-eB974xJf.js.map → Tabs-CTDmye1N.js.map} +1 -1
  125. package/dist/{TextInput-CFTf4Cz1.js → TextInput-BWYXbfs4.js} +10 -14
  126. package/dist/TextInput-BWYXbfs4.js.map +1 -0
  127. package/dist/{ThemeProvider-DAmNn9Sd.js → ThemeProvider-VF38VfUa.js} +13 -31
  128. package/dist/ThemeProvider-VF38VfUa.js.map +1 -0
  129. package/dist/{TimePicker-BGeqc-Ed.js → TimePicker-CDGYrRxi.js} +2 -2
  130. package/dist/{TimePicker-BGeqc-Ed.js.map → TimePicker-CDGYrRxi.js.map} +1 -1
  131. package/dist/{Timeline-C8-wnRRr.js → Timeline-B-I6sb0M.js} +2 -2
  132. package/dist/{Timeline-C8-wnRRr.js.map → Timeline-B-I6sb0M.js.map} +1 -1
  133. package/dist/{Toast-Dr6Uw7ao.js → Toast-PfQf3vj-.js} +2 -2
  134. package/dist/{Toast-Dr6Uw7ao.js.map → Toast-PfQf3vj-.js.map} +1 -1
  135. package/dist/{Workbench-UR2FBC7I.js → Workbench-DmZzpkj7.js} +2 -2
  136. package/dist/{Workbench-UR2FBC7I.js.map → Workbench-DmZzpkj7.js.map} +1 -1
  137. package/dist/{ZoomableSvg-BZlk3PFJ.js → ZoomableSvg-C65nta_r.js} +5 -19
  138. package/dist/ZoomableSvg-C65nta_r.js.map +1 -0
  139. package/dist/charts.d.ts +21 -52
  140. package/dist/charts.js +13 -39
  141. package/dist/charts.js.map +1 -1
  142. package/dist/{context-D7X470dy.js → context-U293r3v-.js} +5 -9
  143. package/dist/context-U293r3v-.js.map +1 -0
  144. package/dist/editors.d.ts +27 -124
  145. package/dist/editors.js +29 -71
  146. package/dist/editors.js.map +1 -1
  147. package/dist/{findLiteralMatches-BGBw8Vbl.js → findLiteralMatches-DgGLzyG0.js} +8 -20
  148. package/dist/findLiteralMatches-DgGLzyG0.js.map +1 -0
  149. package/dist/flint.d.ts +3 -6
  150. package/dist/flint.js +31 -80
  151. package/dist/flint.js.map +1 -1
  152. package/dist/{icons-Cek151jC.js → icons-wcA70iQx.js} +105 -2
  153. package/dist/icons-wcA70iQx.js.map +1 -0
  154. package/dist/icons.d.ts +2 -2
  155. package/dist/icons.js +2 -2
  156. package/dist/{index-m-kQEWiY.d.ts → index-B7k-09i1.d.ts} +14 -2
  157. package/dist/index.d.ts +1180 -1867
  158. package/dist/index.js +69 -68
  159. package/dist/internal/blocks/AIKit.js +2 -2
  160. package/dist/internal/blocks/AppShell.js +1 -1
  161. package/dist/internal/blocks/AuthLayout.js +7 -27
  162. package/dist/internal/blocks/AuthLayout.js.map +1 -1
  163. package/dist/internal/blocks/AutoSaveTextarea.js +1 -1
  164. package/dist/internal/blocks/BrandEditor.js +1 -1
  165. package/dist/internal/blocks/ChartPanel.js +4 -8
  166. package/dist/internal/blocks/ChartPanel.js.map +1 -1
  167. package/dist/internal/blocks/ChipEditor.js +13 -21
  168. package/dist/internal/blocks/ChipEditor.js.map +1 -1
  169. package/dist/internal/blocks/ClipboardButton.js +1 -1
  170. package/dist/internal/blocks/ColorInput.js +1 -1
  171. package/dist/internal/blocks/ColorModeSwitch.js +1 -1
  172. package/dist/internal/blocks/Command.js +1 -1
  173. package/dist/internal/blocks/CopyField.js +1 -1
  174. package/dist/internal/blocks/DashboardHero.js +4 -7
  175. package/dist/internal/blocks/DashboardHero.js.map +1 -1
  176. package/dist/internal/blocks/DateInput.js +1 -1
  177. package/dist/internal/blocks/DateRangeInput.js +1 -1
  178. package/dist/internal/blocks/EmptyConfigurationActionCard.js +1 -1
  179. package/dist/internal/blocks/EmptyState.js +5 -9
  180. package/dist/internal/blocks/EmptyState.js.map +1 -1
  181. package/dist/internal/blocks/ErrorState.js +5 -9
  182. package/dist/internal/blocks/ErrorState.js.map +1 -1
  183. package/dist/internal/blocks/ExportMenu.js +6 -10
  184. package/dist/internal/blocks/ExportMenu.js.map +1 -1
  185. package/dist/internal/blocks/Form/connectors/ComboboxField.js +40 -0
  186. package/dist/internal/blocks/Form/connectors/ComboboxField.js.map +1 -0
  187. package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +5 -22
  188. package/dist/internal/blocks/Form/connectors/MultiComboboxField.js.map +1 -1
  189. package/dist/internal/blocks/Form.js +1 -1
  190. package/dist/internal/blocks/FormGroup.js +1 -1
  191. package/dist/internal/blocks/GoogleButton.js +1 -1
  192. package/dist/internal/blocks/MicrosoftButton.js +1 -1
  193. package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
  194. package/dist/internal/blocks/MultiCombobox.js +160 -154
  195. package/dist/internal/blocks/MultiCombobox.js.map +1 -1
  196. package/dist/internal/blocks/NumberInput.js +1 -1
  197. package/dist/internal/blocks/OtpInput.js +1 -1
  198. package/dist/internal/blocks/PageHeader.js +1 -1
  199. package/dist/internal/blocks/PageLayout.js +1 -1
  200. package/dist/internal/blocks/PageToolbar.js +1 -1
  201. package/dist/internal/blocks/PasswordInput.js +4 -4
  202. package/dist/internal/blocks/PasswordInput.js.map +1 -1
  203. package/dist/internal/blocks/SearchInput.js +1 -1
  204. package/dist/internal/blocks/SearchInputWithNavigation.js +4 -4
  205. package/dist/internal/blocks/SectionNav.js +1 -1
  206. package/dist/internal/blocks/SplitButton.js +3 -3
  207. package/dist/internal/blocks/StatCard.js +4 -8
  208. package/dist/internal/blocks/StatCard.js.map +1 -1
  209. package/dist/internal/blocks/TextInput.js +1 -1
  210. package/dist/internal/blocks/Textarea.js +2 -2
  211. package/dist/internal/blocks/TimeInput.js +5 -5
  212. package/dist/internal/blocks/TimeInput.js.map +1 -1
  213. package/dist/internal/components/Alert.js +1 -1
  214. package/dist/internal/components/AlertModal.js +1 -1
  215. package/dist/internal/components/Avatar.js +1 -1
  216. package/dist/internal/components/Badge.js +1 -1
  217. package/dist/internal/components/BrandLogo.js +1 -1
  218. package/dist/internal/components/Breadcrumb.js +1 -1
  219. package/dist/internal/components/Button.js +1 -1
  220. package/dist/internal/components/ButtonGroup.js.map +1 -1
  221. package/dist/internal/components/Calendar.js +1 -1
  222. package/dist/internal/components/Card.js +1 -1
  223. package/dist/internal/components/Checkbox.js +1 -1
  224. package/dist/internal/components/Clipboard.js +1 -1
  225. package/dist/internal/components/CollapsiblePanel.js +3 -3
  226. package/dist/internal/components/CollapsiblePanel.js.map +1 -1
  227. package/dist/internal/components/Combobox.js +2 -2
  228. package/dist/internal/components/DatePicker.js +1 -1
  229. package/dist/internal/components/DateRangePicker.js +1 -1
  230. package/dist/internal/components/DropdownMenu.js +1 -1
  231. package/dist/internal/components/FileUpload.js +1 -1
  232. package/dist/internal/components/FullScreenModal.js +1 -1
  233. package/dist/internal/components/HoverCard.js +1 -1
  234. package/dist/internal/components/InputGroup.js +1 -1
  235. package/dist/internal/components/Kbd.js +1 -1
  236. package/dist/internal/components/MarkedText.js +1 -1
  237. package/dist/internal/components/MergeEditor/ConflictStatus/ConflictStatusIndicator.js +1 -1
  238. package/dist/internal/components/Modal.js +1 -1
  239. package/dist/internal/components/Navbar.js +1 -1
  240. package/dist/internal/components/Pagination.js +1 -1
  241. package/dist/internal/components/PanelWithDetails.js +1 -1
  242. package/dist/internal/components/Popconfirm.js +1 -1
  243. package/dist/internal/components/Popover.js +1 -1
  244. package/dist/internal/components/RadioGroup.js +1 -1
  245. package/dist/internal/components/Search.js +1 -1
  246. package/dist/internal/components/Select.js +1 -1
  247. package/dist/internal/components/Separator.js.map +1 -1
  248. package/dist/internal/components/Sheet.js +1 -1
  249. package/dist/internal/components/Sidebar.js +1 -1
  250. package/dist/internal/components/Skeleton/Skeleton.js +5 -9
  251. package/dist/internal/components/Skeleton/Skeleton.js.map +1 -1
  252. package/dist/internal/components/Slider.js +1 -1
  253. package/dist/internal/components/SpotlightBackdrop.js +3 -8
  254. package/dist/internal/components/SpotlightBackdrop.js.map +1 -1
  255. package/dist/internal/components/Stepper.js +1 -1
  256. package/dist/internal/components/Switch.js +1 -1
  257. package/dist/internal/components/Table.js +1 -1
  258. package/dist/internal/components/Tabs.js +1 -1
  259. package/dist/internal/components/ThemeProvider.js +3 -3
  260. package/dist/internal/components/TimePicker.js +1 -1
  261. package/dist/internal/components/Timeline.js +1 -1
  262. package/dist/internal/components/Toast.js +2 -2
  263. package/dist/internal/components/Tooltip.js +1 -1
  264. package/dist/internal/components/Truncated.js +4 -8
  265. package/dist/internal/components/Truncated.js.map +1 -1
  266. package/dist/internal/components/Workbench.js +1 -1
  267. package/dist/internal/components/ZoomableSvg.js +1 -1
  268. package/dist/internal/hooks/useScrollbarPosition.js +1 -1
  269. package/dist/internal/hooks/useStableCallback.js +4 -13
  270. package/dist/internal/hooks/useStableCallback.js.map +1 -1
  271. package/dist/internal/utils/accessibleName.js +11 -0
  272. package/dist/internal/utils/accessibleName.js.map +1 -0
  273. package/dist/internal/utils.js +2 -2
  274. package/dist/markdown-editor.d.ts +15 -37
  275. package/dist/markdown-editor.js +17 -23
  276. package/dist/markdown-editor.js.map +1 -1
  277. package/dist/mermaid.d.ts +4 -6
  278. package/dist/mermaid.js +1 -1
  279. package/dist/rjsf.d.ts +9 -19
  280. package/dist/rjsf.js +44 -84
  281. package/dist/rjsf.js.map +1 -1
  282. package/dist/{styles-BAFscjSV.js → styles-gzcjOyKv.js} +6 -6
  283. package/dist/styles-gzcjOyKv.js.map +1 -0
  284. package/dist/styles.css +1 -1
  285. package/dist/svgTransforms-Dw2hRA-i.js +51 -0
  286. package/dist/svgTransforms-Dw2hRA-i.js.map +1 -0
  287. package/dist/testing.d.ts +3 -10
  288. package/dist/testing.js +7 -15
  289. package/dist/testing.js.map +1 -1
  290. package/dist/themeTokens-7DEab0kp.js +31 -0
  291. package/dist/themeTokens-7DEab0kp.js.map +1 -0
  292. package/dist/{toast-DIC0iNRf.js → toast-CyXEdaCZ.js} +3 -3
  293. package/dist/toast-CyXEdaCZ.js.map +1 -0
  294. package/dist/{themeScopeContext-D7fSyb06.js → useColorSchemeState-Cw4MCXUs.js} +14 -14
  295. package/dist/useColorSchemeState-Cw4MCXUs.js.map +1 -0
  296. package/dist/{useControllableState-M6494mCT.js → useControllableState-CUYXMsfz.js} +3 -7
  297. package/dist/useControllableState-CUYXMsfz.js.map +1 -0
  298. package/dist/useElement-D-RjrGa4.js +21 -0
  299. package/dist/useElement-D-RjrGa4.js.map +1 -0
  300. package/dist/useThemeAttributes-B199Regy.js +67 -0
  301. package/dist/useThemeAttributes-B199Regy.js.map +1 -0
  302. package/dist/{utils-BW4AE8VP.js → utils-Bd28bWgm.js} +31 -26
  303. package/dist/utils-Bd28bWgm.js.map +1 -0
  304. package/dist/vega.d.ts +8 -30
  305. package/dist/vega.js +5 -13
  306. package/dist/vega.js.map +1 -1
  307. package/dist/{vegaTheme-CSvFEMBl.js → vegaTheme-VQMWTlBw.js} +7 -21
  308. package/dist/vegaTheme-VQMWTlBw.js.map +1 -0
  309. package/package.json +28 -27
  310. package/src/styles.css +120 -14
  311. package/src/theme.css +130 -5
  312. package/dist/AIKit-ELIsegJz.js.map +0 -1
  313. package/dist/Alert-BsIM_oFK.js.map +0 -1
  314. package/dist/AlertModal-BGJdaRRj.js.map +0 -1
  315. package/dist/AppShell-DRgJxpXW.js.map +0 -1
  316. package/dist/Badge-o5jURtpB.js.map +0 -1
  317. package/dist/BrandLogo-CoKBk4MI.js.map +0 -1
  318. package/dist/Breadcrumb-xqEUxfwn.js.map +0 -1
  319. package/dist/Button-DsRrU5F1.js.map +0 -1
  320. package/dist/Calendar-cG5NIJ4O.js.map +0 -1
  321. package/dist/Card-Dyo7055Q.js.map +0 -1
  322. package/dist/Checkbox-Dq5fPNrO.js.map +0 -1
  323. package/dist/CodeEditor-BJKJXA0c.js.map +0 -1
  324. package/dist/ColorInput-Bj3dfPFA.js.map +0 -1
  325. package/dist/ColorModeSwitch-CWt-DZGC.js.map +0 -1
  326. package/dist/Combobox-D8PrEqTG.js +0 -619
  327. package/dist/Combobox-D8PrEqTG.js.map +0 -1
  328. package/dist/DateRangeInput-7M9Z4cVO.js.map +0 -1
  329. package/dist/DateRangePicker-DjSUFjh4.js.map +0 -1
  330. package/dist/DropdownMenu-CgVwhDvl.js.map +0 -1
  331. package/dist/FieldItem-DZGIdiXE.js.map +0 -1
  332. package/dist/FormGroup-DR_gZ25-.js.map +0 -1
  333. package/dist/FullScreenModal-CN9nAs72.js.map +0 -1
  334. package/dist/InputGroup-Do8K2hvD.js.map +0 -1
  335. package/dist/Kbd-LCFMumoq.js.map +0 -1
  336. package/dist/MermaidDiagram-ZG0iiDV_.js.map +0 -1
  337. package/dist/Modal-I3VWk2oo.js.map +0 -1
  338. package/dist/ModalTitle-CeLM_mX9.js.map +0 -1
  339. package/dist/PageHeader-Q3IpCnQd.js.map +0 -1
  340. package/dist/PageLayout-DSVM-ASz.js.map +0 -1
  341. package/dist/PageToolbar-BKVJiDAL.js.map +0 -1
  342. package/dist/Popover-BEbQrC_P.js.map +0 -1
  343. package/dist/SearchInput-92r2bkA7.js.map +0 -1
  344. package/dist/SectionNav-CNLJsiVG.js.map +0 -1
  345. package/dist/Select-DV3Hak_l.js.map +0 -1
  346. package/dist/Sheet-OparjIi-.js.map +0 -1
  347. package/dist/Sidebar-CLEbBtNY.js.map +0 -1
  348. package/dist/TextInput-CFTf4Cz1.js.map +0 -1
  349. package/dist/ThemeProvider-DAmNn9Sd.js.map +0 -1
  350. package/dist/ZoomableSvg-BZlk3PFJ.js.map +0 -1
  351. package/dist/context-D7X470dy.js.map +0 -1
  352. package/dist/findLiteralMatches-BGBw8Vbl.js.map +0 -1
  353. package/dist/icons-Cek151jC.js.map +0 -1
  354. package/dist/internal/components/StatusDot.js +0 -67
  355. package/dist/internal/components/StatusDot.js.map +0 -1
  356. package/dist/styles-BAFscjSV.js.map +0 -1
  357. package/dist/svgTransforms-CJAYPk7o.js +0 -81
  358. package/dist/svgTransforms-CJAYPk7o.js.map +0 -1
  359. package/dist/themeScopeContext-D7fSyb06.js.map +0 -1
  360. package/dist/themeTokens-qP1-55O7.js +0 -119
  361. package/dist/themeTokens-qP1-55O7.js.map +0 -1
  362. package/dist/toast-DIC0iNRf.js.map +0 -1
  363. package/dist/useControllableState-M6494mCT.js.map +0 -1
  364. package/dist/useElement-BK3AAiIf.js +0 -24
  365. package/dist/useElement-BK3AAiIf.js.map +0 -1
  366. package/dist/utils-BW4AE8VP.js.map +0 -1
  367. package/dist/vegaTheme-CSvFEMBl.js.map +0 -1
@@ -1,23 +1,23 @@
1
1
  import { cn } from "./classNames-BzrkDnOD.js";
2
- import { Tooltip, TooltipProvider } from "./utils-BW4AE8VP.js";
3
- import { AlertCircle, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Ban, BarChart3, Bell, Bug, Calendar, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Circle, CircleAlert, CircleCheck, CircleHelp, CirclePause, CirclePlay, CirclePlus, CircleStop, CircleX, Clock, Cog, Copy, Database, Download, Ellipsis, ExternalLink, Eye, File, FilePen, FileText, Flag, Folder, Gauge, GitBranch, GitMerge, GitPullRequest, Globe, Hand, History, Hourglass, Info, Key, Layers, LineChart, Link, List, ListChecks, Loader2, Lock, Mail, Maximize2, Minimize2, Minus, Pencil, Play, Plus, RefreshCw, Rocket, RotateCcw, RotateCw, Save, Search, Send, Settings, Sparkles, Star, Table, ThumbsDown, ThumbsUp, Trash2, TriangleAlert, Upload, User, Users, WandSparkles, Warehouse, Workflow, X, XCircle, Zap } from "./icons-Cek151jC.js";
4
- import { Button } from "./Button-DsRrU5F1.js";
2
+ import { Tooltip, TooltipProvider } from "./utils-Bd28bWgm.js";
3
+ import { AlertCircle, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Ban, BarChart3, Bell, Bug, Calendar, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Circle, CircleAlert, CircleCheck, CircleHelp, CirclePause, CirclePlay, CirclePlus, CircleStop, CircleX, Clock, Cog, Copy, Database, Download, Ellipsis, ExternalLink, Eye, File, FilePen, FileText, Flag, Folder, Gauge, GitBranch, GitMerge, GitPullRequest, Globe, Hand, History, Hourglass, Info, Key, Layers, LineChart, Link, List, ListChecks, Loader2, Lock, Mail, Maximize2, Minimize2, Minus, Pencil, Play, Plus, RefreshCw, Rocket, RotateCcw, RotateCw, Save, Search, Send, Settings, Sparkles, Star, Table, ThumbsDown, ThumbsUp, Trash2, TriangleAlert, Upload, User, Users, WandSparkles, Warehouse, Workflow, X, XCircle, Zap } from "./icons-wcA70iQx.js";
4
+ import { Button } from "./Button-B21oVcPU.js";
5
5
  import { ButtonGroup } from "./internal/components/ButtonGroup.js";
6
- import { ClipboardButton } from "./ClipboardButton-KHQnPEh_.js";
7
- import { FullScreenModal, FullScreenModalBody, FullScreenModalHeader } from "./FullScreenModal-CN9nAs72.js";
6
+ import { ClipboardButton } from "./ClipboardButton-srUgWgwH.js";
7
+ import { FullScreenModal, FullScreenModalBody, FullScreenModalHeader } from "./FullScreenModal-Cwnm8WdM.js";
8
8
  import { Truncated } from "./internal/components/Truncated.js";
9
- import { HoverCard } from "./HoverCard-B9ZMJxjn.js";
10
- import { Command } from "./Command-BDU3C1gg.js";
11
- import { TextInput } from "./TextInput-CFTf4Cz1.js";
12
- import { Checkbox } from "./Checkbox-Dq5fPNrO.js";
13
- import { Badge } from "./Badge-o5jURtpB.js";
9
+ import { HoverCard } from "./HoverCard-B8OKu9XZ.js";
10
+ import { Command } from "./Command-BQPiZCNz.js";
11
+ import { TextInput } from "./TextInput-BWYXbfs4.js";
12
+ import { Checkbox } from "./Checkbox-8yIIgY7g.js";
13
+ import { Badge } from "./Badge-DIjhwPvh.js";
14
14
  import { Textarea } from "./internal/blocks/Textarea.js";
15
- import { Table as Table$1 } from "./Table-CvYuTuNU.js";
15
+ import { Table as Table$1 } from "./Table-BaVT5Tkn.js";
16
16
  import { DiagramSkeleton } from "./internal/blocks/DiagramShared/DiagramSkeleton.js";
17
17
  import { DiagramSource } from "./DiagramSource-C5_d8Gdj.js";
18
18
  import { Progress } from "./internal/components/Progress.js";
19
19
  import { Slot } from "@radix-ui/react-slot";
20
- import { createContext, isValidElement, use, useCallback, useEffect, useId, useRef, useState } from "react";
20
+ import { createContext, isValidElement, use, useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
21
21
  import { cva } from "class-variance-authority";
22
22
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
23
23
  import TextareaAutosize from "react-textarea-autosize";
@@ -35,7 +35,7 @@ const DEFAULT_LABELS = {
35
35
  up: "Good answer",
36
36
  down: "Bad answer"
37
37
  };
38
- /** Roomier for a panel a reader sits in; the compact one suits a row inside a dense transcript. */
38
+ /** Roomier for a panel; compact for a dense transcript. */
39
39
  const SIZES = {
40
40
  small: {
41
41
  button: "icon-xs",
@@ -47,16 +47,11 @@ const SIZES = {
47
47
  }
48
48
  };
49
49
  /**
50
- * What a reader can do with a finished answer: take it away, and say whether it was any good.
50
+ * Copy and thumbs up/down for a finished answer.
51
51
  *
52
- * - **Always rendered, never hover-only.** A reveal-on-hover row does not exist for anyone
53
- * reaching it without a pointer. The buttons are quiet (`invisible`) rather than hidden.
54
- * - Copy sits first: it is the one action with no round trip.
55
- * - **A vote can be changed but not withdrawn.** `aria-pressed` says which one is recorded, and
56
- * both buttons stay focusable so a reader can switch. Pressing the pressed one re-sends the
57
- * same vote.
58
- * - The recorded vote and the write are the caller's: the two chats vote against different
59
- * endpoints, and only the caller knows whether a turn is finished enough to rate.
52
+ * - Always rendered, never hover-only, so it is reachable without a pointer.
53
+ * - A vote can be changed but not withdrawn; pressing the pressed one re-sends it.
54
+ * - The caller owns the recorded vote and the write.
60
55
  */
61
56
  const AnswerActions = ({ copyText, vote, onVote, onCopy, actions, labels = DEFAULT_LABELS, confirmation, size = "small", layout = "inline" }) => {
62
57
  const { button, gap } = SIZES[size];
@@ -247,15 +242,9 @@ const AnswerSummary = ({ items, variant = "success", noAnswerLabel = "No answer"
247
242
  /**
248
243
  * The Lucide component each `icon:<name>` token draws.
249
244
  *
250
- * - The names are FontAwesome-solid ones: kai-agent's prompt teaches that vocabulary, and
251
- * `@keboola/agent-vocabulary` publishes it as `CHAT_INLINE_ICON_NAMES`.
252
- * - `satisfies Record<ChatInlineIconName, LucideIcon>` is the drift guard — a name the prompt
253
- * teaches and this map lacks is a compile error, not a token that silently disappears from an
254
- * answer.
255
- * - Several names map to one component on purpose: FA distinguishes `gear` from `cog` and
256
- * `warning` from `triangle-exclamation`; Lucide draws each pair with one icon.
257
- * - Read through `Object.hasOwn`, never by plain index: a model-emitted `icon:constructor`
258
- * otherwise answers with an `Object.prototype` member and React tries to render it.
245
+ * - Names are FontAwesome-solid ones, published as `CHAT_INLINE_ICON_NAMES`.
246
+ * - The `satisfies` is the drift guard: a name the prompt teaches and this map lacks fails to compile.
247
+ * - Read through `Object.hasOwn`: `icon:constructor` would otherwise hit `Object.prototype`.
259
248
  */
260
249
  const ICON_REGISTRY = {
261
250
  "alert-circle": AlertCircle,
@@ -353,50 +342,29 @@ const ICON_REGISTRY = {
353
342
  zap: Zap
354
343
  };
355
344
  /**
356
- * Extra classes a single name needs on top of the shared list.
345
+ * Extra classes a single name needs.
357
346
  *
358
- * - `spinner` is the only one: `MAINTAINING.md` maps it to `Loader2` *plus* `tw:animate-spin`,
359
- * because Lucide draws the arc and leaves the motion to the caller. A still `Loader2` reads as
360
- * a damaged ring, not as waiting.
361
- * - Read through `Object.hasOwn`, for the same reason the registry is.
347
+ * - `spinner` spins (per `MAINTAINING.md`): a still `Loader2` reads as a broken ring.
348
+ * - Read through `Object.hasOwn`, like the registry.
362
349
  */
363
350
  const ICON_CLASSES = { spinner: "tw:animate-spin" };
364
351
  /**
365
352
  * A whole `icon:<name>` token and nothing else.
366
353
  *
367
- * - Anchored at both ends, and over the same name shape `remarkIconSyntax` (in
368
- * `@keboola/kai-chat`) matches when it turns bare text into an `inlineCode` node. The two
369
- * have to agree: a looser reader here accepts inline code the plugin never produced.
370
- * - That is what keeps ordinary inline code ordinary. A prefix test alone claims
371
- * `` `icon: database` ``, `` `icon:` `` and a fence body starting `icon:` — text the plugin
372
- * never turned into a token, and none of it is an icon the author asked for.
354
+ * - Must match the name shape `remarkIconSyntax` (`@keboola/kai-chat`) accepts.
355
+ * - Anchored, so inline code like `` `icon: database` `` or `` `icon:` `` stays code.
373
356
  */
374
357
  const TOKEN = /^icon:([a-z][a-z0-9-]*)$/;
375
358
  /** Built once: an answer is re-parsed per streamed chunk, so `isArriving` runs per code node. */
376
359
  const ICON_NAMES = Object.keys(ICON_REGISTRY);
377
- /**
378
- * Whether a name is only ever the start of one this registry draws.
379
- *
380
- * - Strictly shorter, so a whole name is never read as an unfinished one.
381
- * - `tabl` → `true` (`table`); `unicorn` and `table-name` → `false`.
382
- */
360
+ /** Whether a name is a strict prefix of a registry name: `tabl` → `true`, `table-name` → `false`. */
383
361
  const isArriving = (name) => ICON_NAMES.some((key) => key.length > name.length && key.startsWith(name));
384
362
  /**
385
363
  * The icon name in a code node's text, if the text is a token — drawn, or still arriving.
386
364
  *
387
- * - `icon:table` → `table`; anything else, including `icon: table`, `icon:Table` and
388
- * `icon:unicorn` → `undefined`.
389
- * - A name the registry lacks answers `undefined` on purpose, so every call site's own `<code>`
390
- * fall-through keeps the text its author typed. `icon:` is an ordinary way to name a property,
391
- * and this renderer draws vendor descriptions and dashboard text as well as agent answers.
392
- * - A name that only starts one it draws is a token still arriving, answered with so that
393
- * `<ChatInlineIcon />` draws nothing: an answer streams `icon:t`, `icon:ta`, `icon:tab` before
394
- * the name lands, and each of those as code is a chip that reflows the line for a frame.
395
- * - A name that is both — `save`, the start of `save-changes` — is drawn now; waiting would leave
396
- * a finished token blank for the rest of the answer.
397
- * - The token reaches a renderer as inline code either because the model wrote it in backticks
398
- * or because a remark plugin turned the bare text into an `inlineCode` node. Parsing the
399
- * token is the vocabulary's; producing it is the app's, since the plugin is workspace-internal.
365
+ * - An unknown name → `undefined`, so the call site's `<code>` fall-through keeps the text.
366
+ * - A prefix of a known name is a token still streaming; `<ChatInlineIcon />` draws it blank.
367
+ * - A name that is both (`save`, of `save-changes`) is drawn now.
400
368
  */
401
369
  const getChatInlineIconName = (text) => {
402
370
  const name = TOKEN.exec(text)?.[1];
@@ -406,19 +374,10 @@ const getChatInlineIconName = (text) => {
406
374
  /**
407
375
  * One inline `icon:<name>` token, drawn at the size of the text it leads.
408
376
  *
409
- * - An unknown name renders nothing, which is how a token still arriving (`icon:tabl`) stays
410
- * blank instead of flashing as a code chip. A name `getChatInlineIconName` withholds keeps its
411
- * text at the call site's own `<code>` fall-through and never reaches here.
412
- * - `size-[1em]` tracks the font size, so the icon matches a heading instead of sitting small
413
- * beside it, and `align-[-0.125em]` on an inline-block optically centres it against the
414
- * baseline.
415
- * - `mr` tops up the single source space, which is a word gap and reads as cramped between a
416
- * glyph and the phrase it labels.
417
- * - A heavier stroke so an outline icon holds its own next to the bold weight a heading carries.
418
- * - `spinner` also spins; the mapping owns that, not the caller. See `ICON_CLASSES`.
419
- * - `aria-hidden`, because the glyph is decorative: the token's text is removed from the answer and
420
- * nothing replaces it as a label, so a reader that announces unnamed inline SVGs would read an
421
- * unlabelled graphic before every phrase the agent decorates.
377
+ * - An unknown name (a token still arriving) renders nothing.
378
+ * - `mr` widens the single source space, which reads as cramped beside a glyph.
379
+ * - The heavier stroke holds its own beside a bold heading.
380
+ * - `aria-hidden`: the token's text is removed, so the glyph has no label to announce.
422
381
  */
423
382
  const ChatInlineIcon = ({ name }) => {
424
383
  const Icon = Object.hasOwn(ICON_REGISTRY, name) ? ICON_REGISTRY[name] : void 0;
@@ -430,34 +389,48 @@ const ChatInlineIcon = ({ name }) => {
430
389
  });
431
390
  };
432
391
  //#endregion
433
- //#region src/blocks/AIKit/InlineStepGroup.tsx
392
+ //#region src/blocks/AIKit/FileDownloadChip.tsx
434
393
  /**
435
- * The surface colour, as the custom property the step icons mask against.
394
+ * A file the agent wrote, offered inside its own answer.
436
395
  *
437
- * - Annotated rather than asserted: `CSSProperties` admits no custom property of its own, and
438
- * an intersection with the one this sets keeps the value type-checked at the assignment.
396
+ * - The whole chip is the control: a small icon beside a truncating filename is hard to hit.
397
+ * - Presentation only — the host fetches and saves, through its own auth.
439
398
  */
399
+ const FileDownloadChip = ({ fileName, filePath, size, isPending = false, onDownload, className }) => /* @__PURE__ */ jsxs(Button, {
400
+ variant: "secondary",
401
+ size: "small",
402
+ isLoading: isPending,
403
+ "aria-disabled": isPending,
404
+ "aria-label": `Download ${filePath ?? fileName}`,
405
+ title: filePath,
406
+ onClick: () => {
407
+ if (!isPending) onDownload();
408
+ },
409
+ className: cn("tw:max-w-full tw:justify-start tw:gap-2 tw:font-normal", className),
410
+ children: [
411
+ !isPending && /* @__PURE__ */ jsx(FileText, { className: "tw:size-4 tw:shrink-0 tw:text-muted-foreground" }),
412
+ /* @__PURE__ */ jsx("span", {
413
+ className: "tw:truncate",
414
+ children: fileName
415
+ }),
416
+ size !== void 0 && /* @__PURE__ */ jsx("span", {
417
+ className: "tw:shrink-0 tw:text-xs tw:text-muted-foreground",
418
+ children: size
419
+ }),
420
+ !isPending && /* @__PURE__ */ jsx(Download, { className: "tw:size-4 tw:shrink-0" })
421
+ ]
422
+ });
423
+ //#endregion
424
+ //#region src/blocks/AIKit/InlineStepGroup.tsx
425
+ /** The surface colour, as the custom property the step icons mask against. */
440
426
  const surfaceStyle = (surface) => ({ "--inline-step-surface": surface });
441
427
  /**
442
- * A run of steps an agent took, behind one line that says how far along it is.
428
+ * A collapsible run of agent steps behind one summary line; rows arrive as children.
443
429
  *
444
- * - The frame only. What a step *says* is the host's — one product names buckets and tables,
445
- * another must never put those words on screen — so the rows arrive as children and this
446
- * supplies the header, the collapse, the connector and the rhythm they share.
447
- * - One rule behind the whole run at the icon centre line: the list's `pl-5` plus half a
448
- * `size-5` icon box. Drawn once rather than per row, so it does not vanish beside a row that
449
- * happens to have no icon.
450
- * - `grid-template-rows` rather than height, because the rows are text of unknown length and a
451
- * transition needs a number at both ends. The children stay mounted while closed: a group the
452
- * reader reopens must not re-run whatever mounting a row costs. A closed group is `inert` and
453
- * `aria-hidden`, so staying mounted does not mean staying reachable.
454
- * - Expansion is the caller's state. Whether a group opens itself while work is happening, and
455
- * whether a reader's own toggle outranks that, is a transcript-level decision.
456
- * - `role="group"` rather than the `<section>`'s own landmark semantics: a transcript draws one
457
- * of these per run of steps, so a long conversation would hand a screen reader a landmark
458
- * list of dozens of identically named `region`s and drown the page's real landmarks in it.
459
- * The name still reads — a named `group` is announced by it — and it is the role the
460
- * grounding panel already gives its repeated named answer sections.
430
+ * - Connector sits at the icon centre line (`pl-5` + half a `size-5` box), drawn once so it
431
+ * survives icon-less rows.
432
+ * - Animates `grid-template-rows`; children stay mounted while closed.
433
+ * - `role="group"`, not a `region` landmark: dozens per transcript would drown real landmarks.
461
434
  */
462
435
  const InlineStepGroup = ({ label, variant = "success", isExpanded, onToggle, surface = "rgb(var(--background))", "aria-label": ariaLabel, "data-testid": testId, className, children }) => /* @__PURE__ */ jsxs("section", {
463
436
  role: "group",
@@ -491,16 +464,10 @@ const InlineStepGroup = ({ label, variant = "success", isExpanded, onToggle, sur
491
464
  /**
492
465
  * One diagram block: a skeleton while its chunk loads, the fence source plus a retry if it fails.
493
466
  *
494
- * - `load` runs once per mount and once per press of the retry button. The loader drops its own
495
- * rejected promise, so every attempt is a fresh fetch.
496
- * - Recovery is a gesture, not a timer: nothing on the page can tell when a chunk became
497
- * reachable again, and a tab left open across a deploy points at a URL that is gone for good —
498
- * a background loop would retry that one forever.
499
- * - The failure is already logged by the loader, which knows the chunk's name.
500
- * - A new `load` resets the block: every fence renders this same component type, so a block that
501
- * switches dialect would otherwise keep drawing through the dialect it had before.
502
- * - Both the renderer and the loader are functions held in state, so they go in through the
503
- * setter form — React runs a bare function as a lazy initializer or an updater instead.
467
+ * - Each attempt is a fresh fetch; the loader drops its rejected promise and logs the failure.
468
+ * - Retry is manual: a chunk gone after a deploy never returns, so a timer would loop forever.
469
+ * - A new `load` resets the block, or a dialect switch keeps the previous renderer.
470
+ * - Functions in state go through the setter form, or React calls them.
504
471
  */
505
472
  const DiagramBlock = ({ load, code }) => {
506
473
  const [Diagram, setDiagram] = useState(null);
@@ -539,16 +506,12 @@ const DiagramBlock = ({ load, code }) => {
539
506
  //#endregion
540
507
  //#region src/blocks/AIKit/diagramChunk.ts
541
508
  /**
542
- * An on-demand diagram chunk — shared between mounts, and retryable.
509
+ * An on-demand diagram chunk, shared between mounts and retryable.
543
510
  *
544
- * - `lazy()` is deliberately not used here: it memoizes the settled import for the life of the
545
- * page, so one failed fetch (a dev server restarting, a stale tab after a deploy, a flaky
546
- * network) leaves every diagram in that tab rendering raw source until a full reload.
547
- * - A resolved import is kept, so the chunk is fetched once however many blocks mount.
548
- * - A failed one is cleared, so the next mount imports again. Same contract as
549
- * `getFlintRuntime()` one level down, inside `FlintChart`.
550
- * - The failure is logged here, where the chunk's name is known, so the swap to plain source is
551
- * never silent and a retry that fails again says so.
511
+ * - Not `lazy()`: it memoizes a failed import, stranding every diagram until a reload.
512
+ * - A resolved import is kept; a failed one is cleared so the next mount retries.
513
+ * - Same contract as `getFlintRuntime()` in `FlintChart`.
514
+ * - Failures are logged here, where the chunk's name is known.
552
515
  */
553
516
  const diagramChunk = (name, load) => {
554
517
  let pending = null;
@@ -565,7 +528,10 @@ const diagramChunk = (name, load) => {
565
528
  //#region src/blocks/AIKit/Markdown.tsx
566
529
  const loadMermaid = diagramChunk("MermaidDiagram", () => import("@keboola/design/mermaid").then((module) => module.MermaidDiagram));
567
530
  const loadFlint = diagramChunk("FlintChart", () => import("@keboola/design/flint").then((module) => module.FlintChart));
568
- const proseClass = "tw:prose tw:prose-sm tw:max-w-none tw:break-words tw:[&_pre]:bg-muted tw:[&_pre]:text-foreground tw:[&_:not(pre)>code]:bg-muted tw:[&_:not(pre)>code]:text-foreground tw:[&_:not(pre)>code]:rounded tw:[&_:not(pre)>code]:px-1 tw:[&_:not(pre)>code]:py-0.5 tw:[&_:not(pre)>code]:before:content-none tw:[&_:not(pre)>code]:after:content-none";
531
+ const PROSE_CLASS = {
532
+ answer: "tw:prose tw:prose-answer tw:prose-code-chip tw:break-words",
533
+ chrome: "tw:prose tw:prose-sm tw:prose-code-chip tw:break-words"
534
+ };
569
535
  const getText = (node) => {
570
536
  if (typeof node === "string") return node;
571
537
  if (typeof node === "number") return String(node);
@@ -580,25 +546,14 @@ const getLanguage = (element) => {
580
546
  if (typeof props.className !== "string") return void 0;
581
547
  return props.className.match(/language-(\S+)/)?.[1];
582
548
  };
583
- /**
584
- * The plugins this renderer always runs.
585
- *
586
- * - GFM is part of its contract: tables route through the design `Table`, and autolinks and
587
- * task lists are what an assistant answer is written in.
588
- * - A caller's own plugins are appended to these, never substituted for them.
589
- */
549
+ /** Always run; a caller's plugins are appended, never substituted. */
590
550
  const baseRemarkPlugins = [remarkGfm];
591
551
  /**
592
552
  * Fence language → the on-demand chunk that draws it.
593
553
  *
594
- * - Both dialects are the agent's, not this package's: kai-agent's prompt tells the model to
595
- * write ```mermaid for structure and ```flint for quantitative data, so a chat that renders
596
- * one and not the other drops output the agent was instructed to produce.
597
- * - `flint-chart` is an alias — it is the fence tag flint's own docs use, so a model emits it
598
- * unprompted often enough to matter. `markdownComponents.tsx` in kbc-ui carries the same pair.
599
- * - Read through `Object.hasOwn`, never by plain index: a fence tagged ```constructor or
600
- * ```valueOf otherwise answers with an `Object.prototype` member, which is truthy, and calling
601
- * it as a loader throws — taking the whole message subtree down with it.
554
+ * - Dialects come from kai-agent's prompt; keep in sync with kbc-ui `markdownComponents.tsx`.
555
+ * - `flint-chart`: alias models emit unprompted.
556
+ * - Read via `Object.hasOwn`: ```constructor would otherwise hit `Object.prototype` and throw.
602
557
  */
603
558
  const DIAGRAM_CHUNKS = {
604
559
  mermaid: loadMermaid,
@@ -608,38 +563,21 @@ const DIAGRAM_CHUNKS = {
608
563
  /**
609
564
  * The fence languages this renderer draws instead of printing.
610
565
  *
611
- * - For a caller that streams: a diagram mounts a lazily loaded chunk, so an answer arriving a
612
- * token at a time has to keep a fence that is still open an ordinary code block, or every
613
- * keystroke fetches the chunk. Sharing the list is what keeps that hold-back from missing a
614
- * dialect this map has and the caller does not.
615
- * - Frozen, because it is that shared contract: a consumer that pushed onto it would change how
616
- * every other app in the same bundle holds a fence back.
566
+ * - Streaming callers hold an open fence back as plain code, or every keystroke fetches the chunk.
567
+ * - Frozen: it is shared across every app in the bundle.
617
568
  */
618
569
  const DIAGRAM_LANGUAGES = Object.freeze(Object.keys(DIAGRAM_CHUNKS));
619
570
  /**
620
571
  * Whether the `code` being rendered sits inside a fenced block.
621
572
  *
622
- * - A fence with no info string reaches `code` with no `language-…` class, exactly like inline
623
- * code, so the class cannot tell the two apart — `pre` is the only boundary that can.
624
- * - Set by the `pre` renderer, read by `code`: React renders `pre` before its children, so the
625
- * value is in place by the time the `code` inside it renders.
573
+ * - Set by `pre`: a fence without info string has no `language-…` class, like inline code.
626
574
  */
627
575
  const CodeBlockContext = createContext(false);
628
576
  /**
629
- * The `code` renderer: an `icon:<name>` token becomes the icon it names, anything else stays code.
577
+ * The `code` renderer: an inline `icon:<name>` token becomes the icon it names.
630
578
  *
631
- * - Only inline code is a candidate: a fenced block's body is code the reader asked to see, and
632
- * swapping it for a glyph would drop it. `CodeBlockContext` is what separates the two — a fence
633
- * with no info string carries no `language-…` class either, so the class cannot.
634
- * - The token reaches here as inline code because `remarkIconSyntax` (in `@keboola/kai-chat`)
635
- * turned the bare text into one, or because the model wrote the backticks itself. Producing the
636
- * token is the app's, drawing it is this package's.
637
- * - A name no icon answers to stays code: `getChatInlineIconName` answers only for names the
638
- * registry can draw or is still receiving, and this renderer draws vendor descriptions and
639
- * dashboard text too, where `icon:` is an ordinary way to name a property.
640
- * - A name still arriving draws nothing, so a streamed `icon:tabl` is blank for a frame rather
641
- * than a code chip that reflows the line.
642
- * - A component rather than an inline renderer so it can read the context.
579
+ * - Fenced blocks never swap.
580
+ * - Unknown names stay code; a name still streaming renders nothing.
643
581
  */
644
582
  const MarkdownCode = ({ children, className, ...props }) => {
645
583
  const { node: _node, ...rest } = props;
@@ -731,54 +669,60 @@ const baseComponents = {
731
669
  }
732
670
  };
733
671
  /**
734
- * Read-only GFM Markdown renderer with design-token typography. Pair with
735
- * `MessageBubble` to render an assistant message body. Code blocks expose a
736
- * copy button, so mount a `TooltipProvider` once at the app root.
672
+ * The prose surface an answer is drawn on.
673
+ *
674
+ * - Wrap all `Block`s of one answer in one `Root`, so `:first-child`/`:last-child` span the reply.
675
+ * - `data-prose` is `MessageBubble`'s colour hook; Tailwind can't target `tw:prose` directly.
676
+ * - Spacing overrides need `!`: `tw:prose-answer` rules are `(0,2,1)`, above `[&_p]:mb-1`.
737
677
  */
738
- const Markdown = ({ children, className, remarkPlugins, components: componentOverrides }) => {
739
- return /* @__PURE__ */ jsx("div", {
740
- "data-prose": true,
741
- className: cn(proseClass, className),
742
- children: /* @__PURE__ */ jsx(ReactMarkdown, {
743
- remarkPlugins: remarkPlugins === void 0 ? baseRemarkPlugins : [...baseRemarkPlugins, ...remarkPlugins],
744
- components: componentOverrides === void 0 ? baseComponents : {
745
- ...baseComponents,
746
- ...componentOverrides
747
- },
748
- children
749
- })
750
- });
751
- };
678
+ const MarkdownRoot = ({ children, className, rhythm = "answer" }) => /* @__PURE__ */ jsx("div", {
679
+ "data-prose": true,
680
+ className: cn(PROSE_CLASS[rhythm], className),
681
+ children
682
+ });
683
+ /** One parsed piece of an answer, with no surface of its own; memoise at the call site. */
684
+ const MarkdownBlock = ({ children, remarkPlugins, components: componentOverrides }) => /* @__PURE__ */ jsx(ReactMarkdown, {
685
+ remarkPlugins: remarkPlugins === void 0 ? baseRemarkPlugins : [...baseRemarkPlugins, ...remarkPlugins],
686
+ components: componentOverrides === void 0 ? baseComponents : {
687
+ ...baseComponents,
688
+ ...componentOverrides
689
+ },
690
+ children
691
+ });
692
+ /**
693
+ * Read-only GFM Markdown renderer with design-token typography.
694
+ *
695
+ * - Needs a `TooltipProvider` at the app root (code copy button).
696
+ * - Pass `rhythm="chrome"` for content a model did not write.
697
+ */
698
+ const Markdown = ({ className, rhythm, ...blockProps }) => /* @__PURE__ */ jsx(MarkdownRoot, {
699
+ className,
700
+ rhythm,
701
+ children: /* @__PURE__ */ jsx(MarkdownBlock, { ...blockProps })
702
+ });
703
+ Markdown.Root = MarkdownRoot;
704
+ Markdown.Block = MarkdownBlock;
752
705
  //#endregion
753
706
  //#region src/blocks/AIKit/MarkdownTable.tsx
754
707
  /** How much wider than its column a table has to be before the column is the wrong surface. */
755
708
  const EXPAND_RATIO = 1.5;
756
709
  /**
757
- * The token the host paints the table on, which is the colour the fade has to end in.
710
+ * The fade's end colour — must match the host's surface, or it draws a strip over the data.
758
711
  *
759
- * - A gradient hides the cut-off cells by ending in the surface behind them. End it in any other
760
- * token and it draws a contrasting strip over the data instead of hiding it.
761
- * - Full class strings, not a composed `tw:from-${token}`: Tailwind reads literals.
712
+ * - Full class strings: Tailwind reads literals.
762
713
  */
763
714
  const FADE_SURFACE = {
764
715
  card: "tw:from-card",
765
716
  background: "tw:from-background"
766
717
  };
767
718
  /**
768
- * A table in an answer, with the fact that it is cut off on the right said out loud.
719
+ * A table in an answer that shows when it is cut off on the right.
769
720
  *
770
- * - `overflow-x: auto` scrolls silently, so nothing on screen says there is more to the right.
771
- * - The controls sit in a rail above the grid, never over the header row or the last row — the
772
- * data a reader reaches for them to get at. The rail holds its height, so revealing hover
773
- * controls or crossing the ratio cannot shift the table under the pointer.
774
- * - The fade marks the cut edge only while content remains, so it never reads as a border.
775
- * - A painted gradient rather than the `tw:scroll-fade-x` mask, which needs scroll-driven
776
- * animations: the build floor is `baseline-widely-available` (Safari 16, Firefox 104), and there
777
- * the utility either fades both edges of a table that fits or, behind the `supports-` guard
778
- * `PageLayout` uses, says nothing at all — which is the state this component exists to end. The
779
- * mask also has to sit on the scrolling element, and that is `Table`'s own container, not ours.
780
- * - Past `EXPAND_RATIO` the fade is joined by `onExpand`'s action, drawn without waiting for a
781
- * hover: it reports that content is cut off, which the reader needs before going looking.
721
+ * - Controls sit in a fixed-height rail above the grid, so they never cover rows or shift them.
722
+ * - The fade shows only while content remains to the right.
723
+ * - A painted gradient, not `tw:scroll-fade-x`: that needs scroll-driven animations, which the
724
+ * `baseline-widely-available` build floor lacks, and must sit on `Table`'s own container.
725
+ * - Past `EXPAND_RATIO` the expand action shows without hover.
782
726
  */
783
727
  const MarkdownTable = ({ children, actions, onExpand, surface = "card", className }) => {
784
728
  const frameRef = useRef(null);
@@ -888,7 +832,7 @@ const contentClasses = cva([
888
832
  "tw:py-2",
889
833
  "tw:rounded-xl",
890
834
  "tw:overflow-hidden",
891
- "tw:break-anywhere"
835
+ "tw:wrap-anywhere"
892
836
  ]
893
837
  } },
894
838
  defaultVariants: { variant: "assistant" }
@@ -1233,6 +1177,58 @@ const PlanRewritingIndicator = ({ label = "Rewriting the plan..." }) => /* @__PU
1233
1177
  })]
1234
1178
  });
1235
1179
  //#endregion
1180
+ //#region src/blocks/AIKit/constants.ts
1181
+ /**
1182
+ * Names the container query `PromptInput`'s `inline` row reads to fold into the stacked layout.
1183
+ *
1184
+ * It has to sit on an **ancestor**: a container query matches descendants only, so the field that
1185
+ * would read it cannot name it, and its own wrapper is the width the fold changes. A host that
1186
+ * never names it keeps the one-line row at every width.
1187
+ */
1188
+ const PROMPT_INPUT_CONTAINER = "tw:@container/composer";
1189
+ //#endregion
1190
+ //#region src/blocks/AIKit/AnimatedHeight.tsx
1191
+ /**
1192
+ * A box whose height eases to whatever its content measures.
1193
+ *
1194
+ * - Content is measured with a `ResizeObserver`; without one (jsdom) the box is plain `auto`.
1195
+ * - The first measurement lands without a transition, so mounting with content does not grow in.
1196
+ * - Overflow is clipped only while the height moves, so focus rings are not cut off at rest.
1197
+ * - `prefers-reduced-motion` drops the transition.
1198
+ */
1199
+ const AnimatedHeight = ({ children, className }) => {
1200
+ const contentRef = useRef(null);
1201
+ const heightRef = useRef(void 0);
1202
+ const [height, setHeight] = useState();
1203
+ const [isAnimating, setIsAnimating] = useState(false);
1204
+ useLayoutEffect(() => {
1205
+ const node = contentRef.current;
1206
+ if (!node || typeof ResizeObserver === "undefined") return;
1207
+ const observer = new ResizeObserver(() => {
1208
+ const next = node.offsetHeight;
1209
+ const prev = heightRef.current;
1210
+ if (prev !== void 0 && prev !== next && hasMotion()) setIsAnimating(true);
1211
+ heightRef.current = next;
1212
+ setHeight(next);
1213
+ });
1214
+ observer.observe(node);
1215
+ return () => observer.disconnect();
1216
+ }, []);
1217
+ const handleTransitionEnd = (event) => {
1218
+ if (event.target !== event.currentTarget || event.propertyName !== "height") return;
1219
+ setIsAnimating(false);
1220
+ };
1221
+ return /* @__PURE__ */ jsx("div", {
1222
+ style: { height },
1223
+ onTransitionEnd: handleTransitionEnd,
1224
+ className: cn("tw:transition-[height] tw:duration-200 tw:ease-out tw:motion-reduce:transition-none", isAnimating && "tw:overflow-hidden", className),
1225
+ children: /* @__PURE__ */ jsx("div", {
1226
+ ref: contentRef,
1227
+ children
1228
+ })
1229
+ });
1230
+ };
1231
+ //#endregion
1236
1232
  //#region src/blocks/AIKit/PromptInputAction.tsx
1237
1233
  const PromptInputAction = ({ ref, onMouseDown, ...rest }) => {
1238
1234
  const handleOnMouseDown = (event) => {
@@ -1249,24 +1245,81 @@ const PromptInputAction = ({ ref, onMouseDown, ...rest }) => {
1249
1245
  //#region src/blocks/AIKit/PromptInputCommands.tsx
1250
1246
  /** What the menu needs above or below the composer before it will open on that side. */
1251
1247
  const MENU_HEIGHT = 440;
1252
- const PromptInputCommands = ({ items, open, onSelect, filterValue, emptyText = "No commands found", onClose, children }) => {
1253
- const filtered = filterValue ? items.filter((item) => item.label.toLowerCase().includes(filterValue.toLowerCase()) || item.value.toLowerCase().includes(filterValue.toLowerCase())) : items;
1248
+ /** As tall as the list ever gets, however much room there is. */
1249
+ const MENU_MAX_HEIGHT = 416;
1250
+ /** Space kept from the measured edge; `POPUP_CHROME` is the popup's own border and padding. */
1251
+ const MENU_GUTTER = 16;
1252
+ const POPUP_CHROME = 10;
1253
+ /**
1254
+ * The rows a typed filter leaves.
1255
+ *
1256
+ * - An empty filter keeps everything.
1257
+ * - A row matches on its label or its value.
1258
+ * - Matching nothing falls back to the action rows.
1259
+ */
1260
+ const matchesFilter = (items, filterValue) => {
1261
+ if (!filterValue) return items;
1262
+ const query = filterValue.toLowerCase();
1263
+ const matched = items.filter((item) => item.label.toLowerCase().includes(query) || item.value.toLowerCase().includes(query));
1264
+ return matched.length > 0 ? matched : items.filter((item) => item.onSelect !== void 0);
1265
+ };
1266
+ /**
1267
+ * The band the menu has to stay inside: the window, narrowed by every ancestor that clips.
1268
+ *
1269
+ * - The chat sidebar's panel clips well below the window's top edge.
1270
+ */
1271
+ const CLIPS = /* @__PURE__ */ new Set([
1272
+ "hidden",
1273
+ "auto",
1274
+ "scroll",
1275
+ "clip",
1276
+ "overlay"
1277
+ ]);
1278
+ const clipBand = (node) => {
1279
+ let top = 0;
1280
+ let bottom = window.innerHeight;
1281
+ let parent = node.parentElement;
1282
+ while (parent) {
1283
+ const { overflowY, overflowX } = window.getComputedStyle(parent);
1284
+ if (CLIPS.has(overflowY) || CLIPS.has(overflowX)) {
1285
+ const box = parent.getBoundingClientRect();
1286
+ top = Math.max(top, box.top);
1287
+ bottom = Math.min(bottom, box.bottom);
1288
+ }
1289
+ parent = parent.parentElement;
1290
+ }
1291
+ return {
1292
+ top,
1293
+ bottom
1294
+ };
1295
+ };
1296
+ const PromptInputCommands = ({ items, open, onSelect, filterValue, emptyText = "No commands found", panel, groupAction, groupHeading, children }) => {
1297
+ const filtered = matchesFilter(items, filterValue);
1254
1298
  const [highlighted, setHighlighted] = useState();
1299
+ const [wasOpen, setWasOpen] = useState(open);
1300
+ if (open !== wasOpen) {
1301
+ setWasOpen(open);
1302
+ if (open) setHighlighted(void 0);
1303
+ }
1255
1304
  /**
1256
1305
  * Which side the menu opens on.
1257
1306
  *
1258
- * - Above by default: a composer usually sits at the foot of its panel.
1259
- * - Below when there is not room above and there is room below — the dashboard composer sits
1260
- * near the top of the page, where opening upward clipped the list against the header.
1261
- * - Re-measured while it is open: the composer grows as the message does, and the transcript
1262
- * above it moves, so the side that fit on open may not fit a moment later.
1307
+ * - Above by default; below when there is not room above and more room below.
1308
+ * - Re-measured while open: the composer and the transcript above it move.
1263
1309
  */
1264
1310
  const rootRef = useRef(null);
1265
1311
  const [below, setBelow] = useState(false);
1312
+ const [maxHeight, setMaxHeight] = useState(MENU_MAX_HEIGHT);
1266
1313
  const measure = useCallback(() => {
1267
- const box = rootRef.current?.getBoundingClientRect();
1268
- if (!box) return;
1269
- setBelow(box.top < MENU_HEIGHT && window.innerHeight - box.bottom > MENU_HEIGHT);
1314
+ const node = rootRef.current;
1315
+ const box = node?.getBoundingClientRect();
1316
+ if (!node || !box) return;
1317
+ const band = clipBand(node);
1318
+ const above = box.top - band.top;
1319
+ const under = band.bottom - box.bottom;
1320
+ const openBelow = above < MENU_HEIGHT && under > above;
1321
+ setBelow(openBelow);
1322
+ setMaxHeight(Math.max(0, Math.min(MENU_MAX_HEIGHT, (openBelow ? under : above) - MENU_GUTTER - POPUP_CHROME)));
1270
1323
  }, []);
1271
1324
  const attachRoot = useCallback((node) => {
1272
1325
  rootRef.current = node;
@@ -1292,6 +1345,7 @@ const PromptInputCommands = ({ items, open, onSelect, filterValue, emptyText = "
1292
1345
  acc[item.group] = group;
1293
1346
  return acc;
1294
1347
  }, {});
1348
+ const menuStyle = { "--prompt-menu-max-h": `${maxHeight}px` };
1295
1349
  return /* @__PURE__ */ jsxs(Command, {
1296
1350
  ref: attachRoot,
1297
1351
  loop: true,
@@ -1299,41 +1353,45 @@ const PromptInputCommands = ({ items, open, onSelect, filterValue, emptyText = "
1299
1353
  shouldFilter: false,
1300
1354
  value: highlightedValue ?? "",
1301
1355
  onValueChange: setHighlighted,
1302
- children: [open && /* @__PURE__ */ jsxs("div", {
1356
+ children: [open && /* @__PURE__ */ jsx("div", {
1303
1357
  className: cn("tw:absolute tw:left-0 tw:z-50 tw:w-full tw:max-w-lg", below ? "tw:top-full tw:mt-1" : "tw:bottom-full tw:mb-1", "tw:rounded-[14px] tw:border tw:border-border tw:bg-popover tw:text-popover-foreground tw:shadow-lg", "tw:p-1"),
1304
- children: [onClose && /* @__PURE__ */ jsx(Button, {
1305
- type: "button",
1306
- size: "icon-xs",
1307
- variant: "invisible",
1308
- "aria-label": "Close",
1309
- className: "tw:absolute tw:right-2 tw:top-2 tw:z-10",
1310
- onMouseDown: (event) => event.preventDefault(),
1311
- onClick: onClose,
1312
- children: /* @__PURE__ */ jsx(X, {})
1313
- }), filtered.length === 0 ? /* @__PURE__ */ jsx("div", {
1314
- className: "tw:px-4 tw:py-3 tw:text-center tw:text-sm tw:text-muted-foreground",
1358
+ style: menuStyle,
1359
+ children: panel ? /* @__PURE__ */ jsx("div", {
1360
+ className: "tw:p-3",
1361
+ children: panel
1362
+ }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", {
1363
+ className: "tw:py-3 tw:pl-4 tw:text-center tw:text-sm tw:text-muted-foreground",
1315
1364
  children: emptyText
1316
1365
  }) : /* @__PURE__ */ jsx(Command.List, {
1317
1366
  className: "tw:group-data-[variant=argo]/command:px-1 tw:group-data-[variant=argo]/command:pb-1",
1318
- viewPortClassName: "tw:max-h-[26rem]",
1367
+ viewPortClassName: "tw:max-h-[var(--prompt-menu-max-h)] tw:[&>div]:!block",
1319
1368
  children: Object.entries(groups).map(([groupName, groupItems], index) => /* @__PURE__ */ jsx(Command.Group, {
1320
- heading: groupName,
1321
- className: cn("tw:[&_[cmdk-group-heading]]:normal-case", "tw:[&_[cmdk-group-heading]]:tracking-normal", "tw:[&_[cmdk-group-heading]]:border-b-0", "tw:[&_[cmdk-group-heading]]:!py-0", "tw:[&_[cmdk-group-heading]]:mt-2", "tw:[&_[cmdk-group-heading]]:mb-1", "tw:[&_[cmdk-group-heading]]:pl-2", "tw:[&_[cmdk-group-heading]]:pr-9", "tw:[&_[cmdk-group-heading]]:text-xs", index > 0 && "tw:mt-3"),
1322
- children: groupItems.map((item) => /* @__PURE__ */ jsx(Command.Item, {
1369
+ heading: groupName ? /* @__PURE__ */ jsxs("span", {
1370
+ className: "tw:flex tw:items-center tw:justify-between tw:gap-2",
1371
+ children: [/* @__PURE__ */ jsx("span", {
1372
+ className: "tw:min-w-0 tw:truncate",
1373
+ children: groupHeading?.(groupName) ?? groupName
1374
+ }), groupAction?.(groupName)]
1375
+ }) : void 0,
1376
+ className: cn("tw:[&_[cmdk-group-heading]]:normal-case", "tw:[&_[cmdk-group-heading]]:tracking-normal", "tw:[&_[cmdk-group-heading]]:border-b-0", "tw:[&_[cmdk-group-heading]]:!py-0", "tw:[&_[cmdk-group-heading]]:mt-2", "tw:[&_[cmdk-group-heading]]:mb-1", "tw:[&_[cmdk-group-heading]]:pl-2", "tw:[&_[cmdk-group-heading]]:text-xs", index > 0 && "tw:mt-3"),
1377
+ children: groupItems.map((item) => /* @__PURE__ */ jsxs(Command.Item, {
1323
1378
  value: item.value,
1324
- className: "tw:rounded-md",
1379
+ className: "tw:w-full tw:min-w-0 tw:rounded-md",
1325
1380
  onSelect: () => onSelect(item),
1326
- children: /* @__PURE__ */ jsxs("div", {
1381
+ children: [item.icon, /* @__PURE__ */ jsxs("div", {
1327
1382
  className: "tw:flex tw:min-w-0 tw:flex-1 tw:flex-col",
1328
- children: [/* @__PURE__ */ jsx("span", { children: item.label }), item.description && /* @__PURE__ */ jsx(Truncated, {
1383
+ children: [item.content ?? /* @__PURE__ */ jsx("span", {
1384
+ className: "tw:truncate",
1385
+ children: item.label
1386
+ }), item.description && /* @__PURE__ */ jsx(Truncated, {
1329
1387
  text: item.description,
1330
1388
  twoLines: true,
1331
1389
  className: "tw:text-xs tw:text-muted-foreground"
1332
1390
  })]
1333
- })
1391
+ })]
1334
1392
  }, item.value))
1335
1393
  }, groupName))
1336
- })]
1394
+ })
1337
1395
  }), children]
1338
1396
  });
1339
1397
  };
@@ -1404,7 +1462,49 @@ const CMDK_HIJACKED_KEYS = /* @__PURE__ */ new Set([
1404
1462
  "End",
1405
1463
  "Enter"
1406
1464
  ]);
1407
- const isCmdkNavigation = (event) => event.key !== "Enter" || !(event.metaKey || event.ctrlKey || event.altKey);
1465
+ /**
1466
+ * Whether the composer's always-mounted cmdk root would act on this keystroke, cancelling its default.
1467
+ *
1468
+ * - cmdk acts on every `CMDK_HIJACKED_KEYS` key regardless of modifiers, except a modified Enter.
1469
+ * - The textarea and the form stop these from bubbling, or a focused button never clicks on Enter.
1470
+ */
1471
+ const isCmdkNavigation = (event) => CMDK_HIJACKED_KEYS.has(event.key) && (event.key !== "Enter" || !(event.metaKey || event.ctrlKey || event.altKey));
1472
+ /**
1473
+ * The `@token` the caret sits at the end of, if any.
1474
+ *
1475
+ * - `@` has to open a word: it must start the value or follow whitespace.
1476
+ * - The token holds no whitespace or second `@` and runs past the caret, so a mid-word pick takes the word.
1477
+ * - `start` indexes the `@` itself, so replacing `[start, end)` removes the token.
1478
+ */
1479
+ const findMentionToken = (value, caret) => {
1480
+ const before = value.slice(0, caret);
1481
+ const match = /(?:^|\s)@([^\s@]*)$/.exec(before);
1482
+ if (!match) return null;
1483
+ const typed = match[1] ?? "";
1484
+ const tail = /^[^\s@]*/.exec(value.slice(caret))?.[0] ?? "";
1485
+ return {
1486
+ start: caret - typed.length - 1,
1487
+ end: caret + tail.length,
1488
+ query: typed + tail
1489
+ };
1490
+ };
1491
+ /**
1492
+ * The `/token` a message leads with, while the caret is still on it.
1493
+ *
1494
+ * - Only the first token: the Agent SDK expands a skill only at the start of a message.
1495
+ * - Text may follow it.
1496
+ * - The caret past the token closes the menu.
1497
+ */
1498
+ const findSlashToken = (value, caret) => {
1499
+ const match = /^\/(\S*)/.exec(value);
1500
+ if (!match) return null;
1501
+ const end = match[0].length;
1502
+ if (caret > end) return null;
1503
+ return {
1504
+ end,
1505
+ query: match[1] ?? ""
1506
+ };
1507
+ };
1408
1508
  const promptInputVariants = cva([
1409
1509
  "tailwindcss-preflight",
1410
1510
  "tw:relative",
@@ -1443,7 +1543,7 @@ const wrapperVariants = cva(null, {
1443
1543
  inline: ""
1444
1544
  } }
1445
1545
  });
1446
- const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, attachments, className, disabled, disableSubmit, value = "", onValueChange, status = "ready", maxRows = 6, minRows, commands, autoFocus, textareaRef: publishedRef, ...textareaProps }) => {
1546
+ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, attachments, className, disabled, disableSubmit, value = "", onValueChange, status = "ready", maxRows = 6, minRows, commands, commandsGroupAction, panel, mentions, onMentionSelect, mentionsEmptyText = "Nothing to attach", mentionsGroupHeading, mentionsMultiple = false, autoFocus, textareaRef: publishedRef, ...textareaProps }) => {
1447
1547
  const defaultMinRows = variant === "inline" ? 1 : 2;
1448
1548
  const isDisabled = (disabled || disableSubmit || !value.trim()) && status !== "streaming" && status !== "interrupted";
1449
1549
  const textareaId = useId();
@@ -1453,16 +1553,29 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, att
1453
1553
  if (publishedRef) publishedRef.current = node;
1454
1554
  }, [publishedRef]);
1455
1555
  const [dismissedValue, setDismissedValue] = useState(null);
1556
+ const [caret, setCaret] = useState(0);
1557
+ const [caretValue, setCaretValue] = useState(value);
1558
+ const moveCaret = (next, forValue) => {
1559
+ setCaret(next);
1560
+ setCaretValue(forValue);
1561
+ };
1562
+ if (value !== caretValue) moveCaret(value.length, value);
1456
1563
  const autoFocusRef = useRef(autoFocus);
1457
1564
  useEffect(() => {
1458
1565
  if (autoFocusRef.current) textareaRef.current?.focus({ preventScroll: true });
1459
1566
  }, []);
1460
1567
  if (dismissedValue !== null && value !== dismissedValue) setDismissedValue(null);
1461
- const hasMenus = !!commands && commands.length > 0;
1462
- const valueLooksLikeSlashCommand = value.startsWith("/") && !/\s/.test(value);
1463
- const menuOpen = hasMenus && valueLooksLikeSlashCommand && value !== dismissedValue;
1464
- const menuItems = commands ?? [];
1465
- const menuFilter = value.slice(1);
1568
+ const hasCommands = !!commands && commands.length > 0;
1569
+ const panelOpen = panel !== void 0;
1570
+ const hasMentions = mentions !== void 0;
1571
+ const hasMenus = hasCommands || hasMentions;
1572
+ const slashToken = hasCommands && !panelOpen && value !== dismissedValue ? findSlashToken(value, Math.min(caret, value.length)) : null;
1573
+ const commandsOpen = slashToken !== null;
1574
+ const mentionToken = hasMentions && !commandsOpen && !panelOpen && value !== dismissedValue ? findMentionToken(value, Math.min(caret, value.length)) : null;
1575
+ const mentionsOpen = mentionToken !== null;
1576
+ const menuOpen = commandsOpen || mentionsOpen || panelOpen;
1577
+ const menuItems = commandsOpen ? commands ?? [] : mentions ?? [];
1578
+ const menuFilter = commandsOpen ? slashToken.query : mentionToken?.query ?? "";
1466
1579
  const handleSubmit = () => {
1467
1580
  if (isDisabled) return;
1468
1581
  if (status === "streaming") onAbort?.();
@@ -1474,28 +1587,83 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, att
1474
1587
  handleSubmit();
1475
1588
  };
1476
1589
  const handleOnChange = (event) => {
1590
+ moveCaret(event.target.selectionStart ?? event.target.value.length, event.target.value);
1477
1591
  onValueChange?.(event.target.value);
1478
1592
  };
1593
+ const handleOnSelect = (event) => {
1594
+ moveCaret(event.currentTarget.selectionStart ?? 0, event.currentTarget.value);
1595
+ };
1479
1596
  const handleMenuSelect = (item) => {
1480
- onValueChange?.(item.value + " ");
1597
+ if (item.onSelect) {
1598
+ const start = mentionToken?.start ?? 0;
1599
+ const end = mentionToken?.end ?? slashToken?.end;
1600
+ if (end !== void 0) {
1601
+ const next = value.slice(0, start) + value.slice(end);
1602
+ onValueChange?.(next);
1603
+ moveCaret(start, next);
1604
+ }
1605
+ item.onSelect();
1606
+ textareaRef.current?.focus();
1607
+ return;
1608
+ }
1609
+ if (mentionToken) {
1610
+ const keep = mentionsMultiple ? "@" : "";
1611
+ const next = value.slice(0, mentionToken.start) + keep + value.slice(mentionToken.end);
1612
+ const nextCaret = mentionToken.start + keep.length;
1613
+ onValueChange?.(next);
1614
+ moveCaret(nextCaret, next);
1615
+ requestAnimationFrame(() => {
1616
+ textareaRef.current?.setSelectionRange(nextCaret, nextCaret);
1617
+ });
1618
+ onMentionSelect?.(item);
1619
+ } else {
1620
+ const rest = slashToken ? value.slice(slashToken.end).replace(/^\s+/, "") : "";
1621
+ const head = `${item.value} `;
1622
+ onValueChange?.(head + rest);
1623
+ moveCaret(head.length, head + rest);
1624
+ requestAnimationFrame(() => {
1625
+ textareaRef.current?.setSelectionRange(head.length, head.length);
1626
+ });
1627
+ }
1481
1628
  textareaRef.current?.focus();
1482
1629
  };
1630
+ const dismissMenu = () => {
1631
+ if (value.trim() === "/" || value.trim() === "@") {
1632
+ onValueChange?.("");
1633
+ return;
1634
+ }
1635
+ if (mentionToken && mentionToken.query === "") {
1636
+ const next = (value.slice(0, mentionToken.start) + value.slice(mentionToken.end)).replace(/\s+$/, "");
1637
+ onValueChange?.(next);
1638
+ moveCaret(next.length, next);
1639
+ return;
1640
+ }
1641
+ setDismissedValue(value);
1642
+ };
1483
1643
  const handleOnKeyDown = (event) => {
1484
1644
  if (menuOpen) {
1485
1645
  if (event.key === "Escape") {
1486
1646
  event.preventDefault();
1487
- setDismissedValue(value);
1647
+ if (panel) panel.onClose();
1648
+ else dismissMenu();
1488
1649
  return;
1489
1650
  }
1490
- if (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "Enter") return;
1491
- } else if (hasMenus && CMDK_HIJACKED_KEYS.has(event.key) && isCmdkNavigation(event)) event.stopPropagation();
1651
+ if (panelOpen) return;
1652
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") return;
1653
+ if (event.key === "Enter" && matchesFilter(menuItems, menuFilter).length > 0) return;
1654
+ } else if (hasMenus && isCmdkNavigation(event)) event.stopPropagation();
1492
1655
  if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey && !event.metaKey) {
1493
1656
  event.preventDefault();
1494
1657
  if (status !== "streaming") handleSubmit();
1495
1658
  }
1496
1659
  };
1660
+ const handleOnFormKeyDown = (event) => {
1661
+ if (hasMenus && event.target !== textareaRef.current && isCmdkNavigation(event)) event.stopPropagation();
1662
+ };
1497
1663
  const handleOnMouseDown = (event) => {
1498
- if (event.target === textareaRef.current) return;
1664
+ const { currentTarget, target } = event;
1665
+ if (target === textareaRef.current) return;
1666
+ if (!(target instanceof Node && currentTarget.contains(target))) return;
1499
1667
  event.preventDefault();
1500
1668
  textareaRef.current?.focus();
1501
1669
  };
@@ -1504,13 +1672,17 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, att
1504
1672
  children: /* @__PURE__ */ jsxs("form", {
1505
1673
  "data-testid": "prompt-input",
1506
1674
  "data-status": status,
1675
+ onKeyDown: handleOnFormKeyDown,
1507
1676
  onMouseDown: handleOnMouseDown,
1508
1677
  onSubmit: handleOnSubmit,
1509
1678
  className: cn(promptInputVariants({ variant }), disabled && "tw:bg-none tw:border-border tw:shadow-[inset_0_100vw_rgb(var(--muted))] tw:cursor-default", className),
1510
1679
  children: [
1511
- attachments && /* @__PURE__ */ jsx("div", {
1512
- className: "tw:flex tw:flex-col tw:gap-2 tw:pb-2",
1513
- children: attachments
1680
+ /* @__PURE__ */ jsx(AnimatedHeight, {
1681
+ className: "tw:-mb-2",
1682
+ children: attachments && /* @__PURE__ */ jsx("div", {
1683
+ className: "tw:flex tw:flex-col tw:gap-2 tw:pb-4",
1684
+ children: attachments
1685
+ })
1514
1686
  }),
1515
1687
  /* @__PURE__ */ jsx("label", {
1516
1688
  htmlFor: textareaId,
@@ -1518,14 +1690,14 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, att
1518
1690
  children: "Prompt Input"
1519
1691
  }),
1520
1692
  /* @__PURE__ */ jsxs("div", {
1521
- className: cn("tw:flex tw:min-w-0 tw:gap-2", variant === "inline" ? "tw:flex-row tw:items-center" : "tw:flex-col"),
1693
+ className: cn("tw:flex tw:min-w-0 tw:gap-2", variant === "inline" ? "tw:flex-row tw:items-center tw:@max-[480px]/composer:flex-wrap" : "tw:flex-col"),
1522
1694
  children: [
1523
1695
  prefix && /* @__PURE__ */ jsx("div", {
1524
1696
  className: "tw:flex tw:shrink-0 tw:items-center",
1525
1697
  children: prefix
1526
1698
  }),
1527
1699
  /* @__PURE__ */ jsx("div", {
1528
- className: "tw:flex tw:min-w-0 tw:flex-1 tw:items-center",
1700
+ className: cn("tw:flex tw:min-w-0 tw:items-center", variant === "inline" ? "tw:grow tw:@max-[480px]/composer:order-first tw:@max-[480px]/composer:basis-full" : "tw:flex-1"),
1529
1701
  children: /* @__PURE__ */ jsx(TextareaAutosize, {
1530
1702
  ...textareaProps,
1531
1703
  disabled,
@@ -1535,12 +1707,13 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, att
1535
1707
  ref: setTextareaNode,
1536
1708
  value,
1537
1709
  onChange: handleOnChange,
1710
+ onSelect: handleOnSelect,
1538
1711
  onKeyDown: handleOnKeyDown,
1539
- className: cn("tw:resize-none", "tw:outline-hidden", "tw:min-w-32 tw:flex-1", variant === "inline" ? "tw:px-0" : "tw:pl-2 tw:pr-0", "tw:bg-transparent", disabled ? "tw:text-muted-foreground" : "tw:text-foreground", "tw:text-sm", "tw:font-sans", "tw:transition-[width]", "tw:delay-75", "tw:ease-linear", "tw:overflow-auto")
1712
+ className: cn("tw:resize-none", "tw:outline-hidden", "tw:min-w-32 tw:flex-1", variant === "inline" ? "tw:@max-[480px]/composer:pl-2" : "tw:pl-2 tw:pr-0", "tw:bg-transparent", disabled ? "tw:text-muted-foreground" : "tw:text-foreground", "tw:text-sm", "tw:font-sans", "tw:transition-[width]", "tw:delay-75", "tw:ease-linear", "tw:overflow-auto")
1540
1713
  })
1541
1714
  }),
1542
1715
  /* @__PURE__ */ jsxs("div", {
1543
- className: cn("tw:flex tw:shrink-0 tw:items-center tw:gap-1", "tw:[&>*:last-child]:ml-1", variant === "inline" && "tw:pr-2.5"),
1716
+ className: cn("tw:flex tw:items-center tw:gap-1", "tw:[&>*:last-child]:ml-1", "tw:shrink-0", variant === "inline" && "tw:pr-2.5 tw:@max-[480px]/composer:grow tw:@max-[480px]/composer:pr-0"),
1544
1717
  children: [suffix, /* @__PURE__ */ jsx(PromptInputSubmit, {
1545
1718
  disabled: isDisabled,
1546
1719
  status
@@ -1556,8 +1729,10 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, att
1556
1729
  open: menuOpen,
1557
1730
  onSelect: handleMenuSelect,
1558
1731
  filterValue: menuFilter,
1559
- emptyText: "No commands found",
1560
- onClose: () => setDismissedValue(value),
1732
+ emptyText: mentionsOpen ? mentionsEmptyText : "No commands found",
1733
+ panel: panel?.content,
1734
+ groupAction: commandsOpen ? commandsGroupAction : void 0,
1735
+ groupHeading: mentionsOpen ? mentionsGroupHeading : void 0,
1561
1736
  children: formEl
1562
1737
  }) : formEl });
1563
1738
  };
@@ -2041,7 +2216,7 @@ const ToolCallStatusHeader = ({ icon: IconComponent, iconSpin, status, text, nee
2041
2216
  className: "tw:flex tw:min-w-0 tw:flex-1 tw:items-center tw:gap-1.5",
2042
2217
  children: [/* @__PURE__ */ jsx(IconComponent, { className: cn("tw:size-3.5 tw:shrink-0", statusIconInk[status], iconSpin && "tw:animate-spin") }), /* @__PURE__ */ jsx("span", {
2043
2218
  "data-testid": "tool-status-text",
2044
- className: cn("tw:truncate tw:pr-0.5 tw:text-xs tw:font-medium", statusInk[status]),
2219
+ className: cn("tw:truncate tw:pr-0.5 tw:text-xs tw:font-medium", needsConfirmation ? "tw:text-foreground" : statusInk[status]),
2045
2220
  children: text
2046
2221
  })]
2047
2222
  }), (needsConfirmation || isCollapsible) && /* @__PURE__ */ jsxs("div", {
@@ -2085,9 +2260,7 @@ const formatPercentage = (value) => {
2085
2260
  /**
2086
2261
  * One budget, as a labelled row over a bar.
2087
2262
  *
2088
- * - The label and the figure sit on one baseline at either end of the row; the bar spans both.
2089
- * - `value` over 100 or `NaN` is clamped by `clampPercentage` — `Progress`'s indicator is
2090
- * unclamped and would otherwise slide off the row.
2263
+ * - `value` is clamped by `clampPercentage` — `Progress`'s indicator is unclamped.
2091
2264
  */
2092
2265
  const UsageBar = ({ value, label, variant, className, ...props }) => /* @__PURE__ */ jsxs("span", {
2093
2266
  ...props,
@@ -2162,6 +2335,6 @@ const UsageStats = ({ value, alert = false, children, className, ariaLabel, alig
2162
2335
  });
2163
2336
  };
2164
2337
  //#endregion
2165
- export { AnswerActions, AnswerSummary, ChatInlineIcon, CollapsibleStepHeader, DIAGRAM_LANGUAGES, InlineStepDetail, InlineStepGroup, InlineStepLabel, Markdown, MarkdownTable, MessageBubble, PlanApproval, PlanRewritingIndicator, PromptInput, Question, QuestionBlock, SuggestedPrompt, ThinkingIndicator, ToolCallApproval, ToolCallChip, ToolCallStatusHeader, UsageBar, UsageStats, UsageStatsRing, clampPercentage, formatPercentage, getChatInlineIconName };
2338
+ export { AnswerActions, AnswerSummary, ChatInlineIcon, CollapsibleStepHeader, DIAGRAM_LANGUAGES, FileDownloadChip, InlineStepDetail, InlineStepGroup, InlineStepLabel, Markdown, MarkdownTable, MessageBubble, PROMPT_INPUT_CONTAINER, PlanApproval, PlanRewritingIndicator, PromptInput, Question, QuestionBlock, SuggestedPrompt, ThinkingIndicator, ToolCallApproval, ToolCallChip, ToolCallStatusHeader, UsageBar, UsageStats, UsageStatsRing, clampPercentage, findMentionToken, findSlashToken, formatPercentage, getChatInlineIconName };
2166
2339
 
2167
- //# sourceMappingURL=AIKit-ELIsegJz.js.map
2340
+ //# sourceMappingURL=AIKit-DcOlHIBr.js.map