@entropy-softworks/ui 2026.7.31
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/CHANGELOG.md +96 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/lib/components/auth/LockOverlay.d.ts +22 -0
- package/lib/components/auth/LockOverlay.d.ts.map +1 -0
- package/lib/components/auth/LockOverlay.jsx +82 -0
- package/lib/components/auth/LockOverlay.jsx.map +1 -0
- package/lib/components/auth/PasswordStrengthMeter.d.ts +12 -0
- package/lib/components/auth/PasswordStrengthMeter.d.ts.map +1 -0
- package/lib/components/auth/PasswordStrengthMeter.jsx +81 -0
- package/lib/components/auth/PasswordStrengthMeter.jsx.map +1 -0
- package/lib/components/auth/ProtectedRoute.d.ts +24 -0
- package/lib/components/auth/ProtectedRoute.d.ts.map +1 -0
- package/lib/components/auth/ProtectedRoute.jsx +56 -0
- package/lib/components/auth/ProtectedRoute.jsx.map +1 -0
- package/lib/components/auth/PwnedPasswordWarning.d.ts +13 -0
- package/lib/components/auth/PwnedPasswordWarning.d.ts.map +1 -0
- package/lib/components/auth/PwnedPasswordWarning.jsx +29 -0
- package/lib/components/auth/PwnedPasswordWarning.jsx.map +1 -0
- package/lib/components/auth/index.d.ts +5 -0
- package/lib/components/auth/index.d.ts.map +1 -0
- package/lib/components/auth/index.js +5 -0
- package/lib/components/auth/index.js.map +1 -0
- package/lib/components/bottom-nav.d.ts +25 -0
- package/lib/components/bottom-nav.d.ts.map +1 -0
- package/lib/components/bottom-nav.jsx +51 -0
- package/lib/components/bottom-nav.jsx.map +1 -0
- package/lib/components/button.d.ts +16 -0
- package/lib/components/button.d.ts.map +1 -0
- package/lib/components/button.jsx +73 -0
- package/lib/components/button.jsx.map +1 -0
- package/lib/components/card.d.ts +22 -0
- package/lib/components/card.d.ts.map +1 -0
- package/lib/components/card.jsx +36 -0
- package/lib/components/card.jsx.map +1 -0
- package/lib/components/checkbox.d.ts +12 -0
- package/lib/components/checkbox.d.ts.map +1 -0
- package/lib/components/checkbox.jsx +16 -0
- package/lib/components/checkbox.jsx.map +1 -0
- package/lib/components/connection-settings.d.ts +41 -0
- package/lib/components/connection-settings.d.ts.map +1 -0
- package/lib/components/connection-settings.jsx +354 -0
- package/lib/components/connection-settings.jsx.map +1 -0
- package/lib/components/context-menu.d.ts +52 -0
- package/lib/components/context-menu.d.ts.map +1 -0
- package/lib/components/context-menu.jsx +24 -0
- package/lib/components/context-menu.jsx.map +1 -0
- package/lib/components/data-table.d.ts +59 -0
- package/lib/components/data-table.d.ts.map +1 -0
- package/lib/components/data-table.jsx +101 -0
- package/lib/components/data-table.jsx.map +1 -0
- package/lib/components/dialog.d.ts +58 -0
- package/lib/components/dialog.d.ts.map +1 -0
- package/lib/components/dialog.jsx +197 -0
- package/lib/components/dialog.jsx.map +1 -0
- package/lib/components/dropdown-menu.d.ts +55 -0
- package/lib/components/dropdown-menu.d.ts.map +1 -0
- package/lib/components/dropdown-menu.jsx +38 -0
- package/lib/components/dropdown-menu.jsx.map +1 -0
- package/lib/components/entropy-boot-splash.d.ts +24 -0
- package/lib/components/entropy-boot-splash.d.ts.map +1 -0
- package/lib/components/entropy-boot-splash.jsx +83 -0
- package/lib/components/entropy-boot-splash.jsx.map +1 -0
- package/lib/components/entropy-mark-animated.d.ts +20 -0
- package/lib/components/entropy-mark-animated.d.ts.map +1 -0
- package/lib/components/entropy-mark-animated.jsx +148 -0
- package/lib/components/entropy-mark-animated.jsx.map +1 -0
- package/lib/components/entropy-mark-geometry.d.ts +8 -0
- package/lib/components/entropy-mark-geometry.d.ts.map +1 -0
- package/lib/components/entropy-mark-geometry.js +10 -0
- package/lib/components/entropy-mark-geometry.js.map +1 -0
- package/lib/components/entropy-mark-sequential.d.ts +12 -0
- package/lib/components/entropy-mark-sequential.d.ts.map +1 -0
- package/lib/components/entropy-mark-sequential.jsx +53 -0
- package/lib/components/entropy-mark-sequential.jsx.map +1 -0
- package/lib/components/entropy-mark.d.ts +29 -0
- package/lib/components/entropy-mark.d.ts.map +1 -0
- package/lib/components/entropy-mark.jsx +66 -0
- package/lib/components/entropy-mark.jsx.map +1 -0
- package/lib/components/error-boundary.d.ts +31 -0
- package/lib/components/error-boundary.d.ts.map +1 -0
- package/lib/components/error-boundary.jsx +40 -0
- package/lib/components/error-boundary.jsx.map +1 -0
- package/lib/components/index.d.ts +41 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/index.js +43 -0
- package/lib/components/index.js.map +1 -0
- package/lib/components/input.d.ts +26 -0
- package/lib/components/input.d.ts.map +1 -0
- package/lib/components/input.jsx +95 -0
- package/lib/components/input.jsx.map +1 -0
- package/lib/components/item-icon.d.ts +32 -0
- package/lib/components/item-icon.d.ts.map +1 -0
- package/lib/components/item-icon.jsx +54 -0
- package/lib/components/item-icon.jsx.map +1 -0
- package/lib/components/label.d.ts +13 -0
- package/lib/components/label.d.ts.map +1 -0
- package/lib/components/label.jsx +9 -0
- package/lib/components/label.jsx.map +1 -0
- package/lib/components/legal-dialog.d.ts +18 -0
- package/lib/components/legal-dialog.d.ts.map +1 -0
- package/lib/components/legal-dialog.jsx +31 -0
- package/lib/components/legal-dialog.jsx.map +1 -0
- package/lib/components/license-gate.d.ts +17 -0
- package/lib/components/license-gate.d.ts.map +1 -0
- package/lib/components/license-gate.jsx +21 -0
- package/lib/components/license-gate.jsx.map +1 -0
- package/lib/components/logo.d.ts +15 -0
- package/lib/components/logo.d.ts.map +1 -0
- package/lib/components/logo.jsx +20 -0
- package/lib/components/logo.jsx.map +1 -0
- package/lib/components/onboarding-wizard.d.ts +89 -0
- package/lib/components/onboarding-wizard.d.ts.map +1 -0
- package/lib/components/onboarding-wizard.jsx +185 -0
- package/lib/components/onboarding-wizard.jsx.map +1 -0
- package/lib/components/pagination.d.ts +43 -0
- package/lib/components/pagination.d.ts.map +1 -0
- package/lib/components/pagination.jsx +72 -0
- package/lib/components/pagination.jsx.map +1 -0
- package/lib/components/password-generator.d.ts +15 -0
- package/lib/components/password-generator.d.ts.map +1 -0
- package/lib/components/password-generator.jsx +273 -0
- package/lib/components/password-generator.jsx.map +1 -0
- package/lib/components/pin-pad.d.ts +20 -0
- package/lib/components/pin-pad.d.ts.map +1 -0
- package/lib/components/pin-pad.jsx +93 -0
- package/lib/components/pin-pad.jsx.map +1 -0
- package/lib/components/popover.d.ts +27 -0
- package/lib/components/popover.d.ts.map +1 -0
- package/lib/components/popover.jsx +18 -0
- package/lib/components/popover.jsx.map +1 -0
- package/lib/components/profile/ProfileDialog.d.ts +25 -0
- package/lib/components/profile/ProfileDialog.d.ts.map +1 -0
- package/lib/components/profile/ProfileDialog.jsx +71 -0
- package/lib/components/profile/ProfileDialog.jsx.map +1 -0
- package/lib/components/profile/ProfileTab.d.ts +12 -0
- package/lib/components/profile/ProfileTab.d.ts.map +1 -0
- package/lib/components/profile/ProfileTab.jsx +112 -0
- package/lib/components/profile/ProfileTab.jsx.map +1 -0
- package/lib/components/profile/SecurityTab.d.ts +32 -0
- package/lib/components/profile/SecurityTab.d.ts.map +1 -0
- package/lib/components/profile/SecurityTab.jsx +145 -0
- package/lib/components/profile/SecurityTab.jsx.map +1 -0
- package/lib/components/profile/index.d.ts +4 -0
- package/lib/components/profile/index.d.ts.map +1 -0
- package/lib/components/profile/index.js +4 -0
- package/lib/components/profile/index.js.map +1 -0
- package/lib/components/segmented-toggle.d.ts +28 -0
- package/lib/components/segmented-toggle.d.ts.map +1 -0
- package/lib/components/segmented-toggle.jsx +64 -0
- package/lib/components/segmented-toggle.jsx.map +1 -0
- package/lib/components/separator.d.ts +13 -0
- package/lib/components/separator.d.ts.map +1 -0
- package/lib/components/separator.jsx +16 -0
- package/lib/components/separator.jsx.map +1 -0
- package/lib/components/server-settings.d.ts +27 -0
- package/lib/components/server-settings.d.ts.map +1 -0
- package/lib/components/server-settings.jsx +307 -0
- package/lib/components/server-settings.jsx.map +1 -0
- package/lib/components/settings/AccountSection.d.ts +26 -0
- package/lib/components/settings/AccountSection.d.ts.map +1 -0
- package/lib/components/settings/AccountSection.jsx +32 -0
- package/lib/components/settings/AccountSection.jsx.map +1 -0
- package/lib/components/settings/DangerZoneSection.d.ts +13 -0
- package/lib/components/settings/DangerZoneSection.d.ts.map +1 -0
- package/lib/components/settings/DangerZoneSection.jsx +16 -0
- package/lib/components/settings/DangerZoneSection.jsx.map +1 -0
- package/lib/components/settings/GeneralSection.d.ts +11 -0
- package/lib/components/settings/GeneralSection.d.ts.map +1 -0
- package/lib/components/settings/GeneralSection.jsx +12 -0
- package/lib/components/settings/GeneralSection.jsx.map +1 -0
- package/lib/components/settings/SupportSection.d.ts +11 -0
- package/lib/components/settings/SupportSection.d.ts.map +1 -0
- package/lib/components/settings/SupportSection.jsx +22 -0
- package/lib/components/settings/SupportSection.jsx.map +1 -0
- package/lib/components/settings/index.d.ts +5 -0
- package/lib/components/settings/index.d.ts.map +1 -0
- package/lib/components/settings/index.js +5 -0
- package/lib/components/settings/index.js.map +1 -0
- package/lib/components/settings-row.d.ts +28 -0
- package/lib/components/settings-row.d.ts.map +1 -0
- package/lib/components/settings-row.jsx +68 -0
- package/lib/components/settings-row.jsx.map +1 -0
- package/lib/components/skeleton.d.ts +8 -0
- package/lib/components/skeleton.d.ts.map +1 -0
- package/lib/components/skeleton.jsx +24 -0
- package/lib/components/skeleton.jsx.map +1 -0
- package/lib/components/social-connections.d.ts +15 -0
- package/lib/components/social-connections.d.ts.map +1 -0
- package/lib/components/social-connections.jsx +31 -0
- package/lib/components/social-connections.jsx.map +1 -0
- package/lib/components/stat-card.d.ts +29 -0
- package/lib/components/stat-card.d.ts.map +1 -0
- package/lib/components/stat-card.jsx +36 -0
- package/lib/components/stat-card.jsx.map +1 -0
- package/lib/components/tag-chip.d.ts +18 -0
- package/lib/components/tag-chip.d.ts.map +1 -0
- package/lib/components/tag-chip.jsx +54 -0
- package/lib/components/tag-chip.jsx.map +1 -0
- package/lib/components/tag-multi-select.d.ts +27 -0
- package/lib/components/tag-multi-select.d.ts.map +1 -0
- package/lib/components/tag-multi-select.jsx +65 -0
- package/lib/components/tag-multi-select.jsx.map +1 -0
- package/lib/components/text.d.ts +22 -0
- package/lib/components/text.d.ts.map +1 -0
- package/lib/components/text.jsx +43 -0
- package/lib/components/text.jsx.map +1 -0
- package/lib/components/toast/ToastPortal.d.ts +11 -0
- package/lib/components/toast/ToastPortal.d.ts.map +1 -0
- package/lib/components/toast/ToastPortal.jsx +15 -0
- package/lib/components/toast/ToastPortal.jsx.map +1 -0
- package/lib/components/toast/ToastPortal.web.d.ts +14 -0
- package/lib/components/toast/ToastPortal.web.d.ts.map +1 -0
- package/lib/components/toast/ToastPortal.web.jsx +39 -0
- package/lib/components/toast/ToastPortal.web.jsx.map +1 -0
- package/lib/components/toast/index.d.ts +3 -0
- package/lib/components/toast/index.d.ts.map +1 -0
- package/lib/components/toast/index.js +3 -0
- package/lib/components/toast/index.js.map +1 -0
- package/lib/components/toast/toast-config.d.ts +14 -0
- package/lib/components/toast/toast-config.d.ts.map +1 -0
- package/lib/components/toast/toast-config.jsx +24 -0
- package/lib/components/toast/toast-config.jsx.map +1 -0
- package/lib/components/tooltip.d.ts +30 -0
- package/lib/components/tooltip.d.ts.map +1 -0
- package/lib/components/tooltip.jsx +14 -0
- package/lib/components/tooltip.jsx.map +1 -0
- package/lib/components/totp-display.d.ts +23 -0
- package/lib/components/totp-display.d.ts.map +1 -0
- package/lib/components/totp-display.jsx +132 -0
- package/lib/components/totp-display.jsx.map +1 -0
- package/lib/components/user-avatar.d.ts +18 -0
- package/lib/components/user-avatar.d.ts.map +1 -0
- package/lib/components/user-avatar.jsx +25 -0
- package/lib/components/user-avatar.jsx.map +1 -0
- package/lib/context/AppConfigContext.d.ts +56 -0
- package/lib/context/AppConfigContext.d.ts.map +1 -0
- package/lib/context/AppConfigContext.jsx +14 -0
- package/lib/context/AppConfigContext.jsx.map +1 -0
- package/lib/context/NavigationContext.d.ts +42 -0
- package/lib/context/NavigationContext.d.ts.map +1 -0
- package/lib/context/NavigationContext.jsx +53 -0
- package/lib/context/NavigationContext.jsx.map +1 -0
- package/lib/context/NotificationContext.d.ts +32 -0
- package/lib/context/NotificationContext.d.ts.map +1 -0
- package/lib/context/NotificationContext.jsx +49 -0
- package/lib/context/NotificationContext.jsx.map +1 -0
- package/lib/context/ThemeContext.d.ts +12 -0
- package/lib/context/ThemeContext.d.ts.map +1 -0
- package/lib/context/ThemeContext.jsx +66 -0
- package/lib/context/ThemeContext.jsx.map +1 -0
- package/lib/context/index.d.ts +5 -0
- package/lib/context/index.d.ts.map +1 -0
- package/lib/context/index.js +5 -0
- package/lib/context/index.js.map +1 -0
- package/lib/flags.d.ts +9 -0
- package/lib/flags.d.ts.map +1 -0
- package/lib/flags.js +37 -0
- package/lib/flags.js.map +1 -0
- package/lib/forms/form-error.d.ts +12 -0
- package/lib/forms/form-error.d.ts.map +1 -0
- package/lib/forms/form-error.jsx +14 -0
- package/lib/forms/form-error.jsx.map +1 -0
- package/lib/forms/index.d.ts +3 -0
- package/lib/forms/index.d.ts.map +1 -0
- package/lib/forms/index.js +3 -0
- package/lib/forms/index.js.map +1 -0
- package/lib/forms/shake-field.d.ts +18 -0
- package/lib/forms/shake-field.d.ts.map +1 -0
- package/lib/forms/shake-field.jsx +30 -0
- package/lib/forms/shake-field.jsx.map +1 -0
- package/lib/hooks/index.d.ts +6 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +6 -0
- package/lib/hooks/index.js.map +1 -0
- package/lib/hooks/useAuth.d.ts +109 -0
- package/lib/hooks/useAuth.d.ts.map +1 -0
- package/lib/hooks/useAuth.jsx +404 -0
- package/lib/hooks/useAuth.jsx.map +1 -0
- package/lib/hooks/useAutoLock.d.ts +22 -0
- package/lib/hooks/useAutoLock.d.ts.map +1 -0
- package/lib/hooks/useAutoLock.js +85 -0
- package/lib/hooks/useAutoLock.js.map +1 -0
- package/lib/hooks/useDebouncedValue.d.ts +19 -0
- package/lib/hooks/useDebouncedValue.d.ts.map +1 -0
- package/lib/hooks/useDebouncedValue.js +29 -0
- package/lib/hooks/useDebouncedValue.js.map +1 -0
- package/lib/hooks/useShakeAnimation.d.ts +17 -0
- package/lib/hooks/useShakeAnimation.d.ts.map +1 -0
- package/lib/hooks/useShakeAnimation.js +22 -0
- package/lib/hooks/useShakeAnimation.js.map +1 -0
- package/lib/hooks/useUploadBlobUrl.d.ts +13 -0
- package/lib/hooks/useUploadBlobUrl.d.ts.map +1 -0
- package/lib/hooks/useUploadBlobUrl.js +60 -0
- package/lib/hooks/useUploadBlobUrl.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +47 -0
- package/lib/index.js.map +1 -0
- package/lib/screens/auth/AccountPickerScreen.d.ts +24 -0
- package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -0
- package/lib/screens/auth/AccountPickerScreen.jsx +47 -0
- package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -0
- package/lib/screens/auth/AuthScreen.d.ts +59 -0
- package/lib/screens/auth/AuthScreen.d.ts.map +1 -0
- package/lib/screens/auth/AuthScreen.jsx +594 -0
- package/lib/screens/auth/AuthScreen.jsx.map +1 -0
- package/lib/screens/auth/ConsentScreen.d.ts +27 -0
- package/lib/screens/auth/ConsentScreen.d.ts.map +1 -0
- package/lib/screens/auth/ConsentScreen.jsx +55 -0
- package/lib/screens/auth/ConsentScreen.jsx.map +1 -0
- package/lib/screens/auth/ErrorScreen.d.ts +20 -0
- package/lib/screens/auth/ErrorScreen.d.ts.map +1 -0
- package/lib/screens/auth/ErrorScreen.jsx +43 -0
- package/lib/screens/auth/ErrorScreen.jsx.map +1 -0
- package/lib/screens/auth/ExpiredLinkScreen.d.ts +19 -0
- package/lib/screens/auth/ExpiredLinkScreen.d.ts.map +1 -0
- package/lib/screens/auth/ExpiredLinkScreen.jsx +38 -0
- package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -0
- package/lib/screens/auth/ForgotPasswordScreen.d.ts +4 -0
- package/lib/screens/auth/ForgotPasswordScreen.d.ts.map +1 -0
- package/lib/screens/auth/ForgotPasswordScreen.jsx +123 -0
- package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -0
- package/lib/screens/auth/ImportDataScreen.d.ts +17 -0
- package/lib/screens/auth/ImportDataScreen.d.ts.map +1 -0
- package/lib/screens/auth/ImportDataScreen.jsx +73 -0
- package/lib/screens/auth/ImportDataScreen.jsx.map +1 -0
- package/lib/screens/auth/LoginScreen.d.ts +38 -0
- package/lib/screens/auth/LoginScreen.d.ts.map +1 -0
- package/lib/screens/auth/LoginScreen.jsx +326 -0
- package/lib/screens/auth/LoginScreen.jsx.map +1 -0
- package/lib/screens/auth/MfaChallengeScreen.d.ts +4 -0
- package/lib/screens/auth/MfaChallengeScreen.d.ts.map +1 -0
- package/lib/screens/auth/MfaChallengeScreen.jsx +139 -0
- package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -0
- package/lib/screens/auth/MfaEnrollScreen.d.ts +12 -0
- package/lib/screens/auth/MfaEnrollScreen.d.ts.map +1 -0
- package/lib/screens/auth/MfaEnrollScreen.jsx +267 -0
- package/lib/screens/auth/MfaEnrollScreen.jsx.map +1 -0
- package/lib/screens/auth/PasskeyEnrollScreen.d.ts +18 -0
- package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -0
- package/lib/screens/auth/PasskeyEnrollScreen.jsx +51 -0
- package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -0
- package/lib/screens/auth/PrivacyPolicyScreen.d.ts +19 -0
- package/lib/screens/auth/PrivacyPolicyScreen.d.ts.map +1 -0
- package/lib/screens/auth/PrivacyPolicyScreen.jsx +143 -0
- package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -0
- package/lib/screens/auth/RegisterScreen.d.ts +14 -0
- package/lib/screens/auth/RegisterScreen.d.ts.map +1 -0
- package/lib/screens/auth/RegisterScreen.jsx +296 -0
- package/lib/screens/auth/RegisterScreen.jsx.map +1 -0
- package/lib/screens/auth/ResetPasswordScreen.d.ts +12 -0
- package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -0
- package/lib/screens/auth/ResetPasswordScreen.jsx +276 -0
- package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -0
- package/lib/screens/auth/ServerConfigScreen.d.ts +18 -0
- package/lib/screens/auth/ServerConfigScreen.d.ts.map +1 -0
- package/lib/screens/auth/ServerConfigScreen.jsx +259 -0
- package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -0
- package/lib/screens/auth/TermsOfServiceScreen.d.ts +30 -0
- package/lib/screens/auth/TermsOfServiceScreen.d.ts.map +1 -0
- package/lib/screens/auth/TermsOfServiceScreen.jsx +130 -0
- package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -0
- package/lib/screens/auth/VerifyEmailScreen.d.ts +4 -0
- package/lib/screens/auth/VerifyEmailScreen.d.ts.map +1 -0
- package/lib/screens/auth/VerifyEmailScreen.jsx +249 -0
- package/lib/screens/auth/VerifyEmailScreen.jsx.map +1 -0
- package/lib/screens/auth/WelcomeScreen.d.ts +17 -0
- package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -0
- package/lib/screens/auth/WelcomeScreen.jsx +58 -0
- package/lib/screens/auth/WelcomeScreen.jsx.map +1 -0
- package/lib/screens/auth/index.d.ts +19 -0
- package/lib/screens/auth/index.d.ts.map +1 -0
- package/lib/screens/auth/index.js +19 -0
- package/lib/screens/auth/index.js.map +1 -0
- package/lib/screens/index.d.ts +2 -0
- package/lib/screens/index.d.ts.map +1 -0
- package/lib/screens/index.js +2 -0
- package/lib/screens/index.js.map +1 -0
- package/lib/services/api-config.d.ts +87 -0
- package/lib/services/api-config.d.ts.map +1 -0
- package/lib/services/api-config.js +202 -0
- package/lib/services/api-config.js.map +1 -0
- package/lib/services/auth.service.d.ts +140 -0
- package/lib/services/auth.service.d.ts.map +1 -0
- package/lib/services/auth.service.js +376 -0
- package/lib/services/auth.service.js.map +1 -0
- package/lib/services/favicon.d.ts +46 -0
- package/lib/services/favicon.d.ts.map +1 -0
- package/lib/services/favicon.js +84 -0
- package/lib/services/favicon.js.map +1 -0
- package/lib/services/hibp-cache.d.ts +23 -0
- package/lib/services/hibp-cache.d.ts.map +1 -0
- package/lib/services/hibp-cache.js +56 -0
- package/lib/services/hibp-cache.js.map +1 -0
- package/lib/services/hibp.d.ts +28 -0
- package/lib/services/hibp.d.ts.map +1 -0
- package/lib/services/hibp.js +92 -0
- package/lib/services/hibp.js.map +1 -0
- package/lib/services/index.d.ts +8 -0
- package/lib/services/index.d.ts.map +1 -0
- package/lib/services/index.js +7 -0
- package/lib/services/index.js.map +1 -0
- package/lib/services/passkey.d.ts +10 -0
- package/lib/services/passkey.d.ts.map +1 -0
- package/lib/services/passkey.js +192 -0
- package/lib/services/passkey.js.map +1 -0
- package/lib/utils/api-fetch.d.ts +11 -0
- package/lib/utils/api-fetch.d.ts.map +1 -0
- package/lib/utils/api-fetch.js +52 -0
- package/lib/utils/api-fetch.js.map +1 -0
- package/lib/utils/clipboard.d.ts +31 -0
- package/lib/utils/clipboard.d.ts.map +1 -0
- package/lib/utils/clipboard.js +66 -0
- package/lib/utils/clipboard.js.map +1 -0
- package/lib/utils/cn.d.ts +9 -0
- package/lib/utils/cn.d.ts.map +1 -0
- package/lib/utils/cn.js +12 -0
- package/lib/utils/cn.js.map +1 -0
- package/lib/utils/debounce.d.ts +13 -0
- package/lib/utils/debounce.d.ts.map +1 -0
- package/lib/utils/debounce.js +28 -0
- package/lib/utils/debounce.js.map +1 -0
- package/lib/utils/error.d.ts +9 -0
- package/lib/utils/error.d.ts.map +1 -0
- package/lib/utils/error.js +11 -0
- package/lib/utils/error.js.map +1 -0
- package/lib/utils/fetch.d.ts +17 -0
- package/lib/utils/fetch.d.ts.map +1 -0
- package/lib/utils/fetch.js +29 -0
- package/lib/utils/fetch.js.map +1 -0
- package/lib/utils/http-error.d.ts +41 -0
- package/lib/utils/http-error.d.ts.map +1 -0
- package/lib/utils/http-error.js +63 -0
- package/lib/utils/http-error.js.map +1 -0
- package/lib/utils/index.d.ts +11 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +11 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/palette.d.ts +46 -0
- package/lib/utils/palette.d.ts.map +1 -0
- package/lib/utils/palette.js +75 -0
- package/lib/utils/palette.js.map +1 -0
- package/lib/utils/password.d.ts +20 -0
- package/lib/utils/password.d.ts.map +1 -0
- package/lib/utils/password.js +51 -0
- package/lib/utils/password.js.map +1 -0
- package/lib/utils/settings-storage.d.ts +3 -0
- package/lib/utils/settings-storage.d.ts.map +1 -0
- package/lib/utils/settings-storage.js +62 -0
- package/lib/utils/settings-storage.js.map +1 -0
- package/lib/utils/validation.d.ts +29 -0
- package/lib/utils/validation.d.ts.map +1 -0
- package/lib/utils/validation.js +58 -0
- package/lib/utils/validation.js.map +1 -0
- package/package.json +204 -0
- package/src/components/__tests__/data-table.test.ts +94 -0
- package/src/components/__tests__/pagination.test.ts +73 -0
- package/src/components/__tests__/stat-card.test.ts +21 -0
- package/src/components/auth/LockOverlay.tsx +122 -0
- package/src/components/auth/PasswordStrengthMeter.tsx +96 -0
- package/src/components/auth/ProtectedRoute.tsx +91 -0
- package/src/components/auth/PwnedPasswordWarning.tsx +54 -0
- package/src/components/auth/index.ts +10 -0
- package/src/components/bottom-nav.tsx +99 -0
- package/src/components/button.tsx +144 -0
- package/src/components/card.tsx +82 -0
- package/src/components/checkbox.tsx +48 -0
- package/src/components/connection-settings.tsx +583 -0
- package/src/components/context-menu.tsx +95 -0
- package/src/components/data-table.tsx +223 -0
- package/src/components/dialog.tsx +376 -0
- package/src/components/dropdown-menu.tsx +120 -0
- package/src/components/entropy-boot-splash.tsx +114 -0
- package/src/components/entropy-mark-animated.tsx +260 -0
- package/src/components/entropy-mark-geometry.ts +11 -0
- package/src/components/entropy-mark-sequential.tsx +132 -0
- package/src/components/entropy-mark.tsx +135 -0
- package/src/components/error-boundary.tsx +70 -0
- package/src/components/index.ts +156 -0
- package/src/components/input.tsx +246 -0
- package/src/components/item-icon.tsx +111 -0
- package/src/components/label.tsx +25 -0
- package/src/components/legal-dialog.tsx +47 -0
- package/src/components/license-gate.tsx +28 -0
- package/src/components/logo.tsx +34 -0
- package/src/components/onboarding-wizard.tsx +353 -0
- package/src/components/pagination.tsx +154 -0
- package/src/components/password-generator.tsx +393 -0
- package/src/components/pin-pad.tsx +156 -0
- package/src/components/popover.tsx +38 -0
- package/src/components/profile/ProfileDialog.tsx +129 -0
- package/src/components/profile/ProfileTab.tsx +151 -0
- package/src/components/profile/SecurityTab.tsx +229 -0
- package/src/components/profile/index.ts +3 -0
- package/src/components/segmented-toggle.tsx +119 -0
- package/src/components/separator.tsx +41 -0
- package/src/components/server-settings.tsx +442 -0
- package/src/components/settings/AccountSection.tsx +76 -0
- package/src/components/settings/DangerZoneSection.tsx +37 -0
- package/src/components/settings/GeneralSection.tsx +39 -0
- package/src/components/settings/SupportSection.tsx +53 -0
- package/src/components/settings/index.ts +4 -0
- package/src/components/settings-row.tsx +126 -0
- package/src/components/skeleton.tsx +48 -0
- package/src/components/social-connections.tsx +55 -0
- package/src/components/stat-card.tsx +64 -0
- package/src/components/tag-chip.tsx +78 -0
- package/src/components/tag-multi-select.tsx +136 -0
- package/src/components/text.tsx +77 -0
- package/src/components/toast/ToastPortal.tsx +16 -0
- package/src/components/toast/ToastPortal.web.tsx +47 -0
- package/src/components/toast/index.ts +2 -0
- package/src/components/toast/toast-config.tsx +31 -0
- package/src/components/tooltip.tsx +30 -0
- package/src/components/totp-display.tsx +207 -0
- package/src/components/user-avatar.tsx +46 -0
- package/src/context/AppConfigContext.tsx +76 -0
- package/src/context/NavigationContext.tsx +75 -0
- package/src/context/NotificationContext.tsx +69 -0
- package/src/context/ThemeContext.tsx +83 -0
- package/src/context/__tests__/NavigationContext.test.tsx +53 -0
- package/src/context/index.ts +24 -0
- package/src/flags.ts +44 -0
- package/src/forms/form-error.tsx +20 -0
- package/src/forms/index.ts +2 -0
- package/src/forms/shake-field.tsx +52 -0
- package/src/hooks/__tests__/useUploadBlobUrl.test.tsx +77 -0
- package/src/hooks/index.ts +18 -0
- package/src/hooks/useAuth.tsx +567 -0
- package/src/hooks/useAutoLock.ts +110 -0
- package/src/hooks/useDebouncedValue.ts +32 -0
- package/src/hooks/useShakeAnimation.ts +42 -0
- package/src/hooks/useUploadBlobUrl.ts +75 -0
- package/src/index.ts +48 -0
- package/src/screens/auth/AccountPickerScreen.tsx +103 -0
- package/src/screens/auth/AuthScreen.tsx +941 -0
- package/src/screens/auth/ConsentScreen.tsx +118 -0
- package/src/screens/auth/ErrorScreen.tsx +96 -0
- package/src/screens/auth/ExpiredLinkScreen.tsx +87 -0
- package/src/screens/auth/ForgotPasswordScreen.tsx +182 -0
- package/src/screens/auth/ImportDataScreen.tsx +112 -0
- package/src/screens/auth/LoginScreen.tsx +484 -0
- package/src/screens/auth/MfaChallengeScreen.tsx +216 -0
- package/src/screens/auth/MfaEnrollScreen.tsx +388 -0
- package/src/screens/auth/PasskeyEnrollScreen.tsx +96 -0
- package/src/screens/auth/PrivacyPolicyScreen.tsx +249 -0
- package/src/screens/auth/RegisterScreen.tsx +430 -0
- package/src/screens/auth/ResetPasswordScreen.tsx +377 -0
- package/src/screens/auth/ServerConfigScreen.tsx +388 -0
- package/src/screens/auth/TermsOfServiceScreen.tsx +236 -0
- package/src/screens/auth/VerifyEmailScreen.tsx +310 -0
- package/src/screens/auth/WelcomeScreen.tsx +78 -0
- package/src/screens/auth/index.ts +36 -0
- package/src/screens/index.ts +1 -0
- package/src/services/__tests__/api-config.test.ts +67 -0
- package/src/services/__tests__/favicon.test.ts +88 -0
- package/src/services/__tests__/hibp-cache.test.ts +37 -0
- package/src/services/__tests__/hibp.test.ts +39 -0
- package/src/services/api-config.ts +260 -0
- package/src/services/auth.service.ts +498 -0
- package/src/services/favicon.ts +102 -0
- package/src/services/hibp-cache.ts +71 -0
- package/src/services/hibp.ts +120 -0
- package/src/services/index.ts +52 -0
- package/src/services/passkey.ts +213 -0
- package/src/types/external.d.ts +24 -0
- package/src/utils/__tests__/clipboard.test.ts +59 -0
- package/src/utils/__tests__/cn.test.ts +24 -0
- package/src/utils/__tests__/debounce.test.ts +42 -0
- package/src/utils/__tests__/error.test.ts +18 -0
- package/src/utils/__tests__/http-error.test.ts +33 -0
- package/src/utils/__tests__/palette.test.ts +22 -0
- package/src/utils/__tests__/password.test.ts +55 -0
- package/src/utils/__tests__/settings-storage.test.ts +15 -0
- package/src/utils/__tests__/validation.test.ts +52 -0
- package/src/utils/api-fetch.ts +61 -0
- package/src/utils/clipboard.ts +69 -0
- package/src/utils/cn.ts +12 -0
- package/src/utils/debounce.ts +33 -0
- package/src/utils/error.ts +10 -0
- package/src/utils/fetch.ts +33 -0
- package/src/utils/http-error.ts +81 -0
- package/src/utils/index.ts +23 -0
- package/src/utils/palette.ts +105 -0
- package/src/utils/password.ts +64 -0
- package/src/utils/settings-storage.ts +68 -0
- package/src/utils/validation.ts +71 -0
- package/styles/globals.css +262 -0
- package/tailwind-preset.js +163 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Image, Platform, View } from "react-native";
|
|
3
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
import { useColorScheme } from "nativewind";
|
|
5
|
+
import { resolveIcon } from "../services/favicon";
|
|
6
|
+
import { useUploadBlobUrl } from "../hooks/useUploadBlobUrl";
|
|
7
|
+
|
|
8
|
+
export interface ItemIconProps {
|
|
9
|
+
/**
|
|
10
|
+
* Stored icon reference. Either a literal URL, a `custom:<uploadId>`
|
|
11
|
+
* marker (fetched through the auth-aware transport), or null/empty
|
|
12
|
+
* to fall back to the favicon proxy.
|
|
13
|
+
*/
|
|
14
|
+
iconUrl?: string | null;
|
|
15
|
+
/** Site URL the favicon proxy derives a public URL from. */
|
|
16
|
+
siteUrl?: string | null;
|
|
17
|
+
/** Tile size in pixels (square). Defaults to 40. */
|
|
18
|
+
size?: number;
|
|
19
|
+
/** Inner image size. Defaults to roughly 60% of `size`. */
|
|
20
|
+
imageSize?: number;
|
|
21
|
+
/** Tile corner radius. Defaults to 25% of `size`. */
|
|
22
|
+
borderRadius?: number;
|
|
23
|
+
/** Tile background override. Defaults to the secondary surface. */
|
|
24
|
+
background?: string;
|
|
25
|
+
/** Globe-glyph color override. Defaults to muted-foreground. */
|
|
26
|
+
fallbackColor?: string;
|
|
27
|
+
/** Favicon proxy path on the configured API server. Defaults to `/favicon`. */
|
|
28
|
+
faviconPath?: string;
|
|
29
|
+
/** Upload-fetch path prefix. Defaults to `/uploads`. */
|
|
30
|
+
uploadsPath?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// react-native-web forwards `loading` and `decoding` to the underlying
|
|
34
|
+
// <img>; RN's TS types don't expose these props, so we cast through a
|
|
35
|
+
// Record<string, string> for the web branch.
|
|
36
|
+
const WEB_LAZY_PROPS =
|
|
37
|
+
Platform.OS === "web" ? ({ loading: "lazy", decoding: "async" } as Record<string, string>) : {};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Per-item icon: shows the custom upload, falls back to the favicon
|
|
41
|
+
* proxy, then to a generic globe glyph on a tile-toned square. Shared
|
|
42
|
+
* between RN clients (mobile) and RN-Web (extension popup).
|
|
43
|
+
*/
|
|
44
|
+
export const ItemIcon = React.memo(function ItemIcon({
|
|
45
|
+
iconUrl,
|
|
46
|
+
siteUrl,
|
|
47
|
+
size = 40,
|
|
48
|
+
imageSize,
|
|
49
|
+
borderRadius,
|
|
50
|
+
background,
|
|
51
|
+
fallbackColor,
|
|
52
|
+
faviconPath = "/favicon",
|
|
53
|
+
uploadsPath = "/uploads",
|
|
54
|
+
}: ItemIconProps) {
|
|
55
|
+
const { colorScheme } = useColorScheme();
|
|
56
|
+
const isDark = colorScheme === "dark";
|
|
57
|
+
|
|
58
|
+
const resolved = resolveIcon({ iconUrl, siteUrl, faviconPath });
|
|
59
|
+
const uploadId = resolved.kind === "upload" ? resolved.uploadId : null;
|
|
60
|
+
const upload = useUploadBlobUrl(uploadId, uploadsPath);
|
|
61
|
+
|
|
62
|
+
const directUri = resolved.kind === "url" ? resolved.uri : null;
|
|
63
|
+
const [urlErrored, setUrlErrored] = useState(false);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
setUrlErrored(false);
|
|
66
|
+
}, [directUri]);
|
|
67
|
+
|
|
68
|
+
const renderUri = uploadId ? upload.uri : directUri;
|
|
69
|
+
const renderErrored = uploadId ? upload.errored : urlErrored;
|
|
70
|
+
const onError = () => {
|
|
71
|
+
if (uploadId) {
|
|
72
|
+
upload.setErrored(true);
|
|
73
|
+
} else {
|
|
74
|
+
setUrlErrored(true);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const tileBg = background ?? (isDark ? "rgb(38, 38, 38)" : "rgb(245, 245, 245)");
|
|
79
|
+
const tileRadius = borderRadius ?? size * 0.25;
|
|
80
|
+
const innerSize = imageSize ?? Math.round(size * 0.6);
|
|
81
|
+
const globeColor = fallbackColor ?? (isDark ? "rgb(163, 163, 163)" : "rgb(115, 115, 115)");
|
|
82
|
+
|
|
83
|
+
const showFallback = !renderUri || renderErrored;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<View
|
|
87
|
+
style={{
|
|
88
|
+
width: size,
|
|
89
|
+
height: size,
|
|
90
|
+
borderRadius: tileRadius,
|
|
91
|
+
backgroundColor: tileBg,
|
|
92
|
+
alignItems: "center",
|
|
93
|
+
justifyContent: "center",
|
|
94
|
+
overflow: "hidden",
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
{showFallback ? (
|
|
98
|
+
<Ionicons name="globe-outline" size={Math.round(innerSize * 0.85)} color={globeColor} />
|
|
99
|
+
) : (
|
|
100
|
+
<Image
|
|
101
|
+
source={{ uri: renderUri ?? "" }}
|
|
102
|
+
style={{ width: innerSize, height: innerSize }}
|
|
103
|
+
resizeMode="contain"
|
|
104
|
+
onError={onError}
|
|
105
|
+
accessibilityIgnoresInvertColors
|
|
106
|
+
{...WEB_LAZY_PROPS}
|
|
107
|
+
/>
|
|
108
|
+
)}
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
111
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import * as LabelPrimitive from "@rn-primitives/label";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
|
|
5
|
+
const FormLabel = React.forwardRef<
|
|
6
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
7
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
8
|
+
>(({ className, ...props }, ref) => (
|
|
9
|
+
<LabelPrimitive.Root ref={ref} className={cn("", className)} {...props} />
|
|
10
|
+
));
|
|
11
|
+
FormLabel.displayName = "FormLabel";
|
|
12
|
+
|
|
13
|
+
const FormLabelText = React.forwardRef<
|
|
14
|
+
React.ElementRef<typeof LabelPrimitive.Text>,
|
|
15
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Text>
|
|
16
|
+
>(({ className, ...props }, ref) => (
|
|
17
|
+
<LabelPrimitive.Text
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn("text-sm font-medium text-foreground dark:text-white", className)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
));
|
|
23
|
+
FormLabelText.displayName = "FormLabelText";
|
|
24
|
+
|
|
25
|
+
export { FormLabel, FormLabelText };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ScrollView, Text, useWindowDimensions, View } from "react-native";
|
|
3
|
+
import { Dialog } from "./dialog";
|
|
4
|
+
import { EntropyByline } from "./entropy-mark";
|
|
5
|
+
|
|
6
|
+
export interface LegalDialogProps {
|
|
7
|
+
visible: boolean;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
/** e.g. "Terms of Service" / "Privacy Policy". */
|
|
10
|
+
title: string;
|
|
11
|
+
/** Body copy. Split into paragraphs on blank lines. App-supplied so each
|
|
12
|
+
* app customises the wording; the dialog itself is themed by the palette. */
|
|
13
|
+
content?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Reusable legal popup (Terms / Privacy / etc). A scrollable dialog instead
|
|
18
|
+
* of a dedicated route — apps pass their own `content` and inherit the
|
|
19
|
+
* active theme. Built on the shared `Dialog`.
|
|
20
|
+
*/
|
|
21
|
+
export function LegalDialog({ visible, onClose, title, content }: LegalDialogProps) {
|
|
22
|
+
const { height } = useWindowDimensions();
|
|
23
|
+
const paragraphs = (content ?? `${title} for this application.`)
|
|
24
|
+
.trim()
|
|
25
|
+
.split(/\n{2,}/)
|
|
26
|
+
.map((p) => p.trim())
|
|
27
|
+
.filter(Boolean);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Dialog visible={visible} onClose={onClose} title={title} maxWidth={560} draggable>
|
|
31
|
+
<ScrollView style={{ maxHeight: height * 0.6 }} showsVerticalScrollIndicator={false}>
|
|
32
|
+
<View style={{ gap: 12, paddingBottom: 4 }}>
|
|
33
|
+
{paragraphs.map((p, i) => (
|
|
34
|
+
<Text key={i} className="text-sm leading-5 text-muted-foreground">
|
|
35
|
+
{p}
|
|
36
|
+
</Text>
|
|
37
|
+
))}
|
|
38
|
+
<View style={{ marginTop: 16 }}>
|
|
39
|
+
<EntropyByline size={16} />
|
|
40
|
+
</View>
|
|
41
|
+
</View>
|
|
42
|
+
</ScrollView>
|
|
43
|
+
</Dialog>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default LegalDialog;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ENABLE_LICENSING } from "../flags";
|
|
3
|
+
|
|
4
|
+
export interface LicenseGateProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
/** Rendered while a license check is pending/denied once licensing is wired. */
|
|
7
|
+
fallback?: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Structural licensing gate. With `EXPO_PUBLIC_ENABLE_LICENSING` unset/false
|
|
12
|
+
* (self-host default) it's a pass-through. SaaS production ships the flag
|
|
13
|
+
* `true`; the actual licensing/subscription check lives in a separate app and
|
|
14
|
+
* plugs in here later. Wrapping the app root with this now means the gate
|
|
15
|
+
* exists identically everywhere — flipping the env var (and adding the check)
|
|
16
|
+
* is the only change needed to enforce it.
|
|
17
|
+
*/
|
|
18
|
+
export function LicenseGate({ children }: LicenseGateProps) {
|
|
19
|
+
if (!ENABLE_LICENSING) {
|
|
20
|
+
return <>{children}</>;
|
|
21
|
+
}
|
|
22
|
+
// TODO(saas): delegate to the licensing app — validate subscription/seat and
|
|
23
|
+
// render `fallback` when invalid. Until then, enabling the flag is a no-op
|
|
24
|
+
// pass-through so the wiring can ship ahead of the integration.
|
|
25
|
+
return <>{children}</>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default LicenseGate;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { Text } from "./text";
|
|
4
|
+
import { useAppConfig } from "../context/AppConfigContext";
|
|
5
|
+
|
|
6
|
+
export interface BrandHeaderProps {
|
|
7
|
+
/** Icon size in pixels. Wordmark text scales relative. */
|
|
8
|
+
size?: number;
|
|
9
|
+
/** Hide the wordmark and show just the icon. Useful in tight headers. */
|
|
10
|
+
iconOnly?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Brand header — pairs the configured `AppConfig.Logo` with the `appName`
|
|
15
|
+
* wordmark. Apps register their logo + name via `AppConfigProvider`; this
|
|
16
|
+
* widget renders them at a uniform size across screens (welcome, login,
|
|
17
|
+
* settings header, extension popup header).
|
|
18
|
+
*/
|
|
19
|
+
export function BrandHeader({ size = 32, iconOnly = false }: BrandHeaderProps) {
|
|
20
|
+
const { appName, Logo } = useAppConfig();
|
|
21
|
+
return (
|
|
22
|
+
<View className="flex-row items-center" style={{ gap: 10 }}>
|
|
23
|
+
<Logo width={size} height={size} />
|
|
24
|
+
{iconOnly ? null : (
|
|
25
|
+
<Text
|
|
26
|
+
className="font-sans-bold text-foreground"
|
|
27
|
+
style={{ fontSize: Math.round(size * 0.6), letterSpacing: -0.5 }}
|
|
28
|
+
>
|
|
29
|
+
{appName}
|
|
30
|
+
</Text>
|
|
31
|
+
)}
|
|
32
|
+
</View>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { ActivityIndicator, Pressable, ScrollView, Text, View } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
import { usePalette } from "../utils/palette";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Optional brand color overrides so onboarding matches the host app's sign-in
|
|
8
|
+
* styling instead of the generic theme palette. Any omitted key falls back to
|
|
9
|
+
* the active palette.
|
|
10
|
+
*/
|
|
11
|
+
export interface OnboardingTheme {
|
|
12
|
+
background?: string;
|
|
13
|
+
card?: string;
|
|
14
|
+
border?: string;
|
|
15
|
+
foreground?: string;
|
|
16
|
+
mutedForeground?: string;
|
|
17
|
+
primary?: string;
|
|
18
|
+
primaryForeground?: string;
|
|
19
|
+
/** Icon-tile background on OnboardingOptionCard. */
|
|
20
|
+
iconBackground?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Generic, app-agnostic first-run onboarding wizard. The host app supplies the
|
|
24
|
+
// step content (e.g. "secure your account" with MFA/passkey, "connect your
|
|
25
|
+
// accounts" with Plaid); this shell owns the chrome: progress dots, title,
|
|
26
|
+
// scroll body, and the continue/skip/finish footer. Reusable across finance
|
|
27
|
+
// apps (Simple Finance, Paperhands) and beyond — it knows nothing about MFA,
|
|
28
|
+
// Plaid, or any domain.
|
|
29
|
+
|
|
30
|
+
export interface OnboardingStepApi {
|
|
31
|
+
/** Advance to the next step (or finish on the last step). */
|
|
32
|
+
next: () => void;
|
|
33
|
+
/** Go back to the previous step (no-op on the first). */
|
|
34
|
+
back: () => void;
|
|
35
|
+
index: number;
|
|
36
|
+
total: number;
|
|
37
|
+
isLast: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface OnboardingStepDef {
|
|
41
|
+
key: string;
|
|
42
|
+
title: string;
|
|
43
|
+
subtitle?: string;
|
|
44
|
+
/** Step body. Receives the wizard api so its own actions can advance. */
|
|
45
|
+
content: (api: OnboardingStepApi) => React.ReactNode;
|
|
46
|
+
/** Show a "skip" affordance that advances without completing the step. */
|
|
47
|
+
canSkip?: boolean;
|
|
48
|
+
/** Override the primary button label for this step. */
|
|
49
|
+
primaryLabel?: string;
|
|
50
|
+
/** Hide the footer primary button (the step drives its own advance). */
|
|
51
|
+
hidePrimary?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Gate advancing: when false the primary button is disabled (e.g. a required
|
|
54
|
+
* step the user hasn't satisfied yet, like mandatory MFA enrollment).
|
|
55
|
+
* Defaults to true.
|
|
56
|
+
*/
|
|
57
|
+
canContinue?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Already-satisfied step. Counts toward the total + shows a filled progress
|
|
60
|
+
* dot, but its body is never rendered — the wizard starts at the first
|
|
61
|
+
* not-done step and skips done ones. Lets a resumed onboarding show absolute
|
|
62
|
+
* position ("Step 3 of 3") instead of remaining-only ("1 of 1").
|
|
63
|
+
*/
|
|
64
|
+
done?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface OnboardingWizardProps {
|
|
68
|
+
steps: OnboardingStepDef[];
|
|
69
|
+
/** Called after the final step's continue/skip. Mark onboarding complete here. */
|
|
70
|
+
onComplete: () => void | Promise<void>;
|
|
71
|
+
/** Branding slot rendered above the progress + card (e.g. logo + app name). */
|
|
72
|
+
header?: React.ReactNode;
|
|
73
|
+
/** Card max width on large screens / web so it reads as an overlay, not full-bleed. */
|
|
74
|
+
maxWidth?: number;
|
|
75
|
+
/** Show the "Step X of N" label under the progress dots. Default true. */
|
|
76
|
+
showStepLabel?: boolean;
|
|
77
|
+
/** Brand color overrides so the chrome matches the app's sign-in styling. */
|
|
78
|
+
theme?: OnboardingTheme;
|
|
79
|
+
skipLabel?: string;
|
|
80
|
+
finishLabel?: string;
|
|
81
|
+
continueLabel?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function OnboardingWizard({
|
|
85
|
+
steps,
|
|
86
|
+
onComplete,
|
|
87
|
+
header,
|
|
88
|
+
maxWidth = 440,
|
|
89
|
+
showStepLabel = true,
|
|
90
|
+
theme,
|
|
91
|
+
skipLabel = "Skip for now",
|
|
92
|
+
finishLabel = "Finish",
|
|
93
|
+
continueLabel = "Continue",
|
|
94
|
+
}: OnboardingWizardProps) {
|
|
95
|
+
const { palette } = usePalette();
|
|
96
|
+
const c = {
|
|
97
|
+
background: theme?.background ?? palette.background,
|
|
98
|
+
card: theme?.card ?? palette.card,
|
|
99
|
+
border: theme?.border ?? palette.border,
|
|
100
|
+
foreground: theme?.foreground ?? palette.foreground,
|
|
101
|
+
mutedForeground: theme?.mutedForeground ?? palette.mutedForeground,
|
|
102
|
+
primary: theme?.primary ?? palette.primary,
|
|
103
|
+
primaryForeground: theme?.primaryForeground ?? palette.primaryForeground,
|
|
104
|
+
};
|
|
105
|
+
// Start at the first not-done step; done steps before it are completed
|
|
106
|
+
// milestones (filled dots, never rendered).
|
|
107
|
+
const [index, setIndex] = useState(() => {
|
|
108
|
+
const i = steps.findIndex((s) => !s.done);
|
|
109
|
+
return i === -1 ? 0 : i;
|
|
110
|
+
});
|
|
111
|
+
const [finishing, setFinishing] = useState(false);
|
|
112
|
+
|
|
113
|
+
const finish = async () => {
|
|
114
|
+
setFinishing(true);
|
|
115
|
+
try {
|
|
116
|
+
await onComplete();
|
|
117
|
+
} finally {
|
|
118
|
+
setFinishing(false);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Every step satisfied — nothing to render; complete once.
|
|
123
|
+
const allDone = steps.every((s) => s.done);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (allDone) {
|
|
126
|
+
void finish();
|
|
127
|
+
}
|
|
128
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
129
|
+
}, [allDone]);
|
|
130
|
+
|
|
131
|
+
const step = steps[index];
|
|
132
|
+
// Next/prev among the not-done steps only (done ones are skipped over).
|
|
133
|
+
const nextActive = steps.findIndex((s, i) => i > index && !s.done);
|
|
134
|
+
const isLast = nextActive === -1;
|
|
135
|
+
|
|
136
|
+
const next = async () => {
|
|
137
|
+
if (isLast) {
|
|
138
|
+
await finish();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
setIndex(nextActive);
|
|
142
|
+
};
|
|
143
|
+
const back = () => {
|
|
144
|
+
let prev = -1;
|
|
145
|
+
for (let i = 0; i < index; i += 1) {
|
|
146
|
+
if (!steps[i]?.done) {
|
|
147
|
+
prev = i;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (prev >= 0) {
|
|
151
|
+
setIndex(prev);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const api: OnboardingStepApi = { next, back, index, total: steps.length, isLast };
|
|
156
|
+
|
|
157
|
+
if (!step) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<SafeAreaView style={{ flex: 1, backgroundColor: c.background }}>
|
|
163
|
+
<ScrollView
|
|
164
|
+
contentContainerStyle={{
|
|
165
|
+
flexGrow: 1,
|
|
166
|
+
alignItems: "center",
|
|
167
|
+
justifyContent: "center",
|
|
168
|
+
padding: 24,
|
|
169
|
+
}}
|
|
170
|
+
showsVerticalScrollIndicator={false}
|
|
171
|
+
keyboardShouldPersistTaps="handled"
|
|
172
|
+
>
|
|
173
|
+
{/* constrained, centered column — an overlay card on web, full-width on phones */}
|
|
174
|
+
<View style={{ width: "100%", maxWidth }}>
|
|
175
|
+
{header ? <View style={{ alignItems: "center", marginBottom: 22 }}>{header}</View> : null}
|
|
176
|
+
|
|
177
|
+
{/* progress: dots + step label */}
|
|
178
|
+
<View style={{ alignItems: "center", gap: 8, marginBottom: 16 }}>
|
|
179
|
+
<View style={{ flexDirection: "row", gap: 6 }}>
|
|
180
|
+
{steps.map((s, i) => (
|
|
181
|
+
<View
|
|
182
|
+
key={s.key}
|
|
183
|
+
style={{
|
|
184
|
+
width: i === index ? 22 : 7,
|
|
185
|
+
height: 7,
|
|
186
|
+
borderRadius: 999,
|
|
187
|
+
backgroundColor: i <= index || s.done ? c.primary : c.border,
|
|
188
|
+
}}
|
|
189
|
+
/>
|
|
190
|
+
))}
|
|
191
|
+
</View>
|
|
192
|
+
{showStepLabel ? (
|
|
193
|
+
<Text style={{ color: c.mutedForeground, fontFamily: "Geist-Medium", fontSize: 12 }}>
|
|
194
|
+
Step {index + 1} of {steps.length}
|
|
195
|
+
</Text>
|
|
196
|
+
) : null}
|
|
197
|
+
</View>
|
|
198
|
+
|
|
199
|
+
{/* card panel */}
|
|
200
|
+
<View
|
|
201
|
+
style={{
|
|
202
|
+
backgroundColor: c.card,
|
|
203
|
+
borderRadius: 24,
|
|
204
|
+
borderWidth: 1,
|
|
205
|
+
borderColor: c.border,
|
|
206
|
+
padding: 20,
|
|
207
|
+
gap: 8,
|
|
208
|
+
}}
|
|
209
|
+
>
|
|
210
|
+
<Text style={{ color: c.foreground, fontFamily: "Geist-Bold", fontSize: 22 }}>
|
|
211
|
+
{step.title}
|
|
212
|
+
</Text>
|
|
213
|
+
{step.subtitle ? (
|
|
214
|
+
<Text
|
|
215
|
+
style={{
|
|
216
|
+
color: c.mutedForeground,
|
|
217
|
+
fontFamily: "Geist-Regular",
|
|
218
|
+
fontSize: 14,
|
|
219
|
+
lineHeight: 20,
|
|
220
|
+
}}
|
|
221
|
+
>
|
|
222
|
+
{step.subtitle}
|
|
223
|
+
</Text>
|
|
224
|
+
) : null}
|
|
225
|
+
<View style={{ marginTop: 10 }}>{step.content(api)}</View>
|
|
226
|
+
|
|
227
|
+
<View style={{ gap: 10, marginTop: 14 }}>
|
|
228
|
+
{!step.hidePrimary ? (
|
|
229
|
+
<Pressable
|
|
230
|
+
onPress={next}
|
|
231
|
+
disabled={finishing || step.canContinue === false}
|
|
232
|
+
style={{
|
|
233
|
+
backgroundColor: c.primary,
|
|
234
|
+
borderRadius: 14,
|
|
235
|
+
paddingVertical: 15,
|
|
236
|
+
alignItems: "center",
|
|
237
|
+
opacity: finishing || step.canContinue === false ? 0.5 : 1,
|
|
238
|
+
}}
|
|
239
|
+
>
|
|
240
|
+
{finishing ? (
|
|
241
|
+
<ActivityIndicator color={c.primaryForeground} />
|
|
242
|
+
) : (
|
|
243
|
+
<Text
|
|
244
|
+
style={{ color: c.primaryForeground, fontFamily: "Geist-Bold", fontSize: 16 }}
|
|
245
|
+
>
|
|
246
|
+
{step.primaryLabel ?? (isLast ? finishLabel : continueLabel)}
|
|
247
|
+
</Text>
|
|
248
|
+
)}
|
|
249
|
+
</Pressable>
|
|
250
|
+
) : null}
|
|
251
|
+
{step.canSkip ? (
|
|
252
|
+
<Pressable
|
|
253
|
+
onPress={next}
|
|
254
|
+
disabled={finishing}
|
|
255
|
+
style={{ alignItems: "center", paddingVertical: 4 }}
|
|
256
|
+
>
|
|
257
|
+
<Text
|
|
258
|
+
style={{ color: c.mutedForeground, fontFamily: "Geist-Medium", fontSize: 14 }}
|
|
259
|
+
>
|
|
260
|
+
{skipLabel}
|
|
261
|
+
</Text>
|
|
262
|
+
</Pressable>
|
|
263
|
+
) : null}
|
|
264
|
+
</View>
|
|
265
|
+
</View>
|
|
266
|
+
</View>
|
|
267
|
+
</ScrollView>
|
|
268
|
+
</SafeAreaView>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export interface OnboardingOptionCardProps {
|
|
273
|
+
icon?: React.ReactNode;
|
|
274
|
+
title: string;
|
|
275
|
+
subtitle?: string;
|
|
276
|
+
/** Trailing element (e.g. a spinner or chevron). */
|
|
277
|
+
right?: React.ReactNode;
|
|
278
|
+
onPress?: () => void;
|
|
279
|
+
/** Highlight the card (e.g. completed/selected) with the primary color. */
|
|
280
|
+
selected?: boolean;
|
|
281
|
+
/** Brand color overrides to match the app's sign-in styling. */
|
|
282
|
+
theme?: OnboardingTheme;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Tappable card for onboarding step bodies — an icon tile, title + subtitle,
|
|
287
|
+
* and optional trailing slot. Domain-agnostic; apps drop these into a wizard
|
|
288
|
+
* step to offer choices (add passkey, connect a bank, …).
|
|
289
|
+
*/
|
|
290
|
+
export function OnboardingOptionCard({
|
|
291
|
+
icon,
|
|
292
|
+
title,
|
|
293
|
+
subtitle,
|
|
294
|
+
right,
|
|
295
|
+
onPress,
|
|
296
|
+
selected,
|
|
297
|
+
theme,
|
|
298
|
+
}: OnboardingOptionCardProps) {
|
|
299
|
+
const { palette } = usePalette();
|
|
300
|
+
const c = {
|
|
301
|
+
card: theme?.card ?? palette.card,
|
|
302
|
+
border: theme?.border ?? palette.border,
|
|
303
|
+
foreground: theme?.foreground ?? palette.foreground,
|
|
304
|
+
mutedForeground: theme?.mutedForeground ?? palette.mutedForeground,
|
|
305
|
+
primary: theme?.primary ?? palette.primary,
|
|
306
|
+
iconBackground: theme?.iconBackground ?? palette.muted,
|
|
307
|
+
};
|
|
308
|
+
return (
|
|
309
|
+
<Pressable
|
|
310
|
+
onPress={onPress}
|
|
311
|
+
disabled={!onPress}
|
|
312
|
+
style={{
|
|
313
|
+
flexDirection: "row",
|
|
314
|
+
alignItems: "center",
|
|
315
|
+
gap: 12,
|
|
316
|
+
padding: 16,
|
|
317
|
+
borderRadius: 14,
|
|
318
|
+
borderWidth: 1,
|
|
319
|
+
borderColor: selected ? c.primary : c.border,
|
|
320
|
+
backgroundColor: c.card,
|
|
321
|
+
opacity: onPress ? 1 : 0.9,
|
|
322
|
+
}}
|
|
323
|
+
>
|
|
324
|
+
{icon ? (
|
|
325
|
+
<View
|
|
326
|
+
style={{
|
|
327
|
+
width: 42,
|
|
328
|
+
height: 42,
|
|
329
|
+
borderRadius: 12,
|
|
330
|
+
alignItems: "center",
|
|
331
|
+
justifyContent: "center",
|
|
332
|
+
backgroundColor: c.iconBackground,
|
|
333
|
+
}}
|
|
334
|
+
>
|
|
335
|
+
{icon}
|
|
336
|
+
</View>
|
|
337
|
+
) : null}
|
|
338
|
+
<View style={{ flex: 1 }}>
|
|
339
|
+
<Text style={{ color: c.foreground, fontFamily: "Geist-Medium", fontSize: 15 }}>
|
|
340
|
+
{title}
|
|
341
|
+
</Text>
|
|
342
|
+
{subtitle ? (
|
|
343
|
+
<Text style={{ color: c.mutedForeground, fontFamily: "Geist-Regular", fontSize: 12 }}>
|
|
344
|
+
{subtitle}
|
|
345
|
+
</Text>
|
|
346
|
+
) : null}
|
|
347
|
+
</View>
|
|
348
|
+
{right}
|
|
349
|
+
</Pressable>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export default OnboardingWizard;
|