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