@vertesia/ui 1.5.0-dev.20260910.052912Z → 1.6.0-dev.20260912.110338Z

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 (389) hide show
  1. package/README.md +125 -0
  2. package/lib/boot/branding.d.ts +58 -0
  3. package/lib/boot/branding.d.ts.map +1 -0
  4. package/lib/boot/branding.js +62 -0
  5. package/lib/boot/branding.js.map +1 -0
  6. package/lib/boot/index.d.ts +27 -0
  7. package/lib/boot/index.d.ts.map +1 -0
  8. package/lib/boot/index.js +325 -0
  9. package/lib/boot/index.js.map +1 -0
  10. package/lib/boot/vite.d.ts +63 -0
  11. package/lib/boot/vite.d.ts.map +1 -0
  12. package/lib/boot/vite.js +108 -0
  13. package/lib/boot/vite.js.map +1 -0
  14. package/lib/core/components/Link.js +1 -1
  15. package/lib/core/components/Link.js.map +1 -1
  16. package/lib/core/components/Spinner.js +1 -1
  17. package/lib/core/components/Spinner.js.map +1 -1
  18. package/lib/core/components/TagsInput.js +7 -7
  19. package/lib/core/components/TagsInput.js.map +1 -1
  20. package/lib/core/components/shadcn/badge.js +1 -1
  21. package/lib/core/components/shadcn/badge.js.map +1 -1
  22. package/lib/core/components/shadcn/button.d.ts.map +1 -1
  23. package/lib/core/components/shadcn/button.js +3 -4
  24. package/lib/core/components/shadcn/button.js.map +1 -1
  25. package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js +2 -2
  26. package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js.map +1 -1
  27. package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js +1 -1
  28. package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js.map +1 -1
  29. package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.js +1 -1
  30. package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.js.map +1 -1
  31. package/lib/core/components/shadcn/filters/comboBox/TextCombobox.js +1 -1
  32. package/lib/core/components/shadcn/filters/comboBox/TextCombobox.js.map +1 -1
  33. package/lib/core/components/shadcn/filters/filter/dateFilter.js +1 -1
  34. package/lib/core/components/shadcn/filters/filter/dateFilter.js.map +1 -1
  35. package/lib/core/components/shadcn/filters/filter-styles.d.ts +1 -1
  36. package/lib/core/components/shadcn/filters/filter-styles.d.ts.map +1 -1
  37. package/lib/core/components/shadcn/filters/filter-styles.js +4 -4
  38. package/lib/core/components/shadcn/filters/filters.js +1 -1
  39. package/lib/core/components/shadcn/filters/filters.js.map +1 -1
  40. package/lib/core/components/shadcn/label.js +1 -1
  41. package/lib/core/components/shadcn/label.js.map +1 -1
  42. package/lib/core/components/shadcn/overflowTabs.js +2 -2
  43. package/lib/core/components/shadcn/overflowTabs.js.map +1 -1
  44. package/lib/core/components/shadcn/tabs.js +1 -1
  45. package/lib/core/components/shadcn/tabs.js.map +1 -1
  46. package/lib/env/index.d.ts +28 -1
  47. package/lib/env/index.d.ts.map +1 -1
  48. package/lib/env/index.js +51 -3
  49. package/lib/env/index.js.map +1 -1
  50. package/lib/features/activity-doc/ActivityDoc.js +1 -1
  51. package/lib/features/activity-doc/ActivityDoc.js.map +1 -1
  52. package/lib/features/agent/PayloadBuilder.d.ts +4 -0
  53. package/lib/features/agent/PayloadBuilder.d.ts.map +1 -1
  54. package/lib/features/agent/PayloadBuilder.js.map +1 -1
  55. package/lib/features/agent/chat/AgentRunFeedback.d.ts +59 -0
  56. package/lib/features/agent/chat/AgentRunFeedback.d.ts.map +1 -0
  57. package/lib/features/agent/chat/AgentRunFeedback.js +286 -0
  58. package/lib/features/agent/chat/AgentRunFeedback.js.map +1 -0
  59. package/lib/features/agent/chat/AskUserWidget.d.ts +1 -0
  60. package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
  61. package/lib/features/agent/chat/AskUserWidget.js +15 -4
  62. package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
  63. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  64. package/lib/features/agent/chat/ModernAgentConversation.js +3 -2
  65. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  66. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +3 -1
  67. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  68. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +16 -13
  69. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  70. package/lib/features/agent/chat/ModernAgentOutput/Header.d.ts.map +1 -1
  71. package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
  72. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +3 -1
  73. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  74. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +30 -8
  75. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  76. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts.map +1 -1
  77. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js +2 -0
  78. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js.map +1 -1
  79. package/lib/features/agent/chat/index.d.ts +1 -0
  80. package/lib/features/agent/chat/index.d.ts.map +1 -1
  81. package/lib/features/agent/chat/index.js +1 -0
  82. package/lib/features/agent/chat/index.js.map +1 -1
  83. package/lib/features/facets/AgentRunnerFacetsNav.d.ts +8 -0
  84. package/lib/features/facets/AgentRunnerFacetsNav.d.ts.map +1 -1
  85. package/lib/features/facets/AgentRunnerFacetsNav.js +12 -0
  86. package/lib/features/facets/AgentRunnerFacetsNav.js.map +1 -1
  87. package/lib/features/layout/GenericPageNavHeader.js +1 -1
  88. package/lib/features/layout/GenericPageNavHeader.js.map +1 -1
  89. package/lib/features/magic-pdf/AnnotatedImageSlider.js +1 -1
  90. package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
  91. package/lib/features/magic-pdf/GroundedExtractionView.js +3 -3
  92. package/lib/features/magic-pdf/GroundedExtractionView.js.map +1 -1
  93. package/lib/features/permissions/UserPermissionsProvider.d.ts +15 -1
  94. package/lib/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
  95. package/lib/features/permissions/UserPermissionsProvider.js +20 -12
  96. package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
  97. package/lib/features/store/objects/DocumentPreviewPanel.js +5 -5
  98. package/lib/features/store/objects/DocumentPreviewPanel.js.map +1 -1
  99. package/lib/features/store/objects/components/DocumentInput.js +1 -1
  100. package/lib/features/store/objects/components/DocumentInput.js.map +1 -1
  101. package/lib/features/store/objects/selection/SelectionActions.js +1 -1
  102. package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
  103. package/lib/features/store/objects/upload/DocumentUploadModal.js +1 -1
  104. package/lib/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
  105. package/lib/features/store/types/TypeRegistry.d.ts +130 -0
  106. package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
  107. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +130 -0
  108. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
  109. package/lib/features/user/UserInfo.js +1 -1
  110. package/lib/features/user/UserInfo.js.map +1 -1
  111. package/lib/i18n/index.d.ts +5 -4
  112. package/lib/i18n/index.d.ts.map +1 -1
  113. package/lib/i18n/index.js +16 -2
  114. package/lib/i18n/index.js.map +1 -1
  115. package/lib/i18n/locales/ar.json +30 -0
  116. package/lib/i18n/locales/de.json +30 -0
  117. package/lib/i18n/locales/en.json +30 -0
  118. package/lib/i18n/locales/es.json +30 -0
  119. package/lib/i18n/locales/fr.json +30 -0
  120. package/lib/i18n/locales/it.json +30 -0
  121. package/lib/i18n/locales/ja.json +30 -0
  122. package/lib/i18n/locales/ko.json +30 -0
  123. package/lib/i18n/locales/pt.json +30 -0
  124. package/lib/i18n/locales/ru.json +30 -0
  125. package/lib/i18n/locales/tr.json +30 -0
  126. package/lib/i18n/locales/zh-TW.json +30 -0
  127. package/lib/i18n/locales/zh.json +30 -0
  128. package/lib/layout/ErrorPage.js +1 -1
  129. package/lib/layout/ErrorPage.js.map +1 -1
  130. package/lib/layout/Navbar.d.ts.map +1 -1
  131. package/lib/layout/Navbar.js +3 -2
  132. package/lib/layout/Navbar.js.map +1 -1
  133. package/lib/router/RouteComponent.d.ts.map +1 -1
  134. package/lib/router/RouteComponent.js +15 -12
  135. package/lib/router/RouteComponent.js.map +1 -1
  136. package/lib/session/UserSession.d.ts.map +1 -1
  137. package/lib/session/UserSession.js +6 -3
  138. package/lib/session/UserSession.js.map +1 -1
  139. package/lib/session/UserSessionProvider.d.ts +2 -1
  140. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  141. package/lib/session/UserSessionProvider.js +10 -1
  142. package/lib/session/UserSessionProvider.js.map +1 -1
  143. package/lib/session/auth/composable.d.ts.map +1 -1
  144. package/lib/session/auth/composable.js +88 -3
  145. package/lib/session/auth/composable.js.map +1 -1
  146. package/lib/session/auth/domainRouting.d.ts +22 -12
  147. package/lib/session/auth/domainRouting.d.ts.map +1 -1
  148. package/lib/session/auth/domainRouting.js +35 -21
  149. package/lib/session/auth/domainRouting.js.map +1 -1
  150. package/lib/session/auth/useAuthState.d.ts +9 -4
  151. package/lib/session/auth/useAuthState.d.ts.map +1 -1
  152. package/lib/session/auth/useAuthState.js +9 -4
  153. package/lib/session/auth/useAuthState.js.map +1 -1
  154. package/lib/session/index.d.ts.map +1 -1
  155. package/lib/session/index.js +3 -1
  156. package/lib/session/index.js.map +1 -1
  157. package/lib/shell/AuthScreenPreview.d.ts +12 -0
  158. package/lib/shell/AuthScreenPreview.d.ts.map +1 -0
  159. package/lib/shell/AuthScreenPreview.js +140 -0
  160. package/lib/shell/AuthScreenPreview.js.map +1 -0
  161. package/lib/shell/BrandedAuthScreens.d.ts +19 -0
  162. package/lib/shell/BrandedAuthScreens.d.ts.map +1 -0
  163. package/lib/shell/BrandedAuthScreens.js +64 -0
  164. package/lib/shell/BrandedAuthScreens.js.map +1 -0
  165. package/lib/shell/SplashScreen.d.ts +13 -2
  166. package/lib/shell/SplashScreen.d.ts.map +1 -1
  167. package/lib/shell/SplashScreen.js +21 -20
  168. package/lib/shell/SplashScreen.js.map +1 -1
  169. package/lib/shell/VertesiaShell.d.ts +15 -4
  170. package/lib/shell/VertesiaShell.d.ts.map +1 -1
  171. package/lib/shell/VertesiaShell.js +8 -3
  172. package/lib/shell/VertesiaShell.js.map +1 -1
  173. package/lib/shell/apps/StandaloneApp.js +5 -1
  174. package/lib/shell/apps/StandaloneApp.js.map +1 -1
  175. package/lib/shell/index.d.ts +6 -0
  176. package/lib/shell/index.d.ts.map +1 -1
  177. package/lib/shell/index.js +5 -0
  178. package/lib/shell/index.js.map +1 -1
  179. package/lib/shell/login/SignInFlow.d.ts +68 -0
  180. package/lib/shell/login/SignInFlow.d.ts.map +1 -0
  181. package/lib/shell/login/SignInFlow.js +108 -0
  182. package/lib/shell/login/SignInFlow.js.map +1 -0
  183. package/lib/shell/login/SignInPageShell.d.ts +13 -0
  184. package/lib/shell/login/SignInPageShell.d.ts.map +1 -0
  185. package/lib/shell/login/SignInPageShell.js +14 -0
  186. package/lib/shell/login/SignInPageShell.js.map +1 -0
  187. package/lib/shell/login/SignInPrimitives.d.ts +3 -3
  188. package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
  189. package/lib/shell/login/SignInPrimitives.js +13 -11
  190. package/lib/shell/login/SignInPrimitives.js.map +1 -1
  191. package/lib/shell/login/SignInRestrictedEnvStep.d.ts +5 -0
  192. package/lib/shell/login/SignInRestrictedEnvStep.d.ts.map +1 -1
  193. package/lib/shell/login/SignInRestrictedEnvStep.js +8 -4
  194. package/lib/shell/login/SignInRestrictedEnvStep.js.map +1 -1
  195. package/lib/shell/login/SigninScreen.d.ts +38 -3
  196. package/lib/shell/login/SigninScreen.d.ts.map +1 -1
  197. package/lib/shell/login/SigninScreen.js +33 -86
  198. package/lib/shell/login/SigninScreen.js.map +1 -1
  199. package/lib/shell/login/SignupForm.d.ts +3 -1
  200. package/lib/shell/login/SignupForm.d.ts.map +1 -1
  201. package/lib/shell/login/SignupForm.js +7 -3
  202. package/lib/shell/login/SignupForm.js.map +1 -1
  203. package/lib/shell/login/signInUtils.d.ts +11 -0
  204. package/lib/shell/login/signInUtils.d.ts.map +1 -1
  205. package/lib/shell/login/signInUtils.js +29 -5
  206. package/lib/shell/login/signInUtils.js.map +1 -1
  207. package/lib/vertesia-ui-boot.js +237 -0
  208. package/lib/vertesia-ui-boot.js.map +1 -0
  209. package/lib/vertesia-ui-core.js +46 -46
  210. package/lib/vertesia-ui-core.js.map +1 -1
  211. package/lib/vertesia-ui-env.js +1 -1
  212. package/lib/vertesia-ui-env.js.map +1 -1
  213. package/lib/vertesia-ui-features.js +222 -208
  214. package/lib/vertesia-ui-features.js.map +1 -1
  215. package/lib/vertesia-ui-i18n.B3NFclwl.chunk.js +5 -0
  216. package/lib/vertesia-ui-i18n.B3NFclwl.chunk.js.map +1 -0
  217. package/lib/vertesia-ui-i18n.BWSrNIxc.chunk.js +5 -0
  218. package/lib/vertesia-ui-i18n.BWSrNIxc.chunk.js.map +1 -0
  219. package/lib/vertesia-ui-i18n.BWvWeB1p.chunk.js +5 -0
  220. package/lib/vertesia-ui-i18n.BWvWeB1p.chunk.js.map +1 -0
  221. package/lib/vertesia-ui-i18n.BZCXvTC1.chunk.js +5 -0
  222. package/lib/vertesia-ui-i18n.BZCXvTC1.chunk.js.map +1 -0
  223. package/lib/vertesia-ui-i18n.BbJMvFZW.chunk.js +5 -0
  224. package/lib/vertesia-ui-i18n.BbJMvFZW.chunk.js.map +1 -0
  225. package/lib/vertesia-ui-i18n.CE5ldwpt.chunk.js +5 -0
  226. package/lib/vertesia-ui-i18n.CE5ldwpt.chunk.js.map +1 -0
  227. package/lib/vertesia-ui-i18n.CMC0IShY.chunk.js +5 -0
  228. package/lib/vertesia-ui-i18n.CMC0IShY.chunk.js.map +1 -0
  229. package/lib/vertesia-ui-i18n.CkcRqBKU.chunk.js +5 -0
  230. package/lib/vertesia-ui-i18n.CkcRqBKU.chunk.js.map +1 -0
  231. package/lib/vertesia-ui-i18n.D-Mq1sw_.chunk.js +5 -0
  232. package/lib/vertesia-ui-i18n.D-Mq1sw_.chunk.js.map +1 -0
  233. package/lib/vertesia-ui-i18n.DkK68-A-.chunk.js +5 -0
  234. package/lib/vertesia-ui-i18n.DkK68-A-.chunk.js.map +1 -0
  235. package/lib/vertesia-ui-i18n.XE_dcCg6.chunk.js +5 -0
  236. package/lib/vertesia-ui-i18n.XE_dcCg6.chunk.js.map +1 -0
  237. package/lib/vertesia-ui-i18n.js +2 -2
  238. package/lib/vertesia-ui-i18n.js.map +1 -1
  239. package/lib/vertesia-ui-i18n.qgNqMPvK.chunk.js +5 -0
  240. package/lib/vertesia-ui-i18n.qgNqMPvK.chunk.js.map +1 -0
  241. package/lib/vertesia-ui-layout.js +13 -13
  242. package/lib/vertesia-ui-layout.js.map +1 -1
  243. package/lib/vertesia-ui-rich-text.js +68 -68
  244. package/lib/vertesia-ui-rich-text.js.map +1 -1
  245. package/lib/vertesia-ui-router.js +1 -1
  246. package/lib/vertesia-ui-router.js.map +1 -1
  247. package/lib/vertesia-ui-session.js +1 -1
  248. package/lib/vertesia-ui-session.js.map +1 -1
  249. package/lib/vertesia-ui-shell.js +58 -33
  250. package/lib/vertesia-ui-shell.js.map +1 -1
  251. package/lib/vertesia-ui-widgets.js +94 -94
  252. package/lib/vertesia-ui-widgets.js.map +1 -1
  253. package/lib/widgets/json-view/JSONCode.js +1 -1
  254. package/lib/widgets/json-view/JSONCode.js.map +1 -1
  255. package/lib/widgets/upload/DropZone.d.ts.map +1 -1
  256. package/lib/widgets/upload/DropZone.js +1 -1
  257. package/lib/widgets/upload/DropZone.js.map +1 -1
  258. package/llms.txt +6 -2
  259. package/package.json +44 -35
  260. package/src/__tests__/a11y.test.tsx +25 -0
  261. package/src/__tests__/theme.test.ts +91 -0
  262. package/src/boot/branding.test.ts +72 -0
  263. package/src/boot/branding.ts +108 -0
  264. package/src/boot/index.test.ts +216 -0
  265. package/src/boot/index.ts +356 -0
  266. package/src/boot/vite.test.ts +93 -0
  267. package/src/boot/vite.ts +118 -0
  268. package/src/core/components/Link.tsx +1 -1
  269. package/src/core/components/Spinner.tsx +1 -1
  270. package/src/core/components/TagsInput.tsx +7 -7
  271. package/src/core/components/shadcn/badge.tsx +1 -1
  272. package/src/core/components/shadcn/button.tsx +3 -5
  273. package/src/core/components/shadcn/filters/comboBox/DateCombobox.tsx +2 -2
  274. package/src/core/components/shadcn/filters/comboBox/SelectCombobox.tsx +1 -1
  275. package/src/core/components/shadcn/filters/comboBox/StringListCombobox.tsx +1 -1
  276. package/src/core/components/shadcn/filters/comboBox/TextCombobox.tsx +1 -1
  277. package/src/core/components/shadcn/filters/filter/dateFilter.tsx +1 -1
  278. package/src/core/components/shadcn/filters/filter-styles.ts +4 -4
  279. package/src/core/components/shadcn/filters/filters.tsx +1 -1
  280. package/src/core/components/shadcn/label.tsx +1 -1
  281. package/src/core/components/shadcn/overflowTabs.tsx +2 -2
  282. package/src/core/components/shadcn/tabs.tsx +1 -1
  283. package/src/css/utilities.css +7 -0
  284. package/src/env/index.test.ts +100 -0
  285. package/src/env/index.ts +66 -3
  286. package/src/features/activity-doc/ActivityDoc.tsx +1 -1
  287. package/src/features/agent/PayloadBuilder.tsx +4 -0
  288. package/src/features/agent/chat/AgentRunFeedback.test.tsx +330 -0
  289. package/src/features/agent/chat/AgentRunFeedback.tsx +452 -0
  290. package/src/features/agent/chat/AskUserWidget.test.tsx +24 -2
  291. package/src/features/agent/chat/AskUserWidget.tsx +19 -4
  292. package/src/features/agent/chat/ModernAgentConversation.tsx +46 -42
  293. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +207 -0
  294. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +50 -31
  295. package/src/features/agent/chat/ModernAgentOutput/Header.tsx +4 -0
  296. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +42 -8
  297. package/src/features/agent/chat/ModernAgentOutput/SummaryConversation.ts +2 -0
  298. package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +14 -0
  299. package/src/features/agent/chat/hooks/useFileProcessing.test.tsx +0 -5
  300. package/src/features/agent/chat/index.ts +6 -0
  301. package/src/features/facets/AgentRunnerFacetsNav.tsx +26 -0
  302. package/src/features/layout/GenericPageNavHeader.test.tsx +36 -0
  303. package/src/features/layout/GenericPageNavHeader.tsx +1 -1
  304. package/src/features/magic-pdf/AnnotatedImageSlider.tsx +1 -1
  305. package/src/features/magic-pdf/GroundedExtractionView.tsx +3 -3
  306. package/src/features/permissions/UserPermissionsProvider.test.tsx +50 -1
  307. package/src/features/permissions/UserPermissionsProvider.tsx +82 -44
  308. package/src/features/store/objects/DocumentPreviewPanel.tsx +10 -10
  309. package/src/features/store/objects/components/DocumentInput.tsx +1 -1
  310. package/src/features/store/objects/selection/SelectionActions.tsx +4 -1
  311. package/src/features/store/objects/upload/DocumentUploadModal.tsx +1 -1
  312. package/src/features/user/UserInfo.tsx +1 -1
  313. package/src/i18n/index.tsx +25 -2
  314. package/src/i18n/locales/ar.json +30 -0
  315. package/src/i18n/locales/de.json +30 -0
  316. package/src/i18n/locales/en.json +30 -0
  317. package/src/i18n/locales/es.json +30 -0
  318. package/src/i18n/locales/fr.json +30 -0
  319. package/src/i18n/locales/it.json +30 -0
  320. package/src/i18n/locales/ja.json +30 -0
  321. package/src/i18n/locales/ko.json +30 -0
  322. package/src/i18n/locales/pt.json +30 -0
  323. package/src/i18n/locales/ru.json +30 -0
  324. package/src/i18n/locales/tr.json +30 -0
  325. package/src/i18n/locales/zh-TW.json +30 -0
  326. package/src/i18n/locales/zh.json +30 -0
  327. package/src/layout/ErrorPage.tsx +1 -1
  328. package/src/layout/Navbar.test.tsx +58 -0
  329. package/src/layout/Navbar.tsx +5 -2
  330. package/src/router/RouteComponent.test.tsx +65 -1
  331. package/src/router/RouteComponent.tsx +18 -15
  332. package/src/session/UserSession.test.ts +65 -0
  333. package/src/session/UserSession.ts +6 -4
  334. package/src/session/UserSessionProvider.strict-mode.test.tsx +44 -0
  335. package/src/session/UserSessionProvider.test.ts +62 -1
  336. package/src/session/UserSessionProvider.tsx +9 -1
  337. package/src/session/auth/composable.renewal.test.ts +170 -0
  338. package/src/session/auth/composable.test.ts +23 -1
  339. package/src/session/auth/composable.ts +101 -2
  340. package/src/session/auth/domainRouting.test.ts +35 -65
  341. package/src/session/auth/domainRouting.ts +51 -34
  342. package/src/session/auth/useAuthState.ts +11 -4
  343. package/src/session/index.ts +3 -1
  344. package/src/shell/AuthScreenPreview.test.tsx +44 -0
  345. package/src/shell/AuthScreenPreview.tsx +228 -0
  346. package/src/shell/BrandedAuthScreens.test.tsx +181 -0
  347. package/src/shell/BrandedAuthScreens.tsx +117 -0
  348. package/src/shell/SplashScreen.test.tsx +37 -0
  349. package/src/shell/SplashScreen.tsx +45 -40
  350. package/src/shell/VertesiaShell.tsx +49 -22
  351. package/src/shell/apps/StandaloneApp.tsx +5 -1
  352. package/src/shell/index.tsx +6 -0
  353. package/src/shell/login/SignInFlow.tsx +213 -0
  354. package/src/shell/login/SignInPageShell.tsx +62 -0
  355. package/src/shell/login/SignInPrimitives.tsx +14 -11
  356. package/src/shell/login/SignInRestrictedEnvStep.tsx +19 -7
  357. package/src/shell/login/SigninScreen.test.tsx +104 -0
  358. package/src/shell/login/SigninScreen.tsx +115 -145
  359. package/src/shell/login/SignupForm.tsx +8 -3
  360. package/src/shell/login/signInUtils.ts +30 -5
  361. package/src/widgets/json-view/JSONCode.test.tsx +2 -2
  362. package/src/widgets/json-view/JSONCode.tsx +1 -1
  363. package/src/widgets/markdown/markdown.css +21 -21
  364. package/src/widgets/upload/DropZone.tsx +2 -4
  365. package/lib/vertesia-ui-i18n.BfN7iOHb.chunk.js +0 -5
  366. package/lib/vertesia-ui-i18n.BfN7iOHb.chunk.js.map +0 -1
  367. package/lib/vertesia-ui-i18n.BjH2zEZw.chunk.js +0 -5
  368. package/lib/vertesia-ui-i18n.BjH2zEZw.chunk.js.map +0 -1
  369. package/lib/vertesia-ui-i18n.BjSwAZXl.chunk.js +0 -5
  370. package/lib/vertesia-ui-i18n.BjSwAZXl.chunk.js.map +0 -1
  371. package/lib/vertesia-ui-i18n.C7PUm5Au.chunk.js +0 -5
  372. package/lib/vertesia-ui-i18n.C7PUm5Au.chunk.js.map +0 -1
  373. package/lib/vertesia-ui-i18n.CF_1mUS5.chunk.js +0 -5
  374. package/lib/vertesia-ui-i18n.CF_1mUS5.chunk.js.map +0 -1
  375. package/lib/vertesia-ui-i18n.ChfUl3_-.chunk.js +0 -5
  376. package/lib/vertesia-ui-i18n.ChfUl3_-.chunk.js.map +0 -1
  377. package/lib/vertesia-ui-i18n.D-Sk-5d1.chunk.js +0 -5
  378. package/lib/vertesia-ui-i18n.D-Sk-5d1.chunk.js.map +0 -1
  379. package/lib/vertesia-ui-i18n.D4rnu1_S.chunk.js +0 -5
  380. package/lib/vertesia-ui-i18n.D4rnu1_S.chunk.js.map +0 -1
  381. package/lib/vertesia-ui-i18n.DDf0-laK.chunk.js +0 -5
  382. package/lib/vertesia-ui-i18n.DDf0-laK.chunk.js.map +0 -1
  383. package/lib/vertesia-ui-i18n.DN79y06J.chunk.js +0 -5
  384. package/lib/vertesia-ui-i18n.DN79y06J.chunk.js.map +0 -1
  385. package/lib/vertesia-ui-i18n.DWLIciYQ.chunk.js +0 -5
  386. package/lib/vertesia-ui-i18n.DWLIciYQ.chunk.js.map +0 -1
  387. package/lib/vertesia-ui-i18n.DvF_YaAJ.chunk.js +0 -5
  388. package/lib/vertesia-ui-i18n.DvF_YaAJ.chunk.js.map +0 -1
  389. package/src/session/auth/authState.test.ts +0 -35
@@ -0,0 +1,118 @@
1
+ /** Node-only adapter. Import from @vertesia/ui/boot/vite, never from browser code. */
2
+ import { readFileSync } from 'node:fs';
3
+ import { extname } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { type AppBranding, brandedBootOptions, escapeBrandHtml } from './branding.js';
6
+ import { injectBootScreenHtml } from './index.js';
7
+
8
+ const VIRTUAL_ID = 'virtual:vertesia-branding';
9
+ const RESOLVED_ID = `\0${VIRTUAL_ID}`;
10
+ const MIME: Record<string, string> = {
11
+ '.svg': 'image/svg+xml',
12
+ '.png': 'image/png',
13
+ '.jpg': 'image/jpeg',
14
+ '.jpeg': 'image/jpeg',
15
+ '.webp': 'image/webp',
16
+ '.ico': 'image/x-icon',
17
+ '.woff2': 'font/woff2',
18
+ '.woff': 'font/woff',
19
+ '.otf': 'font/otf',
20
+ '.ttf': 'font/ttf',
21
+ '.avif': 'image/avif',
22
+ };
23
+
24
+ /** Inline local assets so React and pre-React markup work under any gateway base path. */
25
+ export function resolveBrandingAssets(branding: AppBranding, moduleUrl: URL, watch: (file: string) => void = () => {}) {
26
+ function asset(value: string, base = moduleUrl): string {
27
+ if (/^(https?:|data:|#)/i.test(value)) return value;
28
+ const url = new URL(value, base);
29
+ if (url.protocol !== 'file:') throw new Error(`Unsupported branding asset URL: ${value}`);
30
+ const file = fileURLToPath(url);
31
+ const mime = MIME[extname(file).toLowerCase()];
32
+ if (!mime) throw new Error(`Unsupported branding asset type: ${file}`);
33
+ watch(file);
34
+ return `data:${mime};base64,${readFileSync(file).toString('base64')}`;
35
+ }
36
+ function source(value: string, css: boolean): string {
37
+ const url = new URL(value, moduleUrl);
38
+ const file = fileURLToPath(url);
39
+ watch(file);
40
+ const text = readFileSync(file, 'utf8');
41
+ return css
42
+ ? text.replace(
43
+ /url\(\s*['"]?([^'"\s)]+)['"]?\s*\)/g,
44
+ (_match, path: string) => `url("${asset(path, url)}")`,
45
+ )
46
+ : text.replace(
47
+ /(<(?:img|source|link)\b[^>]*?\s)(src|href)=["']([^"']+)["']/gi,
48
+ (_match, tag: string, attr: string, path: string) =>
49
+ `${tag}${attr}="${escapeBrandHtml(asset(path, url))}"`,
50
+ );
51
+ }
52
+ return {
53
+ ...branding,
54
+ logo: branding.logo && {
55
+ ...branding.logo,
56
+ light: asset(branding.logo.light),
57
+ dark: branding.logo.dark ? asset(branding.logo.dark) : undefined,
58
+ },
59
+ loadingIcon: branding.loadingIcon && {
60
+ ...branding.loadingIcon,
61
+ light: asset(branding.loadingIcon.light),
62
+ dark: branding.loadingIcon.dark ? asset(branding.loadingIcon.dark) : undefined,
63
+ },
64
+ favicon: branding.favicon ? asset(branding.favicon) : undefined,
65
+ font: branding.font && {
66
+ ...branding.font,
67
+ regular: asset(branding.font.regular),
68
+ bold: branding.font.bold ? asset(branding.font.bold) : undefined,
69
+ },
70
+ boot: branding.boot && {
71
+ html: branding.boot.html ? source(branding.boot.html, false) : undefined,
72
+ styles: branding.boot.styles ? source(branding.boot.styles, true) : undefined,
73
+ },
74
+ } satisfies AppBranding;
75
+ }
76
+
77
+ export function createAppBrandingPlugin(branding: AppBranding, moduleUrl: URL) {
78
+ const watched = new Set<string>();
79
+ const resolve = () => resolveBrandingAssets(branding, moduleUrl, (file) => watched.add(file));
80
+ return {
81
+ name: 'vertesia-app-branding',
82
+ resolveId(id: string) {
83
+ if (id === VIRTUAL_ID) return RESOLVED_ID;
84
+ },
85
+ load(this: { addWatchFile(file: string): void }, id: string) {
86
+ if (id !== RESOLVED_ID) return;
87
+ const resolved = resolve();
88
+ for (const file of watched) this.addWatchFile(file);
89
+ return `export default ${JSON.stringify(resolved)};`;
90
+ },
91
+ handleHotUpdate<Module>(context: {
92
+ file: string;
93
+ server: {
94
+ moduleGraph: { getModuleById(id: string): Module | undefined; invalidateModule(module: Module): void };
95
+ ws: { send(payload: { type: 'full-reload' }): void };
96
+ };
97
+ }) {
98
+ if (!watched.has(context.file)) return;
99
+ const module = context.server.moduleGraph.getModuleById(RESOLVED_ID);
100
+ if (module) context.server.moduleGraph.invalidateModule(module);
101
+ context.server.ws.send({ type: 'full-reload' });
102
+ return [];
103
+ },
104
+ transformIndexHtml(html: string) {
105
+ const resolved = resolve();
106
+ const title = escapeBrandHtml(resolved.title ?? resolved.name);
107
+ let result = html.replace(/<title\b[^>]*>[\s\S]*?<\/title>/i, `<title>${title}</title>`);
108
+ if (resolved.favicon) {
109
+ result = result.replace(/<link\b(?=[^>]*\brel=["'](?:shortcut )?icon["'])[^>]*>/gi, '');
110
+ result = result.replace(
111
+ '</head>',
112
+ `<link rel="icon" href="${escapeBrandHtml(resolved.favicon)}"></head>`,
113
+ );
114
+ }
115
+ return injectBootScreenHtml(result, brandedBootOptions(resolved));
116
+ },
117
+ };
118
+ }
@@ -12,7 +12,7 @@ export function Link({ underline = 'always', className, children, ...others }: L
12
12
  underlineClass = 'hover:underline';
13
13
  }
14
14
  return (
15
- <a className={clsx('text-primary', className, underlineClass)} {...others}>
15
+ <a className={clsx('text-info', className, underlineClass)} {...others}>
16
16
  {children}
17
17
  </a>
18
18
  );
@@ -27,7 +27,7 @@ export function Spinner({ size, className }: SpinnerProps) {
27
27
  const sizeClass = getRealSize(size || 'md');
28
28
  return (
29
29
  <svg
30
- className={clsx('animate-[spin_0.8s_linear_infinite] text-primary', sizeClass, className)}
30
+ className={clsx('animate-[spin_0.8s_linear_infinite] text-info', sizeClass, className)}
31
31
  xmlns="http://www.w3.org/2000/svg"
32
32
  fill="none"
33
33
  viewBox="0 0 24 24"
@@ -278,7 +278,7 @@ function TagsInputContent({
278
278
  <span
279
279
  key={item}
280
280
  className={clsx(
281
- 'inline-flex items-center justify-between gap-2 px-2 py-1 text-sm bg-primary/20 text-foreground ring-1 ring-primary/30 rounded-md w-full transition-all',
281
+ 'inline-flex items-center justify-between gap-2 px-2 py-1 text-sm bg-info text-foreground ring-1 ring-primary/30 rounded-md w-full transition-all',
282
282
  pendingDeleteIndex === index &&
283
283
  'ring-2 ring-destructive shadow-[0_0_8px_rgba(252,165,165,0.5)]',
284
284
  )}
@@ -288,7 +288,7 @@ function TagsInputContent({
288
288
  type="button"
289
289
  onClick={(e) => handleRemove(item, e)}
290
290
  disabled={disabled}
291
- className="hover:bg-primary/30 rounded-sm transition-colors flex-shrink-0"
291
+ className="hover:bg-info rounded-sm transition-colors flex-shrink-0"
292
292
  >
293
293
  <X className="h-3 w-3" />
294
294
  </button>
@@ -303,7 +303,7 @@ function TagsInputContent({
303
303
  <span
304
304
  key={item}
305
305
  className={clsx(
306
- 'inline-flex items-center gap-1 px-2 py-1 text-sm bg-primary/20 text-foreground ring-1 ring-primary/30 rounded-md transition-all',
306
+ 'inline-flex items-center gap-1 px-2 py-1 text-sm bg-info text-foreground ring-1 ring-primary/30 rounded-md transition-all',
307
307
  pendingDeleteIndex === index &&
308
308
  'ring-2 ring-destructive shadow-[0_0_8px_rgba(252,165,165,0.5)]',
309
309
  )}
@@ -313,7 +313,7 @@ function TagsInputContent({
313
313
  type="button"
314
314
  onClick={(e) => handleRemove(item, e)}
315
315
  disabled={disabled}
316
- className="hover:bg-primary/30 rounded-sm transition-colors"
316
+ className="hover:bg-info rounded-sm transition-colors"
317
317
  >
318
318
  <X className="h-3 w-3" />
319
319
  </button>
@@ -381,7 +381,7 @@ function TagsInputContent({
381
381
  className={clsx(
382
382
  'px-3 py-2 text-sm cursor-pointer transition-colors',
383
383
  index === highlightedIndex
384
- ? 'bg-primary/20 text-foreground'
384
+ ? 'bg-info text-foreground'
385
385
  : 'hover:bg-muted/50',
386
386
  )}
387
387
  >
@@ -404,9 +404,9 @@ function TagsInputContent({
404
404
  onClick={() => handleCreate(searchTerm)}
405
405
  onMouseEnter={() => setHighlightedIndex(filteredOptions.length)}
406
406
  className={clsx(
407
- '!flex w-full justify-start px-3 py-2 text-sm cursor-pointer transition-colors text-primary',
407
+ '!flex w-full justify-start px-3 py-2 text-sm cursor-pointer transition-colors text-info',
408
408
  highlightedIndex === filteredOptions.length
409
- ? 'bg-primary/20'
409
+ ? 'bg-info'
410
410
  : 'hover:bg-muted/50',
411
411
  )}
412
412
  >
@@ -8,7 +8,7 @@ const badgeVariants = cva('inline-flex items-center rounded-md px-2 py-1 text-xs
8
8
  variants: {
9
9
  variant: {
10
10
  default: 'text-foreground bg-muted',
11
- secondary: 'bg-secondary text-primary',
11
+ secondary: 'bg-secondary text-info',
12
12
  destructive: 'bg-destructive text-destructive',
13
13
  attention: 'bg-attention text-attention',
14
14
  success: 'bg-success text-success',
@@ -5,7 +5,6 @@ import { Slot } from '@radix-ui/react-slot';
5
5
  // pinned to the fallback language regardless of LanguageProvider changes.
6
6
  import { useUITranslation } from '@vertesia/ui/i18n';
7
7
  import { cva, type VariantProps } from 'class-variance-authority';
8
- import clsx from 'clsx';
9
8
  import { Check, CopyIcon, Loader2 } from 'lucide-react';
10
9
  import * as React from 'react';
11
10
  import { useEffect, useState } from 'react';
@@ -47,11 +46,10 @@ const buttonVariants = cva(
47
46
  destructive:
48
47
  'bg-destructive text-destructive ring-1 ring-inset ring-destructive/50 shadow-xs hover:bg-destructive/50',
49
48
  outline: 'border border-input bg-background shadow-xs hover:bg-muted ring-inset',
50
- secondary:
51
- 'bg-primary/5 dark:bg-primary/10 text-primary shadow-xs hover:bg-primary/10 dark:hover:bg-primary/20 ring-inset',
49
+ secondary: 'bg-info text-info shadow-xs hover:bg-mixer-info/10 ring-inset',
52
50
  ghost: 'hover:bg-muted/50 dark:hover:bg-muted/20 ring-inset',
53
51
  link: 'text-foreground underline-offset-4 hover:underline ring-inset',
54
- primary: 'bg-primary text-white shadow-xs hover:bg-primary/90 ring-inset',
52
+ primary: 'bg-primary text-primary shadow-xs hover:bg-primary/90 ring-inset',
55
53
  unstyled: '',
56
54
  },
57
55
  size: {
@@ -150,7 +148,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
150
148
 
151
149
  const buttonElement = (
152
150
  <Comp
153
- className={clsx(cn(buttonVariants({ variant, size })), className)}
151
+ className={cn(buttonVariants({ variant, size }), className)}
154
152
  disabled={isDisabled || isLoading || props.disabled}
155
153
  ref={ref}
156
154
  onClick={onClick}
@@ -91,7 +91,7 @@ export const DateCombobox = ({
91
91
 
92
92
  return (
93
93
  <Popover _open={open} onOpenChange={setOpen}>
94
- <PopoverTrigger className="rounded-none p-1 h-8 bg-muted hover:bg-muted/50 text-muted hover:text-primary shrink-0 transition">
94
+ <PopoverTrigger className="rounded-none p-1 h-8 bg-muted hover:bg-muted/50 text-muted hover:text-info shrink-0 transition">
95
95
  <div className="flex gap-1.5 items-center min-h-[20px]">{getDisplayText()}</div>
96
96
  </PopoverTrigger>
97
97
  <PopoverContent className="w-auto p-0" align="start" side="bottom" alignOffset={-4} sideOffset={6}>
@@ -137,7 +137,7 @@ export const DateCombobox = ({
137
137
  return 'bg-primary text-primary-foreground rounded-e-md font-semibold';
138
138
  }
139
139
  if (currentDate > startDate && currentDate < endDate) {
140
- return 'bg-primary/20 text-primary font-medium';
140
+ return 'bg-info text-info font-medium';
141
141
  }
142
142
  } else {
143
143
  // Only start date selected
@@ -50,7 +50,7 @@ export const SelectionCombobox = ({
50
50
  }
51
51
  }}
52
52
  >
53
- <PopoverTrigger className="rounded-none p-1 h-8 bg-muted hover:bg-muted/50 transition text-muted hover:text-primary shrink-0">
53
+ <PopoverTrigger className="rounded-none p-1 h-8 bg-muted hover:bg-muted/50 transition text-muted hover:text-info shrink-0">
54
54
  <div className="flex gap-1.5 items-center">
55
55
  {filterValues?.length === 1
56
56
  ? (() => {
@@ -30,7 +30,7 @@ export const StringListCombobox = ({
30
30
  }
31
31
  }}
32
32
  >
33
- <PopoverTrigger className="rounded-none p-1 h-8 bg-muted hover:bg-muted/50 text-muted hover:text-primary shrink-0 transition">
33
+ <PopoverTrigger className="rounded-none p-1 h-8 bg-muted hover:bg-muted/50 text-muted hover:text-info shrink-0 transition">
34
34
  <div className="flex gap-1.5 items-center">
35
35
  {filterValues.length > 0
36
36
  ? filterValues.length === 1
@@ -32,7 +32,7 @@ export const TextCombobox = ({
32
32
  }
33
33
  }}
34
34
  >
35
- <PopoverTrigger className="rounded-none p-1 h-8 bg-muted hover:bg-muted/50 text-muted hover:text-primary shrink-0 transition">
35
+ <PopoverTrigger className="rounded-none p-1 h-8 bg-muted hover:bg-muted/50 text-muted hover:text-info shrink-0 transition">
36
36
  <div className="flex gap-1.5 items-center">{filterValue || 'Enter text...'}</div>
37
37
  </PopoverTrigger>
38
38
  <PopoverContent className="w-[300px] p-3">
@@ -205,7 +205,7 @@ export default function DateFilter({
205
205
  return 'bg-primary text-primary-foreground rounded-e-md font-semibold';
206
206
  }
207
207
  if (currentDate > startDate && currentDate < endDate) {
208
- return 'bg-primary/20 text-primary font-medium';
208
+ return 'bg-info text-info font-medium';
209
209
  }
210
210
  } else {
211
211
  // Only start date selected
@@ -20,13 +20,13 @@ export const calendarStyles = `
20
20
  min-width: 32px !important;
21
21
  height: 32px !important;
22
22
  border-radius: 4px !important;
23
- border: 1px solid hsl(var(--border)) !important;
24
- background: hsl(var(--background)) !important;
25
- color: hsl(var(--foreground)) !important;
23
+ border: 1px solid var(--border) !important;
24
+ background: var(--background) !important;
25
+ color: var(--foreground) !important;
26
26
  }
27
27
 
28
28
  .calendar-wrapper .react-calendar__navigation__arrow:hover {
29
- background: oklch(var(--muted)) !important;
29
+ background: var(--muted) !important;
30
30
  }
31
31
 
32
32
  .calendar-wrapper .react-calendar__tile {
@@ -130,7 +130,7 @@ export default function Filters({ filters, setFilters, filterGroups }: FiltersPr
130
130
  onClick={() => {
131
131
  setFilters((prev) => prev.filter((f) => f !== filter));
132
132
  }}
133
- className="bg-muted rounded-s-none rounded-e-sm size-8 hover:text-primary hover:bg-muted/50 transition shrink-0"
133
+ className="bg-muted rounded-s-none rounded-e-sm size-8 hover:text-info hover:bg-muted/50 transition shrink-0"
134
134
  >
135
135
  <X className="size-6" />
136
136
  </Button>
@@ -17,7 +17,7 @@ const Label: React.ForwardRefExoticComponent<
17
17
  ({ className, required, ...props }, ref) => (
18
18
  <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props}>
19
19
  {props.children}
20
- {required && <span className="text-destructive-foreground ms-1">*</span>}
20
+ {required && <span className="text-destructive ms-1">*</span>}
21
21
  </LabelPrimitive.Root>
22
22
  ),
23
23
  );
@@ -15,7 +15,7 @@ const VARIANT_CLASSES: Record<OverflowTabsVariant, { base: string; active: strin
15
15
  tabs: {
16
16
  base: 'flex items-center border-b-2 px-2 py-1.5 text-sm font-medium whitespace-nowrap cursor-pointer shrink-0',
17
17
  inactive: 'border-transparent text-muted hover:border-border hover:text-foreground',
18
- active: 'border-primary text-primary',
18
+ active: 'border-primary text-info',
19
19
  icon: 'ms-1 size-4',
20
20
  },
21
21
  pills: {
@@ -307,7 +307,7 @@ export function OverflowTabsBar({ tabs, current, onTabChange, variant = 'tabs',
307
307
  key={tab.name}
308
308
  disabled={tab.disabled}
309
309
  onClick={() => onTabChange(tab.name)}
310
- className={cn(tab.name === current && 'text-primary')}
310
+ className={cn(tab.name === current && 'text-info')}
311
311
  >
312
312
  {tab.label}
313
313
  </DropdownMenuItem>
@@ -344,7 +344,7 @@ const TabsTrigger: React.ForwardRefExoticComponent<
344
344
  ? cn(
345
345
  'border-b-2 px-2 py-1.5 text-sm font-medium whitespace-nowrap cursor-pointer',
346
346
  'border-transparent text-muted hover:border-border hover:text-foreground',
347
- 'data-[state=active]:border-primary data-[state=active]:text-primary',
347
+ 'data-[state=active]:border-primary data-[state=active]:text-info',
348
348
  'disabled:pointer-events-none disabled:opacity-50',
349
349
  )
350
350
  : cn(
@@ -4,6 +4,13 @@
4
4
  .dark {
5
5
  --mixer: oklch(0 0 0);
6
6
  }
7
+ /* PROTOTYPE: mirror of `bg-*`. Resolves --color-<name>-foreground first so `text-info`
8
+ * yields white on the solid primary surface; tokens without a -foreground (the six status
9
+ * semantics) fall through to the built-in utility and keep their current meaning. */
10
+ @utility text-* {
11
+ color: --value(--color-* -foreground, [*]);
12
+ }
13
+
7
14
  @utility text-mixer-* {
8
15
  color: color-mix(
9
16
  in oklab,
@@ -92,3 +92,103 @@ describe('VertesiaEnvironment runtime configuration', () => {
92
92
  expect(window.AUTH_MODE).toBe('central');
93
93
  });
94
94
  });
95
+
96
+ describe('VertesiaEnvironment build configuration', () => {
97
+ const buildEnv = {
98
+ VITE_FIREBASE_API_KEY: 'build-key',
99
+ VITE_FIREBASE_AUTH_DOMAIN: 'project.firebaseapp.com',
100
+ VITE_FIREBASE_PROJECT_ID: 'project',
101
+ VITE_FIREBASE_APP_ID: 'build-app',
102
+ };
103
+
104
+ afterEach(() => vi.unstubAllGlobals());
105
+
106
+ it('enables Firebase only with an explicit mode and complete build settings', () => {
107
+ vi.stubGlobal('window', {});
108
+ const env = new VertesiaEnvironment().init(baseProps, { ...buildEnv, VITE_AUTH_MODE: 'firebase' });
109
+ expect(env.firebase?.apiKey).toBe('build-key');
110
+ expect(env.firebase?.authDomain).toBe('project.firebaseapp.com');
111
+ expect(window.AUTH_MODE).toBe('firebase');
112
+ });
113
+
114
+ it.each([buildEnv, { VITE_FIREBASE_API_KEY: 'unused-key' }])(
115
+ 'retains central auth when Firebase values exist without an explicit mode',
116
+ (settings) => {
117
+ vi.stubGlobal('window', {});
118
+ const env = new VertesiaEnvironment().init(baseProps, settings);
119
+ expect(env.firebase).toBeUndefined();
120
+ expect(window.AUTH_MODE).toBeUndefined();
121
+ },
122
+ );
123
+
124
+ it('allows explicit central mode even with Firebase build settings', () => {
125
+ vi.stubGlobal('window', {});
126
+ const env = new VertesiaEnvironment().init(baseProps, { ...buildEnv, VITE_AUTH_MODE: 'central' });
127
+ expect(env.firebase).toBeUndefined();
128
+ expect(window.AUTH_MODE).toBe('central');
129
+ });
130
+
131
+ it('rejects incomplete settings when Firebase is selected', () => {
132
+ vi.stubGlobal('window', {});
133
+ expect(() =>
134
+ new VertesiaEnvironment().init(baseProps, {
135
+ VITE_AUTH_MODE: 'firebase',
136
+ VITE_FIREBASE_API_KEY: 'key',
137
+ }),
138
+ ).toThrow('VITE_FIREBASE_AUTH_DOMAIN, VITE_FIREBASE_PROJECT_ID, VITE_FIREBASE_APP_ID');
139
+ });
140
+
141
+ it('rejects an unknown mode', () => {
142
+ vi.stubGlobal('window', {});
143
+ expect(() => new VertesiaEnvironment().init(baseProps, { VITE_AUTH_MODE: 'typo' })).toThrow(
144
+ 'VITE_AUTH_MODE must be firebase or central',
145
+ );
146
+ });
147
+
148
+ it('preserves gateway Firebase configuration ahead of conflicting build settings', () => {
149
+ const firebase = {
150
+ apiKey: 'runtime-key',
151
+ authDomain: 'gateway.example.com',
152
+ projectId: 'runtime',
153
+ appId: 'runtime-app',
154
+ };
155
+ vi.stubGlobal('window', { __VERTESIA_RUNTIME_CONFIG__: { authMode: 'firebase', firebase } });
156
+ const env = new VertesiaEnvironment().init(baseProps, { VITE_AUTH_MODE: 'central' });
157
+ expect(env.firebase).toEqual(firebase);
158
+ expect(window.AUTH_MODE).toBe('firebase');
159
+ });
160
+
161
+ it('preserves gateway central mode ahead of Firebase build settings', () => {
162
+ vi.stubGlobal('window', { __VERTESIA_RUNTIME_CONFIG__: { authMode: 'central' } });
163
+ const env = new VertesiaEnvironment().init(baseProps, buildEnv);
164
+ expect(env.firebase).toBeUndefined();
165
+ expect(window.AUTH_MODE).toBe('central');
166
+ });
167
+
168
+ it('leaves the existing central default unchanged without settings', () => {
169
+ vi.stubGlobal('window', {});
170
+ const env = new VertesiaEnvironment().init(baseProps, {});
171
+ expect(env.firebase).toBeUndefined();
172
+ expect(window.AUTH_MODE).toBeUndefined();
173
+ });
174
+ });
175
+
176
+ describe('build-time default workspace', () => {
177
+ it('reads and trims Vertesia IDs independently of the Firebase project', () => {
178
+ const env = new VertesiaEnvironment().init(baseProps, {
179
+ VITE_VERTESIA_ACCOUNT_ID: ' account-1 ',
180
+ VITE_VERTESIA_PROJECT_ID: ' project-1 ',
181
+ });
182
+ expect(env.defaultAuthSelection).toEqual({ accountId: 'account-1', projectId: 'project-1' });
183
+ });
184
+ it('ignores blank settings and preserves an explicit selection as a whole pair', () => {
185
+ expect(
186
+ new VertesiaEnvironment().init(baseProps, { VITE_VERTESIA_ACCOUNT_ID: ' ' }).defaultAuthSelection,
187
+ ).toBeUndefined();
188
+ const env = new VertesiaEnvironment().init(
189
+ { ...baseProps, defaultAuthSelection: { accountId: 'explicit' } },
190
+ { VITE_VERTESIA_ACCOUNT_ID: 'build', VITE_VERTESIA_PROJECT_ID: 'other-project' },
191
+ );
192
+ expect(env.defaultAuthSelection).toEqual({ accountId: 'explicit' });
193
+ });
194
+ });
package/src/env/index.ts CHANGED
@@ -15,6 +15,14 @@ export interface EnvProps {
15
15
  zeno: string;
16
16
  studio: string;
17
17
  sts: string; // Security Token Service endpoint
18
+ /**
19
+ * Central Auth broker that issues the sign-in redirect and hosts `/logout`.
20
+ *
21
+ * Optional: when unset the session falls back to the long-standing broker, so an app that
22
+ * does not set it keeps its current behaviour exactly. Set it to move one environment at a
23
+ * time onto a different broker.
24
+ */
25
+ auth?: string;
18
26
  git?: string; // Smart HTTP app source git endpoint
19
27
  mcp?: string;
20
28
  /** Appgen app-gateway endpoint (serves live development previews and app bundles). */
@@ -27,6 +35,8 @@ export interface EnvProps {
27
35
  appId?: string;
28
36
  providerType?: string;
29
37
  };
38
+ /** Default workspace selection when the URL does not explicitly select an account or project. */
39
+ defaultAuthSelection?: { accountId?: string; projectId?: string };
30
40
  region?: string;
31
41
  datadogRum?: boolean;
32
42
  datadogLogs?: boolean;
@@ -87,14 +97,63 @@ function injectedRuntimeConfig(): VertesiaRuntimeConfig | undefined {
87
97
  return runtimeConfig;
88
98
  }
89
99
 
100
+ /** Public build-time settings, for example Vite's import.meta.env. */
101
+ export type AppBuildEnvironment = Record<string, string | boolean | undefined>;
102
+
103
+ function buildRuntimeConfig(env?: AppBuildEnvironment): VertesiaRuntimeConfig | undefined {
104
+ if (!env) return undefined;
105
+ const value = (name: string) => (typeof env[name] === 'string' ? env[name].trim() : '');
106
+ const mode = value('VITE_AUTH_MODE');
107
+ if (mode && mode !== 'firebase' && mode !== 'central') {
108
+ throw new Error('VITE_AUTH_MODE must be firebase or central');
109
+ }
110
+ // Firebase build settings are only consumed after an explicit opt-in.
111
+ if (!mode) return undefined;
112
+ if (mode === 'central') return { authMode: 'central' };
113
+ const fields = {
114
+ apiKey: 'VITE_FIREBASE_API_KEY',
115
+ authDomain: 'VITE_FIREBASE_AUTH_DOMAIN',
116
+ projectId: 'VITE_FIREBASE_PROJECT_ID',
117
+ appId: 'VITE_FIREBASE_APP_ID',
118
+ };
119
+ const missing = Object.values(fields).filter((name) => !value(name));
120
+ if (missing.length) {
121
+ throw new Error(`Firebase authentication requires: ${missing.join(', ')}`);
122
+ }
123
+ return {
124
+ authMode: 'firebase',
125
+ firebase: {
126
+ apiKey: value(fields.apiKey),
127
+ authDomain: value(fields.authDomain),
128
+ projectId: value(fields.projectId),
129
+ appId: value(fields.appId),
130
+ },
131
+ };
132
+ }
133
+
90
134
  export class VertesiaEnvironment implements Readonly<EnvProps> {
91
135
  constructor(private _props?: EnvProps | undefined) {}
92
136
 
93
- init(props?: EnvProps) {
94
- const runtimeConfig = injectedRuntimeConfig();
137
+ init(props?: EnvProps, buildEnv?: AppBuildEnvironment) {
138
+ // Host runtime configuration takes precedence over static deployment defaults.
139
+ const runtimeConfig = injectedRuntimeConfig() ?? buildRuntimeConfig(buildEnv);
95
140
  const runtimeFirebase = runtimeConfig?.authMode === 'firebase' ? runtimeConfig.firebase : undefined;
96
141
  this._props = props && runtimeFirebase && !props.firebase ? { ...props, firebase: runtimeFirebase } : props;
97
- if (runtimeConfig && window.AUTH_MODE === undefined) window.AUTH_MODE = runtimeConfig.authMode;
142
+ if (this._props && this._props.defaultAuthSelection === undefined && buildEnv) {
143
+ const accountId =
144
+ typeof buildEnv.VITE_VERTESIA_ACCOUNT_ID === 'string'
145
+ ? buildEnv.VITE_VERTESIA_ACCOUNT_ID.trim() || undefined
146
+ : undefined;
147
+ const projectId =
148
+ typeof buildEnv.VITE_VERTESIA_PROJECT_ID === 'string'
149
+ ? buildEnv.VITE_VERTESIA_PROJECT_ID.trim() || undefined
150
+ : undefined;
151
+ if (accountId || projectId)
152
+ this._props = { ...this._props, defaultAuthSelection: { accountId, projectId } };
153
+ }
154
+ if (runtimeConfig && typeof window !== 'undefined' && window.AUTH_MODE === undefined) {
155
+ window.AUTH_MODE = runtimeConfig.authMode;
156
+ }
98
157
  return this;
99
158
  }
100
159
 
@@ -173,6 +232,10 @@ export class VertesiaEnvironment implements Readonly<EnvProps> {
173
232
  return this._props?.devAuthToken;
174
233
  }
175
234
 
235
+ get defaultAuthSelection() {
236
+ return this._props?.defaultAuthSelection;
237
+ }
238
+
176
239
  get authTokenProvider() {
177
240
  return this._props?.authTokenProvider;
178
241
  }
@@ -6,7 +6,7 @@ import type React from 'react';
6
6
  import { useMemo, useState } from 'react';
7
7
 
8
8
  function Badge({ children, secondary = false }: { children: React.ReactNode; secondary?: boolean }) {
9
- const className = secondary ? 'bg-secondary text-primary' : 'text-foreground bg-muted';
9
+ const className = secondary ? 'bg-secondary text-info' : 'text-foreground bg-muted';
10
10
  return <span className={className}>{children}</span>;
11
11
  }
12
12
 
@@ -39,6 +39,10 @@ function getExecutableProvider(provider: ExecutionEnvironmentRef['provider']): M
39
39
  }
40
40
 
41
41
  export interface ScheduledWorkflowConfig {
42
+ run_as?: import('@vertesia/common').ScheduleRunAs;
43
+ /** Local datetime input; serialize as delegation_expires_at (ISO or null) for schedule requests. */
44
+ delegation_expiry_input?: string;
45
+ request_id?: string;
42
46
  name: string;
43
47
  description?: string;
44
48
  cron_expression: string;