@vertesia/ui 1.4.0-dev.20260615.042549Z → 1.4.0-dev.20260629.130134Z

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 (337) hide show
  1. package/lib/core/components/shadcn/dropdown.d.ts.map +1 -1
  2. package/lib/core/components/shadcn/dropdown.js +6 -2
  3. package/lib/core/components/shadcn/dropdown.js.map +1 -1
  4. package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  5. package/lib/core/components/shadcn/filters/filterBar.js +9 -2
  6. package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
  7. package/lib/core/components/shadcn/modal/ConfirmModal.d.ts +16 -1
  8. package/lib/core/components/shadcn/modal/ConfirmModal.d.ts.map +1 -1
  9. package/lib/core/components/shadcn/modal/ConfirmModal.js +25 -3
  10. package/lib/core/components/shadcn/modal/ConfirmModal.js.map +1 -1
  11. package/lib/core/components/shadcn/modal/dialog.js +1 -1
  12. package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
  13. package/lib/core/components/shadcn/popover.d.ts.map +1 -1
  14. package/lib/core/components/shadcn/popover.js +3 -1
  15. package/lib/core/components/shadcn/popover.js.map +1 -1
  16. package/lib/core/components/shadcn/switch.js +3 -3
  17. package/lib/core/components/shadcn/switch.js.map +1 -1
  18. package/lib/core/components/shadcn/tabs.js +1 -1
  19. package/lib/core/components/shadcn/tabs.js.map +1 -1
  20. package/lib/core/components/shadcn/tooltip.d.ts.map +1 -1
  21. package/lib/core/components/shadcn/tooltip.js +3 -1
  22. package/lib/core/components/shadcn/tooltip.js.map +1 -1
  23. package/lib/env/index.d.ts +0 -19
  24. package/lib/env/index.d.ts.map +1 -1
  25. package/lib/env/index.js +0 -6
  26. package/lib/env/index.js.map +1 -1
  27. package/lib/features/agent/PayloadBuilder.d.ts +4 -0
  28. package/lib/features/agent/PayloadBuilder.d.ts.map +1 -1
  29. package/lib/features/agent/PayloadBuilder.js +16 -0
  30. package/lib/features/agent/PayloadBuilder.js.map +1 -1
  31. package/lib/features/agent/chat/AgentApprovalModeSelector.d.ts +10 -0
  32. package/lib/features/agent/chat/AgentApprovalModeSelector.d.ts.map +1 -0
  33. package/lib/features/agent/chat/AgentApprovalModeSelector.js +50 -0
  34. package/lib/features/agent/chat/AgentApprovalModeSelector.js.map +1 -0
  35. package/lib/features/agent/chat/AgentChatFixtureReplay.d.ts +2 -1
  36. package/lib/features/agent/chat/AgentChatFixtureReplay.d.ts.map +1 -1
  37. package/lib/features/agent/chat/AgentChatFixtureReplay.js +3 -3
  38. package/lib/features/agent/chat/AgentChatFixtureReplay.js.map +1 -1
  39. package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts +5 -2
  40. package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts.map +1 -1
  41. package/lib/features/agent/chat/AgentRequestInputOverlay.js +41 -7
  42. package/lib/features/agent/chat/AgentRequestInputOverlay.js.map +1 -1
  43. package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
  44. package/lib/features/agent/chat/AgentRightPanel.js +133 -12
  45. package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
  46. package/lib/features/agent/chat/AnimatedThinkingDots.js +1 -1
  47. package/lib/features/agent/chat/AnimatedThinkingDots.js.map +1 -1
  48. package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
  49. package/lib/features/agent/chat/ArtifactsTab.js +11 -65
  50. package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
  51. package/lib/features/agent/chat/AskUserWidget.d.ts +3 -1
  52. package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
  53. package/lib/features/agent/chat/AskUserWidget.js +4 -3
  54. package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
  55. package/lib/features/agent/chat/ModernAgentConversation.d.ts +10 -2
  56. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  57. package/lib/features/agent/chat/ModernAgentConversation.js +526 -117
  58. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  59. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.d.ts +8 -0
  60. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.d.ts.map +1 -0
  61. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js +19 -0
  62. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js.map +1 -0
  63. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +8 -2
  64. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  65. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +551 -92
  66. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  67. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
  68. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +7 -6
  69. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
  70. package/lib/features/agent/chat/ModernAgentOutput/Header.js +2 -2
  71. package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
  72. package/lib/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.d.ts +8 -0
  73. package/lib/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.d.ts.map +1 -0
  74. package/lib/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.js +61 -0
  75. package/lib/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.js.map +1 -0
  76. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +15 -2
  77. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
  78. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +28 -38
  79. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
  80. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +1 -1
  81. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  82. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +3 -2
  83. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  84. package/lib/features/agent/chat/ModernAgentOutput/SlideInPanel.js +2 -1
  85. package/lib/features/agent/chat/ModernAgentOutput/SlideInPanel.js.map +1 -1
  86. package/lib/features/agent/chat/ModernAgentOutput/SlidingMessages.js +1 -1
  87. package/lib/features/agent/chat/ModernAgentOutput/SlidingMessages.js.map +1 -1
  88. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts +5 -0
  89. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts.map +1 -1
  90. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js +59 -4
  91. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js.map +1 -1
  92. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js +2 -2
  93. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js.map +1 -1
  94. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts +9 -0
  95. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts.map +1 -1
  96. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js +180 -0
  97. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js.map +1 -1
  98. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts +6 -0
  99. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
  100. package/lib/features/agent/chat/ModernAgentOutput/utils.js +92 -2
  101. package/lib/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
  102. package/lib/features/agent/chat/clipboardFiles.d.ts +15 -0
  103. package/lib/features/agent/chat/clipboardFiles.d.ts.map +1 -0
  104. package/lib/features/agent/chat/clipboardFiles.js +35 -0
  105. package/lib/features/agent/chat/clipboardFiles.js.map +1 -0
  106. package/lib/features/agent/chat/hooks/useAgentStream.d.ts +6 -0
  107. package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
  108. package/lib/features/agent/chat/hooks/useAgentStream.js +218 -37
  109. package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
  110. package/lib/features/agent/chat/hooks/useArtifacts.d.ts +1 -8
  111. package/lib/features/agent/chat/hooks/useArtifacts.d.ts.map +1 -1
  112. package/lib/features/agent/chat/hooks/useArtifacts.js +10 -28
  113. package/lib/features/agent/chat/hooks/useArtifacts.js.map +1 -1
  114. package/lib/features/agent/chat/index.d.ts +2 -1
  115. package/lib/features/agent/chat/index.d.ts.map +1 -1
  116. package/lib/features/agent/chat/index.js +2 -1
  117. package/lib/features/agent/chat/index.js.map +1 -1
  118. package/lib/features/agent/chat/workstreams.d.ts +1 -0
  119. package/lib/features/agent/chat/workstreams.d.ts.map +1 -1
  120. package/lib/features/agent/chat/workstreams.js +28 -0
  121. package/lib/features/agent/chat/workstreams.js.map +1 -1
  122. package/lib/features/facets/AgentRunnerFacetsNav.d.ts +10 -1
  123. package/lib/features/facets/AgentRunnerFacetsNav.d.ts.map +1 -1
  124. package/lib/features/facets/AgentRunnerFacetsNav.js +6 -3
  125. package/lib/features/facets/AgentRunnerFacetsNav.js.map +1 -1
  126. package/lib/features/facets/CollectionsFacetsNav.d.ts +10 -1
  127. package/lib/features/facets/CollectionsFacetsNav.d.ts.map +1 -1
  128. package/lib/features/facets/CollectionsFacetsNav.js +14 -5
  129. package/lib/features/facets/CollectionsFacetsNav.js.map +1 -1
  130. package/lib/features/facets/PromptsFacetsNav.d.ts +10 -1
  131. package/lib/features/facets/PromptsFacetsNav.d.ts.map +1 -1
  132. package/lib/features/facets/PromptsFacetsNav.js +14 -5
  133. package/lib/features/facets/PromptsFacetsNav.js.map +1 -1
  134. package/lib/features/facets/RunsFacetsNav.d.ts +10 -1
  135. package/lib/features/facets/RunsFacetsNav.d.ts.map +1 -1
  136. package/lib/features/facets/RunsFacetsNav.js +14 -5
  137. package/lib/features/facets/RunsFacetsNav.js.map +1 -1
  138. package/lib/features/index.d.ts +0 -1
  139. package/lib/features/index.d.ts.map +1 -1
  140. package/lib/features/index.js +0 -1
  141. package/lib/features/index.js.map +1 -1
  142. package/lib/features/oauth/McpConnectionsButton.d.ts +30 -0
  143. package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -0
  144. package/lib/features/oauth/McpConnectionsButton.js +70 -0
  145. package/lib/features/oauth/McpConnectionsButton.js.map +1 -0
  146. package/lib/features/oauth/McpConnectionsDialog.d.ts +25 -0
  147. package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -0
  148. package/lib/features/oauth/McpConnectionsDialog.js +26 -0
  149. package/lib/features/oauth/McpConnectionsDialog.js.map +1 -0
  150. package/lib/features/oauth/RemoteMcpConnectionButton.d.ts.map +1 -1
  151. package/lib/features/oauth/RemoteMcpConnectionButton.js +14 -6
  152. package/lib/features/oauth/RemoteMcpConnectionButton.js.map +1 -1
  153. package/lib/features/oauth/index.d.ts +3 -0
  154. package/lib/features/oauth/index.d.ts.map +1 -1
  155. package/lib/features/oauth/index.js +3 -0
  156. package/lib/features/oauth/index.js.map +1 -1
  157. package/lib/features/oauth/useMcpConnections.d.ts +44 -0
  158. package/lib/features/oauth/useMcpConnections.d.ts.map +1 -0
  159. package/lib/features/oauth/useMcpConnections.js +168 -0
  160. package/lib/features/oauth/useMcpConnections.js.map +1 -0
  161. package/lib/features/permissions/UserPermissionsProvider.d.ts +4 -6
  162. package/lib/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
  163. package/lib/features/permissions/UserPermissionsProvider.js +40 -42
  164. package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
  165. package/lib/features/store/objects/DocumentPreviewPanel.d.ts.map +1 -1
  166. package/lib/features/store/objects/DocumentPreviewPanel.js +2 -1
  167. package/lib/features/store/objects/DocumentPreviewPanel.js.map +1 -1
  168. package/lib/features/store/objects/DocumentQuickFilter.d.ts +23 -0
  169. package/lib/features/store/objects/DocumentQuickFilter.d.ts.map +1 -0
  170. package/lib/features/store/objects/DocumentQuickFilter.js +30 -0
  171. package/lib/features/store/objects/DocumentQuickFilter.js.map +1 -0
  172. package/lib/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
  173. package/lib/features/store/objects/DocumentSearchResults.js +17 -1
  174. package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
  175. package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  176. package/lib/features/store/objects/components/ContentOverview.js +17 -74
  177. package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
  178. package/lib/features/store/objects/layout/DocumentTableColumn.d.ts +7 -1
  179. package/lib/features/store/objects/layout/DocumentTableColumn.d.ts.map +1 -1
  180. package/lib/features/store/objects/layout/DocumentTableColumn.js +27 -3
  181. package/lib/features/store/objects/layout/DocumentTableColumn.js.map +1 -1
  182. package/lib/features/store/objects/layout/documentLayout.js +1 -1
  183. package/lib/features/store/objects/layout/renderers.d.ts +2 -1
  184. package/lib/features/store/objects/layout/renderers.d.ts.map +1 -1
  185. package/lib/features/store/objects/layout/renderers.js +27 -20
  186. package/lib/features/store/objects/layout/renderers.js.map +1 -1
  187. package/lib/features/store/types/ContentObjectTypesSearch.d.ts.map +1 -1
  188. package/lib/features/store/types/ContentObjectTypesSearch.js +4 -1
  189. package/lib/features/store/types/ContentObjectTypesSearch.js.map +1 -1
  190. package/lib/features/store/types/ContentObjectTypesTable.d.ts +2 -1
  191. package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
  192. package/lib/features/store/types/ContentObjectTypesTable.js +8 -3
  193. package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
  194. package/lib/i18n/instance.d.ts.map +1 -1
  195. package/lib/i18n/instance.js +1 -0
  196. package/lib/i18n/instance.js.map +1 -1
  197. package/lib/i18n/locales/ar.json +72 -45
  198. package/lib/i18n/locales/de.json +65 -34
  199. package/lib/i18n/locales/en.json +63 -47
  200. package/lib/i18n/locales/es.json +65 -35
  201. package/lib/i18n/locales/fr.json +65 -35
  202. package/lib/i18n/locales/it.json +72 -42
  203. package/lib/i18n/locales/ja.json +72 -41
  204. package/lib/i18n/locales/ko.json +72 -41
  205. package/lib/i18n/locales/pt.json +72 -42
  206. package/lib/i18n/locales/ru.json +72 -43
  207. package/lib/i18n/locales/tr.json +72 -41
  208. package/lib/i18n/locales/zh-TW.json +72 -41
  209. package/lib/i18n/locales/zh.json +72 -41
  210. package/lib/session/UserSession.d.ts +0 -6
  211. package/lib/session/UserSession.d.ts.map +1 -1
  212. package/lib/session/UserSession.js +0 -15
  213. package/lib/session/UserSession.js.map +1 -1
  214. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  215. package/lib/session/UserSessionProvider.js +39 -98
  216. package/lib/session/UserSessionProvider.js.map +1 -1
  217. package/lib/session/auth/composable.d.ts.map +1 -1
  218. package/lib/session/auth/composable.js +3 -33
  219. package/lib/session/auth/composable.js.map +1 -1
  220. package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
  221. package/lib/shell/login/SignInPrimitives.js +3 -2
  222. package/lib/shell/login/SignInPrimitives.js.map +1 -1
  223. package/lib/vertesia-ui-core.js +1 -1
  224. package/lib/vertesia-ui-core.js.map +1 -1
  225. package/lib/vertesia-ui-env.js +1 -1
  226. package/lib/vertesia-ui-env.js.map +1 -1
  227. package/lib/vertesia-ui-features.js +1 -1
  228. package/lib/vertesia-ui-features.js.map +1 -1
  229. package/lib/vertesia-ui-i18n.js +1 -1
  230. package/lib/vertesia-ui-i18n.js.map +1 -1
  231. package/lib/vertesia-ui-session.js +1 -1
  232. package/lib/vertesia-ui-session.js.map +1 -1
  233. package/lib/vertesia-ui-shell.js +1 -1
  234. package/lib/vertesia-ui-shell.js.map +1 -1
  235. package/lib/vertesia-ui-widgets.js +1 -1
  236. package/lib/vertesia-ui-widgets.js.map +1 -1
  237. package/lib/widgets/schema-editor/editor/Editable.js +1 -1
  238. package/lib/widgets/schema-editor/editor/Editable.js.map +1 -1
  239. package/llms.txt +70 -70
  240. package/package.json +35 -32
  241. package/src/core/components/shadcn/dropdown.tsx +2 -0
  242. package/src/core/components/shadcn/filters/filterBar.tsx +9 -2
  243. package/src/core/components/shadcn/modal/ConfirmModal.tsx +65 -3
  244. package/src/core/components/shadcn/modal/dialog.tsx +1 -1
  245. package/src/core/components/shadcn/popover.tsx +1 -0
  246. package/src/core/components/shadcn/switch.tsx +4 -4
  247. package/src/core/components/shadcn/tabs.tsx +1 -1
  248. package/src/core/components/shadcn/tooltip.tsx +1 -0
  249. package/src/env/index.ts +0 -24
  250. package/src/features/agent/PayloadBuilder.tsx +18 -0
  251. package/src/features/agent/chat/AgentApprovalModeSelector.tsx +130 -0
  252. package/src/features/agent/chat/AgentChatFixtureReplay.test.tsx +22 -2
  253. package/src/features/agent/chat/AgentChatFixtureReplay.tsx +33 -27
  254. package/src/features/agent/chat/AgentRequestInputOverlay.test.tsx +122 -0
  255. package/src/features/agent/chat/AgentRequestInputOverlay.tsx +91 -17
  256. package/src/features/agent/chat/AgentRightPanel.tsx +238 -50
  257. package/src/features/agent/chat/AnimatedThinkingDots.tsx +1 -1
  258. package/src/features/agent/chat/ArtifactsTab.tsx +21 -155
  259. package/src/features/agent/chat/AskUserWidget.tsx +6 -2
  260. package/src/features/agent/chat/ModernAgentConversation.test.tsx +1157 -47
  261. package/src/features/agent/chat/ModernAgentConversation.tsx +703 -136
  262. package/src/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.tsx +74 -0
  263. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +1420 -126
  264. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +934 -111
  265. package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +31 -40
  266. package/src/features/agent/chat/ModernAgentOutput/Header.tsx +28 -28
  267. package/src/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.tsx +83 -0
  268. package/src/features/agent/chat/ModernAgentOutput/MessageInput.test.tsx +38 -2
  269. package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +121 -98
  270. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +7 -1
  271. package/src/features/agent/chat/ModernAgentOutput/SlideInPanel.tsx +3 -3
  272. package/src/features/agent/chat/ModernAgentOutput/SlidingMessages.tsx +1 -1
  273. package/src/features/agent/chat/ModernAgentOutput/SummaryConversation.ts +82 -2
  274. package/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx +2 -2
  275. package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.ts +216 -0
  276. package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +149 -0
  277. package/src/features/agent/chat/ModernAgentOutput/utils.ts +103 -2
  278. package/src/features/agent/chat/clipboardFiles.ts +33 -0
  279. package/src/features/agent/chat/hooks/useAgentStream.test.tsx +355 -4
  280. package/src/features/agent/chat/hooks/useAgentStream.ts +270 -43
  281. package/src/features/agent/chat/hooks/useArtifacts.ts +11 -44
  282. package/src/features/agent/chat/index.ts +10 -1
  283. package/src/features/agent/chat/workstreams.ts +42 -0
  284. package/src/features/facets/AgentRunnerFacetsNav.tsx +23 -3
  285. package/src/features/facets/CollectionsFacetsNav.tsx +30 -5
  286. package/src/features/facets/PromptsFacetsNav.tsx +30 -5
  287. package/src/features/facets/RunsFacetsNav.tsx +32 -5
  288. package/src/features/index.ts +0 -1
  289. package/src/features/oauth/McpConnectionsButton.test.tsx +248 -0
  290. package/src/features/oauth/McpConnectionsButton.tsx +269 -0
  291. package/src/features/oauth/McpConnectionsDialog.tsx +142 -0
  292. package/src/features/oauth/RemoteMcpConnectionButton.tsx +37 -14
  293. package/src/features/oauth/index.ts +3 -0
  294. package/src/features/oauth/useMcpConnections.test.ts +94 -0
  295. package/src/features/oauth/useMcpConnections.ts +210 -0
  296. package/src/features/permissions/UserPermissionsProvider.tsx +41 -59
  297. package/src/features/store/objects/DocumentPreviewPanel.tsx +4 -2
  298. package/src/features/store/objects/DocumentQuickFilter.tsx +72 -0
  299. package/src/features/store/objects/DocumentSearchResults.tsx +30 -11
  300. package/src/features/store/objects/components/ContentOverview.tsx +30 -99
  301. package/src/features/store/objects/layout/DocumentTableColumn.tsx +29 -4
  302. package/src/features/store/objects/layout/documentLayout.tsx +1 -1
  303. package/src/features/store/objects/layout/renderers.tsx +80 -20
  304. package/src/features/store/types/ContentObjectTypesSearch.tsx +7 -1
  305. package/src/features/store/types/ContentObjectTypesTable.tsx +34 -4
  306. package/src/i18n/instance.ts +1 -0
  307. package/src/i18n/locales/ar.json +72 -45
  308. package/src/i18n/locales/de.json +65 -34
  309. package/src/i18n/locales/en.json +63 -47
  310. package/src/i18n/locales/es.json +65 -35
  311. package/src/i18n/locales/fr.json +65 -35
  312. package/src/i18n/locales/it.json +72 -42
  313. package/src/i18n/locales/ja.json +72 -41
  314. package/src/i18n/locales/ko.json +72 -41
  315. package/src/i18n/locales/pt.json +72 -42
  316. package/src/i18n/locales/ru.json +72 -43
  317. package/src/i18n/locales/tr.json +72 -41
  318. package/src/i18n/locales/zh-TW.json +72 -41
  319. package/src/i18n/locales/zh.json +72 -41
  320. package/src/i18n/rtl.test.ts +57 -1
  321. package/src/session/UserSession.ts +0 -16
  322. package/src/session/UserSessionProvider.tsx +51 -123
  323. package/src/session/auth/composable.ts +3 -34
  324. package/src/shell/login/SignInPrimitives.tsx +3 -2
  325. package/src/widgets/schema-editor/editor/Editable.tsx +1 -1
  326. package/lib/features/document-viewer/UniversalDocumentViewer.d.ts +0 -73
  327. package/lib/features/document-viewer/UniversalDocumentViewer.d.ts.map +0 -1
  328. package/lib/features/document-viewer/UniversalDocumentViewer.js +0 -415
  329. package/lib/features/document-viewer/UniversalDocumentViewer.js.map +0 -1
  330. package/lib/features/document-viewer/index.d.ts +0 -2
  331. package/lib/features/document-viewer/index.d.ts.map +0 -1
  332. package/lib/features/document-viewer/index.js +0 -2
  333. package/lib/features/document-viewer/index.js.map +0 -1
  334. package/src/features/document-viewer/UniversalDocumentViewer.test.tsx +0 -123
  335. package/src/features/document-viewer/UniversalDocumentViewer.tsx +0 -746
  336. package/src/features/document-viewer/index.ts +0 -1
  337. package/src/session/auth/composable.test.ts +0 -79
@@ -17,6 +17,15 @@ interface CollectionsFacetsNavProps {
17
17
  dynamic?: FacetBucket[];
18
18
  };
19
19
  search: SearchInterface;
20
+ /**
21
+ * Optional controlled filter state. When provided, the parent owns the filter list (and is
22
+ * responsible for translating it into the search query via {@link useCollectionsFilterHandler}).
23
+ * This lets other surfaces — e.g. per-row "quick filter" buttons in the table — add filters that
24
+ * show up in the filter bar. When omitted, the component manages its own filter state internally.
25
+ */
26
+ filters?: BaseFilter[];
27
+ setFilters?: React.Dispatch<React.SetStateAction<BaseFilter[]>>;
28
+ filterGroups?: FilterGroup[];
20
29
  }
21
30
 
22
31
  // Hook to create filter groups for collections
@@ -85,14 +94,30 @@ export function useCollectionsFilterHandler(search: SearchInterface) {
85
94
  }
86
95
 
87
96
  // Component for collections filtering
88
- export function CollectionsFacetsNav({ facets, search }: CollectionsFacetsNavProps) {
89
- const [filters, setFilters] = useState<BaseFilter[]>([]);
90
- const filterGroups = useCollectionsFilterGroups(facets);
97
+ export function CollectionsFacetsNav({
98
+ facets,
99
+ search,
100
+ filters: controlledFilters,
101
+ setFilters: controlledSetFilters,
102
+ filterGroups: controlledFilterGroups,
103
+ }: CollectionsFacetsNavProps) {
104
+ const [internalFilters, setInternalFilters] = useState<BaseFilter[]>([]);
105
+ const internalFilterGroups = useCollectionsFilterGroups(facets);
91
106
  const handleFilterLogic = useCollectionsFilterHandler(search);
92
107
 
108
+ // Controlled when the parent supplies both the filter list and its setter; otherwise self-managed.
109
+ const isControlled = controlledFilters !== undefined && controlledSetFilters !== undefined;
110
+ const filters = isControlled ? controlledFilters : internalFilters;
111
+ const filterGroups = controlledFilterGroups ?? internalFilterGroups;
112
+
93
113
  const handleFilterChange: React.Dispatch<React.SetStateAction<BaseFilter[]>> = (value) => {
94
- const newFilters = typeof value === 'function' ? value(filters) : value;
95
- setFilters(newFilters);
114
+ if (isControlled) {
115
+ // The parent's setter is expected to also run the filter→query translation.
116
+ controlledSetFilters(value);
117
+ return;
118
+ }
119
+ const newFilters = typeof value === 'function' ? value(internalFilters) : value;
120
+ setInternalFilters(newFilters);
96
121
  handleFilterLogic(newFilters);
97
122
  };
98
123
 
@@ -17,6 +17,15 @@ interface PromptsFacetsNavProps {
17
17
  tags?: FacetBucket[];
18
18
  };
19
19
  search: SearchInterface;
20
+ /**
21
+ * Optional controlled filter state. When provided, the parent owns the filter list (and is
22
+ * responsible for translating it into the search query via {@link usePromptsFilterHandler}). This
23
+ * lets other surfaces — e.g. per-row "quick filter" buttons in the table — add filters that show
24
+ * up in the filter bar. When omitted, the component manages its own filter state internally.
25
+ */
26
+ filters?: BaseFilter[];
27
+ setFilters?: React.Dispatch<React.SetStateAction<BaseFilter[]>>;
28
+ filterGroups?: FilterGroup[];
20
29
  }
21
30
 
22
31
  // Hook to create filter groups for prompts
@@ -76,14 +85,30 @@ export function usePromptsFilterHandler(search: SearchInterface) {
76
85
  }
77
86
 
78
87
  // Component for prompts filtering
79
- export function PromptsFacetsNav({ facets, search }: PromptsFacetsNavProps) {
80
- const [filters, setFilters] = useState<BaseFilter[]>([]);
81
- const filterGroups = usePromptsFilterGroups(facets);
88
+ export function PromptsFacetsNav({
89
+ facets,
90
+ search,
91
+ filters: controlledFilters,
92
+ setFilters: controlledSetFilters,
93
+ filterGroups: controlledFilterGroups,
94
+ }: PromptsFacetsNavProps) {
95
+ const [internalFilters, setInternalFilters] = useState<BaseFilter[]>([]);
96
+ const internalFilterGroups = usePromptsFilterGroups(facets);
82
97
  const handleFilterLogic = usePromptsFilterHandler(search);
83
98
 
99
+ // Controlled when the parent supplies both the filter list and its setter; otherwise self-managed.
100
+ const isControlled = controlledFilters !== undefined && controlledSetFilters !== undefined;
101
+ const filters = isControlled ? controlledFilters : internalFilters;
102
+ const filterGroups = controlledFilterGroups ?? internalFilterGroups;
103
+
84
104
  const handleFilterChange: React.Dispatch<React.SetStateAction<BaseFilter[]>> = (value) => {
85
- const newFilters = typeof value === 'function' ? value(filters) : value;
86
- setFilters(newFilters);
105
+ if (isControlled) {
106
+ // The parent's setter is expected to also run the filter→query translation.
107
+ controlledSetFilters(value);
108
+ return;
109
+ }
110
+ const newFilters = typeof value === 'function' ? value(internalFilters) : value;
111
+ setInternalFilters(newFilters);
87
112
  handleFilterLogic(newFilters);
88
113
  };
89
114
 
@@ -31,6 +31,15 @@ interface RunsFacetsNavProps {
31
31
  search: SearchInterface;
32
32
  actions?: React.ReactNode[];
33
33
  selectionCount?: number;
34
+ /**
35
+ * Optional controlled filter state. When provided, the parent owns the filter list (and is
36
+ * responsible for translating it into the search query via {@link useRunsFilterHandler}). This
37
+ * lets other surfaces — e.g. per-row "quick filter" buttons in the table — add filters that show
38
+ * up in the filter bar. When omitted, the component manages its own filter state internally.
39
+ */
40
+ filters?: BaseFilter[];
41
+ setFilters?: React.Dispatch<React.SetStateAction<BaseFilter[]>>;
42
+ filterGroups?: FilterGroup[];
34
43
  }
35
44
 
36
45
  // Hook to create filter groups for runs
@@ -179,14 +188,32 @@ export function useRunsFilterHandler(search: SearchInterface) {
179
188
  }
180
189
 
181
190
  // Legacy component for backward compatibility
182
- export function RunsFacetsNav({ facets, search, actions, selectionCount }: RunsFacetsNavProps) {
183
- const [filters, setFilters] = useState<BaseFilter[]>([]);
184
- const filterGroups = useRunsFilterGroups(facets);
191
+ export function RunsFacetsNav({
192
+ facets,
193
+ search,
194
+ actions,
195
+ selectionCount,
196
+ filters: controlledFilters,
197
+ setFilters: controlledSetFilters,
198
+ filterGroups: controlledFilterGroups,
199
+ }: RunsFacetsNavProps) {
200
+ const [internalFilters, setInternalFilters] = useState<BaseFilter[]>([]);
201
+ const internalFilterGroups = useRunsFilterGroups(facets);
185
202
  const handleFilterLogic = useRunsFilterHandler(search);
186
203
 
204
+ // Controlled when the parent supplies both the filter list and its setter; otherwise self-managed.
205
+ const isControlled = controlledFilters !== undefined && controlledSetFilters !== undefined;
206
+ const filters = isControlled ? controlledFilters : internalFilters;
207
+ const filterGroups = controlledFilterGroups ?? internalFilterGroups;
208
+
187
209
  const handleFilterChange: React.Dispatch<React.SetStateAction<BaseFilter[]>> = (value) => {
188
- const newFilters = typeof value === 'function' ? value(filters) : value;
189
- setFilters(newFilters);
210
+ if (isControlled) {
211
+ // The parent's setter is expected to also run the filter→query translation.
212
+ controlledSetFilters(value);
213
+ return;
214
+ }
215
+ const newFilters = typeof value === 'function' ? value(internalFilters) : value;
216
+ setInternalFilters(newFilters);
190
217
  handleFilterLogic(newFilters);
191
218
  };
192
219
 
@@ -1,6 +1,5 @@
1
1
  export * from './activity-doc/index.js';
2
2
  export * from './agent';
3
- export * from './document-viewer/index.js';
4
3
  export * from './errors/index.js';
5
4
  export * from './facets/index.js';
6
5
  export * from './layout/index.js';
@@ -0,0 +1,248 @@
1
+ import { act, fireEvent, screen, waitFor } from '@testing-library/react';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
+ import { renderWithProviders } from '../../__tests__/test-utils.js';
4
+ import { McpConnectionsActionMenu, McpConnectionsInlineList } from './McpConnectionsButton.js';
5
+
6
+ const mocks = vi.hoisted(() => ({
7
+ getCollectionStatus: vi.fn(),
8
+ getInstalledApps: vi.fn(),
9
+ getStatus: vi.fn(),
10
+ }));
11
+
12
+ const mockClient = {
13
+ apps: {
14
+ getInstalledApps: mocks.getInstalledApps,
15
+ },
16
+ remoteMcpConnections: {
17
+ getCollectionStatus: mocks.getCollectionStatus,
18
+ getStatus: mocks.getStatus,
19
+ },
20
+ };
21
+
22
+ vi.mock('@vertesia/ui/session', () => ({
23
+ useUserSession: () => ({
24
+ client: mockClient,
25
+ }),
26
+ }));
27
+
28
+ function deferred<T>() {
29
+ let resolve!: (value: T) => void;
30
+ let reject!: (reason?: unknown) => void;
31
+ const promise = new Promise<T>((res, rej) => {
32
+ resolve = res;
33
+ reject = rej;
34
+ });
35
+ return { promise, resolve, reject };
36
+ }
37
+
38
+ describe('MCP connection controls', () => {
39
+ afterEach(() => {
40
+ vi.clearAllMocks();
41
+ });
42
+
43
+ it('shows available MCP servers and their connection and availability state inline', async () => {
44
+ mocks.getInstalledApps.mockResolvedValue([
45
+ {
46
+ id: 'app-1',
47
+ manifest: {
48
+ name: 'mcp-app',
49
+ title: 'MCP App',
50
+ oauth_providers: {},
51
+ tool_collections: [
52
+ {
53
+ type: 'mcp',
54
+ id: 'jira',
55
+ name: 'Jira',
56
+ namespace: 'jira',
57
+ url: 'https://jira-mcp.example.com',
58
+ },
59
+ {
60
+ type: 'mcp',
61
+ id: 'miro',
62
+ name: 'Miro',
63
+ namespace: 'miro',
64
+ url: 'https://miro-mcp.example.com',
65
+ },
66
+ {
67
+ type: 'mcp',
68
+ id: 'linear',
69
+ name: 'Linear',
70
+ namespace: 'linear',
71
+ url: 'https://linear-mcp.example.com',
72
+ },
73
+ {
74
+ type: 'mcp',
75
+ id: 'github',
76
+ name: 'GitHub',
77
+ namespace: 'github',
78
+ url: 'https://github-mcp.example.com',
79
+ },
80
+ ],
81
+ },
82
+ oauth_collection_ids: ['jira', 'miro', 'linear', 'github'],
83
+ },
84
+ ]);
85
+ mocks.getStatus.mockResolvedValue([
86
+ {
87
+ authenticated: true,
88
+ collection_id: 'jira',
89
+ collection_name: 'Jira',
90
+ mcp_server_url: 'https://jira-mcp.example.com',
91
+ },
92
+ {
93
+ authenticated: true,
94
+ collection_id: 'miro',
95
+ collection_name: 'Miro',
96
+ mcp_server_url: 'https://miro-mcp.example.com',
97
+ },
98
+ {
99
+ authenticated: true,
100
+ collection_id: 'linear',
101
+ collection_name: 'Linear',
102
+ mcp_server_url: 'https://linear-mcp.example.com',
103
+ },
104
+ {
105
+ authenticated: false,
106
+ collection_id: 'github',
107
+ collection_name: 'GitHub',
108
+ mcp_server_url: 'https://github-mcp.example.com',
109
+ },
110
+ ]);
111
+ const onChange = vi.fn();
112
+
113
+ renderWithProviders(<McpConnectionsInlineList disabledCollections={['linear']} onChange={onChange} />);
114
+
115
+ await screen.findByText('Jira');
116
+ await waitFor(() => expect(mocks.getStatus).toHaveBeenCalledTimes(1));
117
+
118
+ expect(screen.getByText('Miro')).not.toBeNull();
119
+ expect(screen.getByText('Linear')).not.toBeNull();
120
+ expect(screen.getByText('GitHub')).not.toBeNull();
121
+ expect(screen.queryByText('Connected')).toBeNull();
122
+ expect(screen.getAllByRole('button', { name: 'Disconnect' })).toHaveLength(3);
123
+ const connectButton = screen.getByRole('button', { name: 'Connect' });
124
+ const disconnectButton = screen.getAllByRole('button', { name: 'Disconnect' })[0];
125
+ expect(connectButton.className).toContain('w-32');
126
+ expect(disconnectButton.className).toContain('w-32');
127
+ expect(screen.getAllByText('Enabled')).toHaveLength(3);
128
+ expect(screen.getByText('Disabled')).not.toBeNull();
129
+ });
130
+
131
+ it('renders MCP rows before connection statuses finish loading', async () => {
132
+ const statusRequest =
133
+ deferred<
134
+ Array<{
135
+ authenticated: boolean;
136
+ collection_id: string;
137
+ collection_name: string;
138
+ mcp_server_url: string;
139
+ }>
140
+ >();
141
+ mocks.getInstalledApps.mockResolvedValue([
142
+ {
143
+ id: 'app-1',
144
+ manifest: {
145
+ name: 'jira-app',
146
+ title: 'Jira App',
147
+ oauth_providers: {},
148
+ tool_collections: [
149
+ {
150
+ type: 'mcp',
151
+ id: 'jira',
152
+ name: 'Jira',
153
+ namespace: 'jira',
154
+ url: 'https://jira-mcp.example.com',
155
+ },
156
+ ],
157
+ },
158
+ oauth_collection_ids: ['jira'],
159
+ },
160
+ ]);
161
+ mocks.getStatus.mockReturnValue(statusRequest.promise);
162
+
163
+ renderWithProviders(<McpConnectionsInlineList onChange={vi.fn()} />);
164
+
165
+ expect(await screen.findByText('Jira')).not.toBeNull();
166
+ const checkingButton = screen.getByRole('button', { name: 'Checking status' });
167
+ expect((checkingButton as HTMLButtonElement).disabled).toBe(true);
168
+ expect(checkingButton.className).toContain('w-32');
169
+ expect(mocks.getCollectionStatus).not.toHaveBeenCalled();
170
+
171
+ await act(async () => {
172
+ statusRequest.resolve([
173
+ {
174
+ authenticated: true,
175
+ collection_id: 'jira',
176
+ collection_name: 'Jira',
177
+ mcp_server_url: 'https://jira-mcp.example.com',
178
+ },
179
+ ]);
180
+ });
181
+
182
+ await waitFor(() => expect(screen.getByRole('button', { name: 'Disconnect' })).not.toBeNull());
183
+ expect(screen.queryByText('Connected')).toBeNull();
184
+ });
185
+
186
+ it('renders MCP in a separate slash action menu', async () => {
187
+ mocks.getInstalledApps.mockResolvedValue([
188
+ {
189
+ id: 'app-1',
190
+ manifest: {
191
+ name: 'jira-app',
192
+ title: 'Jira',
193
+ oauth_providers: {},
194
+ tool_collections: [
195
+ {
196
+ type: 'mcp',
197
+ id: 'jira',
198
+ name: 'Jira',
199
+ namespace: 'jira',
200
+ url: 'https://mcp.example.com',
201
+ },
202
+ {
203
+ type: 'mcp',
204
+ id: 'github',
205
+ name: 'GitHub',
206
+ namespace: 'github',
207
+ url: 'https://github-mcp.example.com',
208
+ },
209
+ ],
210
+ },
211
+ oauth_collection_ids: ['jira', 'github'],
212
+ },
213
+ ]);
214
+ mocks.getStatus.mockResolvedValue([
215
+ {
216
+ authenticated: true,
217
+ collection_id: 'jira',
218
+ collection_name: 'Jira',
219
+ mcp_server_url: 'https://mcp.example.com',
220
+ },
221
+ {
222
+ authenticated: false,
223
+ collection_id: 'github',
224
+ collection_name: 'GitHub',
225
+ mcp_server_url: 'https://github-mcp.example.com',
226
+ },
227
+ ]);
228
+
229
+ renderWithProviders(<McpConnectionsActionMenu />);
230
+
231
+ const trigger = await screen.findByRole('button', { name: /settings/i });
232
+ await waitFor(() => expect(mocks.getStatus).toHaveBeenCalledTimes(1));
233
+ expect(mocks.getStatus).toHaveBeenCalledWith('app-1');
234
+ expect(trigger.textContent).toBe('/');
235
+
236
+ fireEvent.pointerDown(trigger);
237
+
238
+ const menuItem = await screen.findByText('MCP');
239
+ expect(menuItem).not.toBeNull();
240
+ expect(screen.getByText('1')).not.toBeNull();
241
+
242
+ fireEvent.click(menuItem);
243
+
244
+ await waitFor(() => {
245
+ expect(screen.getByText('Manage the MCP servers available to this conversation.')).not.toBeNull();
246
+ });
247
+ });
248
+ });
@@ -0,0 +1,269 @@
1
+ import { useUITranslation } from '@vertesia/ui/i18n';
2
+ import { Link2, Link2Off } from 'lucide-react';
3
+ import { useState } from 'react';
4
+ import { Badge, Button, Dropdown, MenuItem, Spinner, Switch, VTooltip } from '../../core/index.js';
5
+ import { McpConnectionsDialog } from './McpConnectionsDialog.js';
6
+ import { RemoteMcpConnectionButton } from './RemoteMcpConnectionButton.js';
7
+ import {
8
+ countConnectedActiveGroups,
9
+ getConnectedActiveGroupLabels,
10
+ isGroupDisabled,
11
+ toggleGroupDisabled,
12
+ useMcpConnections,
13
+ } from './useMcpConnections.js';
14
+
15
+ export interface McpConnectionsButtonProps {
16
+ /** Collection ids deactivated for this conversation. `undefined`/empty ⇒ all active. */
17
+ disabledCollections?: string[];
18
+ /** Emits the next disabled-collection list when a server is toggled. Omit for read-only. */
19
+ onChange?: (disabled: string[]) => void;
20
+ /** Called after a successful connect/disconnect (e.g. to trigger tool re-discovery). */
21
+ onConnectionChange?: () => void;
22
+ /** Read-only: show status only, no toggles or connect/disconnect controls. */
23
+ readOnly?: boolean;
24
+ /** Button visual style. */
25
+ variant?: 'primary' | 'outline' | 'ghost' | 'secondary';
26
+ size?: 'xs' | 'sm' | 'md';
27
+ }
28
+
29
+ /**
30
+ * Toolbar button that opens the MCP connections dialog. The badge shows how many MCP
31
+ * servers are connected and active for this conversation.
32
+ * Renders nothing when the project has no OAuth MCP servers installed.
33
+ */
34
+ export function McpConnectionsButton({
35
+ disabledCollections,
36
+ onChange,
37
+ onConnectionChange,
38
+ readOnly = false,
39
+ variant = 'outline',
40
+ size = 'sm',
41
+ }: McpConnectionsButtonProps) {
42
+ const { t } = useUITranslation();
43
+ const { groups, loading, statusLoading, reload } = useMcpConnections();
44
+ const [isOpen, setIsOpen] = useState(false);
45
+
46
+ if (!loading && groups.length === 0) {
47
+ return null;
48
+ }
49
+
50
+ const activeCount = countConnectedActiveGroups(groups, disabledCollections);
51
+ const activeLabels = getConnectedActiveGroupLabels(groups, disabledCollections);
52
+ const visibleLabels = activeLabels.slice(0, 2);
53
+ const hiddenLabelCount = activeLabels.length - visibleLabels.length;
54
+ const summary =
55
+ visibleLabels.length > 0
56
+ ? `${visibleLabels.join(', ')}${hiddenLabelCount > 0 ? ` +${hiddenLabelCount}` : ''}`
57
+ : undefined;
58
+
59
+ return (
60
+ <>
61
+ <div className="flex min-w-0 items-center gap-2">
62
+ {summary && (
63
+ <span className="max-w-48 truncate text-muted" title={activeLabels.join(', ')}>
64
+ {summary}
65
+ </span>
66
+ )}
67
+ <Button
68
+ variant={variant}
69
+ size={size}
70
+ className="shrink-0"
71
+ onClick={() => setIsOpen(true)}
72
+ disabled={loading}
73
+ aria-label={t('mcpConnections.manage')}
74
+ >
75
+ <span>{t('mcpConnections.label')}</span>
76
+ {activeCount > 0 && <Badge variant="success">{activeCount}</Badge>}
77
+ </Button>
78
+ </div>
79
+ <McpConnectionsDialog
80
+ isOpen={isOpen}
81
+ onClose={() => setIsOpen(false)}
82
+ groups={groups}
83
+ loading={loading}
84
+ reload={reload}
85
+ statusLoading={statusLoading}
86
+ disabledCollections={disabledCollections}
87
+ onChange={onChange}
88
+ onConnectionChange={onConnectionChange}
89
+ readOnly={readOnly}
90
+ />
91
+ </>
92
+ );
93
+ }
94
+
95
+ /**
96
+ * Inline MCP status list for forms. It shows all available OAuth MCP groups
97
+ * without requiring the user to open the management dialog.
98
+ */
99
+ export function McpConnectionsInlineList({
100
+ disabledCollections,
101
+ onChange,
102
+ onConnectionChange,
103
+ readOnly = false,
104
+ }: Omit<McpConnectionsButtonProps, 'variant' | 'size'>) {
105
+ const { t } = useUITranslation();
106
+ const { groups, loading, statusLoading, reload } = useMcpConnections();
107
+
108
+ if (!loading && groups.length === 0) {
109
+ return null;
110
+ }
111
+
112
+ const handleAuthChange = () => {
113
+ void reload();
114
+ onConnectionChange?.();
115
+ };
116
+
117
+ if (loading && groups.length === 0) {
118
+ return (
119
+ <div className="flex items-center gap-2 border-y border-border/70 py-2 text-sm text-muted">
120
+ <Spinner className="size-4" />
121
+ <span>{t('mcpConnections.label')}</span>
122
+ </div>
123
+ );
124
+ }
125
+
126
+ return (
127
+ <div className="space-y-1">
128
+ {groups.map((group) => {
129
+ const active = !isGroupDisabled(group, disabledCollections);
130
+ const connected = group.authStatus?.authenticated === true;
131
+ const StatusIcon = connected ? Link2 : Link2Off;
132
+ return (
133
+ <div key={group.key} className="flex flex-wrap items-center gap-x-3 gap-y-2 py-2 text-sm">
134
+ <div className="flex min-w-36 flex-1 items-center gap-2">
135
+ <StatusIcon
136
+ className={`size-4 shrink-0 ${connected ? 'text-success' : 'text-muted'}`}
137
+ aria-hidden="true"
138
+ />
139
+ <VTooltip
140
+ description={
141
+ <div className="space-y-0.5">
142
+ <div className="font-medium">{group.appName}</div>
143
+ {group.memberNames.length > 1 && (
144
+ <div className="text-xs text-muted">{group.memberNames.join(' · ')}</div>
145
+ )}
146
+ </div>
147
+ }
148
+ placement="top"
149
+ asChild
150
+ >
151
+ <span
152
+ className={`block truncate ${active ? 'text-foreground' : 'text-muted line-through'}`}
153
+ >
154
+ {group.label}
155
+ </span>
156
+ </VTooltip>
157
+ </div>
158
+ {statusLoading && !group.authStatus ? (
159
+ <Button
160
+ variant="outline"
161
+ size="sm"
162
+ disabled
163
+ className="h-6 w-32 justify-center px-2 text-xs"
164
+ aria-label={t('mcpConnections.checkingStatus')}
165
+ title={t('mcpConnections.checkingStatus')}
166
+ >
167
+ <Spinner className="size-3" />
168
+ </Button>
169
+ ) : (
170
+ <RemoteMcpConnectionButton
171
+ appId={group.appId}
172
+ collectionId={group.representativeId}
173
+ collectionName={group.label}
174
+ authenticated={group.authStatus?.authenticated}
175
+ onAuthChange={handleAuthChange}
176
+ variant="compact"
177
+ showDisconnect
178
+ readOnly={readOnly}
179
+ />
180
+ )}
181
+ {onChange && (
182
+ <div className="flex min-w-24 items-center justify-end gap-2 text-xs">
183
+ <span className={active ? 'text-muted' : 'text-attention'}>
184
+ {active ? t('mcpConnections.enabled') : t('mcpConnections.disabled')}
185
+ </span>
186
+ <Switch
187
+ size="sm"
188
+ value={active}
189
+ onChange={(checked) =>
190
+ onChange(toggleGroupDisabled(group, disabledCollections, checked))
191
+ }
192
+ disabled={readOnly}
193
+ aria-label={
194
+ active
195
+ ? t('mcpConnections.deactivateAria', { name: group.label })
196
+ : t('mcpConnections.activateAria', { name: group.label })
197
+ }
198
+ />
199
+ </div>
200
+ )}
201
+ </div>
202
+ );
203
+ })}
204
+ </div>
205
+ );
206
+ }
207
+
208
+ /**
209
+ * Compact composer action menu for MCP. The slash trigger keeps MCP out of the
210
+ * main composer toolbar while still exposing the connection dialog when relevant.
211
+ */
212
+ export function McpConnectionsActionMenu({
213
+ disabledCollections,
214
+ onChange,
215
+ onConnectionChange,
216
+ readOnly = false,
217
+ }: Omit<McpConnectionsButtonProps, 'variant' | 'size'>) {
218
+ const { t } = useUITranslation();
219
+ const { groups, loading, statusLoading, reload } = useMcpConnections();
220
+ const [isOpen, setIsOpen] = useState(false);
221
+
222
+ if (!loading && groups.length === 0) {
223
+ return null;
224
+ }
225
+
226
+ const activeCount = countConnectedActiveGroups(groups, disabledCollections);
227
+
228
+ return (
229
+ <>
230
+ <Dropdown
231
+ align="left"
232
+ trigger={
233
+ <Button
234
+ variant="ghost"
235
+ size="icon"
236
+ className="size-8 rounded-md text-muted hover:bg-muted"
237
+ aria-label={t('mcpConnections.settings')}
238
+ title={t('mcpConnections.settings')}
239
+ disabled={loading}
240
+ >
241
+ <span
242
+ className="flex size-5 items-center justify-center rounded-[3px] border border-muted text-sm font-semibold leading-none"
243
+ aria-hidden="true"
244
+ >
245
+ /
246
+ </span>
247
+ </Button>
248
+ }
249
+ >
250
+ <MenuItem onClick={() => setIsOpen(true)} isDisabled={loading}>
251
+ <span>{t('mcpConnections.title')}</span>
252
+ {activeCount > 0 && <Badge variant="success">{activeCount}</Badge>}
253
+ </MenuItem>
254
+ </Dropdown>
255
+ <McpConnectionsDialog
256
+ isOpen={isOpen}
257
+ onClose={() => setIsOpen(false)}
258
+ groups={groups}
259
+ loading={loading}
260
+ reload={reload}
261
+ statusLoading={statusLoading}
262
+ disabledCollections={disabledCollections}
263
+ onChange={onChange}
264
+ onConnectionChange={onConnectionChange}
265
+ readOnly={readOnly}
266
+ />
267
+ </>
268
+ );
269
+ }