@vertesia/ui 1.0.0 → 1.1.0-dev.20260427.060440Z

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 (293) hide show
  1. package/lib/esm/core/components/ComboBox.js +23 -24
  2. package/lib/esm/core/components/ComboBox.js.map +1 -1
  3. package/lib/esm/core/components/FormItem.js +2 -2
  4. package/lib/esm/core/components/FormItem.js.map +1 -1
  5. package/lib/esm/core/components/SidePanel.js +1 -1
  6. package/lib/esm/core/components/SidePanel.js.map +1 -1
  7. package/lib/esm/core/components/shadcn/collaspible.js +3 -4
  8. package/lib/esm/core/components/shadcn/collaspible.js.map +1 -1
  9. package/lib/esm/core/components/shadcn/dropdown.js +37 -12
  10. package/lib/esm/core/components/shadcn/dropdown.js.map +1 -1
  11. package/lib/esm/core/components/shadcn/filters/comboBox/DateCombobox.js +6 -6
  12. package/lib/esm/core/components/shadcn/filters/comboBox/DateCombobox.js.map +1 -1
  13. package/lib/esm/core/components/shadcn/filters/filterBar.js +5 -3
  14. package/lib/esm/core/components/shadcn/filters/filterBar.js.map +1 -1
  15. package/lib/esm/core/components/shadcn/radioGroup.js +1 -2
  16. package/lib/esm/core/components/shadcn/radioGroup.js.map +1 -1
  17. package/lib/esm/core/components/shadcn/switch.js +0 -1
  18. package/lib/esm/core/components/shadcn/switch.js.map +1 -1
  19. package/lib/esm/core/components/shadcn/tabs.js +2 -2
  20. package/lib/esm/core/components/shadcn/tabs.js.map +1 -1
  21. package/lib/esm/core/components/shadcn/tooltip.js +17 -1
  22. package/lib/esm/core/components/shadcn/tooltip.js.map +1 -1
  23. package/lib/esm/core/hooks/PortalContainerProvider.js +9 -3
  24. package/lib/esm/core/hooks/PortalContainerProvider.js.map +1 -1
  25. package/lib/esm/env/index.js +5 -8
  26. package/lib/esm/env/index.js.map +1 -1
  27. package/lib/esm/features/agent/chat/AgentRightPanel.js +21 -11
  28. package/lib/esm/features/agent/chat/AgentRightPanel.js.map +1 -1
  29. package/lib/esm/features/agent/chat/AskUserWidget.js +2 -6
  30. package/lib/esm/features/agent/chat/AskUserWidget.js.map +1 -1
  31. package/lib/esm/features/agent/chat/DocumentPanel.js +8 -5
  32. package/lib/esm/features/agent/chat/DocumentPanel.js.map +1 -1
  33. package/lib/esm/features/agent/chat/DocumentTabBar.js +5 -13
  34. package/lib/esm/features/agent/chat/DocumentTabBar.js.map +1 -1
  35. package/lib/esm/features/agent/chat/ModernAgentConversation.js +57 -26
  36. package/lib/esm/features/agent/chat/ModernAgentConversation.js.map +1 -1
  37. package/lib/esm/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +20 -16
  38. package/lib/esm/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  39. package/lib/esm/features/agent/chat/ModernAgentOutput/Header.js +56 -45
  40. package/lib/esm/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
  41. package/lib/esm/features/agent/chat/ModernAgentOutput/MessageItem.js +1 -1
  42. package/lib/esm/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  43. package/lib/esm/features/agent/chat/SlidingThinkingIndicator.js +3 -9
  44. package/lib/esm/features/agent/chat/SlidingThinkingIndicator.js.map +1 -1
  45. package/lib/esm/features/agent/chat/hooks/useAgentStream.js +9 -5
  46. package/lib/esm/features/agent/chat/hooks/useAgentStream.js.map +1 -1
  47. package/lib/esm/features/agent/chat/hooks/useDocumentPanel.js +4 -0
  48. package/lib/esm/features/agent/chat/hooks/useDocumentPanel.js.map +1 -1
  49. package/lib/esm/features/facets/AgentRunnerFacetsNav.js +1 -1
  50. package/lib/esm/features/facets/AgentRunnerFacetsNav.js.map +1 -1
  51. package/lib/esm/features/facets/DocumentsFacetsNav.js +3 -2
  52. package/lib/esm/features/facets/DocumentsFacetsNav.js.map +1 -1
  53. package/lib/esm/features/facets/RunsFacetsNav.js +8 -1
  54. package/lib/esm/features/facets/RunsFacetsNav.js.map +1 -1
  55. package/lib/esm/features/facets/WorkflowExecutionsFacetsNav.js +1 -1
  56. package/lib/esm/features/facets/WorkflowExecutionsFacetsNav.js.map +1 -1
  57. package/lib/esm/features/facets/index.js +1 -0
  58. package/lib/esm/features/facets/index.js.map +1 -1
  59. package/lib/esm/features/facets/utils/VInteractionFacet.js +5 -5
  60. package/lib/esm/features/facets/utils/VInteractionFacet.js.map +1 -1
  61. package/lib/esm/features/index.js +1 -0
  62. package/lib/esm/features/index.js.map +1 -1
  63. package/lib/esm/features/oauth/OAuthProviderConnectButton.js +85 -0
  64. package/lib/esm/features/oauth/OAuthProviderConnectButton.js.map +1 -0
  65. package/lib/esm/features/oauth/RemoteMcpConnectionButton.js +119 -0
  66. package/lib/esm/features/oauth/RemoteMcpConnectionButton.js.map +1 -0
  67. package/lib/esm/features/oauth/index.js +4 -0
  68. package/lib/esm/features/oauth/index.js.map +1 -0
  69. package/lib/esm/features/oauth/useOAuthPopup.js +89 -0
  70. package/lib/esm/features/oauth/useOAuthPopup.js.map +1 -0
  71. package/lib/esm/features/store/collections/BrowseCollectionView.js.map +1 -1
  72. package/lib/esm/features/store/collections/EditCollectionView.js +9 -2
  73. package/lib/esm/features/store/collections/EditCollectionView.js.map +1 -1
  74. package/lib/esm/features/store/objects/components/ContentOverview.js +19 -7
  75. package/lib/esm/features/store/objects/components/ContentOverview.js.map +1 -1
  76. package/lib/esm/features/store/objects/components/useContentPanelHooks.js +35 -15
  77. package/lib/esm/features/store/objects/components/useContentPanelHooks.js.map +1 -1
  78. package/lib/esm/features/store/objects/selection/ObjectsActionContext.js +3 -3
  79. package/lib/esm/features/store/objects/selection/ObjectsActionContext.js.map +1 -1
  80. package/lib/esm/features/store/objects/selection/SelectionActions.js +4 -3
  81. package/lib/esm/features/store/objects/selection/SelectionActions.js.map +1 -1
  82. package/lib/esm/features/store/objects/selection/actions/ExportPropertiesAction.js +11 -3
  83. package/lib/esm/features/store/objects/selection/actions/ExportPropertiesAction.js.map +1 -1
  84. package/lib/esm/features/store/objects/upload/DocumentUploadModal.js +1 -5
  85. package/lib/esm/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
  86. package/lib/esm/features/user/UserInfo.js +33 -10
  87. package/lib/esm/features/user/UserInfo.js.map +1 -1
  88. package/lib/esm/i18n/locales/ar.json +81 -98
  89. package/lib/esm/i18n/locales/de.json +44 -73
  90. package/lib/esm/i18n/locales/en.json +31 -61
  91. package/lib/esm/i18n/locales/es.json +55 -79
  92. package/lib/esm/i18n/locales/fr.json +55 -81
  93. package/lib/esm/i18n/locales/it.json +55 -79
  94. package/lib/esm/i18n/locales/ja.json +46 -75
  95. package/lib/esm/i18n/locales/ko.json +44 -73
  96. package/lib/esm/i18n/locales/pt.json +55 -79
  97. package/lib/esm/i18n/locales/ru.json +58 -81
  98. package/lib/esm/i18n/locales/tr.json +46 -75
  99. package/lib/esm/i18n/locales/zh-TW.json +46 -75
  100. package/lib/esm/i18n/locales/zh.json +46 -75
  101. package/lib/esm/session/UserSession.js +2 -4
  102. package/lib/esm/session/UserSession.js.map +1 -1
  103. package/lib/esm/session/UserSessionProvider.js +22 -17
  104. package/lib/esm/session/UserSessionProvider.js.map +1 -1
  105. package/lib/esm/session/auth/composable.js +20 -2
  106. package/lib/esm/session/auth/composable.js.map +1 -1
  107. package/lib/esm/session/auth/domainRouting.js +7 -0
  108. package/lib/esm/session/auth/domainRouting.js.map +1 -0
  109. package/lib/esm/shell/login/InviteAcceptModal.js +1 -0
  110. package/lib/esm/shell/login/InviteAcceptModal.js.map +1 -1
  111. package/lib/esm/widgets/form/Form.js +2 -2
  112. package/lib/esm/widgets/form/Form.js.map +1 -1
  113. package/lib/esm/widgets/markdown/MarkdownRenderer.js +2 -1
  114. package/lib/esm/widgets/markdown/MarkdownRenderer.js.map +1 -1
  115. package/lib/esm/widgets/markdown/preprocessMathDelimiters.js +226 -0
  116. package/lib/esm/widgets/markdown/preprocessMathDelimiters.js.map +1 -0
  117. package/lib/esm/widgets/monacoEditor/MonacoEditor.js +40 -5
  118. package/lib/esm/widgets/monacoEditor/MonacoEditor.js.map +1 -1
  119. package/lib/esm/widgets/monacoEditor/foldingProviders.js +132 -0
  120. package/lib/esm/widgets/monacoEditor/foldingProviders.js.map +1 -0
  121. package/lib/tsconfig.tsbuildinfo +1 -1
  122. package/lib/types/core/components/ComboBox.d.ts +12 -2
  123. package/lib/types/core/components/ComboBox.d.ts.map +1 -1
  124. package/lib/types/core/components/FormItem.d.ts +5 -2
  125. package/lib/types/core/components/FormItem.d.ts.map +1 -1
  126. package/lib/types/core/components/SidePanel.d.ts.map +1 -1
  127. package/lib/types/core/components/shadcn/badge.d.ts +2 -2
  128. package/lib/types/core/components/shadcn/collaspible.d.ts +3 -3
  129. package/lib/types/core/components/shadcn/collaspible.d.ts.map +1 -1
  130. package/lib/types/core/components/shadcn/dropdown.d.ts +11 -3
  131. package/lib/types/core/components/shadcn/dropdown.d.ts.map +1 -1
  132. package/lib/types/core/components/shadcn/filters/filterBar.d.ts +2 -1
  133. package/lib/types/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  134. package/lib/types/core/components/shadcn/input.d.ts +1 -1
  135. package/lib/types/core/components/shadcn/radioGroup.d.ts.map +1 -1
  136. package/lib/types/core/components/shadcn/switch.d.ts.map +1 -1
  137. package/lib/types/core/components/shadcn/tabs.d.ts +4 -2
  138. package/lib/types/core/components/shadcn/tabs.d.ts.map +1 -1
  139. package/lib/types/core/components/shadcn/text.d.ts +1 -1
  140. package/lib/types/core/components/shadcn/tooltip.d.ts +1 -1
  141. package/lib/types/core/components/shadcn/tooltip.d.ts.map +1 -1
  142. package/lib/types/core/hooks/PortalContainerProvider.d.ts +1 -0
  143. package/lib/types/core/hooks/PortalContainerProvider.d.ts.map +1 -1
  144. package/lib/types/env/index.d.ts +2 -2
  145. package/lib/types/env/index.d.ts.map +1 -1
  146. package/lib/types/features/agent/chat/AgentChart.d.ts +1 -1
  147. package/lib/types/features/agent/chat/AgentChart.d.ts.map +1 -1
  148. package/lib/types/features/agent/chat/AgentRightPanel.d.ts +7 -2
  149. package/lib/types/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
  150. package/lib/types/features/agent/chat/DocumentPanel.d.ts +2 -2
  151. package/lib/types/features/agent/chat/DocumentPanel.d.ts.map +1 -1
  152. package/lib/types/features/agent/chat/DocumentTabBar.d.ts +1 -2
  153. package/lib/types/features/agent/chat/DocumentTabBar.d.ts.map +1 -1
  154. package/lib/types/features/agent/chat/ModernAgentConversation.d.ts +5 -3
  155. package/lib/types/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  156. package/lib/types/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +4 -2
  157. package/lib/types/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  158. package/lib/types/features/agent/chat/ModernAgentOutput/Header.d.ts +4 -4
  159. package/lib/types/features/agent/chat/ModernAgentOutput/Header.d.ts.map +1 -1
  160. package/lib/types/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  161. package/lib/types/features/agent/chat/VegaLiteChart.d.ts +1 -1
  162. package/lib/types/features/agent/chat/VegaLiteChart.d.ts.map +1 -1
  163. package/lib/types/features/agent/chat/hooks/useAgentStream.d.ts +4 -2
  164. package/lib/types/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
  165. package/lib/types/features/agent/chat/hooks/useDocumentPanel.d.ts +1 -0
  166. package/lib/types/features/agent/chat/hooks/useDocumentPanel.d.ts.map +1 -1
  167. package/lib/types/features/facets/DocumentsFacetsNav.d.ts.map +1 -1
  168. package/lib/types/features/facets/RunsFacetsNav.d.ts.map +1 -1
  169. package/lib/types/features/facets/index.d.ts +1 -0
  170. package/lib/types/features/facets/index.d.ts.map +1 -1
  171. package/lib/types/features/index.d.ts +1 -0
  172. package/lib/types/features/index.d.ts.map +1 -1
  173. package/lib/types/features/oauth/OAuthProviderConnectButton.d.ts +11 -0
  174. package/lib/types/features/oauth/OAuthProviderConnectButton.d.ts.map +1 -0
  175. package/lib/types/features/oauth/RemoteMcpConnectionButton.d.ts +25 -0
  176. package/lib/types/features/oauth/RemoteMcpConnectionButton.d.ts.map +1 -0
  177. package/lib/types/features/oauth/index.d.ts +4 -0
  178. package/lib/types/features/oauth/index.d.ts.map +1 -0
  179. package/lib/types/features/oauth/useOAuthPopup.d.ts +12 -0
  180. package/lib/types/features/oauth/useOAuthPopup.d.ts.map +1 -0
  181. package/lib/types/features/store/collections/BrowseCollectionView.d.ts.map +1 -1
  182. package/lib/types/features/store/collections/EditCollectionView.d.ts.map +1 -1
  183. package/lib/types/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
  184. package/lib/types/features/store/objects/selection/ObjectsActionContext.d.ts +3 -3
  185. package/lib/types/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
  186. package/lib/types/features/store/objects/selection/ObjectsActionSpec.d.ts +2 -1
  187. package/lib/types/features/store/objects/selection/ObjectsActionSpec.d.ts.map +1 -1
  188. package/lib/types/features/store/objects/selection/SelectionActions.d.ts +3 -3
  189. package/lib/types/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
  190. package/lib/types/features/store/objects/selection/actions/ExportPropertiesAction.d.ts.map +1 -1
  191. package/lib/types/features/user/UserInfo.d.ts +2 -1
  192. package/lib/types/features/user/UserInfo.d.ts.map +1 -1
  193. package/lib/types/session/UserSession.d.ts.map +1 -1
  194. package/lib/types/session/UserSessionProvider.d.ts +0 -1
  195. package/lib/types/session/UserSessionProvider.d.ts.map +1 -1
  196. package/lib/types/session/auth/composable.d.ts +4 -0
  197. package/lib/types/session/auth/composable.d.ts.map +1 -1
  198. package/lib/types/session/auth/domainRouting.d.ts +8 -0
  199. package/lib/types/session/auth/domainRouting.d.ts.map +1 -0
  200. package/lib/types/shell/login/InviteAcceptModal.d.ts.map +1 -1
  201. package/lib/types/widgets/markdown/MarkdownRenderer.d.ts.map +1 -1
  202. package/lib/types/widgets/markdown/preprocessMathDelimiters.d.ts +24 -0
  203. package/lib/types/widgets/markdown/preprocessMathDelimiters.d.ts.map +1 -0
  204. package/lib/types/widgets/monacoEditor/MonacoEditor.d.ts +2 -1
  205. package/lib/types/widgets/monacoEditor/MonacoEditor.d.ts.map +1 -1
  206. package/lib/types/widgets/monacoEditor/foldingProviders.d.ts +2 -0
  207. package/lib/types/widgets/monacoEditor/foldingProviders.d.ts.map +1 -0
  208. package/lib/vertesia-ui-core.js +1 -1
  209. package/lib/vertesia-ui-core.js.map +1 -1
  210. package/lib/vertesia-ui-env.js +1 -1
  211. package/lib/vertesia-ui-env.js.map +1 -1
  212. package/lib/vertesia-ui-features.js +1 -1
  213. package/lib/vertesia-ui-features.js.map +1 -1
  214. package/lib/vertesia-ui-i18n.js +1 -1
  215. package/lib/vertesia-ui-i18n.js.map +1 -1
  216. package/lib/vertesia-ui-layout.js +1 -1
  217. package/lib/vertesia-ui-layout.js.map +1 -1
  218. package/lib/vertesia-ui-session.js +1 -1
  219. package/lib/vertesia-ui-session.js.map +1 -1
  220. package/lib/vertesia-ui-shell.js +1 -1
  221. package/lib/vertesia-ui-shell.js.map +1 -1
  222. package/lib/vertesia-ui-widgets.js +1 -1
  223. package/lib/vertesia-ui-widgets.js.map +1 -1
  224. package/package.json +15 -15
  225. package/src/core/components/ComboBox.tsx +66 -29
  226. package/src/core/components/FormItem.tsx +9 -6
  227. package/src/core/components/SidePanel.tsx +5 -3
  228. package/src/core/components/shadcn/collaspible.tsx +5 -7
  229. package/src/core/components/shadcn/dropdown.tsx +68 -13
  230. package/src/core/components/shadcn/filters/comboBox/DateCombobox.tsx +6 -6
  231. package/src/core/components/shadcn/filters/filterBar.tsx +5 -3
  232. package/src/core/components/shadcn/radioGroup.tsx +1 -3
  233. package/src/core/components/shadcn/switch.tsx +0 -2
  234. package/src/core/components/shadcn/tabs.tsx +15 -2
  235. package/src/core/components/shadcn/tooltip.tsx +20 -3
  236. package/src/core/hooks/PortalContainerProvider.tsx +11 -3
  237. package/src/env/index.ts +7 -10
  238. package/src/features/agent/chat/AgentRightPanel.tsx +43 -23
  239. package/src/features/agent/chat/DocumentPanel.tsx +21 -19
  240. package/src/features/agent/chat/DocumentTabBar.tsx +21 -32
  241. package/src/features/agent/chat/ModernAgentConversation.tsx +72 -27
  242. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +21 -9
  243. package/src/features/agent/chat/ModernAgentOutput/Header.tsx +136 -115
  244. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +0 -3
  245. package/src/features/agent/chat/hooks/useAgentStream.ts +13 -7
  246. package/src/features/agent/chat/hooks/useDocumentPanel.ts +8 -0
  247. package/src/features/facets/AgentRunnerFacetsNav.tsx +1 -1
  248. package/src/features/facets/DocumentsFacetsNav.tsx +3 -1
  249. package/src/features/facets/RunsFacetsNav.tsx +9 -1
  250. package/src/features/facets/WorkflowExecutionsFacetsNav.tsx +1 -1
  251. package/src/features/facets/index.ts +1 -0
  252. package/src/features/facets/utils/VInteractionFacet.tsx +12 -12
  253. package/src/features/index.ts +1 -0
  254. package/src/features/oauth/OAuthProviderConnectButton.tsx +125 -0
  255. package/src/features/oauth/RemoteMcpConnectionButton.tsx +274 -0
  256. package/src/features/oauth/index.ts +3 -0
  257. package/src/features/oauth/useOAuthPopup.ts +125 -0
  258. package/src/features/store/collections/BrowseCollectionView.tsx +3 -3
  259. package/src/features/store/collections/EditCollectionView.tsx +10 -1
  260. package/src/features/store/objects/components/ContentOverview.tsx +108 -87
  261. package/src/features/store/objects/components/useContentPanelHooks.ts +50 -15
  262. package/src/features/store/objects/selection/ObjectsActionContext.tsx +5 -5
  263. package/src/features/store/objects/selection/ObjectsActionSpec.ts +2 -1
  264. package/src/features/store/objects/selection/SelectionActions.tsx +6 -5
  265. package/src/features/store/objects/selection/actions/ExportPropertiesAction.tsx +12 -3
  266. package/src/features/user/UserInfo.tsx +82 -10
  267. package/src/i18n/locales/ar.json +81 -98
  268. package/src/i18n/locales/de.json +44 -73
  269. package/src/i18n/locales/en.json +31 -61
  270. package/src/i18n/locales/es.json +55 -79
  271. package/src/i18n/locales/fr.json +55 -81
  272. package/src/i18n/locales/it.json +55 -79
  273. package/src/i18n/locales/ja.json +46 -75
  274. package/src/i18n/locales/ko.json +44 -73
  275. package/src/i18n/locales/pt.json +55 -79
  276. package/src/i18n/locales/ru.json +58 -81
  277. package/src/i18n/locales/tr.json +46 -75
  278. package/src/i18n/locales/zh-TW.json +46 -75
  279. package/src/i18n/locales/zh.json +46 -75
  280. package/src/session/UserSession.ts +2 -5
  281. package/src/session/UserSessionProvider.tsx +23 -18
  282. package/src/session/auth/auth-flow.md +1 -1
  283. package/src/session/auth/composable.ts +21 -2
  284. package/src/session/auth/domainRouting.test.ts +26 -0
  285. package/src/session/auth/domainRouting.ts +13 -0
  286. package/src/shell/login/InviteAcceptModal.tsx +1 -0
  287. package/src/widgets/form/Form.tsx +2 -2
  288. package/src/widgets/markdown/MarkdownRenderer.tsx +2 -1
  289. package/src/widgets/markdown/markdown.css +12 -0
  290. package/src/widgets/markdown/preprocessMathDelimiters.test.ts +87 -0
  291. package/src/widgets/markdown/preprocessMathDelimiters.ts +229 -0
  292. package/src/widgets/monacoEditor/MonacoEditor.tsx +47 -4
  293. package/src/widgets/monacoEditor/foldingProviders.ts +122 -0
@@ -0,0 +1,125 @@
1
+ import type { OAuthProviderAuthStatus } from '@vertesia/common';
2
+ import { useUserSession } from '@vertesia/ui/session';
3
+ import { CheckCircle2, ExternalLink } from 'lucide-react';
4
+ import { useCallback, useEffect, useState } from 'react';
5
+ import { Button, Spinner } from '../../core/index.js';
6
+ import { useUITranslation } from '../../i18n/index.js';
7
+ import { useOAuthPopup } from './useOAuthPopup.js';
8
+
9
+ interface OAuthProviderConnectButtonProps {
10
+ oauthProviderId: string;
11
+ onAuthChange?: () => void;
12
+ }
13
+
14
+ /**
15
+ * Connect button for generic OAuth Providers (not MCP-specific).
16
+ * Uses the OAuth Providers API for authorize/status/disconnect.
17
+ */
18
+ export function OAuthProviderConnectButton({ oauthProviderId, onAuthChange }: OAuthProviderConnectButtonProps) {
19
+ const { client } = useUserSession();
20
+ const { t } = useUITranslation();
21
+ const [status, setStatus] = useState<OAuthProviderAuthStatus | null>(null);
22
+ const [loading, setLoading] = useState(true);
23
+ const [authenticating, setAuthenticating] = useState(false);
24
+ const [disconnecting, setDisconnecting] = useState(false);
25
+
26
+ const loadStatus = useCallback(async () => {
27
+ try {
28
+ setLoading(true);
29
+ const data = await client.oauthProviders.getStatus(oauthProviderId);
30
+ setStatus(data);
31
+ } catch (err: unknown) {
32
+ console.error('Failed to load OAuth provider status:', err);
33
+ } finally {
34
+ setLoading(false);
35
+ }
36
+ }, [client, oauthProviderId]);
37
+
38
+ useEffect(() => {
39
+ loadStatus();
40
+ }, [loadStatus]);
41
+
42
+ const { openOAuthPopup } = useOAuthPopup({
43
+ onComplete: () => {
44
+ setAuthenticating(false);
45
+ loadStatus();
46
+ onAuthChange?.();
47
+ },
48
+ onError: (error) => {
49
+ console.error('OAuth provider auth failed:', error);
50
+ setAuthenticating(false);
51
+ },
52
+ });
53
+
54
+ const handleConnect = async () => {
55
+ if (authenticating) return;
56
+ try {
57
+ setAuthenticating(true);
58
+ const response = await client.oauthProviders.authorize(oauthProviderId);
59
+ if (response.authorization_url) {
60
+ openOAuthPopup(response.authorization_url);
61
+ }
62
+ } catch (err: unknown) {
63
+ console.error('Failed to authorize OAuth provider:', err);
64
+ setAuthenticating(false);
65
+ }
66
+ };
67
+
68
+ const handleDisconnect = async () => {
69
+ if (disconnecting) return;
70
+ try {
71
+ setDisconnecting(true);
72
+ await client.oauthProviders.disconnect(oauthProviderId);
73
+ await loadStatus();
74
+ onAuthChange?.();
75
+ } catch (err: unknown) {
76
+ console.error('Failed to disconnect OAuth provider:', err);
77
+ } finally {
78
+ setDisconnecting(false);
79
+ }
80
+ };
81
+
82
+ if (loading) {
83
+ return <Spinner className="size-4" />;
84
+ }
85
+
86
+ if (status?.authenticated) {
87
+ return (
88
+ <div className="flex items-center gap-2">
89
+ <div className="flex items-center gap-1 text-success text-sm">
90
+ <CheckCircle2 className="size-4" />
91
+ <span>{t('oauthProvider.connected')}</span>
92
+ </div>
93
+ <Button
94
+ variant="outline"
95
+ size="sm"
96
+ onClick={handleDisconnect}
97
+ disabled={disconnecting}
98
+ >
99
+ {disconnecting ? <Spinner className="size-3" /> : t('oauthProvider.disconnect')}
100
+ </Button>
101
+ </div>
102
+ );
103
+ }
104
+
105
+ return (
106
+ <Button
107
+ variant="outline"
108
+ size="sm"
109
+ onClick={handleConnect}
110
+ disabled={authenticating}
111
+ >
112
+ {authenticating ? (
113
+ <>
114
+ <Spinner className="size-4" />
115
+ <span>{t('oauthProvider.authenticating')}</span>
116
+ </>
117
+ ) : (
118
+ <>
119
+ <ExternalLink className="size-4 mr-1" />
120
+ <span>{t('oauthProvider.authenticate')}</span>
121
+ </>
122
+ )}
123
+ </Button>
124
+ );
125
+ }
@@ -0,0 +1,274 @@
1
+ import { useUserSession } from '@vertesia/ui/session';
2
+ import { Button, Spinner } from '../../core/index.js';
3
+ import { useUITranslation } from '../../i18n/index.js';
4
+ import { CheckCircle2, ExternalLink, ShieldAlertIcon } from 'lucide-react';
5
+ import { useCallback, useEffect, useState } from 'react';
6
+ import { useOAuthPopup } from './useOAuthPopup.js';
7
+
8
+ interface RemoteMcpConnectionButtonProps {
9
+ appId: string;
10
+ collectionId: string;
11
+ collectionName?: string;
12
+ /** Pre-fetched authentication status. If not provided, will fetch automatically. */
13
+ authenticated?: boolean;
14
+ onAuthChange?: () => void;
15
+ onError?: (error: string | null) => void;
16
+ variant?: 'default' | 'compact' | 'full';
17
+ /** Show collection name label */
18
+ showLabel?: boolean;
19
+ /** Show disconnect button when authenticated */
20
+ showDisconnect?: boolean;
21
+ /** Read-only mode - only show status, no interactive buttons */
22
+ readOnly?: boolean;
23
+ }
24
+
25
+ interface OAuthStatus {
26
+ authenticated: boolean;
27
+ collection_id: string;
28
+ collection_name: string;
29
+ expires_at?: string;
30
+ mcp_server_url: string;
31
+ }
32
+
33
+ /**
34
+ * Flexible connect button for remote MCP tool collections.
35
+ * - 'compact': minimal size for lists (no disconnect)
36
+ * - 'default': standard button size (no disconnect)
37
+ * - 'full': with label, status, and disconnect button
38
+ */
39
+ export function RemoteMcpConnectionButton({
40
+ appId,
41
+ collectionId,
42
+ collectionName,
43
+ authenticated: providedAuthenticated,
44
+ onAuthChange,
45
+ onError,
46
+ variant = 'default',
47
+ showLabel = false,
48
+ showDisconnect = false,
49
+ readOnly = false
50
+ }: RemoteMcpConnectionButtonProps) {
51
+ const { client } = useUserSession();
52
+ const { t } = useUITranslation();
53
+ const [status, setStatus] = useState<OAuthStatus | null>(null);
54
+ const [loading, setLoading] = useState(providedAuthenticated === undefined);
55
+ const [authenticating, setAuthenticating] = useState(false);
56
+ const [disconnecting, setDisconnecting] = useState(false);
57
+
58
+ const authenticated = providedAuthenticated ?? status?.authenticated ?? false;
59
+ const displayName = collectionName ?? collectionId;
60
+
61
+ const loadStatus = useCallback(async () => {
62
+ if (providedAuthenticated !== undefined) {
63
+ setLoading(false);
64
+ return;
65
+ }
66
+
67
+ try {
68
+ setLoading(true);
69
+ const data = await client.remoteMcpConnections.getCollectionStatus(appId, collectionId);
70
+ setStatus(data);
71
+ } catch (error) {
72
+ console.error('[RemoteMcpConnectionButton] Failed to load OAuth status:', error);
73
+ } finally {
74
+ setLoading(false);
75
+ }
76
+ }, [client, appId, collectionId, providedAuthenticated]);
77
+
78
+ useEffect(() => {
79
+ loadStatus();
80
+ }, [loadStatus]);
81
+
82
+ const { openOAuthPopup } = useOAuthPopup({
83
+ onComplete: () => {
84
+ setAuthenticating(false);
85
+ loadStatus();
86
+ onAuthChange?.();
87
+ },
88
+ onError: (error) => {
89
+ console.error('OAuth failed:', error);
90
+ setAuthenticating(false);
91
+ }
92
+ });
93
+
94
+ const handleConnect = async () => {
95
+ try {
96
+ setAuthenticating(true);
97
+ onError?.(null);
98
+ const response = await client.remoteMcpConnections.authorize(appId, collectionId);
99
+ if (response.connected) {
100
+ setAuthenticating(false);
101
+ await loadStatus();
102
+ onAuthChange?.();
103
+ } else if (response.authorization_url) {
104
+ openOAuthPopup(response.authorization_url);
105
+ } else {
106
+ onError?.(`${displayName}: Authorization URL not provided by server`);
107
+ setAuthenticating(false);
108
+ }
109
+ } catch (error) {
110
+ console.error('Failed to authorize:', error);
111
+ const raw = error instanceof Error
112
+ ? ((error as { original_message?: string }).original_message ?? error.message)
113
+ : 'Failed to connect';
114
+ const detail = raw.replace(/^[A-Za-z\s]+:\s/, '');
115
+ onError?.(`${displayName}: ${detail}`);
116
+ setAuthenticating(false);
117
+ }
118
+ };
119
+
120
+ const handleDisconnect = async () => {
121
+ try {
122
+ setDisconnecting(true);
123
+ await client.remoteMcpConnections.disconnect(appId, collectionId);
124
+ await loadStatus();
125
+ onAuthChange?.();
126
+ } catch (error) {
127
+ console.error('Failed to disconnect:', error);
128
+ } finally {
129
+ setDisconnecting(false);
130
+ }
131
+ };
132
+
133
+ if (loading) {
134
+ return (
135
+ <div className="flex items-center gap-2 text-sm">
136
+ <Spinner className="size-4" />
137
+ </div>
138
+ );
139
+ }
140
+
141
+ if (variant === 'full') {
142
+ if (!authenticated) {
143
+ return (
144
+ <div className="flex items-center gap-2 text-sm">
145
+ {showLabel && <span className="font-medium text-foreground">{displayName}:</span>}
146
+ <Button variant="ghost" size="sm" onClick={handleConnect} disabled={authenticating}>
147
+ {authenticating ? (
148
+ <>
149
+ <Spinner className="size-4" />
150
+ <span>{t('mcpOAuth.authenticating')}</span>
151
+ </>
152
+ ) : (
153
+ <>
154
+ <ShieldAlertIcon className="size-4" />
155
+ <span>{t('mcpOAuth.authenticate')}</span>
156
+ </>
157
+ )}
158
+ </Button>
159
+ </div>
160
+ );
161
+ }
162
+
163
+ return (
164
+ <div className="flex items-center gap-2">
165
+ {showLabel && <span className="font-medium text-sm text-foreground">{displayName}:</span>}
166
+ <div className="flex items-center gap-1 text-success text-sm">
167
+ <CheckCircle2 className="size-4" />
168
+ <span>{t('mcpOAuth.connected')}</span>
169
+ </div>
170
+ {showDisconnect && (
171
+ <Button
172
+ variant="ghost"
173
+ size="sm"
174
+ onClick={handleDisconnect}
175
+ disabled={disconnecting}
176
+ >
177
+ {disconnecting ? <Spinner className="size-4" /> : t('mcpOAuth.disconnect')}
178
+ </Button>
179
+ )}
180
+ </div>
181
+ );
182
+ }
183
+
184
+ if (authenticated) {
185
+ if (variant === 'compact') {
186
+ return (
187
+ <div className="flex items-center gap-2">
188
+ {showLabel && <span className="font-medium text-xs text-foreground">{displayName}:</span>}
189
+ <div className="flex items-center gap-1 text-success">
190
+ <CheckCircle2 className="size-3" />
191
+ <span className="text-xs">{t('mcpOAuth.connected')}</span>
192
+ </div>
193
+ {!readOnly && showDisconnect && (
194
+ <Button
195
+ variant="ghost"
196
+ size="sm"
197
+ onClick={handleDisconnect}
198
+ disabled={disconnecting}
199
+ className="h-6 px-2 text-xs"
200
+ >
201
+ {disconnecting ? <Spinner className="size-3" /> : t('mcpOAuth.disconnect')}
202
+ </Button>
203
+ )}
204
+ </div>
205
+ );
206
+ }
207
+
208
+ return (
209
+ <div className="flex items-center gap-1 text-success">
210
+ <CheckCircle2 className="size-4" />
211
+ <span className="text-sm">{t('mcpOAuth.connected')}</span>
212
+ </div>
213
+ );
214
+ }
215
+
216
+ if (variant === 'compact') {
217
+ if (readOnly) {
218
+ return (
219
+ <div className="flex items-center gap-2">
220
+ {showLabel && <span className="font-medium text-xs text-foreground">{collectionName}:</span>}
221
+ <div className="flex items-center gap-1 text-muted">
222
+ <span className="text-xs">{t('mcpOAuth.notConnected')}</span>
223
+ </div>
224
+ </div>
225
+ );
226
+ }
227
+
228
+ return (
229
+ <div className="flex items-center gap-2">
230
+ {showLabel && <span className="font-medium text-xs text-foreground">{displayName}:</span>}
231
+ <Button
232
+ variant="outline"
233
+ size="sm"
234
+ onClick={handleConnect}
235
+ disabled={authenticating}
236
+ className="h-6 px-2 text-xs"
237
+ >
238
+ {authenticating ? (
239
+ <>
240
+ <Spinner className="size-3" />
241
+ <span>{t('mcpOAuth.connecting')}</span>
242
+ </>
243
+ ) : (
244
+ <>
245
+ <ExternalLink className="size-3 mr-1" />
246
+ <span>{t('mcpOAuth.connect')}</span>
247
+ </>
248
+ )}
249
+ </Button>
250
+ </div>
251
+ );
252
+ }
253
+
254
+ return (
255
+ <Button
256
+ variant="outline"
257
+ size="sm"
258
+ onClick={handleConnect}
259
+ disabled={authenticating}
260
+ >
261
+ {authenticating ? (
262
+ <>
263
+ <Spinner className="size-4" />
264
+ <span>{t('mcpOAuth.connecting')}</span>
265
+ </>
266
+ ) : (
267
+ <>
268
+ <ExternalLink className="size-4 mr-1" />
269
+ <span>{t('mcpOAuth.connect')}</span>
270
+ </>
271
+ )}
272
+ </Button>
273
+ );
274
+ }
@@ -0,0 +1,3 @@
1
+ export * from './OAuthProviderConnectButton.js';
2
+ export * from './RemoteMcpConnectionButton.js';
3
+ export * from './useOAuthPopup.js';
@@ -0,0 +1,125 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ export interface UseOAuthPopupOptions {
4
+ onComplete: () => void;
5
+ onError?: (error: string) => void;
6
+ }
7
+
8
+ /**
9
+ * OAuth message structure from callback page
10
+ */
11
+ interface OAuthMessage {
12
+ type: 'oauth-complete';
13
+ success: boolean;
14
+ error?: string;
15
+ }
16
+
17
+ /**
18
+ * Type guard to validate OAuth message structure
19
+ */
20
+ function isOAuthMessage(data: unknown): data is OAuthMessage {
21
+ return (
22
+ typeof data === 'object' &&
23
+ data !== null &&
24
+ 'type' in data &&
25
+ data.type === 'oauth-complete' &&
26
+ 'success' in data &&
27
+ typeof (data as OAuthMessage).success === 'boolean'
28
+ );
29
+ }
30
+
31
+ /**
32
+ * Hook to manage OAuth popup window with proper cleanup
33
+ * Handles window.postMessage communication and fallback polling
34
+ */
35
+ export function useOAuthPopup({ onComplete, onError }: UseOAuthPopupOptions) {
36
+ const cleanupRef = useRef<(() => void) | null>(null);
37
+ const onCompleteRef = useRef(onComplete);
38
+ const onErrorRef = useRef(onError);
39
+
40
+ // Keep refs up to date
41
+ useEffect(() => {
42
+ onCompleteRef.current = onComplete;
43
+ onErrorRef.current = onError;
44
+ }, [onComplete, onError]);
45
+
46
+ // Cleanup on unmount
47
+ useEffect(() => {
48
+ return () => {
49
+ if (cleanupRef.current) {
50
+ cleanupRef.current();
51
+ cleanupRef.current = null;
52
+ }
53
+ };
54
+ }, []);
55
+
56
+ const openOAuthPopup = (authorizationUrl: string) => {
57
+ // Clean up any previous OAuth flow
58
+ if (cleanupRef.current) {
59
+ cleanupRef.current();
60
+ cleanupRef.current = null;
61
+ }
62
+
63
+ // Open OAuth authorization in centered popup window.
64
+ const width = 600;
65
+ const height = 700;
66
+ const left = window.screenX + (window.outerWidth - width) / 2;
67
+ const top = window.screenY + (window.outerHeight - height) / 2;
68
+
69
+ const popup = window.open(
70
+ authorizationUrl,
71
+ 'oauth_popup',
72
+ `width=${width},height=${height},left=${left},top=${top},popup=yes`
73
+ );
74
+
75
+ let messageReceived = false;
76
+ let intervalId: ReturnType<typeof setInterval> | null = null;
77
+
78
+ // BroadcastChannel is same-origin by spec — no origin check required.
79
+ const channel = new BroadcastChannel('oauth-callback');
80
+
81
+ const handleMessage = (event: MessageEvent) => {
82
+ if (!isOAuthMessage(event.data)) {
83
+ return;
84
+ }
85
+
86
+ messageReceived = true;
87
+ channel.close();
88
+ if (intervalId) clearInterval(intervalId);
89
+
90
+ onCompleteRef.current();
91
+
92
+ if (!event.data.success && event.data.error) {
93
+ onErrorRef.current?.(event.data.error);
94
+ }
95
+
96
+ cleanupRef.current = null;
97
+ };
98
+
99
+ channel.addEventListener('message', handleMessage);
100
+
101
+ // Poll for popup closure so the button resets if the user dismisses the popup early.
102
+ if (popup) {
103
+ intervalId = setInterval(() => {
104
+ if (popup.closed) {
105
+ if (intervalId) clearInterval(intervalId);
106
+ channel.close();
107
+
108
+ if (!messageReceived) {
109
+ onCompleteRef.current();
110
+ }
111
+
112
+ cleanupRef.current = null;
113
+ }
114
+ }, 1000);
115
+ }
116
+
117
+ // Store cleanup function
118
+ cleanupRef.current = () => {
119
+ channel.close();
120
+ if (intervalId) clearInterval(intervalId);
121
+ };
122
+ };
123
+
124
+ return { openOAuthPopup };
125
+ }
@@ -1,10 +1,10 @@
1
- import { Collection, getContentTypeRefId } from "@vertesia/common";
1
+ import { Collection, getContentTypeRefId, ColumnLayout } from "@vertesia/common";
2
2
  import { useToast } from "@vertesia/ui/core";
3
3
  import { useUserSession } from "@vertesia/ui/session";
4
4
  import { TypeRegistry } from "../types/TypeRegistry.js";
5
5
  import { useTypeRegistry } from "../types/TypeRegistryProvider.js";
6
6
  import { DocumentSearchResults, DocumentSearchResultsWithDropZone } from "../objects/DocumentSearchResults";
7
- import { ColumnLayout } from "@vertesia/common";
7
+
8
8
 
9
9
  const collectionDefaultLayout: ColumnLayout[] = [
10
10
  { name: "ID", field: "id", type: "objectId?slice=-7" },
@@ -56,4 +56,4 @@ function getTableLayout(collection: Collection, typeRegistry?: TypeRegistry | un
56
56
  } else {
57
57
  return collectionDefaultLayout;
58
58
  }
59
- }
59
+ }
@@ -4,8 +4,9 @@ import { SharedPropsEditor, SyncMemberHeadsToggle, UserInfo } from "@vertesia/ui
4
4
  import { useUserSession } from "@vertesia/ui/session";
5
5
  import { MonacoEditor, EditorApi, GeneratedForm, ManagedObject, Node } from "@vertesia/ui/widgets";
6
6
  import dayjs from "dayjs";
7
- import { useMemo, useRef, useState } from "react";
7
+ import { useContext, useMemo, useRef, useState } from "react";
8
8
  import { useUITranslation } from '../../../i18n/index.js';
9
+ import { SearchContext } from "../objects/search/DocumentSearchContext";
9
10
  import { SelectContentType, stringifyTableLayout } from "../types";
10
11
 
11
12
  interface UpdateData {
@@ -28,6 +29,7 @@ export function EditCollectionView({ refetch, collection }: EditCollectionViewPr
28
29
  const toast = useToast();
29
30
  const { theme } = useTheme();
30
31
  const { client } = useUserSession();
32
+ const search = useContext(SearchContext);
31
33
  const [isUpdating, setUpdating] = useState(false);
32
34
  const [metadata, setMetadata] = useState<UpdateData>({
33
35
  name: collection.name,
@@ -103,6 +105,11 @@ export function EditCollectionView({ refetch, collection }: EditCollectionViewPr
103
105
  .update(collection.id, payload)
104
106
  .then(() => {
105
107
  refetch();
108
+ // Update search query on provider if it's a dynamic collection to reflect in UI immediately
109
+ if (collection.dynamic && search) {
110
+ search.reset();
111
+ void search.search();
112
+ }
106
113
  toast({
107
114
  title: t('store.collectionUpdated'),
108
115
  description: t('store.collectionUpdatedSuccess'),
@@ -184,6 +191,8 @@ export function EditCollectionView({ refetch, collection }: EditCollectionViewPr
184
191
  <FormItem label={t('store.query')} description={t('store.queryDescription')}>
185
192
  <Textarea
186
193
  className={Styles.INPUT}
194
+ minLines={1}
195
+ maxLines={12}
187
196
  value={metadata.query}
188
197
  onChange={(e) => setField("query", e.target.value)}
189
198
  />