@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.
- package/lib/boot/branding.d.ts +13 -0
- package/lib/boot/branding.d.ts.map +1 -1
- package/lib/boot/branding.js +7 -0
- package/lib/boot/branding.js.map +1 -1
- package/lib/boot/index.d.ts +1 -1
- package/lib/boot/index.d.ts.map +1 -1
- package/lib/boot/index.js +5 -23
- package/lib/boot/index.js.map +1 -1
- package/lib/boot/loading.d.ts +4 -0
- package/lib/boot/loading.d.ts.map +1 -0
- package/lib/boot/loading.js +28 -0
- package/lib/boot/loading.js.map +1 -0
- package/lib/core/components/SelectList.d.ts.map +1 -1
- package/lib/core/components/SelectList.js +3 -3
- package/lib/core/components/SelectList.js.map +1 -1
- package/lib/core/components/SidePanel.d.ts.map +1 -1
- package/lib/core/components/SidePanel.js +2 -1
- package/lib/core/components/SidePanel.js.map +1 -1
- package/lib/core/components/shadcn/theme/ThemeProvider.d.ts.map +1 -1
- package/lib/core/components/shadcn/theme/ThemeProvider.js +21 -5
- package/lib/core/components/shadcn/theme/ThemeProvider.js.map +1 -1
- package/lib/env/index.d.ts +11 -0
- package/lib/env/index.d.ts.map +1 -1
- package/lib/env/index.js +16 -3
- package/lib/env/index.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +4 -1
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +5 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +9 -2
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
- package/lib/features/magic-pdf/GroundedExtractionView.js +3 -3
- package/lib/features/magic-pdf/GroundedExtractionView.js.map +1 -1
- package/lib/features/media-viewer/AudioPanel.d.ts +10 -2
- package/lib/features/media-viewer/AudioPanel.d.ts.map +1 -1
- package/lib/features/media-viewer/AudioPanel.js +78 -13
- package/lib/features/media-viewer/AudioPanel.js.map +1 -1
- package/lib/features/oauth/McpConnectionsButton.d.ts +2 -2
- package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -1
- package/lib/features/oauth/McpConnectionsButton.js +9 -7
- package/lib/features/oauth/McpConnectionsButton.js.map +1 -1
- package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -1
- package/lib/features/oauth/McpConnectionsDialog.js +7 -3
- package/lib/features/oauth/McpConnectionsDialog.js.map +1 -1
- package/lib/features/oauth/useMcpConnections.d.ts +4 -2
- package/lib/features/oauth/useMcpConnections.d.ts.map +1 -1
- package/lib/features/oauth/useMcpConnections.js +15 -9
- package/lib/features/oauth/useMcpConnections.js.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContext.js +2 -0
- package/lib/features/store/objects/selection/ObjectsActionContext.js.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.d.ts +4 -2
- package/lib/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.js +6 -3
- package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowAction.d.ts +4 -0
- package/lib/features/store/objects/selection/actions/StartWorkflowAction.d.ts.map +1 -0
- package/lib/features/store/objects/selection/actions/StartWorkflowAction.js +81 -0
- package/lib/features/store/objects/selection/actions/StartWorkflowAction.js.map +1 -0
- package/lib/features/store/objects/selection/actions/index.d.ts +1 -0
- package/lib/features/store/objects/selection/actions/index.d.ts.map +1 -1
- package/lib/features/store/objects/selection/actions/index.js +1 -0
- package/lib/features/store/objects/selection/actions/index.js.map +1 -1
- package/lib/features/store/types/TypeRegistry.d.ts +345 -155
- package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +345 -155
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
- package/lib/i18n/locales/ar.json +10 -2
- package/lib/i18n/locales/de.json +6 -2
- package/lib/i18n/locales/en.json +6 -2
- package/lib/i18n/locales/es.json +6 -2
- package/lib/i18n/locales/fr.json +7 -2
- package/lib/i18n/locales/it.json +6 -2
- package/lib/i18n/locales/ja.json +6 -2
- package/lib/i18n/locales/ko.json +6 -2
- package/lib/i18n/locales/pt.json +6 -2
- package/lib/i18n/locales/ru.json +8 -2
- package/lib/i18n/locales/tr.json +6 -2
- package/lib/i18n/locales/zh-TW.json +6 -2
- package/lib/i18n/locales/zh.json +6 -2
- package/lib/session/UserSession.d.ts +3 -0
- package/lib/session/UserSession.d.ts.map +1 -1
- package/lib/session/UserSession.js +18 -8
- package/lib/session/UserSession.js.map +1 -1
- package/lib/session/UserSessionProvider.d.ts.map +1 -1
- package/lib/session/UserSessionProvider.js +34 -24
- package/lib/session/UserSessionProvider.js.map +1 -1
- package/lib/session/auth/authRoundTrip.d.ts +8 -0
- package/lib/session/auth/authRoundTrip.d.ts.map +1 -0
- package/lib/session/auth/authRoundTrip.js +22 -0
- package/lib/session/auth/authRoundTrip.js.map +1 -0
- package/lib/session/auth/composable.d.ts.map +1 -1
- package/lib/session/auth/composable.js +9 -7
- package/lib/session/auth/composable.js.map +1 -1
- package/lib/session/auth/domainRouting.d.ts +13 -6
- package/lib/session/auth/domainRouting.d.ts.map +1 -1
- package/lib/session/auth/domainRouting.js +2 -21
- package/lib/session/auth/domainRouting.js.map +1 -1
- package/lib/session/auth/oauth.d.ts +6 -1
- package/lib/session/auth/oauth.d.ts.map +1 -1
- package/lib/session/auth/oauth.js +145 -8
- package/lib/session/auth/oauth.js.map +1 -1
- package/lib/session/auth/tokenIssuer.d.ts +3 -0
- package/lib/session/auth/tokenIssuer.d.ts.map +1 -0
- package/lib/session/auth/tokenIssuer.js +27 -0
- package/lib/session/auth/tokenIssuer.js.map +1 -0
- package/lib/session/auth/vertesiaHosts.d.ts +47 -0
- package/lib/session/auth/vertesiaHosts.d.ts.map +1 -0
- package/lib/session/auth/vertesiaHosts.js +79 -0
- package/lib/session/auth/vertesiaHosts.js.map +1 -0
- package/lib/session/index.d.ts +1 -0
- package/lib/session/index.d.ts.map +1 -1
- package/lib/session/index.js +1 -0
- package/lib/session/index.js.map +1 -1
- package/lib/shell/AuthScreenPreview.d.ts +1 -1
- package/lib/shell/AuthScreenPreview.d.ts.map +1 -1
- package/lib/shell/AuthScreenPreview.js +2 -5
- package/lib/shell/AuthScreenPreview.js.map +1 -1
- package/lib/shell/BrandedAuthScreens.d.ts +1 -4
- package/lib/shell/BrandedAuthScreens.d.ts.map +1 -1
- package/lib/shell/BrandedAuthScreens.js +5 -26
- package/lib/shell/BrandedAuthScreens.js.map +1 -1
- package/lib/shell/BrandedLoadingIndicator.d.ts +11 -0
- package/lib/shell/BrandedLoadingIndicator.d.ts.map +1 -0
- package/lib/shell/BrandedLoadingIndicator.js +27 -0
- package/lib/shell/BrandedLoadingIndicator.js.map +1 -0
- package/lib/shell/SplashScreen.d.ts.map +1 -1
- package/lib/shell/SplashScreen.js +2 -1
- package/lib/shell/SplashScreen.js.map +1 -1
- package/lib/shell/apps/AppInstallationProvider.d.ts +16 -4
- package/lib/shell/apps/AppInstallationProvider.d.ts.map +1 -1
- package/lib/shell/apps/iframe-auth.d.ts +2 -0
- package/lib/shell/apps/iframe-auth.d.ts.map +1 -1
- package/lib/shell/apps/iframe-auth.js +6 -2
- package/lib/shell/apps/iframe-auth.js.map +1 -1
- package/lib/shell/index.d.ts +1 -0
- package/lib/shell/index.d.ts.map +1 -1
- package/lib/shell/index.js +1 -0
- package/lib/shell/index.js.map +1 -1
- package/lib/shell/login/SignInAuthPending.js +2 -2
- package/lib/shell/login/SignInAuthPending.js.map +1 -1
- package/lib/shell/login/SignInEmailStep.d.ts.map +1 -1
- package/lib/shell/login/SignInEmailStep.js +2 -2
- package/lib/shell/login/SignInEmailStep.js.map +1 -1
- package/lib/shell/login/TerminalLogin.d.ts.map +1 -1
- package/lib/shell/login/TerminalLogin.js +28 -21
- package/lib/shell/login/TerminalLogin.js.map +1 -1
- package/lib/vertesia-ui-boot.js +33 -29
- package/lib/vertesia-ui-boot.js.map +1 -1
- package/lib/vertesia-ui-core.js +34 -34
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-env.js +1 -1
- package/lib/vertesia-ui-env.js.map +1 -1
- package/lib/vertesia-ui-features.js +163 -163
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.Bl8KeZUK.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.Bl8KeZUK.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.BmT06XFI.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.BmT06XFI.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.Ch-Jjj0L.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.Ch-Jjj0L.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.CjhloW9s.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.CjhloW9s.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.CkfHdQzY.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.CkfHdQzY.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.DFuKfTe1.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.DFuKfTe1.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.DHqNVekZ.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.DHqNVekZ.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.DOaKbmaj.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.DOaKbmaj.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.Dge3Gl-h.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.Dge3Gl-h.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.GLt-10Lx.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.GLt-10Lx.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.T-XtAGbw.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.T-XtAGbw.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.VOJyTpMM.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.VOJyTpMM.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.js +3 -3
- package/lib/vertesia-ui-i18n.js.map +1 -1
- package/lib/vertesia-ui-layout.js +15 -15
- package/lib/vertesia-ui-layout.js.map +1 -1
- package/lib/vertesia-ui-rich-text.js +44 -44
- package/lib/vertesia-ui-rich-text.js.map +1 -1
- package/lib/vertesia-ui-session.js +1 -1
- package/lib/vertesia-ui-session.js.map +1 -1
- package/lib/vertesia-ui-shell.js +72 -48
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/lib/vertesia-ui-widgets.js +51 -51
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/package.json +19 -19
- package/src/boot/branding.ts +8 -0
- package/src/boot/index.ts +6 -23
- package/src/boot/loading.ts +27 -0
- package/src/core/components/SelectList.tsx +5 -2
- package/src/core/components/SidePanel.tsx +65 -60
- package/src/core/components/shadcn/theme/ThemeProvider.test.tsx +101 -0
- package/src/core/components/shadcn/theme/ThemeProvider.tsx +21 -9
- package/src/env/index.test.ts +21 -0
- package/src/env/index.ts +33 -2
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +81 -0
- package/src/features/agent/chat/ModernAgentConversation.tsx +5 -0
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +17 -1
- package/src/features/magic-pdf/GroundedExtractionView.tsx +3 -3
- package/src/features/media-viewer/AudioPanel.test.ts +84 -0
- package/src/features/media-viewer/AudioPanel.tsx +97 -14
- package/src/features/oauth/McpConnectionsButton.test.tsx +56 -0
- package/src/features/oauth/McpConnectionsButton.tsx +9 -6
- package/src/features/oauth/McpConnectionsDialog.tsx +14 -5
- package/src/features/oauth/useMcpConnections.test.ts +10 -0
- package/src/features/oauth/useMcpConnections.ts +18 -10
- package/src/features/store/objects/selection/ObjectsActionContext.tsx +2 -0
- package/src/features/store/objects/selection/SelectionActions.tsx +13 -6
- package/src/features/store/objects/selection/actions/StartWorkflowAction.tsx +162 -0
- package/src/features/store/objects/selection/actions/index.ts +1 -0
- package/src/i18n/locales/ar.json +10 -2
- package/src/i18n/locales/de.json +6 -2
- package/src/i18n/locales/en.json +6 -2
- package/src/i18n/locales/es.json +6 -2
- package/src/i18n/locales/fr.json +7 -2
- package/src/i18n/locales/it.json +6 -2
- package/src/i18n/locales/ja.json +6 -2
- package/src/i18n/locales/ko.json +6 -2
- package/src/i18n/locales/pt.json +6 -2
- package/src/i18n/locales/ru.json +8 -2
- package/src/i18n/locales/tr.json +6 -2
- package/src/i18n/locales/zh-TW.json +6 -2
- package/src/i18n/locales/zh.json +6 -2
- package/src/session/UserSession.test.ts +48 -3
- package/src/session/UserSession.ts +20 -8
- package/src/session/UserSessionProvider.handoff.test.tsx +103 -0
- package/src/session/UserSessionProvider.iframe.test.tsx +178 -0
- package/src/session/UserSessionProvider.state-mismatch.test.tsx +43 -0
- package/src/session/UserSessionProvider.tsx +35 -23
- package/src/session/auth/auth-flow.md +18 -5
- package/src/session/auth/authRoundTrip.ts +24 -0
- package/src/session/auth/composable.test.ts +23 -1
- package/src/session/auth/composable.ts +10 -8
- package/src/session/auth/domainRouting.ts +4 -25
- package/src/session/auth/oauth.test.ts +121 -4
- package/src/session/auth/oauth.ts +172 -11
- package/src/session/auth/tokenIssuer.test.ts +25 -0
- package/src/session/auth/tokenIssuer.ts +25 -0
- package/src/session/auth/vertesiaHosts.test.ts +26 -0
- package/src/session/auth/vertesiaHosts.ts +85 -0
- package/src/session/index.ts +1 -0
- package/src/shell/AuthScreenPreview.tsx +2 -6
- package/src/shell/BrandedAuthScreens.test.tsx +26 -0
- package/src/shell/BrandedAuthScreens.tsx +4 -40
- package/src/shell/BrandedLoadingIndicator.tsx +42 -0
- package/src/shell/SplashScreen.tsx +4 -4
- package/src/shell/apps/iframe-auth.test.ts +8 -11
- package/src/shell/apps/iframe-auth.ts +10 -1
- package/src/shell/index.tsx +1 -0
- package/src/shell/login/SignInAuthPending.tsx +2 -2
- package/src/shell/login/SignInEmailStep.tsx +4 -2
- package/src/shell/login/TerminalLogin.test.tsx +111 -0
- package/src/shell/login/TerminalLogin.tsx +61 -37
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts +0 -7
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts.map +0 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +0 -51
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +0 -1
- package/lib/vertesia-ui-i18n.B3NFclwl.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.B3NFclwl.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.BWSrNIxc.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.BWSrNIxc.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.BWvWeB1p.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.BWvWeB1p.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.BZCXvTC1.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.BZCXvTC1.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.BbJMvFZW.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.BbJMvFZW.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.CE5ldwpt.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.CE5ldwpt.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.CMC0IShY.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.CMC0IShY.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.CkcRqBKU.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.CkcRqBKU.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.D-Mq1sw_.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.D-Mq1sw_.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.DkK68-A-.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.DkK68-A-.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.XE_dcCg6.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.XE_dcCg6.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.qgNqMPvK.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.qgNqMPvK.chunk.js.map +0 -1
- 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.
|
|
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.
|
|
49
|
-
"@vertesia/common": "1.6.0-dev.
|
|
50
|
-
"@vertesia/fusion-ux": "1.6.0-dev.
|
|
51
|
-
"@vertesia/json": "1.6.0-dev.
|
|
52
|
-
"@vertesia/rich-text": "1.6.0-dev.
|
|
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.
|
|
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.
|
|
70
|
-
"mermaid": "^
|
|
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.
|
|
74
|
+
"react": "19.3.0",
|
|
75
75
|
"react-calendar": "^6.0.1",
|
|
76
76
|
"react-date-picker": "^12.1.0",
|
|
77
|
-
"react-dom": "19.
|
|
77
|
+
"react-dom": "19.3.0",
|
|
78
78
|
"react-error-boundary": "^6.1.5",
|
|
79
|
-
"react-i18next": "^17.0.
|
|
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.
|
|
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.
|
|
106
|
+
"@types/node": "^24.13.5",
|
|
107
107
|
"@types/papaparse": "^5.5.2",
|
|
108
|
-
"@types/react": "19.
|
|
109
|
-
"@types/react-dom": "19.
|
|
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.
|
|
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.
|
|
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": "
|
|
191
|
+
"gitHead": "8732631fd8099bdcab4f5375da2309f3cff34e19",
|
|
192
192
|
"scripts": {
|
|
193
193
|
"clean:lib": "rimraf ./lib ./tsconfig.tsbuildinfo",
|
|
194
194
|
"clean": "rimraf ./node_modules ./lib ./tsconfig.tsbuildinfo",
|
package/src/boot/branding.ts
CHANGED
|
@@ -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 =
|
|
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="
|
|
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 !
|
|
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
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
103
|
-
{
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
130
|
-
)}
|
|
132
|
+
)}
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
</
|
|
144
|
+
</motion.div>
|
|
145
|
+
</div>
|
|
141
146
|
</div>
|
|
142
147
|
</div>
|
|
143
148
|
</div>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
</
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/src/env/index.test.ts
CHANGED
|
@@ -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
|
}
|