@vertesia/ui 1.6.0-dev.20260918.090710Z → 1.6.0-dev.20260923.085325Z

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 (288) hide show
  1. package/lib/boot/branding.d.ts +13 -0
  2. package/lib/boot/branding.d.ts.map +1 -1
  3. package/lib/boot/branding.js +7 -0
  4. package/lib/boot/branding.js.map +1 -1
  5. package/lib/boot/index.d.ts +1 -1
  6. package/lib/boot/index.d.ts.map +1 -1
  7. package/lib/boot/index.js +5 -23
  8. package/lib/boot/index.js.map +1 -1
  9. package/lib/boot/loading.d.ts +4 -0
  10. package/lib/boot/loading.d.ts.map +1 -0
  11. package/lib/boot/loading.js +28 -0
  12. package/lib/boot/loading.js.map +1 -0
  13. package/lib/core/components/SelectList.d.ts.map +1 -1
  14. package/lib/core/components/SelectList.js +3 -3
  15. package/lib/core/components/SelectList.js.map +1 -1
  16. package/lib/core/components/SidePanel.d.ts.map +1 -1
  17. package/lib/core/components/SidePanel.js +2 -1
  18. package/lib/core/components/SidePanel.js.map +1 -1
  19. package/lib/core/components/shadcn/theme/ThemeProvider.d.ts.map +1 -1
  20. package/lib/core/components/shadcn/theme/ThemeProvider.js +21 -5
  21. package/lib/core/components/shadcn/theme/ThemeProvider.js.map +1 -1
  22. package/lib/env/index.d.ts +11 -0
  23. package/lib/env/index.d.ts.map +1 -1
  24. package/lib/env/index.js +16 -3
  25. package/lib/env/index.js.map +1 -1
  26. package/lib/features/agent/chat/ModernAgentConversation.js +4 -1
  27. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  28. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +5 -1
  29. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
  30. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +9 -2
  31. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
  32. package/lib/features/magic-pdf/GroundedExtractionView.js +3 -3
  33. package/lib/features/magic-pdf/GroundedExtractionView.js.map +1 -1
  34. package/lib/features/media-viewer/AudioPanel.d.ts +10 -2
  35. package/lib/features/media-viewer/AudioPanel.d.ts.map +1 -1
  36. package/lib/features/media-viewer/AudioPanel.js +78 -13
  37. package/lib/features/media-viewer/AudioPanel.js.map +1 -1
  38. package/lib/features/oauth/McpConnectionsButton.d.ts +2 -2
  39. package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -1
  40. package/lib/features/oauth/McpConnectionsButton.js +9 -7
  41. package/lib/features/oauth/McpConnectionsButton.js.map +1 -1
  42. package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -1
  43. package/lib/features/oauth/McpConnectionsDialog.js +7 -3
  44. package/lib/features/oauth/McpConnectionsDialog.js.map +1 -1
  45. package/lib/features/oauth/useMcpConnections.d.ts +4 -2
  46. package/lib/features/oauth/useMcpConnections.d.ts.map +1 -1
  47. package/lib/features/oauth/useMcpConnections.js +15 -9
  48. package/lib/features/oauth/useMcpConnections.js.map +1 -1
  49. package/lib/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
  50. package/lib/features/store/objects/selection/ObjectsActionContext.js +2 -0
  51. package/lib/features/store/objects/selection/ObjectsActionContext.js.map +1 -1
  52. package/lib/features/store/objects/selection/SelectionActions.d.ts +4 -2
  53. package/lib/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
  54. package/lib/features/store/objects/selection/SelectionActions.js +6 -3
  55. package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
  56. package/lib/features/store/objects/selection/actions/StartWorkflowAction.d.ts +4 -0
  57. package/lib/features/store/objects/selection/actions/StartWorkflowAction.d.ts.map +1 -0
  58. package/lib/features/store/objects/selection/actions/StartWorkflowAction.js +81 -0
  59. package/lib/features/store/objects/selection/actions/StartWorkflowAction.js.map +1 -0
  60. package/lib/features/store/objects/selection/actions/index.d.ts +1 -0
  61. package/lib/features/store/objects/selection/actions/index.d.ts.map +1 -1
  62. package/lib/features/store/objects/selection/actions/index.js +1 -0
  63. package/lib/features/store/objects/selection/actions/index.js.map +1 -1
  64. package/lib/features/store/types/TypeRegistry.d.ts +345 -155
  65. package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
  66. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +345 -155
  67. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
  68. package/lib/i18n/locales/ar.json +10 -2
  69. package/lib/i18n/locales/de.json +6 -2
  70. package/lib/i18n/locales/en.json +6 -2
  71. package/lib/i18n/locales/es.json +6 -2
  72. package/lib/i18n/locales/fr.json +7 -2
  73. package/lib/i18n/locales/it.json +6 -2
  74. package/lib/i18n/locales/ja.json +6 -2
  75. package/lib/i18n/locales/ko.json +6 -2
  76. package/lib/i18n/locales/pt.json +6 -2
  77. package/lib/i18n/locales/ru.json +8 -2
  78. package/lib/i18n/locales/tr.json +6 -2
  79. package/lib/i18n/locales/zh-TW.json +6 -2
  80. package/lib/i18n/locales/zh.json +6 -2
  81. package/lib/session/UserSession.d.ts +3 -0
  82. package/lib/session/UserSession.d.ts.map +1 -1
  83. package/lib/session/UserSession.js +18 -8
  84. package/lib/session/UserSession.js.map +1 -1
  85. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  86. package/lib/session/UserSessionProvider.js +34 -24
  87. package/lib/session/UserSessionProvider.js.map +1 -1
  88. package/lib/session/auth/authRoundTrip.d.ts +8 -0
  89. package/lib/session/auth/authRoundTrip.d.ts.map +1 -0
  90. package/lib/session/auth/authRoundTrip.js +22 -0
  91. package/lib/session/auth/authRoundTrip.js.map +1 -0
  92. package/lib/session/auth/composable.d.ts.map +1 -1
  93. package/lib/session/auth/composable.js +9 -7
  94. package/lib/session/auth/composable.js.map +1 -1
  95. package/lib/session/auth/domainRouting.d.ts +13 -6
  96. package/lib/session/auth/domainRouting.d.ts.map +1 -1
  97. package/lib/session/auth/domainRouting.js +2 -21
  98. package/lib/session/auth/domainRouting.js.map +1 -1
  99. package/lib/session/auth/oauth.d.ts +6 -1
  100. package/lib/session/auth/oauth.d.ts.map +1 -1
  101. package/lib/session/auth/oauth.js +145 -8
  102. package/lib/session/auth/oauth.js.map +1 -1
  103. package/lib/session/auth/tokenIssuer.d.ts +3 -0
  104. package/lib/session/auth/tokenIssuer.d.ts.map +1 -0
  105. package/lib/session/auth/tokenIssuer.js +27 -0
  106. package/lib/session/auth/tokenIssuer.js.map +1 -0
  107. package/lib/session/auth/vertesiaHosts.d.ts +47 -0
  108. package/lib/session/auth/vertesiaHosts.d.ts.map +1 -0
  109. package/lib/session/auth/vertesiaHosts.js +79 -0
  110. package/lib/session/auth/vertesiaHosts.js.map +1 -0
  111. package/lib/session/index.d.ts +1 -0
  112. package/lib/session/index.d.ts.map +1 -1
  113. package/lib/session/index.js +1 -0
  114. package/lib/session/index.js.map +1 -1
  115. package/lib/shell/AuthScreenPreview.d.ts +1 -1
  116. package/lib/shell/AuthScreenPreview.d.ts.map +1 -1
  117. package/lib/shell/AuthScreenPreview.js +2 -5
  118. package/lib/shell/AuthScreenPreview.js.map +1 -1
  119. package/lib/shell/BrandedAuthScreens.d.ts +1 -4
  120. package/lib/shell/BrandedAuthScreens.d.ts.map +1 -1
  121. package/lib/shell/BrandedAuthScreens.js +5 -26
  122. package/lib/shell/BrandedAuthScreens.js.map +1 -1
  123. package/lib/shell/BrandedLoadingIndicator.d.ts +11 -0
  124. package/lib/shell/BrandedLoadingIndicator.d.ts.map +1 -0
  125. package/lib/shell/BrandedLoadingIndicator.js +27 -0
  126. package/lib/shell/BrandedLoadingIndicator.js.map +1 -0
  127. package/lib/shell/SplashScreen.d.ts.map +1 -1
  128. package/lib/shell/SplashScreen.js +2 -1
  129. package/lib/shell/SplashScreen.js.map +1 -1
  130. package/lib/shell/apps/AppInstallationProvider.d.ts +16 -4
  131. package/lib/shell/apps/AppInstallationProvider.d.ts.map +1 -1
  132. package/lib/shell/apps/iframe-auth.d.ts +2 -0
  133. package/lib/shell/apps/iframe-auth.d.ts.map +1 -1
  134. package/lib/shell/apps/iframe-auth.js +6 -2
  135. package/lib/shell/apps/iframe-auth.js.map +1 -1
  136. package/lib/shell/index.d.ts +1 -0
  137. package/lib/shell/index.d.ts.map +1 -1
  138. package/lib/shell/index.js +1 -0
  139. package/lib/shell/index.js.map +1 -1
  140. package/lib/shell/login/SignInAuthPending.js +2 -2
  141. package/lib/shell/login/SignInAuthPending.js.map +1 -1
  142. package/lib/shell/login/SignInEmailStep.d.ts.map +1 -1
  143. package/lib/shell/login/SignInEmailStep.js +2 -2
  144. package/lib/shell/login/SignInEmailStep.js.map +1 -1
  145. package/lib/shell/login/TerminalLogin.d.ts.map +1 -1
  146. package/lib/shell/login/TerminalLogin.js +28 -21
  147. package/lib/shell/login/TerminalLogin.js.map +1 -1
  148. package/lib/vertesia-ui-boot.js +33 -29
  149. package/lib/vertesia-ui-boot.js.map +1 -1
  150. package/lib/vertesia-ui-core.js +34 -34
  151. package/lib/vertesia-ui-core.js.map +1 -1
  152. package/lib/vertesia-ui-env.js +1 -1
  153. package/lib/vertesia-ui-env.js.map +1 -1
  154. package/lib/vertesia-ui-features.js +163 -163
  155. package/lib/vertesia-ui-features.js.map +1 -1
  156. package/lib/vertesia-ui-i18n.Bl8KeZUK.chunk.js +5 -0
  157. package/lib/vertesia-ui-i18n.Bl8KeZUK.chunk.js.map +1 -0
  158. package/lib/vertesia-ui-i18n.BmT06XFI.chunk.js +5 -0
  159. package/lib/vertesia-ui-i18n.BmT06XFI.chunk.js.map +1 -0
  160. package/lib/vertesia-ui-i18n.Ch-Jjj0L.chunk.js +5 -0
  161. package/lib/vertesia-ui-i18n.Ch-Jjj0L.chunk.js.map +1 -0
  162. package/lib/vertesia-ui-i18n.CjhloW9s.chunk.js +5 -0
  163. package/lib/vertesia-ui-i18n.CjhloW9s.chunk.js.map +1 -0
  164. package/lib/vertesia-ui-i18n.CkfHdQzY.chunk.js +5 -0
  165. package/lib/vertesia-ui-i18n.CkfHdQzY.chunk.js.map +1 -0
  166. package/lib/vertesia-ui-i18n.DFuKfTe1.chunk.js +5 -0
  167. package/lib/vertesia-ui-i18n.DFuKfTe1.chunk.js.map +1 -0
  168. package/lib/vertesia-ui-i18n.DHqNVekZ.chunk.js +5 -0
  169. package/lib/vertesia-ui-i18n.DHqNVekZ.chunk.js.map +1 -0
  170. package/lib/vertesia-ui-i18n.DOaKbmaj.chunk.js +5 -0
  171. package/lib/vertesia-ui-i18n.DOaKbmaj.chunk.js.map +1 -0
  172. package/lib/vertesia-ui-i18n.Dge3Gl-h.chunk.js +5 -0
  173. package/lib/vertesia-ui-i18n.Dge3Gl-h.chunk.js.map +1 -0
  174. package/lib/vertesia-ui-i18n.GLt-10Lx.chunk.js +5 -0
  175. package/lib/vertesia-ui-i18n.GLt-10Lx.chunk.js.map +1 -0
  176. package/lib/vertesia-ui-i18n.T-XtAGbw.chunk.js +5 -0
  177. package/lib/vertesia-ui-i18n.T-XtAGbw.chunk.js.map +1 -0
  178. package/lib/vertesia-ui-i18n.VOJyTpMM.chunk.js +5 -0
  179. package/lib/vertesia-ui-i18n.VOJyTpMM.chunk.js.map +1 -0
  180. package/lib/vertesia-ui-i18n.js +3 -3
  181. package/lib/vertesia-ui-i18n.js.map +1 -1
  182. package/lib/vertesia-ui-layout.js +15 -15
  183. package/lib/vertesia-ui-layout.js.map +1 -1
  184. package/lib/vertesia-ui-rich-text.js +44 -44
  185. package/lib/vertesia-ui-rich-text.js.map +1 -1
  186. package/lib/vertesia-ui-session.js +1 -1
  187. package/lib/vertesia-ui-session.js.map +1 -1
  188. package/lib/vertesia-ui-shell.js +72 -48
  189. package/lib/vertesia-ui-shell.js.map +1 -1
  190. package/lib/vertesia-ui-widgets.js +51 -51
  191. package/lib/vertesia-ui-widgets.js.map +1 -1
  192. package/package.json +19 -19
  193. package/src/boot/branding.ts +8 -0
  194. package/src/boot/index.ts +6 -23
  195. package/src/boot/loading.ts +27 -0
  196. package/src/core/components/SelectList.tsx +5 -2
  197. package/src/core/components/SidePanel.tsx +65 -60
  198. package/src/core/components/shadcn/theme/ThemeProvider.test.tsx +101 -0
  199. package/src/core/components/shadcn/theme/ThemeProvider.tsx +21 -9
  200. package/src/env/index.test.ts +21 -0
  201. package/src/env/index.ts +33 -2
  202. package/src/features/agent/chat/ModernAgentConversation.test.tsx +81 -0
  203. package/src/features/agent/chat/ModernAgentConversation.tsx +5 -0
  204. package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +17 -1
  205. package/src/features/magic-pdf/GroundedExtractionView.tsx +3 -3
  206. package/src/features/media-viewer/AudioPanel.test.ts +84 -0
  207. package/src/features/media-viewer/AudioPanel.tsx +97 -14
  208. package/src/features/oauth/McpConnectionsButton.test.tsx +56 -0
  209. package/src/features/oauth/McpConnectionsButton.tsx +9 -6
  210. package/src/features/oauth/McpConnectionsDialog.tsx +14 -5
  211. package/src/features/oauth/useMcpConnections.test.ts +10 -0
  212. package/src/features/oauth/useMcpConnections.ts +18 -10
  213. package/src/features/store/objects/selection/ObjectsActionContext.tsx +2 -0
  214. package/src/features/store/objects/selection/SelectionActions.tsx +13 -6
  215. package/src/features/store/objects/selection/actions/StartWorkflowAction.tsx +162 -0
  216. package/src/features/store/objects/selection/actions/index.ts +1 -0
  217. package/src/i18n/locales/ar.json +10 -2
  218. package/src/i18n/locales/de.json +6 -2
  219. package/src/i18n/locales/en.json +6 -2
  220. package/src/i18n/locales/es.json +6 -2
  221. package/src/i18n/locales/fr.json +7 -2
  222. package/src/i18n/locales/it.json +6 -2
  223. package/src/i18n/locales/ja.json +6 -2
  224. package/src/i18n/locales/ko.json +6 -2
  225. package/src/i18n/locales/pt.json +6 -2
  226. package/src/i18n/locales/ru.json +8 -2
  227. package/src/i18n/locales/tr.json +6 -2
  228. package/src/i18n/locales/zh-TW.json +6 -2
  229. package/src/i18n/locales/zh.json +6 -2
  230. package/src/session/UserSession.test.ts +48 -3
  231. package/src/session/UserSession.ts +20 -8
  232. package/src/session/UserSessionProvider.handoff.test.tsx +103 -0
  233. package/src/session/UserSessionProvider.iframe.test.tsx +178 -0
  234. package/src/session/UserSessionProvider.state-mismatch.test.tsx +43 -0
  235. package/src/session/UserSessionProvider.tsx +35 -23
  236. package/src/session/auth/auth-flow.md +18 -5
  237. package/src/session/auth/authRoundTrip.ts +24 -0
  238. package/src/session/auth/composable.test.ts +23 -1
  239. package/src/session/auth/composable.ts +10 -8
  240. package/src/session/auth/domainRouting.ts +4 -25
  241. package/src/session/auth/oauth.test.ts +121 -4
  242. package/src/session/auth/oauth.ts +172 -11
  243. package/src/session/auth/tokenIssuer.test.ts +25 -0
  244. package/src/session/auth/tokenIssuer.ts +25 -0
  245. package/src/session/auth/vertesiaHosts.test.ts +26 -0
  246. package/src/session/auth/vertesiaHosts.ts +85 -0
  247. package/src/session/index.ts +1 -0
  248. package/src/shell/AuthScreenPreview.tsx +2 -6
  249. package/src/shell/BrandedAuthScreens.test.tsx +26 -0
  250. package/src/shell/BrandedAuthScreens.tsx +4 -40
  251. package/src/shell/BrandedLoadingIndicator.tsx +42 -0
  252. package/src/shell/SplashScreen.tsx +4 -4
  253. package/src/shell/apps/iframe-auth.test.ts +8 -11
  254. package/src/shell/apps/iframe-auth.ts +10 -1
  255. package/src/shell/index.tsx +1 -0
  256. package/src/shell/login/SignInAuthPending.tsx +2 -2
  257. package/src/shell/login/SignInEmailStep.tsx +4 -2
  258. package/src/shell/login/TerminalLogin.test.tsx +111 -0
  259. package/src/shell/login/TerminalLogin.tsx +61 -37
  260. package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts +0 -7
  261. package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts.map +0 -1
  262. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +0 -51
  263. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +0 -1
  264. package/lib/vertesia-ui-i18n.B3NFclwl.chunk.js +0 -5
  265. package/lib/vertesia-ui-i18n.B3NFclwl.chunk.js.map +0 -1
  266. package/lib/vertesia-ui-i18n.BWSrNIxc.chunk.js +0 -5
  267. package/lib/vertesia-ui-i18n.BWSrNIxc.chunk.js.map +0 -1
  268. package/lib/vertesia-ui-i18n.BWvWeB1p.chunk.js +0 -5
  269. package/lib/vertesia-ui-i18n.BWvWeB1p.chunk.js.map +0 -1
  270. package/lib/vertesia-ui-i18n.BZCXvTC1.chunk.js +0 -5
  271. package/lib/vertesia-ui-i18n.BZCXvTC1.chunk.js.map +0 -1
  272. package/lib/vertesia-ui-i18n.BbJMvFZW.chunk.js +0 -5
  273. package/lib/vertesia-ui-i18n.BbJMvFZW.chunk.js.map +0 -1
  274. package/lib/vertesia-ui-i18n.CE5ldwpt.chunk.js +0 -5
  275. package/lib/vertesia-ui-i18n.CE5ldwpt.chunk.js.map +0 -1
  276. package/lib/vertesia-ui-i18n.CMC0IShY.chunk.js +0 -5
  277. package/lib/vertesia-ui-i18n.CMC0IShY.chunk.js.map +0 -1
  278. package/lib/vertesia-ui-i18n.CkcRqBKU.chunk.js +0 -5
  279. package/lib/vertesia-ui-i18n.CkcRqBKU.chunk.js.map +0 -1
  280. package/lib/vertesia-ui-i18n.D-Mq1sw_.chunk.js +0 -5
  281. package/lib/vertesia-ui-i18n.D-Mq1sw_.chunk.js.map +0 -1
  282. package/lib/vertesia-ui-i18n.DkK68-A-.chunk.js +0 -5
  283. package/lib/vertesia-ui-i18n.DkK68-A-.chunk.js.map +0 -1
  284. package/lib/vertesia-ui-i18n.XE_dcCg6.chunk.js +0 -5
  285. package/lib/vertesia-ui-i18n.XE_dcCg6.chunk.js.map +0 -1
  286. package/lib/vertesia-ui-i18n.qgNqMPvK.chunk.js +0 -5
  287. package/lib/vertesia-ui-i18n.qgNqMPvK.chunk.js.map +0 -1
  288. package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +0 -104
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/ui",
3
- "version": "1.6.0-dev.20260918.090710Z",
3
+ "version": "1.6.0-dev.20260923.085325Z",
4
4
  "description": "Vertesia UI components and and hooks",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -45,11 +45,11 @@
45
45
  "@radix-ui/react-switch": "^1.3.7",
46
46
  "@radix-ui/react-tabs": "^1.1.21",
47
47
  "@radix-ui/react-tooltip": "^1.2.16",
48
- "@vertesia/client": "1.6.0-dev.20260918.090710Z",
49
- "@vertesia/common": "1.6.0-dev.20260918.090710Z",
50
- "@vertesia/fusion-ux": "1.6.0-dev.20260918.090710Z",
51
- "@vertesia/json": "1.6.0-dev.20260918.090710Z",
52
- "@vertesia/rich-text": "1.6.0-dev.20260918.090710Z",
48
+ "@vertesia/client": "1.6.0-dev.20260923.085325Z",
49
+ "@vertesia/common": "1.6.0-dev.20260923.085325Z",
50
+ "@vertesia/fusion-ux": "1.6.0-dev.20260923.085325Z",
51
+ "@vertesia/json": "1.6.0-dev.20260923.085325Z",
52
+ "@vertesia/rich-text": "1.6.0-dev.20260923.085325Z",
53
53
  "ajv": "^8.20.0",
54
54
  "ajv-formats": "^3.0.1",
55
55
  "aria-hidden": "^1.2.6",
@@ -60,23 +60,23 @@
60
60
  "debounce": "^3.0.0",
61
61
  "dompurify": "^3.4.15",
62
62
  "fast-xml-parser": "^5.11.1",
63
- "firebase": "^12.18.0",
63
+ "firebase": "^12.19.0",
64
64
  "framer-motion": "^13.2.0",
65
65
  "i18next": "^26.4.2",
66
66
  "jwt-decode": "^4.0.0",
67
67
  "katex": "^0.18.7",
68
68
  "lodash-es": "^4.18.1",
69
- "lucide-react": "^1.42.0",
70
- "mermaid": "^11.17.2",
69
+ "lucide-react": "^1.46.0",
70
+ "mermaid": "^12.0.0",
71
71
  "monaco-editor": "^0.56.0",
72
72
  "motion": "^13.2.0",
73
73
  "papaparse": "^5.7.0",
74
- "react": "19.2.8",
74
+ "react": "19.3.0",
75
75
  "react-calendar": "^6.0.1",
76
76
  "react-date-picker": "^12.1.0",
77
- "react-dom": "19.2.8",
77
+ "react-dom": "19.3.0",
78
78
  "react-error-boundary": "^6.1.5",
79
- "react-i18next": "^17.0.13",
79
+ "react-i18next": "^17.0.14",
80
80
  "react-markdown": "^10.1.0",
81
81
  "react-pdf": "^10.5.0",
82
82
  "react-remove-scroll": "^2.7.2",
@@ -91,7 +91,7 @@
91
91
  "remark-github-blockquote-alert": "^2.1.0",
92
92
  "remark-math": "^6.0.0",
93
93
  "remark-supersub": "^1.0.0",
94
- "tailwind-merge": "^3.6.0",
94
+ "tailwind-merge": "^3.7.0",
95
95
  "ts-md5": "^2.0.1",
96
96
  "unist-util-visit": "^5.1.0",
97
97
  "vega": "^6.4.0",
@@ -103,19 +103,19 @@
103
103
  "@testing-library/user-event": "^14.6.7",
104
104
  "@types/hast": "^3.0.5",
105
105
  "@types/lodash-es": "^4.17.12",
106
- "@types/node": "^24.13.3",
106
+ "@types/node": "^24.13.5",
107
107
  "@types/papaparse": "^5.5.2",
108
- "@types/react": "19.2.18",
109
- "@types/react-dom": "19.2.7",
108
+ "@types/react": "19.3.0",
109
+ "@types/react-dom": "19.3.0",
110
110
  "@vertesia/tsconfig": "0.1.0",
111
111
  "axe-core": "^4.13.0",
112
112
  "culori": "^4.0.2",
113
- "i18next-cli": "^1.73.1",
113
+ "i18next-cli": "^1.73.3",
114
114
  "jsdom": "^30.0.1",
115
115
  "rolldown": "1.2.8",
116
116
  "tailwindcss": "^4.3.3",
117
117
  "typescript": "7.0.2",
118
- "vitest": "5.0.0",
118
+ "vitest": "5.0.1",
119
119
  "vitest-axe": "^0.1.0",
120
120
  "wcag-contrast": "^3.0.0"
121
121
  },
@@ -188,7 +188,7 @@
188
188
  "url": "https://github.com/vertesia/composableai.git",
189
189
  "directory": "packages/ui"
190
190
  },
191
- "gitHead": "10f35da1a210e1950f9c8b1368fea89328a23ea0",
191
+ "gitHead": "8732631fd8099bdcab4f5375da2309f3cff34e19",
192
192
  "scripts": {
193
193
  "clean:lib": "rimraf ./lib ./tsconfig.tsbuildinfo",
194
194
  "clean": "rimraf ./node_modules ./lib ./tsconfig.tsbuildinfo",
@@ -106,3 +106,11 @@ ${brand.colors?.dark?.background ? `:root.dark{--vertesia-boot-background:${bran
106
106
  ${brand.boot?.styles ?? ''}`,
107
107
  };
108
108
  }
109
+
110
+ /** Platform defaults shared by boot markup and Firebase/OAuth React screens. */
111
+ export const vertesiaBranding = {
112
+ name: 'Vertesia',
113
+ logo: { light: '/logo-light.png', dark: '/logo-dark.png', alt: 'Vertesia' },
114
+ loadingIcon: { light: '/icon.svg' },
115
+ favicon: '/icon.svg',
116
+ } satisfies AppBranding;
package/src/boot/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { vertesiaBranding } from './branding.js';
2
+ import { LOADING_ICON_SIZE, LOADING_INDICATOR_STYLES } from './loading.js';
3
+
1
4
  export interface BootScreenOptions {
2
5
  storageKey?: string;
3
6
  iconSrc?: string;
@@ -18,10 +21,11 @@ export interface BootScreenHtmlPlugin {
18
21
  }
19
22
 
20
23
  const DEFAULT_STORAGE_KEY = 'vite-ui-theme';
21
- const DEFAULT_ICON_SRC = '/icon.svg';
24
+ const DEFAULT_ICON_SRC = vertesiaBranding.loadingIcon.light;
22
25
  const DEFAULT_LOADING_LABEL = 'Loading';
23
26
 
24
27
  export const BOOT_SCREEN_STYLES = `
28
+ ${LOADING_INDICATOR_STYLES}
25
29
  .vboot {
26
30
  --vb-bg: #ffffff;
27
31
  --vb-fg: #0a0a0a;
@@ -44,10 +48,6 @@ export const BOOT_SCREEN_STYLES = `
44
48
  --vb-primary-fg: #fafafa;
45
49
  --vb-glow: rgba(79, 70, 229, 0.2);
46
50
  }
47
- @keyframes vboot-spin {
48
- from { transform: rotate(0deg); }
49
- to { transform: rotate(360deg); }
50
- }
51
51
  .vboot-overlay {
52
52
  position: absolute !important;
53
53
  inset: 0 !important;
@@ -75,20 +75,6 @@ export const BOOT_SCREEN_STYLES = `
75
75
  padding: 0 !important;
76
76
  transform: none !important;
77
77
  }
78
- .vboot-spinner {
79
- width: 2.5rem !important;
80
- min-width: 2.5rem !important;
81
- max-width: 2.5rem !important;
82
- height: 2.5rem !important;
83
- min-height: 2.5rem !important;
84
- max-height: 2.5rem !important;
85
- margin: 0 !important;
86
- padding: 0 !important;
87
- object-fit: contain !important;
88
- flex: none !important;
89
- border-radius: 100% !important;
90
- animation: var(--vertesia-loading-animation, vboot-spin 2s linear infinite) !important;
91
- }
92
78
  .vboot-icon-dark { display: none; }
93
79
  .vboot-dark .vboot-icon-light { display: none; }
94
80
  .vboot-dark .vboot-icon-dark { display: block; }
@@ -115,9 +101,6 @@ export const BOOT_SCREEN_STYLES = `
115
101
  .vboot-btn:hover {
116
102
  opacity: 0.9;
117
103
  }
118
- @media (prefers-reduced-motion: reduce) {
119
- .vboot-spinner { animation: none !important; }
120
- }
121
104
  `;
122
105
 
123
106
  function inlineJson(value: string): string {
@@ -144,7 +127,7 @@ export function renderDefaultBootContent(options: BootScreenOptions = {}): strin
144
127
  );
145
128
  const { iconSrc, loadingLabel } = normalizedOptions(options);
146
129
  const icon = (src: string, className = '') =>
147
- `<img class="vboot-spinner ${className}" width="40" height="40" src="${escapeMarkup(src)}" alt="${escapeMarkup(loadingLabel)}" />`;
130
+ `<img class="vboot-spinner vertesia-loading-icon vertesia-loading-motion ${className}" width="${LOADING_ICON_SIZE}" height="${LOADING_ICON_SIZE}" src="${escapeMarkup(src)}" alt="${escapeMarkup(loadingLabel)}" />`;
148
131
  return `<div class="vboot-overlay" role="status" aria-live="polite">${options.darkIconSrc ? icon(iconSrc, 'vboot-icon-light') + icon(options.darkIconSrc, 'vboot-icon-dark') : icon(iconSrc)}
149
132
  <div id="loading-slow-notice" class="vboot-slow" style="display: none;">
150
133
  <p>${escapeMarkup(options.slowLoadingLabel ?? 'Still loading — this is taking longer than usual.')}</p>
@@ -0,0 +1,27 @@
1
+ /** Shared by server-rendered boot markup and React auth loaders. No React dependency. */
2
+ export const LOADING_ICON_SIZE = 40;
3
+ export const LOADING_INDICATOR_STYLES = `
4
+ @keyframes vertesia-loading-spin {
5
+ from { transform: rotate(0deg); }
6
+ to { transform: rotate(360deg); }
7
+ }
8
+ .vertesia-loading-motion {
9
+ animation: var(--vertesia-loading-animation, vertesia-loading-spin 2s linear infinite);
10
+ }
11
+ .vertesia-loading-icon {
12
+ width: ${LOADING_ICON_SIZE}px !important;
13
+ min-width: ${LOADING_ICON_SIZE}px !important;
14
+ max-width: ${LOADING_ICON_SIZE}px !important;
15
+ height: ${LOADING_ICON_SIZE}px !important;
16
+ min-height: ${LOADING_ICON_SIZE}px !important;
17
+ max-height: ${LOADING_ICON_SIZE}px !important;
18
+ margin: 0;
19
+ padding: 0;
20
+ object-fit: contain;
21
+ flex: none;
22
+ border-radius: 100% !important;
23
+ }
24
+ @media (prefers-reduced-motion: reduce) {
25
+ .vertesia-loading-motion { animation: none !important; }
26
+ }
27
+ `;
@@ -88,6 +88,7 @@ export function SelectList<T>({
88
88
  onSelect={onSelect}
89
89
  layout={layout}
90
90
  noCheck={noCheck}
91
+ isSelected={isSelected}
91
92
  />
92
93
  );
93
94
  })}
@@ -99,17 +100,19 @@ interface SelectListOptionProps<T> {
99
100
  onSelect: (value: T) => void;
100
101
  layout: OptionLayout;
101
102
  noCheck?: boolean;
103
+ isSelected: boolean;
102
104
  }
103
105
 
104
- function SelectListOption<T>({ option, onSelect, layout, noCheck }: SelectListOptionProps<T>) {
106
+ function SelectListOption<T>({ option, onSelect, layout, noCheck, isSelected }: SelectListOptionProps<T>) {
105
107
  return (
106
108
  <Button
107
109
  variant="unstyled"
108
110
  size="none"
109
111
  className={clsx(
110
- 'group !flex w-full items-center cursor-pointer gap-x-2 hover:bg-muted',
112
+ 'group flex! w-full items-center cursor-pointer gap-x-2 hover:bg-muted',
111
113
  layout.reverse && 'flex-row-reverse',
112
114
  layout.className,
115
+ isSelected && 'bg-muted',
113
116
  )}
114
117
  onClick={() => onSelect(option)}
115
118
  >
@@ -2,6 +2,7 @@ import { AnimatePresence, motion } from 'framer-motion';
2
2
  import { Minus, X } from 'lucide-react';
3
3
  import type React from 'react';
4
4
  import { useState } from 'react';
5
+ import { Portal } from './Portal.js';
5
6
  import { Button } from './shadcn/button';
6
7
 
7
8
  interface SidePanelProps {
@@ -72,78 +73,82 @@ export function SidePanel({
72
73
  const initialX = isLeft ? '-100%' : '100%';
73
74
 
74
75
  return (
75
- <AnimatePresence>
76
- {isOpen && (
77
- <div className="relative z-20">
78
- {/* Backdrop */}
79
- {backdrop && (
80
- <motion.div
81
- className="fixed inset-0 bg-black/70"
82
- initial={{ opacity: 0 }}
83
- animate={{ opacity: 1 }}
84
- exit={{ opacity: 0 }}
85
- onClick={onClose}
86
- />
87
- )}
76
+ <Portal>
77
+ <AnimatePresence>
78
+ {isOpen && (
79
+ <div className="relative z-20">
80
+ {/* Backdrop */}
81
+ {backdrop && (
82
+ <motion.div
83
+ className="fixed inset-0 bg-black/70"
84
+ initial={{ opacity: 0 }}
85
+ animate={{ opacity: 1 }}
86
+ exit={{ opacity: 0 }}
87
+ onClick={onClose}
88
+ />
89
+ )}
88
90
 
89
- <div className={`fixed inset-y-0 ${positionClass} overflow-hidden`}>
90
- <div className="absolute inset-0 overflow-hidden">
91
- <div
92
- className={`pointer-events-none fixed inset-y-0 ${positionClass} flex max-w-full ${paddingClass}`}
93
- >
94
- <motion.div
95
- className={`pointer-events-auto ${borderClass}`}
96
- style={{ width: `${_panelWidth}px` }}
97
- initial={{ x: initialX }}
98
- animate={{ x: 0 }}
99
- exit={{ x: initialX }}
100
- transition={{ type: 'spring', stiffness: 300, damping: 30 }}
91
+ <div className={`fixed inset-y-0 ${positionClass} overflow-hidden`}>
92
+ <div className="absolute inset-0 overflow-hidden">
93
+ <div
94
+ className={`pointer-events-none fixed inset-y-0 ${positionClass} flex max-w-full ${paddingClass}`}
101
95
  >
102
- <div className="relative flex h-full">
103
- {/* Drag Handle */}
104
- {resizable && (
105
- // biome-ignore lint/a11y/noStaticElementInteractions: drag handle is pointer-only (no keyboard equivalent for continuous resize); ARIA role omitted to avoid useAriaPropsForRole false positive.
106
- // biome-ignore lint/a11y/useAriaPropsSupportedByRole: aria-label kept for AT users; div has no semantic role because separator/slider would require valuenow/min/max
96
+ <motion.div
97
+ className={`pointer-events-auto ${borderClass}`}
98
+ style={{ width: `${_panelWidth}px` }}
99
+ initial={{ x: initialX }}
100
+ animate={{ x: 0 }}
101
+ exit={{ x: initialX }}
102
+ transition={{ type: 'spring', stiffness: 300, damping: 30 }}
103
+ >
104
+ <div className="relative flex h-full">
105
+ {/* Drag Handle */}
106
+ {resizable && (
107
+ // biome-ignore lint/a11y/noStaticElementInteractions: drag handle is pointer-only (no keyboard equivalent for continuous resize); ARIA role omitted to avoid useAriaPropsForRole false positive.
108
+ // biome-ignore lint/a11y/useAriaPropsSupportedByRole: aria-label kept for AT users; div has no semantic role because separator/slider would require valuenow/min/max
109
+ <div
110
+ aria-label="Resize panel"
111
+ className={`absolute ${dragHandleClass} top-0 bottom-0 w-3 z-25 cursor-ew-resize hover:bg-primary transition-colors flex items-center justify-center`}
112
+ onMouseDown={handleDragStart}
113
+ >
114
+ <Minus className="rotate-90 font-semibold" strokeWidth={4} />
115
+ </div>
116
+ )}
107
117
  <div
108
- aria-label="Resize panel"
109
- className={`absolute ${dragHandleClass} top-0 bottom-0 w-3 z-25 cursor-ew-resize hover:bg-primary transition-colors flex items-center justify-center`}
110
- onMouseDown={handleDragStart}
118
+ className={`flex-1 min-w-0 flex flex-col bg-background shadow-xl ${className}`}
111
119
  >
112
- <Minus className="rotate-90 font-semibold" strokeWidth={4} />
113
- </div>
114
- )}
115
- <div
116
- className={`flex-1 min-w-0 flex flex-col bg-background shadow-xl ${className}`}
117
- >
118
- {/* Sticky header */}
119
- {title && (
120
- <div className="sticky top-0 z-10 bg-background px-2 sm:px-4 py-2 border-b">
121
- <div className="flex items-start justify-between">
122
- <h2 className="w-full text-base font-semibold leading-6">
123
- <div className="text-2xl">{title ?? ''}</div>
124
- </h2>
125
- <div className="ms-3 flex h-7 items-center">
126
- <CloseButton onClose={onClose} />
120
+ {/* Sticky header */}
121
+ {title && (
122
+ <div className="sticky top-0 z-10 bg-background px-2 sm:px-4 py-2 border-b">
123
+ <div className="flex items-start justify-between">
124
+ <h2 className="w-full text-base font-semibold leading-6">
125
+ <div className="text-2xl">{title ?? ''}</div>
126
+ </h2>
127
+ <div className="ms-3 flex h-7 items-center">
128
+ <CloseButton onClose={onClose} />
129
+ </div>
127
130
  </div>
128
131
  </div>
129
- </div>
130
- )}
132
+ )}
131
133
 
132
- {/* Scrollable content */}
133
- <div
134
- className={contentClassName ?? 'flex-1 overflow-auto px-2 sm:px-4 py-4'}
135
- >
136
- <div className="w-full h-full flex-1 flex flex-col">{children}</div>
134
+ {/* Scrollable content */}
135
+ <div
136
+ className={
137
+ contentClassName ?? 'flex-1 overflow-auto px-2 sm:px-4 py-4'
138
+ }
139
+ >
140
+ <div className="w-full h-full flex-1 flex flex-col">{children}</div>
141
+ </div>
137
142
  </div>
138
143
  </div>
139
- </div>
140
- </motion.div>
144
+ </motion.div>
145
+ </div>
141
146
  </div>
142
147
  </div>
143
148
  </div>
144
- </div>
145
- )}
146
- </AnimatePresence>
149
+ )}
150
+ </AnimatePresence>
151
+ </Portal>
147
152
  );
148
153
  }
149
154
 
@@ -0,0 +1,101 @@
1
+ // @vitest-environment jsdom
2
+ import { act, cleanup, render } from '@testing-library/react';
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { ThemeProvider } from './ThemeProvider.js';
5
+
6
+ let dark = false;
7
+ let media: EventTarget;
8
+
9
+ beforeEach(() => {
10
+ dark = false;
11
+ media = new EventTarget();
12
+ vi.stubGlobal('matchMedia', () => ({
13
+ get matches() {
14
+ return dark;
15
+ },
16
+ addEventListener: media.addEventListener.bind(media),
17
+ removeEventListener: media.removeEventListener.bind(media),
18
+ }));
19
+ });
20
+
21
+ afterEach(() => {
22
+ cleanup();
23
+ localStorage.clear();
24
+ document.documentElement.classList.remove('light', 'dark');
25
+ vi.unstubAllGlobals();
26
+ });
27
+
28
+ function systemTheme(isDark: boolean) {
29
+ act(() => {
30
+ dark = isDark;
31
+ media.dispatchEvent(new Event('change'));
32
+ });
33
+ }
34
+
35
+ function storedTheme(value: string | null, key: string | null = 'vite-ui-theme') {
36
+ act(() => {
37
+ const event = new StorageEvent('storage', { key, newValue: value });
38
+ Object.defineProperty(event, 'storageArea', { value: localStorage });
39
+ window.dispatchEvent(event);
40
+ });
41
+ }
42
+
43
+ describe('ThemeProvider live preferences', () => {
44
+ it('follows system changes while the authorization page remains open', () => {
45
+ render(
46
+ <ThemeProvider>
47
+ <div />
48
+ </ThemeProvider>,
49
+ );
50
+ expect(document.documentElement.classList.contains('light')).toBe(true);
51
+ systemTheme(true);
52
+ expect(document.documentElement.classList.contains('dark')).toBe(true);
53
+ systemTheme(false);
54
+ expect(document.documentElement.classList.contains('light')).toBe(true);
55
+ });
56
+
57
+ it.each(['light', 'dark'])('keeps explicit %s preference over the system', (theme) => {
58
+ localStorage.setItem('vite-ui-theme', theme);
59
+ render(
60
+ <ThemeProvider>
61
+ <div />
62
+ </ThemeProvider>,
63
+ );
64
+ systemTheme(true);
65
+ systemTheme(false);
66
+ expect(document.documentElement.classList.contains(theme)).toBe(true);
67
+ });
68
+
69
+ it('syncs another tab and resumes system tracking when the preference is cleared', () => {
70
+ render(
71
+ <ThemeProvider>
72
+ <div />
73
+ </ThemeProvider>,
74
+ );
75
+ storedTheme('dark');
76
+ expect(document.documentElement.classList.contains('dark')).toBe(true);
77
+ systemTheme(false);
78
+ expect(document.documentElement.classList.contains('dark')).toBe(true);
79
+ storedTheme('light');
80
+ expect(document.documentElement.classList.contains('light')).toBe(true);
81
+ storedTheme('system');
82
+ systemTheme(true);
83
+ expect(document.documentElement.classList.contains('dark')).toBe(true);
84
+ storedTheme('light');
85
+ storedTheme(null, null);
86
+ expect(document.documentElement.classList.contains('dark')).toBe(true);
87
+ storedTheme('light', 'unrelated');
88
+ expect(document.documentElement.classList.contains('dark')).toBe(true);
89
+ });
90
+
91
+ it('removes system listeners when unmounted', () => {
92
+ const { unmount } = render(
93
+ <ThemeProvider>
94
+ <div />
95
+ </ThemeProvider>,
96
+ );
97
+ unmount();
98
+ systemTheme(true);
99
+ expect(document.documentElement.classList.contains('light')).toBe(true);
100
+ });
101
+ });
@@ -32,19 +32,31 @@ export function ThemeProvider({
32
32
 
33
33
  useEffect(() => {
34
34
  const root = window.document.documentElement;
35
-
36
- root.classList.remove('light', 'dark');
37
-
38
- if (theme === 'system') {
39
- const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
40
-
41
- root.classList.add(systemTheme);
35
+ if (theme !== 'system') {
36
+ root.classList.remove('light', 'dark');
37
+ root.classList.add(theme);
42
38
  return;
43
39
  }
44
-
45
- root.classList.add(theme);
40
+ const media = window.matchMedia('(prefers-color-scheme: dark)');
41
+ const applyTheme = () => {
42
+ root.classList.remove('light', 'dark');
43
+ root.classList.add(media.matches ? 'dark' : 'light');
44
+ };
45
+ applyTheme();
46
+ media.addEventListener('change', applyTheme);
47
+ return () => media.removeEventListener('change', applyTheme);
46
48
  }, [theme]);
47
49
 
50
+ useEffect(() => {
51
+ const syncStoredTheme = (event: StorageEvent) => {
52
+ if (event.storageArea !== localStorage || (event.key !== storageKey && event.key !== null)) return;
53
+ const value = event.newValue;
54
+ setThemeState(value === 'dark' || value === 'light' || value === 'system' ? value : defaultTheme);
55
+ };
56
+ window.addEventListener('storage', syncStoredTheme);
57
+ return () => window.removeEventListener('storage', syncStoredTheme);
58
+ }, [defaultTheme, storageKey]);
59
+
48
60
  const setTheme = useCallback(
49
61
  (nextTheme: Theme, options?: { persist?: boolean }) => {
50
62
  if (options?.persist !== false) localStorage.setItem(storageKey, nextTheme);
@@ -240,3 +240,24 @@ describe('fixed Firebase tenant', () => {
240
240
  expect(env.firebase).toBeUndefined();
241
241
  });
242
242
  });
243
+
244
+ afterEach(() => vi.unstubAllGlobals());
245
+
246
+ it('uses injected branch OAuth identity and API endpoints over build-time preview settings', () => {
247
+ const origin = 'https://app-gateway-dev-auth-test.api.dev1.vertesia.io';
248
+ const oauth = {
249
+ clientId: `${origin}/tenants/05948c_98b1eb/apps/test-app/.well-known/oauth-client/vertesia-app`,
250
+ redirectUri: `${origin}/tenants/05948c_98b1eb/apps/test-app/app/`,
251
+ };
252
+ const endpoints = {
253
+ studio: 'https://studio-server-dev-auth-test.api.dev1.vertesia.io',
254
+ zeno: 'https://zeno-server-dev-auth-test.api.dev1.vertesia.io',
255
+ sts: 'https://token-server-dev-auth-test.api.dev1.vertesia.io',
256
+ };
257
+ vi.stubGlobal('window', { __VERTESIA_RUNTIME_CONFIG__: { authMode: 'central', oauth, endpoints } });
258
+ const env = new VertesiaEnvironment().init(baseProps);
259
+ expect(env.oauth).toEqual(oauth);
260
+ expect(env.endpoints).toMatchObject(endpoints);
261
+ expect(window.AUTH_MODE).toBe('central');
262
+ expect(env.firebase).toBeUndefined();
263
+ });
package/src/env/index.ts CHANGED
@@ -1,3 +1,13 @@
1
+ import { isTrustedAuthBrokerUrl } from '../session/auth/vertesiaHosts.js';
2
+
3
+ export {
4
+ FIRST_PARTY_HOST_PATTERNS,
5
+ GATEWAY_HOST_PATTERNS,
6
+ isLoopbackHostname,
7
+ isTrustedAuthBrokerUrl,
8
+ normalizeHostname,
9
+ } from '../session/auth/vertesiaHosts.js';
10
+
1
11
  // hook to initialize the environment and auth session
2
12
  // the main app must call this hook before rendering the page.
3
13
 
@@ -28,8 +38,10 @@ export interface EnvProps {
28
38
  /** Appgen app-gateway endpoint (serves live development previews and app bundles). */
29
39
  gateway?: string;
30
40
  };
41
+ /** Explicitly allow legacy Firebase/central sign-in in an iframe when host authentication is unavailable. */
42
+ allowLegacyIframeAuth?: boolean;
31
43
  /** Public OAuth client for independently hosted apps; ignored inside Studio or a gateway session. */
32
- oauth?: { clientId: string; redirectUri: string; scopes?: string[] };
44
+ oauth?: { clientId: string; redirectUri: string; scopes?: string[]; offlineAccess?: boolean };
33
45
  firebase?: {
34
46
  apiKey: string;
35
47
  authDomain: string;
@@ -71,6 +83,7 @@ export interface EnvProps {
71
83
  export type VertesiaRuntimeConfig =
72
84
  | {
73
85
  authMode: 'firebase';
86
+ allowLegacyIframeAuth?: boolean;
74
87
  firebase: {
75
88
  apiKey: string;
76
89
  authDomain: string;
@@ -80,6 +93,10 @@ export type VertesiaRuntimeConfig =
80
93
  }
81
94
  | {
82
95
  authMode: 'central';
96
+ allowLegacyIframeAuth?: boolean;
97
+ oauth?: EnvProps['oauth'];
98
+ /** Deployment-injected API endpoints for dev branch path-served apps. */
99
+ endpoints?: Pick<EnvProps['endpoints'], 'studio' | 'zeno' | 'sts'>;
83
100
  /** Broker selected by the serving gateway. Overrides build-time configuration. */
84
101
  authUrl?: string;
85
102
  /** The serving gateway owns this standalone app's OAuth session. */
@@ -147,9 +164,19 @@ export class VertesiaEnvironment implements Readonly<EnvProps> {
147
164
  const runtimeFirebase = runtimeConfig?.authMode === 'firebase' ? runtimeConfig.firebase : undefined;
148
165
  this._props = props && runtimeFirebase && !props.firebase ? { ...props, firebase: runtimeFirebase } : props;
149
166
  const authUrl = runtimeConfig?.authMode === 'central' ? runtimeConfig.authUrl?.trim() : undefined;
150
- if (this._props && authUrl) {
167
+ if (this._props && authUrl && isTrustedAuthBrokerUrl(authUrl, { allowLoopback: this._props.isLocalDev })) {
151
168
  this._props = { ...this._props, endpoints: { ...this._props.endpoints, auth: authUrl } };
152
169
  }
170
+ if (this._props && runtimeConfig?.authMode === 'central' && runtimeConfig.oauth) {
171
+ this._props = {
172
+ ...this._props,
173
+ oauth: runtimeConfig.oauth,
174
+ endpoints: { ...this._props.endpoints, ...runtimeConfig.endpoints },
175
+ };
176
+ }
177
+ if (this._props && runtimeConfig?.allowLegacyIframeAuth !== undefined) {
178
+ this._props = { ...this._props, allowLegacyIframeAuth: runtimeConfig.allowLegacyIframeAuth };
179
+ }
153
180
  const tenantId =
154
181
  typeof buildEnv?.VITE_FIREBASE_TENANT_ID === 'string' ? buildEnv.VITE_FIREBASE_TENANT_ID.trim() : '';
155
182
  if (this._props?.firebase && tenantId && !this._props.firebase.tenantId) {
@@ -252,6 +279,10 @@ export class VertesiaEnvironment implements Readonly<EnvProps> {
252
279
  return this._props?.defaultAuthSelection;
253
280
  }
254
281
 
282
+ get allowLegacyIframeAuth() {
283
+ return this._props?.allowLegacyIframeAuth ?? false;
284
+ }
285
+
255
286
  get oauth() {
256
287
  return this._props?.oauth;
257
288
  }