@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.
Files changed (590) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +21 -0
  3. package/README.md +115 -0
  4. package/lib/components/auth/LockOverlay.d.ts +22 -0
  5. package/lib/components/auth/LockOverlay.d.ts.map +1 -0
  6. package/lib/components/auth/LockOverlay.jsx +82 -0
  7. package/lib/components/auth/LockOverlay.jsx.map +1 -0
  8. package/lib/components/auth/PasswordStrengthMeter.d.ts +12 -0
  9. package/lib/components/auth/PasswordStrengthMeter.d.ts.map +1 -0
  10. package/lib/components/auth/PasswordStrengthMeter.jsx +81 -0
  11. package/lib/components/auth/PasswordStrengthMeter.jsx.map +1 -0
  12. package/lib/components/auth/ProtectedRoute.d.ts +24 -0
  13. package/lib/components/auth/ProtectedRoute.d.ts.map +1 -0
  14. package/lib/components/auth/ProtectedRoute.jsx +56 -0
  15. package/lib/components/auth/ProtectedRoute.jsx.map +1 -0
  16. package/lib/components/auth/PwnedPasswordWarning.d.ts +13 -0
  17. package/lib/components/auth/PwnedPasswordWarning.d.ts.map +1 -0
  18. package/lib/components/auth/PwnedPasswordWarning.jsx +29 -0
  19. package/lib/components/auth/PwnedPasswordWarning.jsx.map +1 -0
  20. package/lib/components/auth/index.d.ts +5 -0
  21. package/lib/components/auth/index.d.ts.map +1 -0
  22. package/lib/components/auth/index.js +5 -0
  23. package/lib/components/auth/index.js.map +1 -0
  24. package/lib/components/bottom-nav.d.ts +25 -0
  25. package/lib/components/bottom-nav.d.ts.map +1 -0
  26. package/lib/components/bottom-nav.jsx +51 -0
  27. package/lib/components/bottom-nav.jsx.map +1 -0
  28. package/lib/components/button.d.ts +16 -0
  29. package/lib/components/button.d.ts.map +1 -0
  30. package/lib/components/button.jsx +73 -0
  31. package/lib/components/button.jsx.map +1 -0
  32. package/lib/components/card.d.ts +22 -0
  33. package/lib/components/card.d.ts.map +1 -0
  34. package/lib/components/card.jsx +36 -0
  35. package/lib/components/card.jsx.map +1 -0
  36. package/lib/components/checkbox.d.ts +12 -0
  37. package/lib/components/checkbox.d.ts.map +1 -0
  38. package/lib/components/checkbox.jsx +16 -0
  39. package/lib/components/checkbox.jsx.map +1 -0
  40. package/lib/components/connection-settings.d.ts +41 -0
  41. package/lib/components/connection-settings.d.ts.map +1 -0
  42. package/lib/components/connection-settings.jsx +354 -0
  43. package/lib/components/connection-settings.jsx.map +1 -0
  44. package/lib/components/context-menu.d.ts +52 -0
  45. package/lib/components/context-menu.d.ts.map +1 -0
  46. package/lib/components/context-menu.jsx +24 -0
  47. package/lib/components/context-menu.jsx.map +1 -0
  48. package/lib/components/data-table.d.ts +59 -0
  49. package/lib/components/data-table.d.ts.map +1 -0
  50. package/lib/components/data-table.jsx +101 -0
  51. package/lib/components/data-table.jsx.map +1 -0
  52. package/lib/components/dialog.d.ts +58 -0
  53. package/lib/components/dialog.d.ts.map +1 -0
  54. package/lib/components/dialog.jsx +197 -0
  55. package/lib/components/dialog.jsx.map +1 -0
  56. package/lib/components/dropdown-menu.d.ts +55 -0
  57. package/lib/components/dropdown-menu.d.ts.map +1 -0
  58. package/lib/components/dropdown-menu.jsx +38 -0
  59. package/lib/components/dropdown-menu.jsx.map +1 -0
  60. package/lib/components/entropy-boot-splash.d.ts +24 -0
  61. package/lib/components/entropy-boot-splash.d.ts.map +1 -0
  62. package/lib/components/entropy-boot-splash.jsx +83 -0
  63. package/lib/components/entropy-boot-splash.jsx.map +1 -0
  64. package/lib/components/entropy-mark-animated.d.ts +20 -0
  65. package/lib/components/entropy-mark-animated.d.ts.map +1 -0
  66. package/lib/components/entropy-mark-animated.jsx +148 -0
  67. package/lib/components/entropy-mark-animated.jsx.map +1 -0
  68. package/lib/components/entropy-mark-geometry.d.ts +8 -0
  69. package/lib/components/entropy-mark-geometry.d.ts.map +1 -0
  70. package/lib/components/entropy-mark-geometry.js +10 -0
  71. package/lib/components/entropy-mark-geometry.js.map +1 -0
  72. package/lib/components/entropy-mark-sequential.d.ts +12 -0
  73. package/lib/components/entropy-mark-sequential.d.ts.map +1 -0
  74. package/lib/components/entropy-mark-sequential.jsx +53 -0
  75. package/lib/components/entropy-mark-sequential.jsx.map +1 -0
  76. package/lib/components/entropy-mark.d.ts +29 -0
  77. package/lib/components/entropy-mark.d.ts.map +1 -0
  78. package/lib/components/entropy-mark.jsx +66 -0
  79. package/lib/components/entropy-mark.jsx.map +1 -0
  80. package/lib/components/error-boundary.d.ts +31 -0
  81. package/lib/components/error-boundary.d.ts.map +1 -0
  82. package/lib/components/error-boundary.jsx +40 -0
  83. package/lib/components/error-boundary.jsx.map +1 -0
  84. package/lib/components/index.d.ts +41 -0
  85. package/lib/components/index.d.ts.map +1 -0
  86. package/lib/components/index.js +43 -0
  87. package/lib/components/index.js.map +1 -0
  88. package/lib/components/input.d.ts +26 -0
  89. package/lib/components/input.d.ts.map +1 -0
  90. package/lib/components/input.jsx +95 -0
  91. package/lib/components/input.jsx.map +1 -0
  92. package/lib/components/item-icon.d.ts +32 -0
  93. package/lib/components/item-icon.d.ts.map +1 -0
  94. package/lib/components/item-icon.jsx +54 -0
  95. package/lib/components/item-icon.jsx.map +1 -0
  96. package/lib/components/label.d.ts +13 -0
  97. package/lib/components/label.d.ts.map +1 -0
  98. package/lib/components/label.jsx +9 -0
  99. package/lib/components/label.jsx.map +1 -0
  100. package/lib/components/legal-dialog.d.ts +18 -0
  101. package/lib/components/legal-dialog.d.ts.map +1 -0
  102. package/lib/components/legal-dialog.jsx +31 -0
  103. package/lib/components/legal-dialog.jsx.map +1 -0
  104. package/lib/components/license-gate.d.ts +17 -0
  105. package/lib/components/license-gate.d.ts.map +1 -0
  106. package/lib/components/license-gate.jsx +21 -0
  107. package/lib/components/license-gate.jsx.map +1 -0
  108. package/lib/components/logo.d.ts +15 -0
  109. package/lib/components/logo.d.ts.map +1 -0
  110. package/lib/components/logo.jsx +20 -0
  111. package/lib/components/logo.jsx.map +1 -0
  112. package/lib/components/onboarding-wizard.d.ts +89 -0
  113. package/lib/components/onboarding-wizard.d.ts.map +1 -0
  114. package/lib/components/onboarding-wizard.jsx +185 -0
  115. package/lib/components/onboarding-wizard.jsx.map +1 -0
  116. package/lib/components/pagination.d.ts +43 -0
  117. package/lib/components/pagination.d.ts.map +1 -0
  118. package/lib/components/pagination.jsx +72 -0
  119. package/lib/components/pagination.jsx.map +1 -0
  120. package/lib/components/password-generator.d.ts +15 -0
  121. package/lib/components/password-generator.d.ts.map +1 -0
  122. package/lib/components/password-generator.jsx +273 -0
  123. package/lib/components/password-generator.jsx.map +1 -0
  124. package/lib/components/pin-pad.d.ts +20 -0
  125. package/lib/components/pin-pad.d.ts.map +1 -0
  126. package/lib/components/pin-pad.jsx +93 -0
  127. package/lib/components/pin-pad.jsx.map +1 -0
  128. package/lib/components/popover.d.ts +27 -0
  129. package/lib/components/popover.d.ts.map +1 -0
  130. package/lib/components/popover.jsx +18 -0
  131. package/lib/components/popover.jsx.map +1 -0
  132. package/lib/components/profile/ProfileDialog.d.ts +25 -0
  133. package/lib/components/profile/ProfileDialog.d.ts.map +1 -0
  134. package/lib/components/profile/ProfileDialog.jsx +71 -0
  135. package/lib/components/profile/ProfileDialog.jsx.map +1 -0
  136. package/lib/components/profile/ProfileTab.d.ts +12 -0
  137. package/lib/components/profile/ProfileTab.d.ts.map +1 -0
  138. package/lib/components/profile/ProfileTab.jsx +112 -0
  139. package/lib/components/profile/ProfileTab.jsx.map +1 -0
  140. package/lib/components/profile/SecurityTab.d.ts +32 -0
  141. package/lib/components/profile/SecurityTab.d.ts.map +1 -0
  142. package/lib/components/profile/SecurityTab.jsx +145 -0
  143. package/lib/components/profile/SecurityTab.jsx.map +1 -0
  144. package/lib/components/profile/index.d.ts +4 -0
  145. package/lib/components/profile/index.d.ts.map +1 -0
  146. package/lib/components/profile/index.js +4 -0
  147. package/lib/components/profile/index.js.map +1 -0
  148. package/lib/components/segmented-toggle.d.ts +28 -0
  149. package/lib/components/segmented-toggle.d.ts.map +1 -0
  150. package/lib/components/segmented-toggle.jsx +64 -0
  151. package/lib/components/segmented-toggle.jsx.map +1 -0
  152. package/lib/components/separator.d.ts +13 -0
  153. package/lib/components/separator.d.ts.map +1 -0
  154. package/lib/components/separator.jsx +16 -0
  155. package/lib/components/separator.jsx.map +1 -0
  156. package/lib/components/server-settings.d.ts +27 -0
  157. package/lib/components/server-settings.d.ts.map +1 -0
  158. package/lib/components/server-settings.jsx +307 -0
  159. package/lib/components/server-settings.jsx.map +1 -0
  160. package/lib/components/settings/AccountSection.d.ts +26 -0
  161. package/lib/components/settings/AccountSection.d.ts.map +1 -0
  162. package/lib/components/settings/AccountSection.jsx +32 -0
  163. package/lib/components/settings/AccountSection.jsx.map +1 -0
  164. package/lib/components/settings/DangerZoneSection.d.ts +13 -0
  165. package/lib/components/settings/DangerZoneSection.d.ts.map +1 -0
  166. package/lib/components/settings/DangerZoneSection.jsx +16 -0
  167. package/lib/components/settings/DangerZoneSection.jsx.map +1 -0
  168. package/lib/components/settings/GeneralSection.d.ts +11 -0
  169. package/lib/components/settings/GeneralSection.d.ts.map +1 -0
  170. package/lib/components/settings/GeneralSection.jsx +12 -0
  171. package/lib/components/settings/GeneralSection.jsx.map +1 -0
  172. package/lib/components/settings/SupportSection.d.ts +11 -0
  173. package/lib/components/settings/SupportSection.d.ts.map +1 -0
  174. package/lib/components/settings/SupportSection.jsx +22 -0
  175. package/lib/components/settings/SupportSection.jsx.map +1 -0
  176. package/lib/components/settings/index.d.ts +5 -0
  177. package/lib/components/settings/index.d.ts.map +1 -0
  178. package/lib/components/settings/index.js +5 -0
  179. package/lib/components/settings/index.js.map +1 -0
  180. package/lib/components/settings-row.d.ts +28 -0
  181. package/lib/components/settings-row.d.ts.map +1 -0
  182. package/lib/components/settings-row.jsx +68 -0
  183. package/lib/components/settings-row.jsx.map +1 -0
  184. package/lib/components/skeleton.d.ts +8 -0
  185. package/lib/components/skeleton.d.ts.map +1 -0
  186. package/lib/components/skeleton.jsx +24 -0
  187. package/lib/components/skeleton.jsx.map +1 -0
  188. package/lib/components/social-connections.d.ts +15 -0
  189. package/lib/components/social-connections.d.ts.map +1 -0
  190. package/lib/components/social-connections.jsx +31 -0
  191. package/lib/components/social-connections.jsx.map +1 -0
  192. package/lib/components/stat-card.d.ts +29 -0
  193. package/lib/components/stat-card.d.ts.map +1 -0
  194. package/lib/components/stat-card.jsx +36 -0
  195. package/lib/components/stat-card.jsx.map +1 -0
  196. package/lib/components/tag-chip.d.ts +18 -0
  197. package/lib/components/tag-chip.d.ts.map +1 -0
  198. package/lib/components/tag-chip.jsx +54 -0
  199. package/lib/components/tag-chip.jsx.map +1 -0
  200. package/lib/components/tag-multi-select.d.ts +27 -0
  201. package/lib/components/tag-multi-select.d.ts.map +1 -0
  202. package/lib/components/tag-multi-select.jsx +65 -0
  203. package/lib/components/tag-multi-select.jsx.map +1 -0
  204. package/lib/components/text.d.ts +22 -0
  205. package/lib/components/text.d.ts.map +1 -0
  206. package/lib/components/text.jsx +43 -0
  207. package/lib/components/text.jsx.map +1 -0
  208. package/lib/components/toast/ToastPortal.d.ts +11 -0
  209. package/lib/components/toast/ToastPortal.d.ts.map +1 -0
  210. package/lib/components/toast/ToastPortal.jsx +15 -0
  211. package/lib/components/toast/ToastPortal.jsx.map +1 -0
  212. package/lib/components/toast/ToastPortal.web.d.ts +14 -0
  213. package/lib/components/toast/ToastPortal.web.d.ts.map +1 -0
  214. package/lib/components/toast/ToastPortal.web.jsx +39 -0
  215. package/lib/components/toast/ToastPortal.web.jsx.map +1 -0
  216. package/lib/components/toast/index.d.ts +3 -0
  217. package/lib/components/toast/index.d.ts.map +1 -0
  218. package/lib/components/toast/index.js +3 -0
  219. package/lib/components/toast/index.js.map +1 -0
  220. package/lib/components/toast/toast-config.d.ts +14 -0
  221. package/lib/components/toast/toast-config.d.ts.map +1 -0
  222. package/lib/components/toast/toast-config.jsx +24 -0
  223. package/lib/components/toast/toast-config.jsx.map +1 -0
  224. package/lib/components/tooltip.d.ts +30 -0
  225. package/lib/components/tooltip.d.ts.map +1 -0
  226. package/lib/components/tooltip.jsx +14 -0
  227. package/lib/components/tooltip.jsx.map +1 -0
  228. package/lib/components/totp-display.d.ts +23 -0
  229. package/lib/components/totp-display.d.ts.map +1 -0
  230. package/lib/components/totp-display.jsx +132 -0
  231. package/lib/components/totp-display.jsx.map +1 -0
  232. package/lib/components/user-avatar.d.ts +18 -0
  233. package/lib/components/user-avatar.d.ts.map +1 -0
  234. package/lib/components/user-avatar.jsx +25 -0
  235. package/lib/components/user-avatar.jsx.map +1 -0
  236. package/lib/context/AppConfigContext.d.ts +56 -0
  237. package/lib/context/AppConfigContext.d.ts.map +1 -0
  238. package/lib/context/AppConfigContext.jsx +14 -0
  239. package/lib/context/AppConfigContext.jsx.map +1 -0
  240. package/lib/context/NavigationContext.d.ts +42 -0
  241. package/lib/context/NavigationContext.d.ts.map +1 -0
  242. package/lib/context/NavigationContext.jsx +53 -0
  243. package/lib/context/NavigationContext.jsx.map +1 -0
  244. package/lib/context/NotificationContext.d.ts +32 -0
  245. package/lib/context/NotificationContext.d.ts.map +1 -0
  246. package/lib/context/NotificationContext.jsx +49 -0
  247. package/lib/context/NotificationContext.jsx.map +1 -0
  248. package/lib/context/ThemeContext.d.ts +12 -0
  249. package/lib/context/ThemeContext.d.ts.map +1 -0
  250. package/lib/context/ThemeContext.jsx +66 -0
  251. package/lib/context/ThemeContext.jsx.map +1 -0
  252. package/lib/context/index.d.ts +5 -0
  253. package/lib/context/index.d.ts.map +1 -0
  254. package/lib/context/index.js +5 -0
  255. package/lib/context/index.js.map +1 -0
  256. package/lib/flags.d.ts +9 -0
  257. package/lib/flags.d.ts.map +1 -0
  258. package/lib/flags.js +37 -0
  259. package/lib/flags.js.map +1 -0
  260. package/lib/forms/form-error.d.ts +12 -0
  261. package/lib/forms/form-error.d.ts.map +1 -0
  262. package/lib/forms/form-error.jsx +14 -0
  263. package/lib/forms/form-error.jsx.map +1 -0
  264. package/lib/forms/index.d.ts +3 -0
  265. package/lib/forms/index.d.ts.map +1 -0
  266. package/lib/forms/index.js +3 -0
  267. package/lib/forms/index.js.map +1 -0
  268. package/lib/forms/shake-field.d.ts +18 -0
  269. package/lib/forms/shake-field.d.ts.map +1 -0
  270. package/lib/forms/shake-field.jsx +30 -0
  271. package/lib/forms/shake-field.jsx.map +1 -0
  272. package/lib/hooks/index.d.ts +6 -0
  273. package/lib/hooks/index.d.ts.map +1 -0
  274. package/lib/hooks/index.js +6 -0
  275. package/lib/hooks/index.js.map +1 -0
  276. package/lib/hooks/useAuth.d.ts +109 -0
  277. package/lib/hooks/useAuth.d.ts.map +1 -0
  278. package/lib/hooks/useAuth.jsx +404 -0
  279. package/lib/hooks/useAuth.jsx.map +1 -0
  280. package/lib/hooks/useAutoLock.d.ts +22 -0
  281. package/lib/hooks/useAutoLock.d.ts.map +1 -0
  282. package/lib/hooks/useAutoLock.js +85 -0
  283. package/lib/hooks/useAutoLock.js.map +1 -0
  284. package/lib/hooks/useDebouncedValue.d.ts +19 -0
  285. package/lib/hooks/useDebouncedValue.d.ts.map +1 -0
  286. package/lib/hooks/useDebouncedValue.js +29 -0
  287. package/lib/hooks/useDebouncedValue.js.map +1 -0
  288. package/lib/hooks/useShakeAnimation.d.ts +17 -0
  289. package/lib/hooks/useShakeAnimation.d.ts.map +1 -0
  290. package/lib/hooks/useShakeAnimation.js +22 -0
  291. package/lib/hooks/useShakeAnimation.js.map +1 -0
  292. package/lib/hooks/useUploadBlobUrl.d.ts +13 -0
  293. package/lib/hooks/useUploadBlobUrl.d.ts.map +1 -0
  294. package/lib/hooks/useUploadBlobUrl.js +60 -0
  295. package/lib/hooks/useUploadBlobUrl.js.map +1 -0
  296. package/lib/index.d.ts +10 -0
  297. package/lib/index.d.ts.map +1 -0
  298. package/lib/index.js +47 -0
  299. package/lib/index.js.map +1 -0
  300. package/lib/screens/auth/AccountPickerScreen.d.ts +24 -0
  301. package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -0
  302. package/lib/screens/auth/AccountPickerScreen.jsx +47 -0
  303. package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -0
  304. package/lib/screens/auth/AuthScreen.d.ts +59 -0
  305. package/lib/screens/auth/AuthScreen.d.ts.map +1 -0
  306. package/lib/screens/auth/AuthScreen.jsx +594 -0
  307. package/lib/screens/auth/AuthScreen.jsx.map +1 -0
  308. package/lib/screens/auth/ConsentScreen.d.ts +27 -0
  309. package/lib/screens/auth/ConsentScreen.d.ts.map +1 -0
  310. package/lib/screens/auth/ConsentScreen.jsx +55 -0
  311. package/lib/screens/auth/ConsentScreen.jsx.map +1 -0
  312. package/lib/screens/auth/ErrorScreen.d.ts +20 -0
  313. package/lib/screens/auth/ErrorScreen.d.ts.map +1 -0
  314. package/lib/screens/auth/ErrorScreen.jsx +43 -0
  315. package/lib/screens/auth/ErrorScreen.jsx.map +1 -0
  316. package/lib/screens/auth/ExpiredLinkScreen.d.ts +19 -0
  317. package/lib/screens/auth/ExpiredLinkScreen.d.ts.map +1 -0
  318. package/lib/screens/auth/ExpiredLinkScreen.jsx +38 -0
  319. package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -0
  320. package/lib/screens/auth/ForgotPasswordScreen.d.ts +4 -0
  321. package/lib/screens/auth/ForgotPasswordScreen.d.ts.map +1 -0
  322. package/lib/screens/auth/ForgotPasswordScreen.jsx +123 -0
  323. package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -0
  324. package/lib/screens/auth/ImportDataScreen.d.ts +17 -0
  325. package/lib/screens/auth/ImportDataScreen.d.ts.map +1 -0
  326. package/lib/screens/auth/ImportDataScreen.jsx +73 -0
  327. package/lib/screens/auth/ImportDataScreen.jsx.map +1 -0
  328. package/lib/screens/auth/LoginScreen.d.ts +38 -0
  329. package/lib/screens/auth/LoginScreen.d.ts.map +1 -0
  330. package/lib/screens/auth/LoginScreen.jsx +326 -0
  331. package/lib/screens/auth/LoginScreen.jsx.map +1 -0
  332. package/lib/screens/auth/MfaChallengeScreen.d.ts +4 -0
  333. package/lib/screens/auth/MfaChallengeScreen.d.ts.map +1 -0
  334. package/lib/screens/auth/MfaChallengeScreen.jsx +139 -0
  335. package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -0
  336. package/lib/screens/auth/MfaEnrollScreen.d.ts +12 -0
  337. package/lib/screens/auth/MfaEnrollScreen.d.ts.map +1 -0
  338. package/lib/screens/auth/MfaEnrollScreen.jsx +267 -0
  339. package/lib/screens/auth/MfaEnrollScreen.jsx.map +1 -0
  340. package/lib/screens/auth/PasskeyEnrollScreen.d.ts +18 -0
  341. package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -0
  342. package/lib/screens/auth/PasskeyEnrollScreen.jsx +51 -0
  343. package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -0
  344. package/lib/screens/auth/PrivacyPolicyScreen.d.ts +19 -0
  345. package/lib/screens/auth/PrivacyPolicyScreen.d.ts.map +1 -0
  346. package/lib/screens/auth/PrivacyPolicyScreen.jsx +143 -0
  347. package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -0
  348. package/lib/screens/auth/RegisterScreen.d.ts +14 -0
  349. package/lib/screens/auth/RegisterScreen.d.ts.map +1 -0
  350. package/lib/screens/auth/RegisterScreen.jsx +296 -0
  351. package/lib/screens/auth/RegisterScreen.jsx.map +1 -0
  352. package/lib/screens/auth/ResetPasswordScreen.d.ts +12 -0
  353. package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -0
  354. package/lib/screens/auth/ResetPasswordScreen.jsx +276 -0
  355. package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -0
  356. package/lib/screens/auth/ServerConfigScreen.d.ts +18 -0
  357. package/lib/screens/auth/ServerConfigScreen.d.ts.map +1 -0
  358. package/lib/screens/auth/ServerConfigScreen.jsx +259 -0
  359. package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -0
  360. package/lib/screens/auth/TermsOfServiceScreen.d.ts +30 -0
  361. package/lib/screens/auth/TermsOfServiceScreen.d.ts.map +1 -0
  362. package/lib/screens/auth/TermsOfServiceScreen.jsx +130 -0
  363. package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -0
  364. package/lib/screens/auth/VerifyEmailScreen.d.ts +4 -0
  365. package/lib/screens/auth/VerifyEmailScreen.d.ts.map +1 -0
  366. package/lib/screens/auth/VerifyEmailScreen.jsx +249 -0
  367. package/lib/screens/auth/VerifyEmailScreen.jsx.map +1 -0
  368. package/lib/screens/auth/WelcomeScreen.d.ts +17 -0
  369. package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -0
  370. package/lib/screens/auth/WelcomeScreen.jsx +58 -0
  371. package/lib/screens/auth/WelcomeScreen.jsx.map +1 -0
  372. package/lib/screens/auth/index.d.ts +19 -0
  373. package/lib/screens/auth/index.d.ts.map +1 -0
  374. package/lib/screens/auth/index.js +19 -0
  375. package/lib/screens/auth/index.js.map +1 -0
  376. package/lib/screens/index.d.ts +2 -0
  377. package/lib/screens/index.d.ts.map +1 -0
  378. package/lib/screens/index.js +2 -0
  379. package/lib/screens/index.js.map +1 -0
  380. package/lib/services/api-config.d.ts +87 -0
  381. package/lib/services/api-config.d.ts.map +1 -0
  382. package/lib/services/api-config.js +202 -0
  383. package/lib/services/api-config.js.map +1 -0
  384. package/lib/services/auth.service.d.ts +140 -0
  385. package/lib/services/auth.service.d.ts.map +1 -0
  386. package/lib/services/auth.service.js +376 -0
  387. package/lib/services/auth.service.js.map +1 -0
  388. package/lib/services/favicon.d.ts +46 -0
  389. package/lib/services/favicon.d.ts.map +1 -0
  390. package/lib/services/favicon.js +84 -0
  391. package/lib/services/favicon.js.map +1 -0
  392. package/lib/services/hibp-cache.d.ts +23 -0
  393. package/lib/services/hibp-cache.d.ts.map +1 -0
  394. package/lib/services/hibp-cache.js +56 -0
  395. package/lib/services/hibp-cache.js.map +1 -0
  396. package/lib/services/hibp.d.ts +28 -0
  397. package/lib/services/hibp.d.ts.map +1 -0
  398. package/lib/services/hibp.js +92 -0
  399. package/lib/services/hibp.js.map +1 -0
  400. package/lib/services/index.d.ts +8 -0
  401. package/lib/services/index.d.ts.map +1 -0
  402. package/lib/services/index.js +7 -0
  403. package/lib/services/index.js.map +1 -0
  404. package/lib/services/passkey.d.ts +10 -0
  405. package/lib/services/passkey.d.ts.map +1 -0
  406. package/lib/services/passkey.js +192 -0
  407. package/lib/services/passkey.js.map +1 -0
  408. package/lib/utils/api-fetch.d.ts +11 -0
  409. package/lib/utils/api-fetch.d.ts.map +1 -0
  410. package/lib/utils/api-fetch.js +52 -0
  411. package/lib/utils/api-fetch.js.map +1 -0
  412. package/lib/utils/clipboard.d.ts +31 -0
  413. package/lib/utils/clipboard.d.ts.map +1 -0
  414. package/lib/utils/clipboard.js +66 -0
  415. package/lib/utils/clipboard.js.map +1 -0
  416. package/lib/utils/cn.d.ts +9 -0
  417. package/lib/utils/cn.d.ts.map +1 -0
  418. package/lib/utils/cn.js +12 -0
  419. package/lib/utils/cn.js.map +1 -0
  420. package/lib/utils/debounce.d.ts +13 -0
  421. package/lib/utils/debounce.d.ts.map +1 -0
  422. package/lib/utils/debounce.js +28 -0
  423. package/lib/utils/debounce.js.map +1 -0
  424. package/lib/utils/error.d.ts +9 -0
  425. package/lib/utils/error.d.ts.map +1 -0
  426. package/lib/utils/error.js +11 -0
  427. package/lib/utils/error.js.map +1 -0
  428. package/lib/utils/fetch.d.ts +17 -0
  429. package/lib/utils/fetch.d.ts.map +1 -0
  430. package/lib/utils/fetch.js +29 -0
  431. package/lib/utils/fetch.js.map +1 -0
  432. package/lib/utils/http-error.d.ts +41 -0
  433. package/lib/utils/http-error.d.ts.map +1 -0
  434. package/lib/utils/http-error.js +63 -0
  435. package/lib/utils/http-error.js.map +1 -0
  436. package/lib/utils/index.d.ts +11 -0
  437. package/lib/utils/index.d.ts.map +1 -0
  438. package/lib/utils/index.js +11 -0
  439. package/lib/utils/index.js.map +1 -0
  440. package/lib/utils/palette.d.ts +46 -0
  441. package/lib/utils/palette.d.ts.map +1 -0
  442. package/lib/utils/palette.js +75 -0
  443. package/lib/utils/palette.js.map +1 -0
  444. package/lib/utils/password.d.ts +20 -0
  445. package/lib/utils/password.d.ts.map +1 -0
  446. package/lib/utils/password.js +51 -0
  447. package/lib/utils/password.js.map +1 -0
  448. package/lib/utils/settings-storage.d.ts +3 -0
  449. package/lib/utils/settings-storage.d.ts.map +1 -0
  450. package/lib/utils/settings-storage.js +62 -0
  451. package/lib/utils/settings-storage.js.map +1 -0
  452. package/lib/utils/validation.d.ts +29 -0
  453. package/lib/utils/validation.d.ts.map +1 -0
  454. package/lib/utils/validation.js +58 -0
  455. package/lib/utils/validation.js.map +1 -0
  456. package/package.json +204 -0
  457. package/src/components/__tests__/data-table.test.ts +94 -0
  458. package/src/components/__tests__/pagination.test.ts +73 -0
  459. package/src/components/__tests__/stat-card.test.ts +21 -0
  460. package/src/components/auth/LockOverlay.tsx +122 -0
  461. package/src/components/auth/PasswordStrengthMeter.tsx +96 -0
  462. package/src/components/auth/ProtectedRoute.tsx +91 -0
  463. package/src/components/auth/PwnedPasswordWarning.tsx +54 -0
  464. package/src/components/auth/index.ts +10 -0
  465. package/src/components/bottom-nav.tsx +99 -0
  466. package/src/components/button.tsx +144 -0
  467. package/src/components/card.tsx +82 -0
  468. package/src/components/checkbox.tsx +48 -0
  469. package/src/components/connection-settings.tsx +583 -0
  470. package/src/components/context-menu.tsx +95 -0
  471. package/src/components/data-table.tsx +223 -0
  472. package/src/components/dialog.tsx +376 -0
  473. package/src/components/dropdown-menu.tsx +120 -0
  474. package/src/components/entropy-boot-splash.tsx +114 -0
  475. package/src/components/entropy-mark-animated.tsx +260 -0
  476. package/src/components/entropy-mark-geometry.ts +11 -0
  477. package/src/components/entropy-mark-sequential.tsx +132 -0
  478. package/src/components/entropy-mark.tsx +135 -0
  479. package/src/components/error-boundary.tsx +70 -0
  480. package/src/components/index.ts +156 -0
  481. package/src/components/input.tsx +246 -0
  482. package/src/components/item-icon.tsx +111 -0
  483. package/src/components/label.tsx +25 -0
  484. package/src/components/legal-dialog.tsx +47 -0
  485. package/src/components/license-gate.tsx +28 -0
  486. package/src/components/logo.tsx +34 -0
  487. package/src/components/onboarding-wizard.tsx +353 -0
  488. package/src/components/pagination.tsx +154 -0
  489. package/src/components/password-generator.tsx +393 -0
  490. package/src/components/pin-pad.tsx +156 -0
  491. package/src/components/popover.tsx +38 -0
  492. package/src/components/profile/ProfileDialog.tsx +129 -0
  493. package/src/components/profile/ProfileTab.tsx +151 -0
  494. package/src/components/profile/SecurityTab.tsx +229 -0
  495. package/src/components/profile/index.ts +3 -0
  496. package/src/components/segmented-toggle.tsx +119 -0
  497. package/src/components/separator.tsx +41 -0
  498. package/src/components/server-settings.tsx +442 -0
  499. package/src/components/settings/AccountSection.tsx +76 -0
  500. package/src/components/settings/DangerZoneSection.tsx +37 -0
  501. package/src/components/settings/GeneralSection.tsx +39 -0
  502. package/src/components/settings/SupportSection.tsx +53 -0
  503. package/src/components/settings/index.ts +4 -0
  504. package/src/components/settings-row.tsx +126 -0
  505. package/src/components/skeleton.tsx +48 -0
  506. package/src/components/social-connections.tsx +55 -0
  507. package/src/components/stat-card.tsx +64 -0
  508. package/src/components/tag-chip.tsx +78 -0
  509. package/src/components/tag-multi-select.tsx +136 -0
  510. package/src/components/text.tsx +77 -0
  511. package/src/components/toast/ToastPortal.tsx +16 -0
  512. package/src/components/toast/ToastPortal.web.tsx +47 -0
  513. package/src/components/toast/index.ts +2 -0
  514. package/src/components/toast/toast-config.tsx +31 -0
  515. package/src/components/tooltip.tsx +30 -0
  516. package/src/components/totp-display.tsx +207 -0
  517. package/src/components/user-avatar.tsx +46 -0
  518. package/src/context/AppConfigContext.tsx +76 -0
  519. package/src/context/NavigationContext.tsx +75 -0
  520. package/src/context/NotificationContext.tsx +69 -0
  521. package/src/context/ThemeContext.tsx +83 -0
  522. package/src/context/__tests__/NavigationContext.test.tsx +53 -0
  523. package/src/context/index.ts +24 -0
  524. package/src/flags.ts +44 -0
  525. package/src/forms/form-error.tsx +20 -0
  526. package/src/forms/index.ts +2 -0
  527. package/src/forms/shake-field.tsx +52 -0
  528. package/src/hooks/__tests__/useUploadBlobUrl.test.tsx +77 -0
  529. package/src/hooks/index.ts +18 -0
  530. package/src/hooks/useAuth.tsx +567 -0
  531. package/src/hooks/useAutoLock.ts +110 -0
  532. package/src/hooks/useDebouncedValue.ts +32 -0
  533. package/src/hooks/useShakeAnimation.ts +42 -0
  534. package/src/hooks/useUploadBlobUrl.ts +75 -0
  535. package/src/index.ts +48 -0
  536. package/src/screens/auth/AccountPickerScreen.tsx +103 -0
  537. package/src/screens/auth/AuthScreen.tsx +941 -0
  538. package/src/screens/auth/ConsentScreen.tsx +118 -0
  539. package/src/screens/auth/ErrorScreen.tsx +96 -0
  540. package/src/screens/auth/ExpiredLinkScreen.tsx +87 -0
  541. package/src/screens/auth/ForgotPasswordScreen.tsx +182 -0
  542. package/src/screens/auth/ImportDataScreen.tsx +112 -0
  543. package/src/screens/auth/LoginScreen.tsx +484 -0
  544. package/src/screens/auth/MfaChallengeScreen.tsx +216 -0
  545. package/src/screens/auth/MfaEnrollScreen.tsx +388 -0
  546. package/src/screens/auth/PasskeyEnrollScreen.tsx +96 -0
  547. package/src/screens/auth/PrivacyPolicyScreen.tsx +249 -0
  548. package/src/screens/auth/RegisterScreen.tsx +430 -0
  549. package/src/screens/auth/ResetPasswordScreen.tsx +377 -0
  550. package/src/screens/auth/ServerConfigScreen.tsx +388 -0
  551. package/src/screens/auth/TermsOfServiceScreen.tsx +236 -0
  552. package/src/screens/auth/VerifyEmailScreen.tsx +310 -0
  553. package/src/screens/auth/WelcomeScreen.tsx +78 -0
  554. package/src/screens/auth/index.ts +36 -0
  555. package/src/screens/index.ts +1 -0
  556. package/src/services/__tests__/api-config.test.ts +67 -0
  557. package/src/services/__tests__/favicon.test.ts +88 -0
  558. package/src/services/__tests__/hibp-cache.test.ts +37 -0
  559. package/src/services/__tests__/hibp.test.ts +39 -0
  560. package/src/services/api-config.ts +260 -0
  561. package/src/services/auth.service.ts +498 -0
  562. package/src/services/favicon.ts +102 -0
  563. package/src/services/hibp-cache.ts +71 -0
  564. package/src/services/hibp.ts +120 -0
  565. package/src/services/index.ts +52 -0
  566. package/src/services/passkey.ts +213 -0
  567. package/src/types/external.d.ts +24 -0
  568. package/src/utils/__tests__/clipboard.test.ts +59 -0
  569. package/src/utils/__tests__/cn.test.ts +24 -0
  570. package/src/utils/__tests__/debounce.test.ts +42 -0
  571. package/src/utils/__tests__/error.test.ts +18 -0
  572. package/src/utils/__tests__/http-error.test.ts +33 -0
  573. package/src/utils/__tests__/palette.test.ts +22 -0
  574. package/src/utils/__tests__/password.test.ts +55 -0
  575. package/src/utils/__tests__/settings-storage.test.ts +15 -0
  576. package/src/utils/__tests__/validation.test.ts +52 -0
  577. package/src/utils/api-fetch.ts +61 -0
  578. package/src/utils/clipboard.ts +69 -0
  579. package/src/utils/cn.ts +12 -0
  580. package/src/utils/debounce.ts +33 -0
  581. package/src/utils/error.ts +10 -0
  582. package/src/utils/fetch.ts +33 -0
  583. package/src/utils/http-error.ts +81 -0
  584. package/src/utils/index.ts +23 -0
  585. package/src/utils/palette.ts +105 -0
  586. package/src/utils/password.ts +64 -0
  587. package/src/utils/settings-storage.ts +68 -0
  588. package/src/utils/validation.ts +71 -0
  589. package/styles/globals.css +262 -0
  590. package/tailwind-preset.js +163 -0
@@ -0,0 +1,38 @@
1
+ import React from "react";
2
+ import * as PopoverPrimitive from "@rn-primitives/popover";
3
+ import { cn } from "../utils/cn";
4
+ import { useColorScheme } from "nativewind";
5
+ import { Platform } from "react-native";
6
+
7
+ const Popover = PopoverPrimitive.Root;
8
+ const PopoverTrigger = PopoverPrimitive.Trigger;
9
+
10
+ const PopoverContent = React.forwardRef<
11
+ React.ElementRef<typeof PopoverPrimitive.Content>,
12
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & { className?: string }
13
+ >(({ className, sideOffset = 8, align = "start", side = "bottom", ...props }, ref) => {
14
+ const { colorScheme } = useColorScheme();
15
+ const isDark = colorScheme === "dark";
16
+
17
+ return (
18
+ <PopoverPrimitive.Portal>
19
+ <PopoverPrimitive.Overlay />
20
+ <PopoverPrimitive.Content
21
+ ref={ref}
22
+ sideOffset={sideOffset}
23
+ align={align}
24
+ side={side}
25
+ className={cn(
26
+ "z-50 w-72 rounded-lg border border-border p-4",
27
+ isDark ? "bg-neutral-900" : "bg-white",
28
+ Platform.OS === "web" && "shadow-lg",
29
+ className
30
+ )}
31
+ {...props}
32
+ />
33
+ </PopoverPrimitive.Portal>
34
+ );
35
+ });
36
+ PopoverContent.displayName = "PopoverContent";
37
+
38
+ export { Popover, PopoverTrigger, PopoverContent };
@@ -0,0 +1,129 @@
1
+ import React, { useCallback, useEffect, useState } from "react";
2
+ import { Pressable, ScrollView, useWindowDimensions, View } from "react-native";
3
+ import { useAuth } from "../../hooks/useAuth";
4
+ import { cn } from "../../utils/cn";
5
+ import { Dialog } from "../dialog";
6
+ import { MutedText, Text } from "../text";
7
+ import { UserAvatar } from "../user-avatar";
8
+ import { ProfileTab } from "./ProfileTab";
9
+ import { SecurityTab, type SecurityTabProps } from "./SecurityTab";
10
+
11
+ type Tab = "profile" | "security";
12
+
13
+ export interface ProfileDialogProps {
14
+ visible: boolean;
15
+ onClose: () => void;
16
+ initialTab?: Tab;
17
+ /** Wired to `SecurityTab`'s MFA-enrolment callback. */
18
+ onEnableMfa: () => void;
19
+ /**
20
+ * Optional client-side key-rewrap hook for password changes. Vault
21
+ * supplies `KeyManager.computeReWrap`; apps without a wrapped key
22
+ * omit it.
23
+ */
24
+ onComputeReWrap?: SecurityTabProps["onComputeReWrap"];
25
+ }
26
+
27
+ /**
28
+ * Account-management dialog with Profile / Security tabs. The avatar
29
+ * strip across the top pulls from the entropy-ui `UserAvatar` (which
30
+ * reads `useAuth().user`). Apps inject their MFA enrolment route via
31
+ * `onEnableMfa` and (optionally) a key-rewrap hook for password change.
32
+ */
33
+ export function ProfileDialog({
34
+ visible,
35
+ onClose,
36
+ initialTab,
37
+ onEnableMfa,
38
+ onComputeReWrap,
39
+ }: ProfileDialogProps) {
40
+ const { user } = useAuth();
41
+ const { height: screenHeight } = useWindowDimensions();
42
+
43
+ const [tab, setTab] = useState<Tab>(initialTab ?? "profile");
44
+
45
+ useEffect(() => {
46
+ if (visible) {
47
+ setTab(initialTab ?? "profile");
48
+ }
49
+ }, [visible, initialTab]);
50
+
51
+ const handleClose = useCallback(() => {
52
+ setTab("profile");
53
+ onClose();
54
+ }, [onClose]);
55
+
56
+ return (
57
+ <Dialog visible={visible} draggable onClose={handleClose} title="Manage Account" maxWidth={580}>
58
+ <View className="mb-5 flex-row items-center gap-3.5">
59
+ <UserAvatar size={52} />
60
+ <View className="flex-1">
61
+ <Text className="text-base font-bold">
62
+ {user?.display_name || user?.username || "User"}
63
+ </Text>
64
+ <MutedText className="mt-0.5 font-mono text-xs">{user?.email ?? ""}</MutedText>
65
+ </View>
66
+ <View className="rounded-xl bg-muted px-2.5 py-1 dark:bg-neutral-800">
67
+ <MutedText className="text-xs font-medium uppercase tracking-wider">
68
+ {user?.role ?? "User"}
69
+ </MutedText>
70
+ </View>
71
+ </View>
72
+
73
+ <View className="mb-5 flex-row border-b border-border dark:border-neutral-800">
74
+ {(["profile", "security"] as Tab[]).map((t) => {
75
+ const active = tab === t;
76
+ return (
77
+ <Pressable
78
+ key={t}
79
+ onPress={() => setTab(t)}
80
+ className="mr-1"
81
+ testID={`profile-tab-${t}`}
82
+ >
83
+ {(state) => {
84
+ // RN-Web extends PressableStateCallbackType with `hovered`;
85
+ // base RN doesn't, so widen via cast.
86
+ const hovered = (state as { hovered?: boolean }).hovered === true;
87
+ return (
88
+ <View
89
+ className={cn(
90
+ "-mb-px border-b-2 px-3.5 py-2",
91
+ active ? "border-foreground dark:border-white" : "border-transparent"
92
+ )}
93
+ >
94
+ <Text
95
+ className={cn(
96
+ "text-sm font-medium",
97
+ active || hovered
98
+ ? "text-foreground dark:text-white"
99
+ : "text-muted-foreground dark:text-neutral-400"
100
+ )}
101
+ >
102
+ {t === "profile" ? "Profile" : "Security"}
103
+ </Text>
104
+ </View>
105
+ );
106
+ }}
107
+ </Pressable>
108
+ );
109
+ })}
110
+ </View>
111
+
112
+ <ScrollView
113
+ style={{ maxHeight: screenHeight * 0.55 }}
114
+ showsVerticalScrollIndicator={false}
115
+ keyboardShouldPersistTaps="handled"
116
+ >
117
+ {tab === "profile" && <ProfileTab visible={visible} />}
118
+ {tab === "security" && (
119
+ <SecurityTab
120
+ visible={visible}
121
+ onClose={handleClose}
122
+ onEnableMfa={onEnableMfa}
123
+ onComputeReWrap={onComputeReWrap}
124
+ />
125
+ )}
126
+ </ScrollView>
127
+ </Dialog>
128
+ );
129
+ }
@@ -0,0 +1,151 @@
1
+ import React, { useCallback, useEffect, useState } from "react";
2
+ import { View } from "react-native";
3
+ import { useAuth } from "../../hooks/useAuth";
4
+ import { useNotificationContext } from "../../context/NotificationContext";
5
+ import AuthService from "../../services/auth.service";
6
+ import { Button } from "../button";
7
+ import { Card, CardContent } from "../card";
8
+ import { EmailInput, Input, PasswordInput } from "../input";
9
+ import { FormLabel as Label } from "../label";
10
+
11
+ export interface ProfileTabProps {
12
+ /** Whether the surrounding dialog is currently open — drives state reset. */
13
+ visible: boolean;
14
+ }
15
+
16
+ /**
17
+ * "Profile" tab body: edit display name + email. Drops in unchanged
18
+ * between vault and bootstrap because every consumer of entropy-ui's
19
+ * AuthProvider already supports the corresponding AuthService methods.
20
+ */
21
+ export function ProfileTab({ visible }: ProfileTabProps) {
22
+ const { user, refreshUser } = useAuth();
23
+ const { push } = useNotificationContext();
24
+
25
+ const [displayName, setDisplayName] = useState("");
26
+ const [displayNameLoading, setDisplayNameLoading] = useState(false);
27
+ const [displayNameError, setDisplayNameError] = useState("");
28
+
29
+ const [newEmail, setNewEmail] = useState("");
30
+ const [emailPassword, setEmailPassword] = useState("");
31
+ const [emailLoading, setEmailLoading] = useState(false);
32
+ const [emailError, setEmailError] = useState("");
33
+
34
+ useEffect(() => {
35
+ if (visible) {
36
+ return;
37
+ }
38
+ setDisplayName("");
39
+ setDisplayNameError("");
40
+ setNewEmail("");
41
+ setEmailPassword("");
42
+ setEmailError("");
43
+ }, [visible]);
44
+
45
+ const handleSaveDisplayName = useCallback(async () => {
46
+ setDisplayNameError("");
47
+ const trimmed = displayName.trim();
48
+ if (!trimmed) {
49
+ setDisplayNameError("Display name is required");
50
+ return;
51
+ }
52
+ if (trimmed.length > 100) {
53
+ setDisplayNameError("Max 100 characters");
54
+ return;
55
+ }
56
+ setDisplayNameLoading(true);
57
+ try {
58
+ await AuthService.updateDisplayName(trimmed);
59
+ await refreshUser();
60
+ setDisplayName("");
61
+ push({ level: "success", title: "Display name updated" });
62
+ } catch (err) {
63
+ setDisplayNameError(err instanceof Error ? err.message : "Failed to update");
64
+ } finally {
65
+ setDisplayNameLoading(false);
66
+ }
67
+ }, [displayName, refreshUser, push]);
68
+
69
+ const handleSaveEmail = useCallback(async () => {
70
+ setEmailError("");
71
+ if (!AuthService.validateEmail(newEmail)) {
72
+ setEmailError("Invalid email address");
73
+ return;
74
+ }
75
+ if (!emailPassword) {
76
+ setEmailError("Password is required");
77
+ return;
78
+ }
79
+ setEmailLoading(true);
80
+ try {
81
+ await AuthService.changeEmail(newEmail, emailPassword);
82
+ await refreshUser();
83
+ setNewEmail("");
84
+ setEmailPassword("");
85
+ push({ level: "success", title: "Email updated" });
86
+ } catch (err) {
87
+ setEmailError(err instanceof Error ? err.message : "Failed to update email");
88
+ } finally {
89
+ setEmailLoading(false);
90
+ }
91
+ }, [newEmail, emailPassword, refreshUser, push]);
92
+
93
+ return (
94
+ <View className="gap-3">
95
+ <Card>
96
+ <CardContent className="p-4">
97
+ <Label className="mb-3 text-xs font-semibold uppercase tracking-wider text-foreground dark:text-white">
98
+ Display Name
99
+ </Label>
100
+ <Input
101
+ value={displayName}
102
+ onChangeText={setDisplayName}
103
+ placeholder={user?.display_name || user?.username || "Display name"}
104
+ autoCapitalize="words"
105
+ maxLength={100}
106
+ error={displayNameError}
107
+ />
108
+ <Button
109
+ variant="outline"
110
+ size="sm"
111
+ className="mt-3 self-end"
112
+ onPress={handleSaveDisplayName}
113
+ isLoading={displayNameLoading}
114
+ >
115
+ Save
116
+ </Button>
117
+ </CardContent>
118
+ </Card>
119
+
120
+ <Card>
121
+ <CardContent className="p-4">
122
+ <Label className="mb-3 text-xs font-semibold uppercase tracking-wider text-foreground dark:text-white">
123
+ Email Address
124
+ </Label>
125
+ <EmailInput
126
+ value={newEmail}
127
+ onChangeText={setNewEmail}
128
+ placeholder={user?.email || "New email address"}
129
+ containerClassName="mb-2.5"
130
+ />
131
+ <PasswordInput
132
+ value={emailPassword}
133
+ onChangeText={setEmailPassword}
134
+ placeholder="Your current password"
135
+ label="Confirm Password"
136
+ error={emailError}
137
+ />
138
+ <Button
139
+ variant="outline"
140
+ size="sm"
141
+ className="mt-3 self-end"
142
+ onPress={handleSaveEmail}
143
+ isLoading={emailLoading}
144
+ >
145
+ Save
146
+ </Button>
147
+ </CardContent>
148
+ </Card>
149
+ </View>
150
+ );
151
+ }
@@ -0,0 +1,229 @@
1
+ import React, { useCallback, useEffect, useState } from "react";
2
+ import { View } from "react-native";
3
+ import { useAuth, validatePassword } from "../../hooks/useAuth";
4
+ import { useNotificationContext } from "../../context/NotificationContext";
5
+ import AuthService from "../../services/auth.service";
6
+ import { cn } from "../../utils/cn";
7
+ import { Button } from "../button";
8
+ import { Card, CardContent } from "../card";
9
+ import { PasswordInput } from "../input";
10
+ import { FormLabel as Label } from "../label";
11
+ import { ErrorText, Text } from "../text";
12
+
13
+ export interface SecurityTabProps {
14
+ /** Whether the surrounding dialog is currently open — drives state reset. */
15
+ visible: boolean;
16
+ /** Close the surrounding dialog (consumed when navigating to MFA setup). */
17
+ onClose: () => void;
18
+ /**
19
+ * Fired when the user taps "Enable MFA" — apps wire this to whatever
20
+ * screen they use for TOTP enrollment (vault routes to its mfa-setup
21
+ * tab; bootstrap could route somewhere else).
22
+ */
23
+ onEnableMfa: () => void;
24
+ /**
25
+ * Optional hook invoked before the password change is submitted to the
26
+ * server. Apps that re-wrap a client-side encryption key on rotation
27
+ * (vault) wire it here; the returned payload is forwarded to
28
+ * `AuthService.changePassword` so the server can apply both changes
29
+ * atomically. Apps without a wrapped key pass nothing — the password
30
+ * change happens unmodified.
31
+ */
32
+ onComputeReWrap?: (
33
+ currentPassword: string,
34
+ newPassword: string
35
+ ) => Promise<{ symmetric_key_enc: string; pbkdf2_salt: string } | undefined>;
36
+ }
37
+
38
+ /**
39
+ * "Security" tab body: change master password (with optional client-side
40
+ * key re-wrap) and toggle MFA. The MFA-enrolment route is consumer-
41
+ * specific — pass it via `onEnableMfa`.
42
+ */
43
+ export function SecurityTab({ visible, onClose, onEnableMfa, onComputeReWrap }: SecurityTabProps) {
44
+ const { user, refreshUser } = useAuth();
45
+ const { push } = useNotificationContext();
46
+
47
+ const [currentPassword, setCurrentPassword] = useState("");
48
+ const [newPassword, setNewPassword] = useState("");
49
+ const [confirmPassword, setConfirmPassword] = useState("");
50
+ const [passwordLoading, setPasswordLoading] = useState(false);
51
+ const [passwordError, setPasswordError] = useState("");
52
+
53
+ const [mfaPassword, setMfaPassword] = useState("");
54
+ const [mfaLoading, setMfaLoading] = useState(false);
55
+ const [mfaError, setMfaError] = useState("");
56
+ const [mfaSuccess, setMfaSuccess] = useState("");
57
+
58
+ useEffect(() => {
59
+ if (visible) {
60
+ return;
61
+ }
62
+ setCurrentPassword("");
63
+ setNewPassword("");
64
+ setConfirmPassword("");
65
+ setPasswordError("");
66
+ setMfaPassword("");
67
+ setMfaError("");
68
+ setMfaSuccess("");
69
+ }, [visible]);
70
+
71
+ const handleSavePassword = useCallback(async () => {
72
+ setPasswordError("");
73
+ if (!currentPassword) {
74
+ setPasswordError("Current password is required");
75
+ return;
76
+ }
77
+ if (newPassword !== confirmPassword) {
78
+ setPasswordError("Passwords do not match");
79
+ return;
80
+ }
81
+ const validation = validatePassword(newPassword);
82
+ if (!validation.valid) {
83
+ setPasswordError(validation.errors.join(", "));
84
+ return;
85
+ }
86
+ setPasswordLoading(true);
87
+ try {
88
+ const reWrappedKey = onComputeReWrap
89
+ ? await onComputeReWrap(currentPassword, newPassword)
90
+ : undefined;
91
+ await AuthService.changePassword(currentPassword, newPassword, reWrappedKey);
92
+ setCurrentPassword("");
93
+ setNewPassword("");
94
+ setConfirmPassword("");
95
+ push({ level: "success", title: "Password updated" });
96
+ } catch (err) {
97
+ setPasswordError(err instanceof Error ? err.message : "Failed to update password");
98
+ } finally {
99
+ setPasswordLoading(false);
100
+ }
101
+ }, [currentPassword, newPassword, confirmPassword, push, onComputeReWrap]);
102
+
103
+ const handleDisableMfa = useCallback(async () => {
104
+ setMfaError("");
105
+ setMfaSuccess("");
106
+ if (!mfaPassword) {
107
+ setMfaError("Password is required to disable MFA");
108
+ return;
109
+ }
110
+ setMfaLoading(true);
111
+ try {
112
+ await AuthService.disableMfa(mfaPassword);
113
+ await refreshUser();
114
+ setMfaPassword("");
115
+ setMfaSuccess("MFA disabled");
116
+ } catch (err) {
117
+ setMfaError(err instanceof Error ? err.message : "Failed to disable MFA");
118
+ } finally {
119
+ setMfaLoading(false);
120
+ }
121
+ }, [mfaPassword, refreshUser]);
122
+
123
+ const handleEnableMfa = useCallback(() => {
124
+ onClose();
125
+ onEnableMfa();
126
+ }, [onClose, onEnableMfa]);
127
+
128
+ return (
129
+ <View className="gap-3">
130
+ <Card>
131
+ <CardContent className="p-4">
132
+ <Label className="mb-3 text-xs font-semibold uppercase tracking-wider text-foreground dark:text-white">
133
+ Change Password
134
+ </Label>
135
+ <PasswordInput
136
+ value={currentPassword}
137
+ onChangeText={setCurrentPassword}
138
+ placeholder="Current password"
139
+ containerClassName="mb-2.5"
140
+ />
141
+ <PasswordInput
142
+ value={newPassword}
143
+ onChangeText={setNewPassword}
144
+ placeholder="New password"
145
+ containerClassName="mb-2.5"
146
+ />
147
+ <PasswordInput
148
+ value={confirmPassword}
149
+ onChangeText={setConfirmPassword}
150
+ placeholder="Confirm new password"
151
+ error={passwordError}
152
+ />
153
+ <Button
154
+ variant="outline"
155
+ size="sm"
156
+ className="mt-3 self-end"
157
+ onPress={handleSavePassword}
158
+ isLoading={passwordLoading}
159
+ >
160
+ Update Password
161
+ </Button>
162
+ </CardContent>
163
+ </Card>
164
+
165
+ <Card>
166
+ <CardContent className="p-4">
167
+ <Label className="mb-3 text-xs font-semibold uppercase tracking-wider text-foreground dark:text-white">
168
+ Two-Factor Authentication
169
+ </Label>
170
+ <View className="mb-3.5 flex-row items-center">
171
+ <View
172
+ className={cn(
173
+ "mr-2 h-2 w-2 rounded-full",
174
+ user?.mfa_enabled
175
+ ? "bg-foreground dark:bg-white"
176
+ : "bg-muted-foreground/40 dark:bg-neutral-600"
177
+ )}
178
+ />
179
+ <Text
180
+ className={cn(
181
+ "text-sm font-medium",
182
+ user?.mfa_enabled
183
+ ? "text-foreground dark:text-white"
184
+ : "text-muted-foreground dark:text-neutral-400"
185
+ )}
186
+ >
187
+ {user?.mfa_enabled ? "Enabled" : "Not enabled"}
188
+ </Text>
189
+ </View>
190
+ {user?.mfa_enabled ? (
191
+ <>
192
+ <PasswordInput
193
+ value={mfaPassword}
194
+ onChangeText={setMfaPassword}
195
+ placeholder="Your current password"
196
+ label="Password to Disable"
197
+ error={mfaError}
198
+ />
199
+ {mfaSuccess ? (
200
+ <Text className="mt-1.5 text-sm text-foreground dark:text-white">{mfaSuccess}</Text>
201
+ ) : null}
202
+ <Button
203
+ variant="destructive"
204
+ size="sm"
205
+ className="mt-3 self-end"
206
+ onPress={handleDisableMfa}
207
+ isLoading={mfaLoading}
208
+ >
209
+ Disable MFA
210
+ </Button>
211
+ </>
212
+ ) : (
213
+ <>
214
+ {mfaError ? <ErrorText className="mb-2">{mfaError}</ErrorText> : null}
215
+ <Button
216
+ variant="outline"
217
+ size="sm"
218
+ className="mt-1 self-end"
219
+ onPress={handleEnableMfa}
220
+ >
221
+ Enable MFA
222
+ </Button>
223
+ </>
224
+ )}
225
+ </CardContent>
226
+ </Card>
227
+ </View>
228
+ );
229
+ }
@@ -0,0 +1,3 @@
1
+ export { ProfileTab, type ProfileTabProps } from "./ProfileTab";
2
+ export { SecurityTab, type SecurityTabProps } from "./SecurityTab";
3
+ export { ProfileDialog, type ProfileDialogProps } from "./ProfileDialog";
@@ -0,0 +1,119 @@
1
+ import React, { useState } from "react";
2
+ import { Pressable, Text, View, type LayoutChangeEvent } from "react-native";
3
+ import Animated, {
4
+ Easing,
5
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withTiming,
8
+ } from "react-native-reanimated";
9
+ import { usePalette } from "../utils/palette";
10
+
11
+ export interface SegmentedOption<T extends string> {
12
+ key: T;
13
+ label: string;
14
+ /** When false the segment is shown but not selectable (e.g. "coming soon"). */
15
+ disabled?: boolean;
16
+ }
17
+
18
+ export interface SegmentedToggleProps<T extends string> {
19
+ value: T;
20
+ options: SegmentedOption<T>[];
21
+ onChange: (next: T) => void;
22
+ /** Visual size — `sm` is used inside dialogs, `default` on the auth screen. */
23
+ size?: "sm" | "default";
24
+ className?: string;
25
+ }
26
+
27
+ /**
28
+ * Themed segmented control with a sliding indicator (reanimated). Mirrors the
29
+ * website/simple-finance "Sign In / Sign Up" pill, but palette-driven so it
30
+ * tracks the active theme instead of hard-coded brand colors. Shared by the
31
+ * consolidated AuthScreen mode switch and the ConnectionSettings run-mode pills.
32
+ *
33
+ * Colors are applied via inline `style` (not className) on the Text so Android's
34
+ * NativeWind doesn't override the active/inactive foreground — see the
35
+ * android-nativewind-styling note.
36
+ */
37
+ export function SegmentedToggle<T extends string>({
38
+ value,
39
+ options,
40
+ onChange,
41
+ size = "default",
42
+ className,
43
+ }: SegmentedToggleProps<T>) {
44
+ const { palette: p } = usePalette();
45
+ const [trackW, setTrackW] = useState(0);
46
+ const slide = useSharedValue(0);
47
+
48
+ const count = options.length;
49
+ const index = Math.max(
50
+ 0,
51
+ options.findIndex((o) => o.key === value)
52
+ );
53
+ const pad = 4;
54
+ const segW = trackW > 0 ? (trackW - pad * 2) / count : 0;
55
+
56
+ // Drive the indicator off the selected index whenever it changes.
57
+ slide.value = withTiming(index * segW, { duration: 240, easing: Easing.out(Easing.cubic) });
58
+
59
+ const indicatorStyle = useAnimatedStyle(() => ({
60
+ width: segW,
61
+ transform: [{ translateX: slide.value }],
62
+ }));
63
+
64
+ const vPad = size === "sm" ? 9 : 11;
65
+ const fontSize = size === "sm" ? 14 : 15;
66
+
67
+ return (
68
+ <View
69
+ className={className}
70
+ style={{ backgroundColor: p.muted, borderRadius: 999, padding: pad }}
71
+ onLayout={(e: LayoutChangeEvent) => setTrackW(e.nativeEvent.layout.width)}
72
+ >
73
+ <View style={{ flexDirection: "row" }}>
74
+ <Animated.View
75
+ style={[
76
+ {
77
+ position: "absolute",
78
+ top: 0,
79
+ bottom: 0,
80
+ borderRadius: 999,
81
+ backgroundColor: p.primary,
82
+ },
83
+ indicatorStyle,
84
+ ]}
85
+ />
86
+ {options.map((opt) => {
87
+ const active = opt.key === value;
88
+ return (
89
+ <Pressable
90
+ key={opt.key}
91
+ accessibilityRole="tab"
92
+ accessibilityState={{ selected: active, disabled: !!opt.disabled }}
93
+ disabled={opt.disabled}
94
+ onPress={() => !opt.disabled && onChange(opt.key)}
95
+ style={{
96
+ flex: 1,
97
+ alignItems: "center",
98
+ paddingVertical: vPad,
99
+ opacity: opt.disabled ? 0.45 : 1,
100
+ }}
101
+ >
102
+ <Text
103
+ style={{
104
+ fontSize,
105
+ fontWeight: active ? "700" : "500",
106
+ color: active ? p.primaryForeground : p.mutedForeground,
107
+ }}
108
+ >
109
+ {opt.label}
110
+ </Text>
111
+ </Pressable>
112
+ );
113
+ })}
114
+ </View>
115
+ </View>
116
+ );
117
+ }
118
+
119
+ export default SegmentedToggle;