@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,154 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ import { useColorScheme } from "nativewind";
5
+ import { cn } from "../utils/cn";
6
+ import { Button } from "./button";
7
+ import { MutedText } from "./text";
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuTrigger,
13
+ } from "./dropdown-menu";
14
+
15
+ export interface PaginationProps {
16
+ /** Current page, 1-based. */
17
+ page: number;
18
+ /** Number of rows per page. Used to derive total pages from `totalItems`. */
19
+ pageSize: number;
20
+ /** Total number of items across all pages. */
21
+ totalItems: number;
22
+ /** Called with the next page (1-based) when prev/next is pressed. */
23
+ onPageChange: (page: number) => void;
24
+ /**
25
+ * Page-size choices. When provided (and `onPageSizeChange` is set) a
26
+ * size selector renders, built on the existing `DropdownMenu` primitive.
27
+ */
28
+ pageSizeOptions?: number[];
29
+ onPageSizeChange?: (pageSize: number) => void;
30
+ className?: string;
31
+ }
32
+
33
+ export interface PaginationState {
34
+ totalPages: number;
35
+ /** Clamped current page (never below 1, never above totalPages). */
36
+ currentPage: number;
37
+ canPrev: boolean;
38
+ canNext: boolean;
39
+ /** 1-based index of the first item on the current page (0 when empty). */
40
+ rangeStart: number;
41
+ /** 1-based index of the last item on the current page (0 when empty). */
42
+ rangeEnd: number;
43
+ }
44
+
45
+ /**
46
+ * Pure pagination maths, separated from the view so it can be unit-tested
47
+ * without a render host. Tolerates degenerate inputs (zero items, negative
48
+ * page, oversized page) by clamping.
49
+ */
50
+ export function getPaginationState(
51
+ page: number,
52
+ pageSize: number,
53
+ totalItems: number
54
+ ): PaginationState {
55
+ const safeSize = Math.max(1, Math.floor(pageSize));
56
+ const safeTotal = Math.max(0, Math.floor(totalItems));
57
+ const totalPages = Math.max(1, Math.ceil(safeTotal / safeSize));
58
+ const currentPage = Math.min(Math.max(1, Math.floor(page) || 1), totalPages);
59
+ const rangeStart = safeTotal === 0 ? 0 : (currentPage - 1) * safeSize + 1;
60
+ const rangeEnd = Math.min(currentPage * safeSize, safeTotal);
61
+ return {
62
+ totalPages,
63
+ currentPage,
64
+ canPrev: currentPage > 1,
65
+ canNext: currentPage < totalPages,
66
+ rangeStart,
67
+ rangeEnd,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Page controls for paginated lists/tables: prev/next, a "X–Y of Z" summary,
73
+ * and an optional page-size selector. Disabled states are derived from the
74
+ * clamped pagination state and exposed to assistive tech via
75
+ * `accessibilityState`.
76
+ */
77
+ export function Pagination({
78
+ page,
79
+ pageSize,
80
+ totalItems,
81
+ onPageChange,
82
+ pageSizeOptions,
83
+ onPageSizeChange,
84
+ className,
85
+ }: PaginationProps) {
86
+ const { colorScheme } = useColorScheme();
87
+ const isDark = colorScheme === "dark";
88
+ const iconColor = isDark ? "#FFFFFF" : "#000000";
89
+
90
+ const { totalPages, currentPage, canPrev, canNext, rangeStart, rangeEnd } = getPaginationState(
91
+ page,
92
+ pageSize,
93
+ totalItems
94
+ );
95
+
96
+ const showSizeSelector = !!pageSizeOptions && pageSizeOptions.length > 0 && !!onPageSizeChange;
97
+
98
+ return (
99
+ <View className={cn("flex-row items-center justify-between gap-3", className)}>
100
+ <View className="flex-row items-center gap-3">
101
+ <MutedText className="text-sm">
102
+ {totalItems === 0 ? "No items" : `${rangeStart}–${rangeEnd} of ${totalItems}`}
103
+ </MutedText>
104
+ {showSizeSelector ? (
105
+ <DropdownMenu>
106
+ <DropdownMenuTrigger asChild>
107
+ <Button
108
+ variant="outline"
109
+ size="sm"
110
+ accessibilityRole="button"
111
+ accessibilityLabel={`Rows per page, currently ${pageSize}`}
112
+ >
113
+ {`${pageSize} / page`}
114
+ </Button>
115
+ </DropdownMenuTrigger>
116
+ <DropdownMenuContent>
117
+ {pageSizeOptions.map((size) => (
118
+ <DropdownMenuItem key={size} onPress={() => onPageSizeChange(size)}>
119
+ <MutedText className="text-sm text-foreground">{`${size} / page`}</MutedText>
120
+ </DropdownMenuItem>
121
+ ))}
122
+ </DropdownMenuContent>
123
+ </DropdownMenu>
124
+ ) : null}
125
+ </View>
126
+
127
+ <View className="flex-row items-center gap-2">
128
+ <MutedText className="text-sm">{`Page ${currentPage} of ${totalPages}`}</MutedText>
129
+ <Button
130
+ variant="outline"
131
+ size="sm"
132
+ disabled={!canPrev}
133
+ onPress={() => onPageChange(currentPage - 1)}
134
+ accessibilityRole="button"
135
+ accessibilityLabel="Previous page"
136
+ accessibilityState={{ disabled: !canPrev }}
137
+ >
138
+ <Ionicons name="chevron-back" size={18} color={iconColor} />
139
+ </Button>
140
+ <Button
141
+ variant="outline"
142
+ size="sm"
143
+ disabled={!canNext}
144
+ onPress={() => onPageChange(currentPage + 1)}
145
+ accessibilityRole="button"
146
+ accessibilityLabel="Next page"
147
+ accessibilityState={{ disabled: !canNext }}
148
+ >
149
+ <Ionicons name="chevron-forward" size={18} color={iconColor} />
150
+ </Button>
151
+ </View>
152
+ </View>
153
+ );
154
+ }
@@ -0,0 +1,393 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
+ import { ActivityIndicator, Platform, Pressable, View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ import { useColorScheme } from "nativewind";
5
+ import * as Haptics from "expo-haptics";
6
+ import Slider from "@react-native-community/slider";
7
+ import Toast from "react-native-toast-message";
8
+ import { Text } from "./text";
9
+ import { copyToClipboard } from "../utils/clipboard";
10
+ import { generatePassword } from "../utils/password";
11
+ import { checkPasswordPwned } from "../services/hibp";
12
+
13
+ // A randomly drawn password being in a breach corpus is astronomically
14
+ // unlikely, but if it ever hits, regenerate until clean — capped so a
15
+ // misbehaving API can't loop forever.
16
+ const MAX_BREACH_REGEN = 5;
17
+ type BreachState = "idle" | "checking" | "safe" | "unverified";
18
+
19
+ const isWeb = Platform.OS === "web";
20
+
21
+ export interface PasswordGeneratorProps {
22
+ /** Called when the user taps "Use Password" (only shown when `onSelect` is provided). */
23
+ onSelect?: (password: string) => void;
24
+ /** Hide the copy button (useful in embedded / inline mode). */
25
+ hideCopy?: boolean;
26
+ }
27
+
28
+ /**
29
+ * UI shell over `utils/password.generatePassword`. Owns length slider,
30
+ * character-set toggles, copy-to-clipboard (via shared util with auto-
31
+ * clear), and a coarse strength indicator.
32
+ */
33
+ export const PasswordGenerator = React.memo(function PasswordGenerator({
34
+ onSelect,
35
+ hideCopy,
36
+ }: PasswordGeneratorProps) {
37
+ const [password, setPassword] = useState("");
38
+ const [length, setLength] = useState(16);
39
+ const [includeUppercase, setIncludeUppercase] = useState(true);
40
+ const [includeLowercase, setIncludeLowercase] = useState(true);
41
+ const [includeNumbers, setIncludeNumbers] = useState(true);
42
+ const [includeSymbols, setIncludeSymbols] = useState(true);
43
+ const [excludeSimilar, setExcludeSimilar] = useState(false);
44
+ const [breach, setBreach] = useState<BreachState>("idle");
45
+
46
+ const { colorScheme } = useColorScheme();
47
+ const isDark = colorScheme === "dark";
48
+
49
+ // Latest options, read by the breach-regen path without re-subscribing the
50
+ // check effect on every option change.
51
+ const optsRef = useRef({
52
+ length,
53
+ includeUppercase,
54
+ includeLowercase,
55
+ includeNumbers,
56
+ includeSymbols,
57
+ excludeSimilar,
58
+ });
59
+ optsRef.current = {
60
+ length,
61
+ includeUppercase,
62
+ includeLowercase,
63
+ includeNumbers,
64
+ includeSymbols,
65
+ excludeSimilar,
66
+ };
67
+ // Ignore stale async HIBP results once a newer password supersedes them.
68
+ const breachTokenRef = useRef(0);
69
+ const breachAttemptRef = useRef(0);
70
+
71
+ // 150 ms throttle on Generate. The CSPRNG draw itself is cheap
72
+ // (synchronous bytes), but a held-down Enter key or a stuck
73
+ // accessibility-keyboard repeat could fire many calls per second
74
+ // and the rejection-sampling loop inside `generatePassword` does
75
+ // a small amount of work per call. The throttle also gates the
76
+ // haptic so the user isn't getting buzzed at keypress rate.
77
+ const lastGenerateRef = useRef(0);
78
+ const handleGenerate = useCallback(() => {
79
+ const now = Date.now();
80
+ if (now - lastGenerateRef.current < 150) {
81
+ return;
82
+ }
83
+ lastGenerateRef.current = now;
84
+ try {
85
+ const next = generatePassword({
86
+ length,
87
+ includeUppercase,
88
+ includeLowercase,
89
+ includeNumbers,
90
+ includeSymbols,
91
+ excludeSimilar,
92
+ });
93
+ setPassword(next);
94
+ if (!isWeb) {
95
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
96
+ }
97
+ } catch (e) {
98
+ Toast.show({
99
+ type: "error",
100
+ text1: e instanceof Error ? e.message : "Please select at least one character type",
101
+ visibilityTime: 3000,
102
+ });
103
+ }
104
+ }, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
105
+
106
+ const handleCopy = useCallback(async () => {
107
+ if (!password) {
108
+ return;
109
+ }
110
+ const ok = await copyToClipboard(password);
111
+ if (!ok) {
112
+ return;
113
+ }
114
+ if (!isWeb) {
115
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
116
+ }
117
+ Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
118
+ }, [password]);
119
+
120
+ // Generate on mount and when character-class options change.
121
+ useEffect(() => {
122
+ handleGenerate();
123
+ }, [handleGenerate]);
124
+
125
+ // Verify each generated password against HIBP (k-anonymity — only the SHA-1
126
+ // prefix leaves the device). Debounced so spamming Generate only checks the
127
+ // final password. On the (near-impossible) breach hit, silently regenerate;
128
+ // a failed/offline check surfaces as "unverified" rather than blocking.
129
+ useEffect(() => {
130
+ if (!password) {
131
+ setBreach("idle");
132
+ return;
133
+ }
134
+ const token = ++breachTokenRef.current;
135
+ setBreach("checking");
136
+ const timer = setTimeout(async () => {
137
+ const res = await checkPasswordPwned(password);
138
+ if (token !== breachTokenRef.current) {
139
+ return;
140
+ } // superseded
141
+ if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
142
+ breachAttemptRef.current += 1;
143
+ try {
144
+ setPassword(generatePassword(optsRef.current));
145
+ } catch {
146
+ setBreach("unverified");
147
+ }
148
+ return;
149
+ }
150
+ breachAttemptRef.current = 0;
151
+ setBreach(res.isPwned === null ? "unverified" : "safe");
152
+ }, 400);
153
+ return () => clearTimeout(timer);
154
+ }, [password]);
155
+
156
+ const passwordStrength = useStrength(password);
157
+
158
+ return (
159
+ <>
160
+ <View className="mb-4 rounded-lg border border-border bg-card p-4 dark:bg-neutral-900/50">
161
+ <View className="mb-3 flex-row items-center justify-between">
162
+ <Text className="text-sm font-medium text-muted-foreground">Generated Password</Text>
163
+ <View className="flex-row items-center">
164
+ <View
165
+ className="h-2 w-2 rounded-full"
166
+ style={{ backgroundColor: passwordStrength.color }}
167
+ />
168
+ <Text className="ml-2 text-xs font-medium" style={{ color: passwordStrength.color }}>
169
+ {passwordStrength.label}
170
+ </Text>
171
+ </View>
172
+ </View>
173
+
174
+ <View className="mb-2 rounded-lg bg-background p-3">
175
+ <Text
176
+ className="font-mono text-base text-foreground"
177
+ selectable
178
+ numberOfLines={2}
179
+ testID="generator-output"
180
+ >
181
+ {password || "Press generate to create a password"}
182
+ </Text>
183
+ </View>
184
+
185
+ {/* HIBP breach-check status. Fixed height so the layout never shifts. */}
186
+ <View className="mb-3 h-4 flex-row items-center gap-1.5" testID="generator-breach-status">
187
+ {breach === "checking" ? (
188
+ <>
189
+ <ActivityIndicator size="small" color={isDark ? "#a3a3a3" : "#737373"} />
190
+ <Text className="text-xs text-muted-foreground">Checking breach database…</Text>
191
+ </>
192
+ ) : breach === "safe" ? (
193
+ <>
194
+ <Ionicons name="shield-checkmark" size={12} color="#22c55e" />
195
+ <Text className="text-xs" style={{ color: "#22c55e" }}>
196
+ Not found in any known breach
197
+ </Text>
198
+ </>
199
+ ) : breach === "unverified" ? (
200
+ <>
201
+ <Ionicons
202
+ name="cloud-offline-outline"
203
+ size={12}
204
+ color={isDark ? "#a3a3a3" : "#737373"}
205
+ />
206
+ <Text className="text-xs text-muted-foreground">
207
+ Couldn&apos;t check breaches (offline)
208
+ </Text>
209
+ </>
210
+ ) : null}
211
+ </View>
212
+
213
+ <View className="flex-row gap-2">
214
+ <Pressable
215
+ onPress={handleGenerate}
216
+ className="active:scale-98 flex-1 rounded-lg bg-primary px-4 py-3"
217
+ accessibilityRole="button"
218
+ accessibilityLabel="Generate password"
219
+ testID="generator-generate"
220
+ >
221
+ <View className="flex-row items-center justify-center">
222
+ <Ionicons name="refresh" size={18} color={isDark ? "black" : "white"} />
223
+ <Text className="ml-2 font-semibold text-primary-foreground">Generate</Text>
224
+ </View>
225
+ </Pressable>
226
+
227
+ {!hideCopy && (
228
+ <Pressable
229
+ onPress={handleCopy}
230
+ disabled={!password}
231
+ className="active:scale-98 rounded-lg bg-secondary px-4 py-3"
232
+ accessibilityRole="button"
233
+ accessibilityLabel="Copy password"
234
+ accessibilityState={{ disabled: !password }}
235
+ >
236
+ <View className="flex-row items-center justify-center">
237
+ <Ionicons name="copy-outline" size={18} color={isDark ? "white" : "black"} />
238
+ <Text className="ml-2 font-semibold text-foreground">Copy</Text>
239
+ </View>
240
+ </Pressable>
241
+ )}
242
+
243
+ {onSelect && (
244
+ <Pressable
245
+ onPress={() => onSelect(password)}
246
+ disabled={!password}
247
+ className="active:scale-98 rounded-lg bg-secondary px-4 py-3"
248
+ accessibilityRole="button"
249
+ accessibilityLabel="Use password"
250
+ accessibilityState={{ disabled: !password }}
251
+ >
252
+ <View className="flex-row items-center justify-center">
253
+ <Ionicons name="checkmark" size={18} color={isDark ? "white" : "black"} />
254
+ <Text className="ml-2 font-semibold text-foreground">Use</Text>
255
+ </View>
256
+ </Pressable>
257
+ )}
258
+ </View>
259
+ </View>
260
+
261
+ <View className="rounded-lg border border-border bg-card p-4 dark:bg-neutral-900/50">
262
+ <Text className="mb-4 text-base font-semibold text-foreground">Options</Text>
263
+
264
+ <View className="mb-5">
265
+ <View className="mb-2 flex-row items-center justify-between">
266
+ <Text className="text-sm text-foreground">Password Length</Text>
267
+ <Text className="font-mono text-sm font-semibold text-primary">{length}</Text>
268
+ </View>
269
+ <Slider
270
+ value={length}
271
+ onValueChange={setLength}
272
+ onSlidingComplete={setLength}
273
+ minimumValue={8}
274
+ maximumValue={128}
275
+ step={1}
276
+ minimumTrackTintColor={isDark ? "white" : "black"}
277
+ maximumTrackTintColor={isDark ? "#404040" : "#e5e5e5"}
278
+ thumbTintColor={isDark ? "white" : "black"}
279
+ accessibilityLabel={`Password length: ${length}`}
280
+ accessibilityRole="adjustable"
281
+ />
282
+ <View className="mt-1 flex-row justify-between">
283
+ <Text className="text-xs text-muted-foreground">8</Text>
284
+ <Text className="text-xs text-muted-foreground">128</Text>
285
+ </View>
286
+ </View>
287
+
288
+ <View className="gap-3">
289
+ <CharsetCheckbox
290
+ label="Uppercase Letters (A-Z)"
291
+ checked={includeUppercase}
292
+ onToggle={() => setIncludeUppercase((v) => !v)}
293
+ isDark={isDark}
294
+ />
295
+ <CharsetCheckbox
296
+ label="Lowercase Letters (a-z)"
297
+ checked={includeLowercase}
298
+ onToggle={() => setIncludeLowercase((v) => !v)}
299
+ isDark={isDark}
300
+ />
301
+ <CharsetCheckbox
302
+ label="Numbers (0-9)"
303
+ checked={includeNumbers}
304
+ onToggle={() => setIncludeNumbers((v) => !v)}
305
+ isDark={isDark}
306
+ />
307
+ <CharsetCheckbox
308
+ label="Symbols (!@#$%...)"
309
+ checked={includeSymbols}
310
+ onToggle={() => setIncludeSymbols((v) => !v)}
311
+ isDark={isDark}
312
+ />
313
+ <CharsetCheckbox
314
+ label="Exclude Similar Characters"
315
+ checked={excludeSimilar}
316
+ onToggle={() => setExcludeSimilar((v) => !v)}
317
+ isDark={isDark}
318
+ />
319
+ </View>
320
+ </View>
321
+ </>
322
+ );
323
+ });
324
+
325
+ export default PasswordGenerator;
326
+
327
+ /** Coarse strength indicator — purely UX, server doesn't see it. */
328
+ function useStrength(password: string): { label: string; color: string } {
329
+ if (!password) {
330
+ return { label: "Generate a password", color: "gray" };
331
+ }
332
+ let strength = 0;
333
+ if (password.length >= 12) {
334
+ strength++;
335
+ }
336
+ if (password.length >= 16) {
337
+ strength++;
338
+ }
339
+ if (/[a-z]/.test(password)) {
340
+ strength++;
341
+ }
342
+ if (/[A-Z]/.test(password)) {
343
+ strength++;
344
+ }
345
+ if (/[0-9]/.test(password)) {
346
+ strength++;
347
+ }
348
+ if (/[^A-Za-z0-9]/.test(password)) {
349
+ strength++;
350
+ }
351
+ if (strength <= 2) {
352
+ return { label: "Weak", color: "#ef4444" };
353
+ }
354
+ if (strength <= 4) {
355
+ return { label: "Medium", color: "#f59e0b" };
356
+ }
357
+ return { label: "Strong", color: "#22c55e" };
358
+ }
359
+
360
+ interface CharsetCheckboxProps {
361
+ label: string;
362
+ checked: boolean;
363
+ onToggle: () => void;
364
+ isDark: boolean;
365
+ }
366
+
367
+ function CharsetCheckbox({ label, checked, onToggle, isDark }: CharsetCheckboxProps) {
368
+ return (
369
+ <Pressable
370
+ onPress={onToggle}
371
+ className="flex-row items-center gap-3"
372
+ accessibilityRole="checkbox"
373
+ accessibilityLabel={label}
374
+ accessibilityState={{ checked }}
375
+ >
376
+ <View
377
+ style={{
378
+ width: 20,
379
+ height: 20,
380
+ borderRadius: 4,
381
+ borderWidth: 1.5,
382
+ alignItems: "center",
383
+ justifyContent: "center",
384
+ borderColor: checked ? (isDark ? "#ffffff" : "#000000") : isDark ? "#525252" : "#d4d4d4",
385
+ backgroundColor: checked ? (isDark ? "#ffffff" : "#000000") : "transparent",
386
+ }}
387
+ >
388
+ {checked && <Ionicons name="checkmark" size={14} color={isDark ? "#000000" : "#ffffff"} />}
389
+ </View>
390
+ <Text className="flex-1 text-sm text-foreground">{label}</Text>
391
+ </Pressable>
392
+ );
393
+ }
@@ -0,0 +1,156 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
+ import { Platform, Pressable, Text, View } from "react-native";
3
+ import { useColorScheme } from "nativewind";
4
+ import { Delete, Fingerprint } from "lucide-react-native";
5
+
6
+ /**
7
+ * Numeric PIN entry pad with a filled-dots indicator. Theme-driven (uses
8
+ * the active palette via NativeWind classes). Self-manages the entered
9
+ * digits and fires `onComplete` when `length` is reached; pass a changing
10
+ * `resetKey` to clear it (e.g. after a wrong PIN).
11
+ */
12
+ export interface PinPadProps {
13
+ length?: number;
14
+ onComplete: (pin: string) => void;
15
+ onChange?: (pin: string) => void;
16
+ /** Show a biometric key (bottom-left); omit to leave that slot blank. */
17
+ onBiometric?: () => void;
18
+ /** Bump to clear the entered digits. */
19
+ resetKey?: string | number;
20
+ error?: boolean;
21
+ }
22
+
23
+ const KEYS = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] as const;
24
+
25
+ export function PinPad({
26
+ length = 6,
27
+ onComplete,
28
+ onChange,
29
+ onBiometric,
30
+ resetKey,
31
+ error,
32
+ }: PinPadProps) {
33
+ const { colorScheme } = useColorScheme();
34
+ const iconColor = colorScheme === "dark" ? "#FFFFFF" : "#000000";
35
+ const [pin, setPin] = useState("");
36
+ // Source of truth for the latest digits — lets callbacks fire OUTSIDE the
37
+ // setPin updater (calling a parent setState inside an updater triggers
38
+ // "setState while rendering"). Kept in sync with state on every render.
39
+ const pinRef = useRef(pin);
40
+ pinRef.current = pin;
41
+
42
+ // Clear when resetKey changes.
43
+ const [seenKey, setSeenKey] = useState(resetKey);
44
+ if (resetKey !== seenKey) {
45
+ setSeenKey(resetKey);
46
+ if (pin) {
47
+ pinRef.current = "";
48
+ setPin("");
49
+ }
50
+ }
51
+
52
+ const press = useCallback(
53
+ (d: string) => {
54
+ const cur = pinRef.current;
55
+ if (cur.length >= length) {
56
+ return;
57
+ }
58
+ const next = cur + d;
59
+ pinRef.current = next;
60
+ setPin(next);
61
+ onChange?.(next);
62
+ if (next.length === length) {
63
+ onComplete(next);
64
+ }
65
+ },
66
+ [length, onChange, onComplete]
67
+ );
68
+
69
+ const back = useCallback(() => {
70
+ const next = pinRef.current.slice(0, -1);
71
+ pinRef.current = next;
72
+ setPin(next);
73
+ onChange?.(next);
74
+ }, [onChange]);
75
+
76
+ // Physical keyboard / numpad support (web): digits type, Backspace/Delete
77
+ // removes, Enter submits a full PIN.
78
+ useEffect(() => {
79
+ if (Platform.OS !== "web" || typeof window === "undefined") {
80
+ return;
81
+ }
82
+ const onKey = (e: KeyboardEvent) => {
83
+ if (e.key >= "0" && e.key <= "9") {
84
+ e.preventDefault();
85
+ press(e.key);
86
+ } else if (e.key === "Backspace" || e.key === "Delete") {
87
+ e.preventDefault();
88
+ back();
89
+ } else if (e.key === "Enter") {
90
+ e.preventDefault();
91
+ if (pinRef.current.length === length) {
92
+ onComplete(pinRef.current);
93
+ }
94
+ }
95
+ };
96
+ window.addEventListener("keydown", onKey);
97
+ return () => window.removeEventListener("keydown", onKey);
98
+ }, [press, back, onComplete, length]);
99
+
100
+ return (
101
+ <View className="items-center gap-7">
102
+ {/* dots */}
103
+ <View className="flex-row gap-3">
104
+ {Array.from({ length }).map((_, i) => (
105
+ <View
106
+ key={i}
107
+ className={
108
+ i < pin.length
109
+ ? error
110
+ ? "h-3.5 w-3.5 rounded-full bg-destructive"
111
+ : "h-3.5 w-3.5 rounded-full bg-primary"
112
+ : "h-3.5 w-3.5 rounded-full border-2 border-border"
113
+ }
114
+ />
115
+ ))}
116
+ </View>
117
+
118
+ {/* keypad */}
119
+ <View className="w-72 flex-row flex-wrap justify-center gap-3">
120
+ {KEYS.map((k) => (
121
+ <Key key={k} label={k} onPress={() => press(k)} />
122
+ ))}
123
+ {onBiometric ? (
124
+ <Key onPress={onBiometric} icon={<Fingerprint size={26} color={iconColor} />} />
125
+ ) : (
126
+ <View className="h-20 w-20" />
127
+ )}
128
+ <Key label="0" onPress={() => press("0")} />
129
+ <Key onPress={back} icon={<Delete size={24} color={iconColor} />} />
130
+ </View>
131
+ </View>
132
+ );
133
+ }
134
+
135
+ function Key({
136
+ label,
137
+ icon,
138
+ onPress,
139
+ }: {
140
+ label?: string;
141
+ icon?: React.ReactNode;
142
+ onPress: () => void;
143
+ }) {
144
+ return (
145
+ <Pressable
146
+ onPress={onPress}
147
+ className="h-20 w-20 items-center justify-center rounded-full active:bg-secondary"
148
+ accessibilityRole="button"
149
+ accessibilityLabel={label ?? "key"}
150
+ >
151
+ {icon ?? <Text className="font-sans-medium text-2xl text-foreground">{label}</Text>}
152
+ </Pressable>
153
+ );
154
+ }
155
+
156
+ export default PinPad;