@data-club/ai-hub 0.0.1
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/dist/auth/passwordPolicy.d.ts +55 -0
- package/dist/auth/passwordPolicy.d.ts.map +1 -0
- package/dist/auth/passwordPolicy.js +63 -0
- package/dist/auth/passwordPolicy.js.map +1 -0
- package/dist/auth/texts.d.ts +815 -0
- package/dist/auth/texts.d.ts.map +1 -0
- package/dist/auth/texts.js +429 -0
- package/dist/auth/texts.js.map +1 -0
- package/dist/client/AIHubClient.d.ts +1058 -0
- package/dist/client/AIHubClient.d.ts.map +1 -0
- package/dist/client/AIHubClient.js +950 -0
- package/dist/client/AIHubClient.js.map +1 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +9 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/sseParser.d.ts +41 -0
- package/dist/client/sseParser.d.ts.map +1 -0
- package/dist/client/sseParser.js +153 -0
- package/dist/client/sseParser.js.map +1 -0
- package/dist/client/tokenStorage.d.ts +56 -0
- package/dist/client/tokenStorage.d.ts.map +1 -0
- package/dist/client/tokenStorage.js +116 -0
- package/dist/client/tokenStorage.js.map +1 -0
- package/dist/components/AgentsPicker/index.d.ts +70 -0
- package/dist/components/AgentsPicker/index.d.ts.map +1 -0
- package/dist/components/AgentsPicker/index.js +63 -0
- package/dist/components/AgentsPicker/index.js.map +1 -0
- package/dist/components/AuthGate/index.d.ts +79 -0
- package/dist/components/AuthGate/index.d.ts.map +1 -0
- package/dist/components/AuthGate/index.js +61 -0
- package/dist/components/AuthGate/index.js.map +1 -0
- package/dist/components/CacheBadge/index.d.ts +88 -0
- package/dist/components/CacheBadge/index.d.ts.map +1 -0
- package/dist/components/CacheBadge/index.js +127 -0
- package/dist/components/CacheBadge/index.js.map +1 -0
- package/dist/components/ChangePassword/index.d.ts +109 -0
- package/dist/components/ChangePassword/index.d.ts.map +1 -0
- package/dist/components/ChangePassword/index.js +142 -0
- package/dist/components/ChangePassword/index.js.map +1 -0
- package/dist/components/ChatShell/ShellHeader.d.ts +44 -0
- package/dist/components/ChatShell/ShellHeader.d.ts.map +1 -0
- package/dist/components/ChatShell/ShellHeader.js +14 -0
- package/dist/components/ChatShell/ShellHeader.js.map +1 -0
- package/dist/components/ChatShell/UserMenu.d.ts +48 -0
- package/dist/components/ChatShell/UserMenu.d.ts.map +1 -0
- package/dist/components/ChatShell/UserMenu.js +86 -0
- package/dist/components/ChatShell/UserMenu.js.map +1 -0
- package/dist/components/ChatShell/index.d.ts +98 -0
- package/dist/components/ChatShell/index.d.ts.map +1 -0
- package/dist/components/ChatShell/index.js +124 -0
- package/dist/components/ChatShell/index.js.map +1 -0
- package/dist/components/DataClubAIHub/index.d.ts +132 -0
- package/dist/components/DataClubAIHub/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHub/index.js +124 -0
- package/dist/components/DataClubAIHub/index.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts +89 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js +199 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts +33 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js +137 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts +50 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.js +93 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts +49 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js +94 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts +37 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js +44 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts +56 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js +158 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts +29 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js +183 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts +38 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js +294 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts +36 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js +373 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts +47 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.js +233 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/index.d.ts +87 -0
- package/dist/components/DataClubAIHubAdmin/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/index.js +137 -0
- package/dist/components/DataClubAIHubAdmin/index.js.map +1 -0
- package/dist/components/DataClubAIHubLogin/index.d.ts +113 -0
- package/dist/components/DataClubAIHubLogin/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHubLogin/index.js +123 -0
- package/dist/components/DataClubAIHubLogin/index.js.map +1 -0
- package/dist/components/EffortControl/index.d.ts +57 -0
- package/dist/components/EffortControl/index.d.ts.map +1 -0
- package/dist/components/EffortControl/index.js +84 -0
- package/dist/components/EffortControl/index.js.map +1 -0
- package/dist/components/Layout/index.d.ts +63 -0
- package/dist/components/Layout/index.d.ts.map +1 -0
- package/dist/components/Layout/index.js +45 -0
- package/dist/components/Layout/index.js.map +1 -0
- package/dist/components/MainColumn/index.d.ts +69 -0
- package/dist/components/MainColumn/index.d.ts.map +1 -0
- package/dist/components/MainColumn/index.js +47 -0
- package/dist/components/MainColumn/index.js.map +1 -0
- package/dist/components/MarkdownRenderer/index.d.ts +66 -0
- package/dist/components/MarkdownRenderer/index.d.ts.map +1 -0
- package/dist/components/MarkdownRenderer/index.js +153 -0
- package/dist/components/MarkdownRenderer/index.js.map +1 -0
- package/dist/components/MessageInput/index.d.ts +85 -0
- package/dist/components/MessageInput/index.d.ts.map +1 -0
- package/dist/components/MessageInput/index.js +139 -0
- package/dist/components/MessageInput/index.js.map +1 -0
- package/dist/components/MessageList/index.d.ts +156 -0
- package/dist/components/MessageList/index.d.ts.map +1 -0
- package/dist/components/MessageList/index.js +352 -0
- package/dist/components/MessageList/index.js.map +1 -0
- package/dist/components/ReasoningSelector/index.d.ts +77 -0
- package/dist/components/ReasoningSelector/index.d.ts.map +1 -0
- package/dist/components/ReasoningSelector/index.js +156 -0
- package/dist/components/ReasoningSelector/index.js.map +1 -0
- package/dist/components/ReasoningView/index.d.ts +94 -0
- package/dist/components/ReasoningView/index.d.ts.map +1 -0
- package/dist/components/ReasoningView/index.js +83 -0
- package/dist/components/ReasoningView/index.js.map +1 -0
- package/dist/components/ScrollToPresentButton/index.d.ts +54 -0
- package/dist/components/ScrollToPresentButton/index.d.ts.map +1 -0
- package/dist/components/ScrollToPresentButton/index.js +44 -0
- package/dist/components/ScrollToPresentButton/index.js.map +1 -0
- package/dist/components/SessionsPanel/SessionTitleModal.d.ts +79 -0
- package/dist/components/SessionsPanel/SessionTitleModal.d.ts.map +1 -0
- package/dist/components/SessionsPanel/SessionTitleModal.js +102 -0
- package/dist/components/SessionsPanel/SessionTitleModal.js.map +1 -0
- package/dist/components/SessionsPanel/index.d.ts +100 -0
- package/dist/components/SessionsPanel/index.d.ts.map +1 -0
- package/dist/components/SessionsPanel/index.js +176 -0
- package/dist/components/SessionsPanel/index.js.map +1 -0
- package/dist/components/SystemNotice/index.d.ts +66 -0
- package/dist/components/SystemNotice/index.d.ts.map +1 -0
- package/dist/components/SystemNotice/index.js +81 -0
- package/dist/components/SystemNotice/index.js.map +1 -0
- package/dist/components/ThemeSwitcher/index.d.ts +66 -0
- package/dist/components/ThemeSwitcher/index.d.ts.map +1 -0
- package/dist/components/ThemeSwitcher/index.js +73 -0
- package/dist/components/ThemeSwitcher/index.js.map +1 -0
- package/dist/components/ToolCallView/formatContent.d.ts +36 -0
- package/dist/components/ToolCallView/formatContent.d.ts.map +1 -0
- package/dist/components/ToolCallView/formatContent.js +53 -0
- package/dist/components/ToolCallView/formatContent.js.map +1 -0
- package/dist/components/ToolCallView/index.d.ts +120 -0
- package/dist/components/ToolCallView/index.d.ts.map +1 -0
- package/dist/components/ToolCallView/index.js +104 -0
- package/dist/components/ToolCallView/index.js.map +1 -0
- package/dist/components/TpmBanner/index.d.ts +53 -0
- package/dist/components/TpmBanner/index.d.ts.map +1 -0
- package/dist/components/TpmBanner/index.js +116 -0
- package/dist/components/TpmBanner/index.js.map +1 -0
- package/dist/components/TurnStatusIndicator/index.d.ts +77 -0
- package/dist/components/TurnStatusIndicator/index.d.ts.map +1 -0
- package/dist/components/TurnStatusIndicator/index.js +122 -0
- package/dist/components/TurnStatusIndicator/index.js.map +1 -0
- package/dist/components/internal/PasswordInput.d.ts +62 -0
- package/dist/components/internal/PasswordInput.d.ts.map +1 -0
- package/dist/components/internal/PasswordInput.js +42 -0
- package/dist/components/internal/PasswordInput.js.map +1 -0
- package/dist/context/AIHubContext.d.ts +220 -0
- package/dist/context/AIHubContext.d.ts.map +1 -0
- package/dist/context/AIHubContext.js +19 -0
- package/dist/context/AIHubContext.js.map +1 -0
- package/dist/context/AIHubProvider.d.ts +75 -0
- package/dist/context/AIHubProvider.d.ts.map +1 -0
- package/dist/context/AIHubProvider.js +954 -0
- package/dist/context/AIHubProvider.js.map +1 -0
- package/dist/context/index.d.ts +9 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +9 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/theme.d.ts +43 -0
- package/dist/context/theme.d.ts.map +1 -0
- package/dist/context/theme.js +70 -0
- package/dist/context/theme.js.map +1 -0
- package/dist/context/useAIHub.d.ts +104 -0
- package/dist/context/useAIHub.d.ts.map +1 -0
- package/dist/context/useAIHub.js +63 -0
- package/dist/context/useAIHub.js.map +1 -0
- package/dist/context/useAIHubAuth.d.ts +44 -0
- package/dist/context/useAIHubAuth.d.ts.map +1 -0
- package/dist/context/useAIHubAuth.js +35 -0
- package/dist/context/useAIHubAuth.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/primitives/index.d.ts +99 -0
- package/dist/primitives/index.d.ts.map +1 -0
- package/dist/primitives/index.js +97 -0
- package/dist/primitives/index.js.map +1 -0
- package/dist/state/affordances.d.ts +182 -0
- package/dist/state/affordances.d.ts.map +1 -0
- package/dist/state/affordances.js +189 -0
- package/dist/state/affordances.js.map +1 -0
- package/dist/state/stateReducer.d.ts +72 -0
- package/dist/state/stateReducer.d.ts.map +1 -0
- package/dist/state/stateReducer.js +122 -0
- package/dist/state/stateReducer.js.map +1 -0
- package/dist/stories/EffortControl.stories.d.ts +14 -0
- package/dist/stories/EffortControl.stories.d.ts.map +1 -0
- package/dist/stories/EffortControl.stories.js +43 -0
- package/dist/stories/EffortControl.stories.js.map +1 -0
- package/dist/stories/MarkdownRenderer.stories.d.ts +25 -0
- package/dist/stories/MarkdownRenderer.stories.d.ts.map +1 -0
- package/dist/stories/MarkdownRenderer.stories.js +178 -0
- package/dist/stories/MarkdownRenderer.stories.js.map +1 -0
- package/dist/stories/MessageBubble.stories.d.ts +21 -0
- package/dist/stories/MessageBubble.stories.d.ts.map +1 -0
- package/dist/stories/MessageBubble.stories.js +59 -0
- package/dist/stories/MessageBubble.stories.js.map +1 -0
- package/dist/stories/ReasoningView.stories.d.ts +18 -0
- package/dist/stories/ReasoningView.stories.d.ts.map +1 -0
- package/dist/stories/ReasoningView.stories.js +62 -0
- package/dist/stories/ReasoningView.stories.js.map +1 -0
- package/dist/stories/ScrollToPresentButton.stories.d.ts +18 -0
- package/dist/stories/ScrollToPresentButton.stories.d.ts.map +1 -0
- package/dist/stories/ScrollToPresentButton.stories.js +34 -0
- package/dist/stories/ScrollToPresentButton.stories.js.map +1 -0
- package/dist/stories/SystemNotice.stories.d.ts +17 -0
- package/dist/stories/SystemNotice.stories.d.ts.map +1 -0
- package/dist/stories/SystemNotice.stories.js +42 -0
- package/dist/stories/SystemNotice.stories.js.map +1 -0
- package/dist/stories/ThemeSwitcher.stories.d.ts +15 -0
- package/dist/stories/ThemeSwitcher.stories.d.ts.map +1 -0
- package/dist/stories/ThemeSwitcher.stories.js +35 -0
- package/dist/stories/ThemeSwitcher.stories.js.map +1 -0
- package/dist/stories/ToolCallView.stories.d.ts +20 -0
- package/dist/stories/ToolCallView.stories.d.ts.map +1 -0
- package/dist/stories/ToolCallView.stories.js +65 -0
- package/dist/stories/ToolCallView.stories.js.map +1 -0
- package/dist/stories/TpmBanner.stories.d.ts +24 -0
- package/dist/stories/TpmBanner.stories.d.ts.map +1 -0
- package/dist/stories/TpmBanner.stories.js +63 -0
- package/dist/stories/TpmBanner.stories.js.map +1 -0
- package/dist/stories/TurnStatusIndicator.stories.d.ts +25 -0
- package/dist/stories/TurnStatusIndicator.stories.d.ts.map +1 -0
- package/dist/stories/TurnStatusIndicator.stories.js +56 -0
- package/dist/stories/TurnStatusIndicator.stories.js.map +1 -0
- package/dist/stories/_mockProvider.d.ts +44 -0
- package/dist/stories/_mockProvider.d.ts.map +1 -0
- package/dist/stories/_mockProvider.js +84 -0
- package/dist/stories/_mockProvider.js.map +1 -0
- package/dist/types.d.ts +787 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/webcomponent/defineWebComponent.d.ts +106 -0
- package/dist/webcomponent/defineWebComponent.d.ts.map +1 -0
- package/dist/webcomponent/defineWebComponent.js +126 -0
- package/dist/webcomponent/defineWebComponent.js.map +1 -0
- package/dist/webcomponent/index.d.ts +7 -0
- package/dist/webcomponent/index.d.ts.map +1 -0
- package/dist/webcomponent/index.js +7 -0
- package/dist/webcomponent/index.js.map +1 -0
- package/package.json +63 -0
- package/src/__lint-canary__/step1-lint-canary.ts.disabled +28 -0
- package/src/auth/passwordPolicy.ts +75 -0
- package/src/auth/texts.ts +1294 -0
- package/src/client/AIHubClient.ts +1718 -0
- package/src/client/index.ts +30 -0
- package/src/client/sseParser.ts +168 -0
- package/src/client/tokenStorage.ts +131 -0
- package/src/components/AgentsPicker/index.tsx +147 -0
- package/src/components/AuthGate/index.tsx +132 -0
- package/src/components/CacheBadge/index.tsx +245 -0
- package/src/components/ChangePassword/index.tsx +375 -0
- package/src/components/ChatShell/ShellHeader.tsx +80 -0
- package/src/components/ChatShell/UserMenu.tsx +196 -0
- package/src/components/ChatShell/index.tsx +307 -0
- package/src/components/DataClubAIHub/index.tsx +300 -0
- package/src/components/DataClubAIHubAdmin/AgentEditorModal.tsx +505 -0
- package/src/components/DataClubAIHubAdmin/AgentsPage.tsx +342 -0
- package/src/components/DataClubAIHubAdmin/CreateUserModal.tsx +214 -0
- package/src/components/DataClubAIHubAdmin/PasswordRevealModal.tsx +204 -0
- package/src/components/DataClubAIHubAdmin/ResetPasswordModal.tsx +122 -0
- package/src/components/DataClubAIHubAdmin/RotatingMessageEditorModal.tsx +408 -0
- package/src/components/DataClubAIHubAdmin/RotatingMessagesPage.tsx +373 -0
- package/src/components/DataClubAIHubAdmin/SessionsOverviewPage.tsx +639 -0
- package/src/components/DataClubAIHubAdmin/SettingsPage.tsx +979 -0
- package/src/components/DataClubAIHubAdmin/UsersPage.tsx +495 -0
- package/src/components/DataClubAIHubAdmin/index.tsx +277 -0
- package/src/components/DataClubAIHubLogin/index.tsx +282 -0
- package/src/components/EffortControl/index.tsx +138 -0
- package/src/components/Layout/index.tsx +92 -0
- package/src/components/MainColumn/index.tsx +131 -0
- package/src/components/MarkdownRenderer/index.tsx +256 -0
- package/src/components/MessageInput/index.tsx +283 -0
- package/src/components/MessageList/index.tsx +753 -0
- package/src/components/ReasoningSelector/index.tsx +268 -0
- package/src/components/ReasoningView/index.tsx +183 -0
- package/src/components/ScrollToPresentButton/index.tsx +85 -0
- package/src/components/SessionsPanel/SessionTitleModal.tsx +256 -0
- package/src/components/SessionsPanel/index.tsx +406 -0
- package/src/components/SystemNotice/index.tsx +151 -0
- package/src/components/ThemeSwitcher/index.tsx +136 -0
- package/src/components/ToolCallView/formatContent.ts +50 -0
- package/src/components/ToolCallView/index.tsx +274 -0
- package/src/components/TpmBanner/index.tsx +172 -0
- package/src/components/TurnStatusIndicator/index.tsx +203 -0
- package/src/components/internal/PasswordInput.tsx +145 -0
- package/src/context/AIHubContext.ts +249 -0
- package/src/context/AIHubProvider.tsx +1084 -0
- package/src/context/index.ts +9 -0
- package/src/context/theme.ts +76 -0
- package/src/context/useAIHub.ts +151 -0
- package/src/context/useAIHubAuth.ts +65 -0
- package/src/index.ts +275 -0
- package/src/primitives/index.ts +101 -0
- package/src/state/affordances.ts +321 -0
- package/src/state/stateReducer.ts +131 -0
- package/src/stories/EffortControl.stories.tsx +63 -0
- package/src/stories/MarkdownRenderer.stories.tsx +201 -0
- package/src/stories/MessageBubble.stories.tsx +101 -0
- package/src/stories/ReasoningView.stories.tsx +81 -0
- package/src/stories/ScrollToPresentButton.stories.tsx +52 -0
- package/src/stories/SystemNotice.stories.tsx +59 -0
- package/src/stories/ThemeSwitcher.stories.tsx +55 -0
- package/src/stories/ToolCallView.stories.tsx +95 -0
- package/src/stories/TpmBanner.stories.tsx +93 -0
- package/src/stories/TurnStatusIndicator.stories.tsx +86 -0
- package/src/stories/_mockProvider.tsx +116 -0
- package/src/styles/CLASSES.md +356 -0
- package/src/styles/default.css +2937 -0
- package/src/types.ts +867 -0
- package/src/webcomponent/defineWebComponent.tsx +193 -0
- package/src/webcomponent/index.ts +11 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `AIHub.AuthGate` — the auth-state router.
|
|
3
|
+
*
|
|
4
|
+
* Reads `useAIHubAuth()` and renders:
|
|
5
|
+
*
|
|
6
|
+
* - `<DataClubAIHubLogin />` when no user is present.
|
|
7
|
+
* - `<DataClubAIHubChangePassword variant="forced" />` when a user is
|
|
8
|
+
* present but `mustChangePassword === true`.
|
|
9
|
+
* - `children` (the chat / admin / whatever) otherwise.
|
|
10
|
+
*
|
|
11
|
+
* While the provider is still hydrating from a stored token (`hydrating ===
|
|
12
|
+
* true`) the gate renders nothing — preventing a one-frame flash of the
|
|
13
|
+
* login card before the cached session resolves.
|
|
14
|
+
*
|
|
15
|
+
* Tier 3 wrappers compose this primitive as the second layer (after
|
|
16
|
+
* `AIHub.Provider`) so the auth UX is handled automatically; Tier 1 / 2
|
|
17
|
+
* wrappers get the same behavior for free via `<DataClubAIHub />`.
|
|
18
|
+
*
|
|
19
|
+
* @see SPECIFICATIONS.md §7.1 — exports
|
|
20
|
+
* @see SPECIFICATIONS.md §7.2.1 — auth-owned-by-library policy
|
|
21
|
+
* @see SPECIFICATIONS.md §7.9 — Tier-3 composition example
|
|
22
|
+
* @see ui-sketches/01-login.md
|
|
23
|
+
* @see ui-sketches/02-change-password.md
|
|
24
|
+
*/
|
|
25
|
+
import { type ReactNode } from "react";
|
|
26
|
+
import type { AIHubTexts, DeepPartial } from "../../auth/texts.js";
|
|
27
|
+
import { type AIHubLoginClassNames } from "../DataClubAIHubLogin/index.js";
|
|
28
|
+
import { type AIHubChangePasswordClassNames } from "../ChangePassword/index.js";
|
|
29
|
+
/**
|
|
30
|
+
* Props for `AIHub.AuthGate`. The branding / i18n props mirror
|
|
31
|
+
* `<DataClubAIHub />` — Tier 3 wrappers pass them through here so the
|
|
32
|
+
* auto-rendered Login + ChangePassword screens get branded without the
|
|
33
|
+
* wrapper having to render those screens manually.
|
|
34
|
+
*/
|
|
35
|
+
export interface AuthGateProps {
|
|
36
|
+
/**
|
|
37
|
+
* Rendered when the user is authenticated and is not in the forced
|
|
38
|
+
* change-password state. The wrapper is responsible for the actual
|
|
39
|
+
* chat / admin tree.
|
|
40
|
+
*/
|
|
41
|
+
children: ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* Title text on the login card. Forwarded to `<DataClubAIHubLogin />`.
|
|
44
|
+
* Defaults to `"ai-hub"`.
|
|
45
|
+
*/
|
|
46
|
+
productName?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Placeholder shown inside the username input on the login card.
|
|
49
|
+
* Forwarded to `<DataClubAIHubLogin />`. Defaults to empty.
|
|
50
|
+
*/
|
|
51
|
+
usernamePlaceholder?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Wrapper-supplied i18n applied to both auto-rendered auth screens.
|
|
54
|
+
*/
|
|
55
|
+
texts?: DeepPartial<AIHubTexts>;
|
|
56
|
+
/** Per-element class overrides forwarded to `<DataClubAIHubLogin />`. */
|
|
57
|
+
loginClassNames?: Partial<AIHubLoginClassNames>;
|
|
58
|
+
/**
|
|
59
|
+
* Per-element class overrides forwarded to
|
|
60
|
+
* `<DataClubAIHubChangePassword />`.
|
|
61
|
+
*/
|
|
62
|
+
changePasswordClassNames?: Partial<AIHubChangePasswordClassNames>;
|
|
63
|
+
/**
|
|
64
|
+
* Optional `className` forwarded onto the login root.
|
|
65
|
+
*/
|
|
66
|
+
loginClassName?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Optional `className` forwarded onto the change-password root.
|
|
69
|
+
*/
|
|
70
|
+
changePasswordClassName?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Renders the right screen for the current auth state.
|
|
74
|
+
*
|
|
75
|
+
* @param props - see `AuthGateProps`.
|
|
76
|
+
* @returns the rendered subtree.
|
|
77
|
+
*/
|
|
78
|
+
export declare function AuthGate(props: AuthGateProps): ReactNode;
|
|
79
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/AuthGate/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,4BAA4B,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;OAEG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAChC,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAChD;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;IAClE;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CA2CxD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* `AIHub.AuthGate` — the auth-state router.
|
|
4
|
+
*
|
|
5
|
+
* Reads `useAIHubAuth()` and renders:
|
|
6
|
+
*
|
|
7
|
+
* - `<DataClubAIHubLogin />` when no user is present.
|
|
8
|
+
* - `<DataClubAIHubChangePassword variant="forced" />` when a user is
|
|
9
|
+
* present but `mustChangePassword === true`.
|
|
10
|
+
* - `children` (the chat / admin / whatever) otherwise.
|
|
11
|
+
*
|
|
12
|
+
* While the provider is still hydrating from a stored token (`hydrating ===
|
|
13
|
+
* true`) the gate renders nothing — preventing a one-frame flash of the
|
|
14
|
+
* login card before the cached session resolves.
|
|
15
|
+
*
|
|
16
|
+
* Tier 3 wrappers compose this primitive as the second layer (after
|
|
17
|
+
* `AIHub.Provider`) so the auth UX is handled automatically; Tier 1 / 2
|
|
18
|
+
* wrappers get the same behavior for free via `<DataClubAIHub />`.
|
|
19
|
+
*
|
|
20
|
+
* @see SPECIFICATIONS.md §7.1 — exports
|
|
21
|
+
* @see SPECIFICATIONS.md §7.2.1 — auth-owned-by-library policy
|
|
22
|
+
* @see SPECIFICATIONS.md §7.9 — Tier-3 composition example
|
|
23
|
+
* @see ui-sketches/01-login.md
|
|
24
|
+
* @see ui-sketches/02-change-password.md
|
|
25
|
+
*/
|
|
26
|
+
import {} from "react";
|
|
27
|
+
import { useAIHubAuth } from "../../context/useAIHubAuth.js";
|
|
28
|
+
import { DataClubAIHubLogin, } from "../DataClubAIHubLogin/index.js";
|
|
29
|
+
import { DataClubAIHubChangePassword, } from "../ChangePassword/index.js";
|
|
30
|
+
/**
|
|
31
|
+
* Renders the right screen for the current auth state.
|
|
32
|
+
*
|
|
33
|
+
* @param props - see `AuthGateProps`.
|
|
34
|
+
* @returns the rendered subtree.
|
|
35
|
+
*/
|
|
36
|
+
export function AuthGate(props) {
|
|
37
|
+
const auth = useAIHubAuth();
|
|
38
|
+
if (auth.hydrating) {
|
|
39
|
+
// Render nothing — we don't know yet whether the stored token is valid.
|
|
40
|
+
// FOUC-of-login-card avoidance.
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (!auth.user) {
|
|
44
|
+
return (_jsx(DataClubAIHubLogin, { ...(props.productName !== undefined ? { productName: props.productName } : {}), ...(props.usernamePlaceholder !== undefined
|
|
45
|
+
? { usernamePlaceholder: props.usernamePlaceholder }
|
|
46
|
+
: {}), ...(props.texts !== undefined ? { texts: props.texts } : {}), ...(props.loginClassNames !== undefined
|
|
47
|
+
? { classNames: props.loginClassNames }
|
|
48
|
+
: {}), ...(props.loginClassName !== undefined
|
|
49
|
+
? { className: props.loginClassName }
|
|
50
|
+
: {}) }));
|
|
51
|
+
}
|
|
52
|
+
if (auth.mustChangePassword) {
|
|
53
|
+
return (_jsx(DataClubAIHubChangePassword, { variant: "forced", ...(props.texts !== undefined ? { texts: props.texts } : {}), ...(props.changePasswordClassNames !== undefined
|
|
54
|
+
? { classNames: props.changePasswordClassNames }
|
|
55
|
+
: {}), ...(props.changePasswordClassName !== undefined
|
|
56
|
+
? { className: props.changePasswordClassName }
|
|
57
|
+
: {}) }));
|
|
58
|
+
}
|
|
59
|
+
return _jsx(_Fragment, { children: props.children });
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/AuthGate/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EACL,kBAAkB,GAEnB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,2BAA2B,GAE5B,MAAM,4BAA4B,CAAC;AA8CpC;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAE5B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,wEAAwE;QACxE,gCAAgC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,CACL,KAAC,kBAAkB,OACb,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAC3E,CAAC,KAAK,CAAC,mBAAmB,KAAK,SAAS;gBAC1C,CAAC,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,mBAAmB,EAAE;gBACpD,CAAC,CAAC,EAAE,CAAC,KACH,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACzD,CAAC,KAAK,CAAC,eAAe,KAAK,SAAS;gBACtC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,eAAe,EAAE;gBACvC,CAAC,CAAC,EAAE,CAAC,KACH,CAAC,KAAK,CAAC,cAAc,KAAK,SAAS;gBACrC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,cAAc,EAAE;gBACrC,CAAC,CAAC,EAAE,CAAC,GACP,CACH,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,OAAO,CACL,KAAC,2BAA2B,IAC1B,OAAO,EAAC,QAAQ,KACZ,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACzD,CAAC,KAAK,CAAC,wBAAwB,KAAK,SAAS;gBAC/C,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,wBAAwB,EAAE;gBAChD,CAAC,CAAC,EAAE,CAAC,KACH,CAAC,KAAK,CAAC,uBAAuB,KAAK,SAAS;gBAC9C,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,uBAAuB,EAAE;gBAC9C,CAAC,CAAC,EAAE,CAAC,GACP,CACH,CAAC;IACJ,CAAC;IAED,OAAO,4BAAG,KAAK,CAAC,QAAQ,GAAI,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<CacheBadge />` — small per-message badge surfacing provider token
|
|
3
|
+
* usage on an assistant bubble. Renders an inline icon + a compact
|
|
4
|
+
* token count, with the full breakdown in a `title`-attribute tooltip
|
|
5
|
+
* on hover.
|
|
6
|
+
*
|
|
7
|
+
* **What it shows.** Letta reports five things per assistant turn (via
|
|
8
|
+
* `AIHubMessageUsage`): input tokens, output tokens, cache-read tokens
|
|
9
|
+
* (prompt-cache HIT — provider charged ~0.1x), cache-write tokens
|
|
10
|
+
* (provider charged ~1.25x, Anthropic only), reasoning tokens, and a
|
|
11
|
+
* rolling context-window estimate.
|
|
12
|
+
*
|
|
13
|
+
* The headline number we show in the badge is the **cache-hit
|
|
14
|
+
* percentage of the prompt** — `cache_read / (input + cache_read +
|
|
15
|
+
* cache_write)`. That number is the single most useful at-a-glance
|
|
16
|
+
* signal:
|
|
17
|
+
* - 0% → cache is cold (cost is the worst case)
|
|
18
|
+
* - 80% → ~10x cheaper system prompt than a cold turn
|
|
19
|
+
*
|
|
20
|
+
* The full numbers live in the tooltip. We use the native `title`
|
|
21
|
+
* attribute instead of a custom popover to keep the component
|
|
22
|
+
* lightweight and dependency-free (no portals, no animation, no
|
|
23
|
+
* focus management — perfect for a peripheral info chip).
|
|
24
|
+
*
|
|
25
|
+
* **When to render.** Only when the parent passes a `usage` prop with
|
|
26
|
+
* at least one non-null number. Messages without a usage stamp (e.g.
|
|
27
|
+
* historical conversations from before this feature shipped, or
|
|
28
|
+
* tool-only assistant turns where Letta didn't report anything)
|
|
29
|
+
* simply don't render the badge.
|
|
30
|
+
*
|
|
31
|
+
* Exported under `AIHub.CacheBadge` (re-exported via the namespace).
|
|
32
|
+
*
|
|
33
|
+
* @see AIHubMessageUsage in `../../types.ts`
|
|
34
|
+
* @see SPECIFICATIONS.md §9.4 — `usage` SSE event
|
|
35
|
+
*/
|
|
36
|
+
import type { ReactNode } from "react";
|
|
37
|
+
import type { AIHubMessageUsage } from "../../types.js";
|
|
38
|
+
/** BEM class names emitted by `<CacheBadge />`. */
|
|
39
|
+
export interface CacheBadgeClassNames {
|
|
40
|
+
/** Outer span; `data-cache-state="hot|warm|cold|none"` for skinning. */
|
|
41
|
+
root: string;
|
|
42
|
+
/** The leading icon character (default 💾). */
|
|
43
|
+
icon: string;
|
|
44
|
+
/** The headline percentage label. */
|
|
45
|
+
label: string;
|
|
46
|
+
}
|
|
47
|
+
/** Localizable strings for the badge headline + tooltip. */
|
|
48
|
+
export interface CacheBadgeTexts {
|
|
49
|
+
/** Headline shown when there's no cache info at all (e.g. providers that don't report). */
|
|
50
|
+
noCacheLabel: string;
|
|
51
|
+
/** Headline shown when this turn was a full cache miss (no tokens served from cache). */
|
|
52
|
+
missLabel: string;
|
|
53
|
+
/** Headline shown when this turn was a cache priming write — read=0 but write>0. */
|
|
54
|
+
writePrimingLabel: string;
|
|
55
|
+
/** Headline prefix for cache hits — full text is `${hitLabel} ${N}%`. */
|
|
56
|
+
hitLabel: string;
|
|
57
|
+
/** Tooltip line prefixes. Keep short — the tooltip is `title` only. */
|
|
58
|
+
hitTooltipLabel: string;
|
|
59
|
+
writeTooltipLabel: string;
|
|
60
|
+
inputLabel: string;
|
|
61
|
+
outputLabel: string;
|
|
62
|
+
reasoningLabel: string;
|
|
63
|
+
contextLabel: string;
|
|
64
|
+
totalLabel: string;
|
|
65
|
+
}
|
|
66
|
+
export declare const DEFAULT_TEXTS: CacheBadgeTexts;
|
|
67
|
+
/** Props for `<CacheBadge />`. */
|
|
68
|
+
export interface CacheBadgeProps {
|
|
69
|
+
/** Per-message usage, as returned by `GET /sessions/:id/messages`. */
|
|
70
|
+
usage: AIHubMessageUsage;
|
|
71
|
+
/** Optional class-name overrides. */
|
|
72
|
+
classNames?: Partial<CacheBadgeClassNames>;
|
|
73
|
+
/** Optional text overrides (l10n). */
|
|
74
|
+
texts?: Partial<CacheBadgeTexts>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Computes the cache-hit ratio (0..1) over the prompt-side tokens.
|
|
78
|
+
*
|
|
79
|
+
* Denominator: `input + cache_read + cache_write` — every prompt
|
|
80
|
+
* token the provider saw, regardless of whether it served from cache
|
|
81
|
+
* or wrote new. We exclude `output` and `reasoning` because those
|
|
82
|
+
* are completion-side and can't benefit from prompt caching.
|
|
83
|
+
*
|
|
84
|
+
* Exported so the test suite can assert numerically.
|
|
85
|
+
*/
|
|
86
|
+
export declare function computeCacheHitRatio(usage: AIHubMessageUsage): number | null;
|
|
87
|
+
export declare function CacheBadge({ usage, classNames, texts, }: CacheBadgeProps): ReactNode;
|
|
88
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CacheBadge/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,mDAAmD;AACnD,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;CACf;AAQD,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,YAAY,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,aAAa,EAAE,eAYP,CAAC;AAEtB,kCAAkC;AAClC,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,KAAK,EAAE,iBAAiB,CAAC;IACzB,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC3C,sCAAsC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAO5E;AAsFD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,UAAU,EACV,KAAK,GACN,EAAE,eAAe,GAAG,SAAS,CAqC7B"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const DEFAULT_CLASS_NAMES = Object.freeze({
|
|
3
|
+
root: "data-club-ai-hub__cache-badge",
|
|
4
|
+
icon: "data-club-ai-hub__cache-badge__icon",
|
|
5
|
+
label: "data-club-ai-hub__cache-badge__label",
|
|
6
|
+
});
|
|
7
|
+
export const DEFAULT_TEXTS = Object.freeze({
|
|
8
|
+
noCacheLabel: "no data",
|
|
9
|
+
missLabel: "MISS",
|
|
10
|
+
writePrimingLabel: "PRIMING",
|
|
11
|
+
hitLabel: "HIT",
|
|
12
|
+
hitTooltipLabel: "Cache hit",
|
|
13
|
+
writeTooltipLabel: "Cache write",
|
|
14
|
+
inputLabel: "Input",
|
|
15
|
+
outputLabel: "Output",
|
|
16
|
+
reasoningLabel: "Reasoning",
|
|
17
|
+
contextLabel: "Context",
|
|
18
|
+
totalLabel: "Total",
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Computes the cache-hit ratio (0..1) over the prompt-side tokens.
|
|
22
|
+
*
|
|
23
|
+
* Denominator: `input + cache_read + cache_write` — every prompt
|
|
24
|
+
* token the provider saw, regardless of whether it served from cache
|
|
25
|
+
* or wrote new. We exclude `output` and `reasoning` because those
|
|
26
|
+
* are completion-side and can't benefit from prompt caching.
|
|
27
|
+
*
|
|
28
|
+
* Exported so the test suite can assert numerically.
|
|
29
|
+
*/
|
|
30
|
+
export function computeCacheHitRatio(usage) {
|
|
31
|
+
const read = usage.cache_read ?? 0;
|
|
32
|
+
const write = usage.cache_write ?? 0;
|
|
33
|
+
const input = usage.input ?? 0;
|
|
34
|
+
const denom = read + write + input;
|
|
35
|
+
if (denom === 0)
|
|
36
|
+
return null;
|
|
37
|
+
return read / denom;
|
|
38
|
+
}
|
|
39
|
+
function bucketState(usage, ratio) {
|
|
40
|
+
if (ratio === null)
|
|
41
|
+
return "none";
|
|
42
|
+
if (ratio === 0) {
|
|
43
|
+
// No cache hit. Distinguish full-miss (nothing cached anywhere)
|
|
44
|
+
// from priming (we WROTE to the cache this turn — next turn will
|
|
45
|
+
// benefit). The latter is good news, the former is bad news, so
|
|
46
|
+
// the user-facing labels should differ.
|
|
47
|
+
return (usage.cache_write ?? 0) > 0 ? "priming" : "miss";
|
|
48
|
+
}
|
|
49
|
+
if (ratio >= 0.6)
|
|
50
|
+
return "hot";
|
|
51
|
+
if (ratio >= 0.1)
|
|
52
|
+
return "warm";
|
|
53
|
+
return "cold";
|
|
54
|
+
}
|
|
55
|
+
/** Formats a token count as a compact human-readable string: 1234 → 1.2k. */
|
|
56
|
+
function formatTokens(n) {
|
|
57
|
+
if (n === null || n === undefined)
|
|
58
|
+
return "—";
|
|
59
|
+
if (n < 1000)
|
|
60
|
+
return String(n);
|
|
61
|
+
if (n < 1_000_000)
|
|
62
|
+
return `${(n / 1000).toFixed(1)}k`;
|
|
63
|
+
return `${(n / 1_000_000).toFixed(2)}M`;
|
|
64
|
+
}
|
|
65
|
+
/** Builds the `title`-attribute tooltip text from the usage object. */
|
|
66
|
+
function buildTooltip(usage, texts) {
|
|
67
|
+
const lines = [];
|
|
68
|
+
if (usage.cache_read !== null && usage.cache_read !== undefined) {
|
|
69
|
+
lines.push(`${texts.hitTooltipLabel}: ${formatTokens(usage.cache_read)}`);
|
|
70
|
+
}
|
|
71
|
+
if (usage.cache_write !== null && usage.cache_write !== undefined) {
|
|
72
|
+
lines.push(`${texts.writeTooltipLabel}: ${formatTokens(usage.cache_write)}`);
|
|
73
|
+
}
|
|
74
|
+
if (usage.input !== null && usage.input !== undefined) {
|
|
75
|
+
lines.push(`${texts.inputLabel}: ${formatTokens(usage.input)}`);
|
|
76
|
+
}
|
|
77
|
+
if (usage.output !== null && usage.output !== undefined) {
|
|
78
|
+
lines.push(`${texts.outputLabel}: ${formatTokens(usage.output)}`);
|
|
79
|
+
}
|
|
80
|
+
if (usage.reasoning !== null && usage.reasoning !== undefined && usage.reasoning > 0) {
|
|
81
|
+
lines.push(`${texts.reasoningLabel}: ${formatTokens(usage.reasoning)}`);
|
|
82
|
+
}
|
|
83
|
+
if (usage.context !== null && usage.context !== undefined) {
|
|
84
|
+
lines.push(`${texts.contextLabel}: ${formatTokens(usage.context)}`);
|
|
85
|
+
}
|
|
86
|
+
if (usage.total !== null && usage.total !== undefined) {
|
|
87
|
+
lines.push(`${texts.totalLabel}: ${formatTokens(usage.total)}`);
|
|
88
|
+
}
|
|
89
|
+
return lines.join("\n");
|
|
90
|
+
}
|
|
91
|
+
/** Returns true when the usage object has at least one usable numeric field. */
|
|
92
|
+
function hasAnyData(usage) {
|
|
93
|
+
return ((usage.input ?? null) !== null ||
|
|
94
|
+
(usage.output ?? null) !== null ||
|
|
95
|
+
(usage.cache_read ?? null) !== null ||
|
|
96
|
+
(usage.cache_write ?? null) !== null ||
|
|
97
|
+
(usage.reasoning ?? null) !== null ||
|
|
98
|
+
(usage.context ?? null) !== null ||
|
|
99
|
+
(usage.total ?? null) !== null);
|
|
100
|
+
}
|
|
101
|
+
export function CacheBadge({ usage, classNames, texts, }) {
|
|
102
|
+
if (!hasAnyData(usage))
|
|
103
|
+
return null;
|
|
104
|
+
const cls = { ...DEFAULT_CLASS_NAMES, ...(classNames ?? {}) };
|
|
105
|
+
const t = { ...DEFAULT_TEXTS, ...(texts ?? {}) };
|
|
106
|
+
const ratio = computeCacheHitRatio(usage);
|
|
107
|
+
const state = bucketState(usage, ratio);
|
|
108
|
+
// Headline label — varies by state so the user gets an immediate
|
|
109
|
+
// diagnostic signal at-a-glance.
|
|
110
|
+
let headline;
|
|
111
|
+
if (state === "none") {
|
|
112
|
+
headline = t.noCacheLabel;
|
|
113
|
+
}
|
|
114
|
+
else if (state === "miss") {
|
|
115
|
+
headline = t.missLabel;
|
|
116
|
+
}
|
|
117
|
+
else if (state === "priming") {
|
|
118
|
+
headline = t.writePrimingLabel;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// hot / warm / cold — ratio is guaranteed non-null here.
|
|
122
|
+
headline = `${t.hitLabel} ${Math.round((ratio ?? 0) * 100)}%`;
|
|
123
|
+
}
|
|
124
|
+
const tooltip = buildTooltip(usage, t);
|
|
125
|
+
return (_jsxs("span", { className: cls.root, "data-testid": "cache-badge", "data-cache-state": state, title: tooltip, role: "status", "aria-label": tooltip, children: [_jsx("span", { className: cls.icon, "aria-hidden": "true", children: "\uD83D\uDCBE" }), _jsx("span", { className: cls.label, children: headline })] }));
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/CacheBadge/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<DataClubAIHubChangePassword />` — the built-in change-password screen.
|
|
3
|
+
*
|
|
4
|
+
* Two variants in one component:
|
|
5
|
+
*
|
|
6
|
+
* - **Forced** (`variant="forced"`) — auto-rendered after login when
|
|
7
|
+
* `useAIHubAuth().mustChangePassword === true`. No `current_password`
|
|
8
|
+
* field, no cancel button (there is nowhere else to go), with an
|
|
9
|
+
* `intro_message` explaining the requirement. Submit calls
|
|
10
|
+
* `client.changePassword(null, newPassword)` — the change-only token
|
|
11
|
+
* is itself the proof of authentication.
|
|
12
|
+
* - **Voluntary** (`variant="voluntary"`) — opened from the user-menu
|
|
13
|
+
* ([00-shell.md](ui-sketches/00-shell.md)). Includes `current_password`
|
|
14
|
+
* and a `Cancel` button that fires `onCancel`. Submit calls
|
|
15
|
+
* `client.changePassword(currentPassword, newPassword)`.
|
|
16
|
+
*
|
|
17
|
+
* Each password field carries its own `show_password_toggle` — the
|
|
18
|
+
* regression-guard for the per-field pattern is in
|
|
19
|
+
* `__tests__/components/ChangePassword.test.tsx`.
|
|
20
|
+
*
|
|
21
|
+
* The client mirrors the §8.5 password policy via
|
|
22
|
+
* `validatePasswordPolicyClient` so the form surfaces too-short /
|
|
23
|
+
* too-simple errors *before* sending; the server is authoritative and
|
|
24
|
+
* its banner wins if the two ever disagree.
|
|
25
|
+
*
|
|
26
|
+
* @see SPECIFICATIONS.md §7.2.1 (and §7.3 — auth is owned by the library)
|
|
27
|
+
* @see SPECIFICATIONS.md §8.5 — password policy + forced/voluntary semantics
|
|
28
|
+
* @see ui-sketches/02-change-password.md — visual contract
|
|
29
|
+
*/
|
|
30
|
+
import { type ReactNode } from "react";
|
|
31
|
+
import { type AIHubTexts, type DeepPartial } from "../../auth/texts.js";
|
|
32
|
+
/**
|
|
33
|
+
* BEM class names emitted by `<DataClubAIHubChangePassword />`. Wrappers
|
|
34
|
+
* pass a partial map via `classNames` to override individual entries for
|
|
35
|
+
* utility-CSS frameworks.
|
|
36
|
+
*/
|
|
37
|
+
export interface AIHubChangePasswordClassNames {
|
|
38
|
+
/** Outer page wrapper (centers the card in the viewport). */
|
|
39
|
+
root: string;
|
|
40
|
+
/** The card itself. */
|
|
41
|
+
card: string;
|
|
42
|
+
/** Gradient title at the top. */
|
|
43
|
+
title: string;
|
|
44
|
+
/** Intro message (forced variant only). */
|
|
45
|
+
intro: string;
|
|
46
|
+
/** Error banner shown above the form on failure. */
|
|
47
|
+
errorBanner: string;
|
|
48
|
+
/** Form container. */
|
|
49
|
+
form: string;
|
|
50
|
+
/** Label / field group wrapper. */
|
|
51
|
+
fieldGroup: string;
|
|
52
|
+
/** `<label>` for an input. */
|
|
53
|
+
fieldLabel: string;
|
|
54
|
+
/** Inline field-level error message. */
|
|
55
|
+
fieldError: string;
|
|
56
|
+
/** Password input wrapper (input + show toggle). */
|
|
57
|
+
passwordWrapper: string;
|
|
58
|
+
/** The password `<input>` itself. */
|
|
59
|
+
passwordInput: string;
|
|
60
|
+
/** The show/hide-password toggle button. */
|
|
61
|
+
passwordToggle: string;
|
|
62
|
+
/** Submit button. */
|
|
63
|
+
submitButton: string;
|
|
64
|
+
/** Cancel button (voluntary variant only). */
|
|
65
|
+
cancelButton: string;
|
|
66
|
+
/** Spinner glyph rendered inside the submit button while saving. */
|
|
67
|
+
spinner: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Props for `<DataClubAIHubChangePassword />`. See §7.2.1.
|
|
71
|
+
*/
|
|
72
|
+
export interface DataClubAIHubChangePasswordProps {
|
|
73
|
+
/**
|
|
74
|
+
* Which variant to render. **`"forced"`** (default — what `AuthGate`
|
|
75
|
+
* mounts after a successful login with `is_initial_password=true`) hides
|
|
76
|
+
* the current-password field + cancel button, and shows an intro
|
|
77
|
+
* message. **`"voluntary"`** (from the user-menu) shows all three fields
|
|
78
|
+
* + a cancel button.
|
|
79
|
+
*/
|
|
80
|
+
variant?: "forced" | "voluntary";
|
|
81
|
+
/**
|
|
82
|
+
* Fires after a successful password change. The provider has already
|
|
83
|
+
* stored the fresh session token + refreshed `/auth/me` when this fires,
|
|
84
|
+
* so the caller can read the now-updated `useAIHubAuth().user` directly.
|
|
85
|
+
*/
|
|
86
|
+
onChanged?: () => void;
|
|
87
|
+
/**
|
|
88
|
+
* Fires when the user clicks Cancel on the voluntary variant. Ignored on
|
|
89
|
+
* the forced variant (there is nowhere else to go).
|
|
90
|
+
*/
|
|
91
|
+
onCancel?: () => void;
|
|
92
|
+
/** Extra class merged onto the root wrapper alongside the BEM defaults. */
|
|
93
|
+
className?: string;
|
|
94
|
+
/** Per-element class-name overrides. */
|
|
95
|
+
classNames?: Partial<AIHubChangePasswordClassNames>;
|
|
96
|
+
/**
|
|
97
|
+
* Wrapper-supplied i18n. Missing keys fall back to English defaults.
|
|
98
|
+
* Only the `changePassword` namespace is consulted by this component.
|
|
99
|
+
*/
|
|
100
|
+
texts?: DeepPartial<AIHubTexts>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Renders the change-password card.
|
|
104
|
+
*
|
|
105
|
+
* @param props - see `DataClubAIHubChangePasswordProps`.
|
|
106
|
+
* @returns the rendered card.
|
|
107
|
+
*/
|
|
108
|
+
export declare function DataClubAIHubChangePassword(props: DataClubAIHubChangePasswordProps): ReactNode;
|
|
109
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ChangePassword/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIjE,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAI7B;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AA0BD;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;IACpD;;;OAGG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,gCAAgC,GACtC,SAAS,CAoOX"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* `<DataClubAIHubChangePassword />` — the built-in change-password screen.
|
|
4
|
+
*
|
|
5
|
+
* Two variants in one component:
|
|
6
|
+
*
|
|
7
|
+
* - **Forced** (`variant="forced"`) — auto-rendered after login when
|
|
8
|
+
* `useAIHubAuth().mustChangePassword === true`. No `current_password`
|
|
9
|
+
* field, no cancel button (there is nowhere else to go), with an
|
|
10
|
+
* `intro_message` explaining the requirement. Submit calls
|
|
11
|
+
* `client.changePassword(null, newPassword)` — the change-only token
|
|
12
|
+
* is itself the proof of authentication.
|
|
13
|
+
* - **Voluntary** (`variant="voluntary"`) — opened from the user-menu
|
|
14
|
+
* ([00-shell.md](ui-sketches/00-shell.md)). Includes `current_password`
|
|
15
|
+
* and a `Cancel` button that fires `onCancel`. Submit calls
|
|
16
|
+
* `client.changePassword(currentPassword, newPassword)`.
|
|
17
|
+
*
|
|
18
|
+
* Each password field carries its own `show_password_toggle` — the
|
|
19
|
+
* regression-guard for the per-field pattern is in
|
|
20
|
+
* `__tests__/components/ChangePassword.test.tsx`.
|
|
21
|
+
*
|
|
22
|
+
* The client mirrors the §8.5 password policy via
|
|
23
|
+
* `validatePasswordPolicyClient` so the form surfaces too-short /
|
|
24
|
+
* too-simple errors *before* sending; the server is authoritative and
|
|
25
|
+
* its banner wins if the two ever disagree.
|
|
26
|
+
*
|
|
27
|
+
* @see SPECIFICATIONS.md §7.2.1 (and §7.3 — auth is owned by the library)
|
|
28
|
+
* @see SPECIFICATIONS.md §8.5 — password policy + forced/voluntary semantics
|
|
29
|
+
* @see ui-sketches/02-change-password.md — visual contract
|
|
30
|
+
*/
|
|
31
|
+
import { useState } from "react";
|
|
32
|
+
import { AIHubRequestError } from "../../client/AIHubClient.js";
|
|
33
|
+
import { useAIHubAuth } from "../../context/useAIHubAuth.js";
|
|
34
|
+
import { resolveAIHubTexts, } from "../../auth/texts.js";
|
|
35
|
+
import { validatePasswordPolicyClient } from "../../auth/passwordPolicy.js";
|
|
36
|
+
import { PasswordInput } from "../internal/PasswordInput.js";
|
|
37
|
+
/**
|
|
38
|
+
* Default BEM class names. Stable, kebab-case, scoped under
|
|
39
|
+
* `data-club-ai-hub__change-password`. Wrappers override individual entries
|
|
40
|
+
* via `classNames`.
|
|
41
|
+
*/
|
|
42
|
+
const DEFAULT_CLASS_NAMES = Object.freeze({
|
|
43
|
+
root: "data-club-ai-hub__change-password",
|
|
44
|
+
card: "data-club-ai-hub__change-password-card",
|
|
45
|
+
title: "data-club-ai-hub__change-password-card__title",
|
|
46
|
+
intro: "data-club-ai-hub__change-password-card__intro",
|
|
47
|
+
errorBanner: "data-club-ai-hub__change-password-card__error",
|
|
48
|
+
form: "data-club-ai-hub__change-password-card__form",
|
|
49
|
+
fieldGroup: "data-club-ai-hub__change-password-card__field",
|
|
50
|
+
fieldLabel: "data-club-ai-hub__change-password-card__label",
|
|
51
|
+
fieldError: "data-club-ai-hub__change-password-card__field-error",
|
|
52
|
+
passwordWrapper: "data-club-ai-hub__change-password-card__password",
|
|
53
|
+
passwordInput: "data-club-ai-hub__change-password-card__input data-club-ai-hub__change-password-card__input--password",
|
|
54
|
+
passwordToggle: "data-club-ai-hub__change-password-card__toggle",
|
|
55
|
+
submitButton: "data-club-ai-hub__change-password-card__submit",
|
|
56
|
+
cancelButton: "data-club-ai-hub__change-password-card__cancel",
|
|
57
|
+
spinner: "data-club-ai-hub__spinner",
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Renders the change-password card.
|
|
61
|
+
*
|
|
62
|
+
* @param props - see `DataClubAIHubChangePasswordProps`.
|
|
63
|
+
* @returns the rendered card.
|
|
64
|
+
*/
|
|
65
|
+
export function DataClubAIHubChangePassword(props) {
|
|
66
|
+
const auth = useAIHubAuth();
|
|
67
|
+
const variant = props.variant ?? "forced";
|
|
68
|
+
const classes = { ...DEFAULT_CLASS_NAMES, ...(props.classNames ?? {}) };
|
|
69
|
+
const texts = resolveAIHubTexts(props.texts).changePassword;
|
|
70
|
+
const [currentPassword, setCurrentPassword] = useState("");
|
|
71
|
+
const [newPassword, setNewPassword] = useState("");
|
|
72
|
+
const [confirmPassword, setConfirmPassword] = useState("");
|
|
73
|
+
const [submitting, setSubmitting] = useState(false);
|
|
74
|
+
const [errorMessage, setErrorMessage] = useState(null);
|
|
75
|
+
// Client-side validation produces an inline field-error message — separate
|
|
76
|
+
// from the top-of-card error banner, which is reserved for server errors.
|
|
77
|
+
const fieldError = (() => {
|
|
78
|
+
if (newPassword.length === 0 && confirmPassword.length === 0)
|
|
79
|
+
return null;
|
|
80
|
+
const policy = validatePasswordPolicyClient(newPassword);
|
|
81
|
+
if (!policy.ok) {
|
|
82
|
+
if (policy.code === "password_too_short")
|
|
83
|
+
return texts.passwordTooShortError;
|
|
84
|
+
return texts.passwordTooSimpleError;
|
|
85
|
+
}
|
|
86
|
+
if (confirmPassword.length > 0 && newPassword !== confirmPassword) {
|
|
87
|
+
return texts.passwordsMustMatchError;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
})();
|
|
91
|
+
// Submit-disable predicate: voluntary needs all three; forced needs two.
|
|
92
|
+
const fieldsFilled = newPassword.length > 0 &&
|
|
93
|
+
confirmPassword.length > 0 &&
|
|
94
|
+
newPassword === confirmPassword &&
|
|
95
|
+
(variant === "forced" || currentPassword.length > 0);
|
|
96
|
+
const submitDisabled = submitting || !fieldsFilled || fieldError !== null;
|
|
97
|
+
const handleSubmit = async (e) => {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
if (submitDisabled)
|
|
100
|
+
return;
|
|
101
|
+
setSubmitting(true);
|
|
102
|
+
setErrorMessage(null);
|
|
103
|
+
try {
|
|
104
|
+
await auth.changePassword(variant === "voluntary" ? currentPassword : null, newPassword);
|
|
105
|
+
props.onChanged?.();
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
if (err instanceof AIHubRequestError) {
|
|
109
|
+
// Map the wire error code to a localized banner. Server's
|
|
110
|
+
// password-policy errors trump the client mirror — the server is
|
|
111
|
+
// authoritative (§8.5).
|
|
112
|
+
if (err.code === "password_too_short") {
|
|
113
|
+
setErrorMessage(texts.passwordTooShortError);
|
|
114
|
+
}
|
|
115
|
+
else if (err.code === "password_too_simple") {
|
|
116
|
+
setErrorMessage(texts.passwordTooSimpleError);
|
|
117
|
+
}
|
|
118
|
+
else if (err.code === "unauthorized" || err.status === 401) {
|
|
119
|
+
setErrorMessage(texts.invalidCurrentPasswordError);
|
|
120
|
+
}
|
|
121
|
+
else if (err.code === "network") {
|
|
122
|
+
setErrorMessage(texts.networkError);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Fall back to the server message — it's safe to surface per §9.
|
|
126
|
+
setErrorMessage(err.message || texts.networkError);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
setErrorMessage(texts.networkError);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
setSubmitting(false);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const rootClass = props.className !== undefined
|
|
138
|
+
? `${classes.root} ${props.className}`
|
|
139
|
+
: classes.root;
|
|
140
|
+
return (_jsx("div", { className: rootClass, "data-testid": "change-password-root", "data-variant": variant, children: _jsxs("div", { className: classes.card, role: "form", "aria-labelledby": "dc-aih-change-password-title", children: [_jsx("h1", { id: "dc-aih-change-password-title", className: classes.title, children: texts.title }), variant === "forced" ? (_jsx("p", { className: classes.intro, "data-testid": "change-password-intro", children: texts.forcedChangePasswordIntro })) : null, errorMessage !== null ? (_jsx("div", { className: classes.errorBanner, role: "alert", "aria-live": "polite", "data-testid": "change-password-error", children: errorMessage })) : null, _jsxs("form", { className: classes.form, onSubmit: handleSubmit, noValidate: true, children: [variant === "voluntary" ? (_jsxs("div", { className: classes.fieldGroup, children: [_jsx("label", { htmlFor: "dc-aih-change-current", className: classes.fieldLabel, children: texts.currentPasswordLabel }), _jsx(PasswordInput, { id: "dc-aih-change-current", value: currentPassword, onChange: setCurrentPassword, name: "current_password", autoComplete: "current-password", disabled: submitting, required: true, autoFocus: true, showAriaLabel: texts.showPasswordAriaLabel, hideAriaLabel: texts.hidePasswordAriaLabel, wrapperClassName: classes.passwordWrapper, inputClassName: classes.passwordInput, toggleClassName: classes.passwordToggle, testId: "change-current-field" })] })) : null, _jsxs("div", { className: classes.fieldGroup, children: [_jsx("label", { htmlFor: "dc-aih-change-new", className: classes.fieldLabel, children: texts.newPasswordLabel }), _jsx(PasswordInput, { id: "dc-aih-change-new", value: newPassword, onChange: setNewPassword, name: "new_password", autoComplete: "new-password", disabled: submitting, required: true, ...(variant === "forced" ? { autoFocus: true } : {}), showAriaLabel: texts.showPasswordAriaLabel, hideAriaLabel: texts.hidePasswordAriaLabel, wrapperClassName: classes.passwordWrapper, inputClassName: classes.passwordInput, toggleClassName: classes.passwordToggle, testId: "change-new-field" })] }), _jsxs("div", { className: classes.fieldGroup, children: [_jsx("label", { htmlFor: "dc-aih-change-confirm", className: classes.fieldLabel, children: texts.confirmPasswordLabel }), _jsx(PasswordInput, { id: "dc-aih-change-confirm", value: confirmPassword, onChange: setConfirmPassword, name: "confirm_password", autoComplete: "new-password", disabled: submitting, required: true, showAriaLabel: texts.showPasswordAriaLabel, hideAriaLabel: texts.hidePasswordAriaLabel, wrapperClassName: classes.passwordWrapper, inputClassName: classes.passwordInput, toggleClassName: classes.passwordToggle, testId: "change-confirm-field" }), fieldError !== null ? (_jsx("div", { className: classes.fieldError, role: "status", "aria-live": "polite", "data-testid": "change-password-field-error", children: fieldError })) : null] }), _jsx("button", { type: "submit", className: classes.submitButton, disabled: submitDisabled, "data-testid": "change-password-submit", children: submitting ? (_jsxs(_Fragment, { children: [_jsx("span", { className: classes.spinner, "aria-hidden": "true" }), texts.savingButton] })) : (texts.changePasswordButton) }), variant === "voluntary" ? (_jsx("button", { type: "button", className: classes.cancelButton, onClick: () => props.onCancel?.(), disabled: submitting, "data-testid": "change-password-cancel", children: texts.cancelButton })) : null] })] }) }));
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=index.js.map
|