@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,583 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import {
3
+ ActivityIndicator,
4
+ Modal,
5
+ Pressable,
6
+ ScrollView,
7
+ Text,
8
+ useWindowDimensions,
9
+ View,
10
+ type ViewStyle,
11
+ } from "react-native";
12
+ import { Gesture, GestureDetector, GestureHandlerRootView } from "react-native-gesture-handler";
13
+ import Animated, {
14
+ FadeIn,
15
+ FadeOut,
16
+ useAnimatedStyle,
17
+ useSharedValue,
18
+ withTiming,
19
+ } from "react-native-reanimated";
20
+ import { Check, ChevronDown, GripHorizontal, X } from "lucide-react-native";
21
+ import { Button } from "./button";
22
+ import { Input } from "./input";
23
+ import { usePalette } from "../utils/palette";
24
+
25
+ export type RunMode = "standalone" | "cloud";
26
+ type Endpoint = "entropy" | "custom";
27
+ type TestState = "idle" | "testing" | "ok" | "fail";
28
+
29
+ const SUCCESS = "#16a34a";
30
+
31
+ export interface ConnectionSettingsProps {
32
+ visible: boolean;
33
+ onClose: () => void;
34
+ /** Current persisted run mode. */
35
+ mode: RunMode;
36
+ /** Current persisted server URL (cloud mode). */
37
+ serverUrl: string;
38
+ /** Persist the chosen mode + resolved URL (empty string in standalone). */
39
+ onSave: (mode: RunMode, url: string) => void;
40
+ /** Default hosted endpoint shown as "Entropy Cloud". */
41
+ entropyUrl: string;
42
+ /**
43
+ * Health path appended to the resolved server URL. Default `/health`.
44
+ * (Vault's API base already includes `/api`, so the default resolves to
45
+ * `<base>/api/health`.)
46
+ */
47
+ healthPath?: string;
48
+ /**
49
+ * Validate the parsed JSON health body. Default accepts `{ status: "ok" }`
50
+ * or `{ status: "healthy" }` — the two Entropy server shapes.
51
+ */
52
+ validateHealth?: (body: unknown) => boolean;
53
+ /**
54
+ * When false (default) Standalone is shown but disabled with a "coming
55
+ * soon" note — vault has no local-only mode yet. Set true once it ships.
56
+ */
57
+ allowStandalone?: boolean;
58
+ }
59
+
60
+ function normalizeCustom(input: string): string {
61
+ const v = input.trim().replace(/\/+$/, "");
62
+ if (!v) {
63
+ return "";
64
+ }
65
+ return /^https?:\/\//i.test(v) ? v : `https://${v}`;
66
+ }
67
+
68
+ const defaultValidate = (body: unknown): boolean => {
69
+ const s = (body as { status?: string } | null)?.status;
70
+ return s === "ok" || s === "healthy";
71
+ };
72
+
73
+ /**
74
+ * Connection dialog — a faithful port of simple-finance's, themed from the
75
+ * palette (so it tracks the active theme instead of hard-coded brand colors)
76
+ * and upstreamed here so both apps share one implementation. Draggable card
77
+ * (grip header, double-tap to recenter), Standalone/Cloud mode pills, a
78
+ * server dropdown (Entropy-hosted or self-hosted), and a live debounced
79
+ * health check. Standalone is gated until its backend lands.
80
+ */
81
+ export function ConnectionSettings({
82
+ visible,
83
+ onClose,
84
+ mode,
85
+ serverUrl,
86
+ onSave,
87
+ entropyUrl,
88
+ healthPath = "/health",
89
+ validateHealth = defaultValidate,
90
+ allowStandalone = false,
91
+ }: ConnectionSettingsProps) {
92
+ const { palette: p } = usePalette();
93
+
94
+ const [localMode, setLocalMode] = useState<RunMode>(mode);
95
+ const [endpoint, setEndpoint] = useState<Endpoint>(
96
+ serverUrl && serverUrl !== entropyUrl ? "custom" : "entropy"
97
+ );
98
+ const [custom, setCustom] = useState(serverUrl && serverUrl !== entropyUrl ? serverUrl : "");
99
+ const [open, setOpen] = useState(false);
100
+ const [test, setTest] = useState<TestState>("idle");
101
+ const [testMsg, setTestMsg] = useState("");
102
+
103
+ // Re-sync to props whenever the dialog opens.
104
+ useEffect(() => {
105
+ if (!visible) {
106
+ return;
107
+ }
108
+ setLocalMode(mode);
109
+ setEndpoint(serverUrl && serverUrl !== entropyUrl ? "custom" : "entropy");
110
+ setCustom(serverUrl && serverUrl !== entropyUrl ? serverUrl : "");
111
+ setOpen(false);
112
+ setTest("idle");
113
+ setTestMsg("");
114
+ }, [visible, mode, serverUrl, entropyUrl]);
115
+
116
+ // Dropdown is rendered as the card's last child (so it always paints on
117
+ // top); we measure the selector to anchor it.
118
+ const cardRef = useRef<View>(null);
119
+ const selRef = useRef<View>(null);
120
+ const [menu, setMenu] = useState<{ top: number; left: number; width: number } | null>(null);
121
+
122
+ const toggleMenu = () => {
123
+ if (open) {
124
+ setOpen(false);
125
+ return;
126
+ }
127
+ selRef.current?.measureInWindow((x, y, w, h) => {
128
+ cardRef.current?.measureInWindow((cx, cy) => {
129
+ setMenu({ top: y - cy + h + 6, left: x - cx, width: w });
130
+ setOpen(true);
131
+ });
132
+ });
133
+ };
134
+
135
+ // ── Draggable card, clamped to stay on screen ──────────────────────────────
136
+ const win = useWindowDimensions();
137
+ const tx = useSharedValue(0);
138
+ const ty = useSharedValue(0);
139
+ const startX = useSharedValue(0);
140
+ const startY = useSharedValue(0);
141
+ const cardW = useSharedValue(0);
142
+ const cardH = useSharedValue(0);
143
+
144
+ useEffect(() => {
145
+ if (visible) {
146
+ tx.value = 0;
147
+ ty.value = 0;
148
+ }
149
+ }, [visible, tx, ty]);
150
+
151
+ const drag = Gesture.Pan()
152
+ .onBegin(() => {
153
+ startX.value = tx.value;
154
+ startY.value = ty.value;
155
+ })
156
+ .onUpdate((e) => {
157
+ const maxX = Math.max(0, (win.width - cardW.value) / 2 - 8);
158
+ const maxY = Math.max(0, (win.height - cardH.value) / 2 - 8);
159
+ tx.value = Math.min(maxX, Math.max(-maxX, startX.value + e.translationX));
160
+ ty.value = Math.min(maxY, Math.max(-maxY, startY.value + e.translationY));
161
+ });
162
+
163
+ const recenter = Gesture.Tap()
164
+ .numberOfTaps(2)
165
+ .onEnd(() => {
166
+ tx.value = withTiming(0, { duration: 220 });
167
+ ty.value = withTiming(0, { duration: 220 });
168
+ });
169
+
170
+ const handleGesture = Gesture.Race(drag, recenter);
171
+
172
+ const dragStyle = useAnimatedStyle(() => ({
173
+ transform: [{ translateX: tx.value }, { translateY: ty.value }],
174
+ }));
175
+
176
+ const resolvedUrl = endpoint === "entropy" ? entropyUrl : normalizeCustom(custom);
177
+
178
+ const runTest = async (url: string) => {
179
+ if (!url) {
180
+ setTest("fail");
181
+ setTestMsg("Enter a server address");
182
+ return;
183
+ }
184
+ setTest("testing");
185
+ setTestMsg("");
186
+ try {
187
+ const res = await fetch(`${url}${healthPath}`, { method: "GET" });
188
+ const body = (await res.json().catch(() => ({}))) as unknown;
189
+ if (res.ok && validateHealth(body)) {
190
+ setTest("ok");
191
+ setTestMsg("Connected");
192
+ } else {
193
+ setTest("fail");
194
+ setTestMsg(`Responded ${res.status}`);
195
+ }
196
+ } catch (e) {
197
+ setTest("fail");
198
+ setTestMsg(e instanceof Error ? e.message : "Could not reach server");
199
+ }
200
+ };
201
+
202
+ const canSave = localMode === "standalone" || test === "ok";
203
+
204
+ // Auto-test (debounced) whenever the cloud endpoint changes.
205
+ useEffect(() => {
206
+ if (!visible || localMode !== "cloud") {
207
+ return;
208
+ }
209
+ if (!resolvedUrl) {
210
+ setTest("idle");
211
+ return;
212
+ }
213
+ const h = setTimeout(() => void runTest(resolvedUrl), 500);
214
+ return () => clearTimeout(h);
215
+ // eslint-disable-next-line react-hooks/exhaustive-deps
216
+ }, [visible, localMode, endpoint, custom, resolvedUrl]);
217
+
218
+ return (
219
+ <Modal
220
+ visible={visible}
221
+ transparent
222
+ animationType="none"
223
+ onRequestClose={onClose}
224
+ statusBarTranslucent
225
+ >
226
+ <GestureHandlerRootView style={{ flex: 1 }}>
227
+ <Animated.View
228
+ entering={FadeIn.duration(160)}
229
+ exiting={FadeOut.duration(120)}
230
+ style={{ flex: 1 }}
231
+ >
232
+ <Pressable
233
+ style={{
234
+ flex: 1,
235
+ backgroundColor: "#000000B0",
236
+ alignItems: "center",
237
+ justifyContent: "center",
238
+ padding: 24,
239
+ }}
240
+ onPress={onClose}
241
+ >
242
+ <Animated.View
243
+ entering={FadeIn.duration(180)}
244
+ onLayout={(e) => {
245
+ cardW.value = e.nativeEvent.layout.width;
246
+ cardH.value = e.nativeEvent.layout.height;
247
+ }}
248
+ style={[{ width: "100%", maxWidth: 520 }, dragStyle]}
249
+ >
250
+ <Pressable
251
+ ref={cardRef}
252
+ onPress={(e) => e.stopPropagation()}
253
+ style={{
254
+ backgroundColor: p.popover,
255
+ borderRadius: 24,
256
+ borderColor: p.border,
257
+ borderWidth: 1,
258
+ paddingHorizontal: 22,
259
+ paddingBottom: 22,
260
+ paddingTop: 10,
261
+ gap: 16,
262
+ }}
263
+ >
264
+ {/* drag handle: whole header bar drags; double-tap recenters */}
265
+ <GestureDetector gesture={handleGesture}>
266
+ <View
267
+ style={
268
+ {
269
+ marginHorizontal: -22,
270
+ paddingHorizontal: 22,
271
+ userSelect: "none",
272
+ } as ViewStyle
273
+ }
274
+ >
275
+ <View style={{ alignItems: "center", paddingVertical: 4 }}>
276
+ <GripHorizontal size={20} color={p.mutedForeground} />
277
+ </View>
278
+ <View
279
+ style={{
280
+ flexDirection: "row",
281
+ alignItems: "center",
282
+ justifyContent: "space-between",
283
+ }}
284
+ >
285
+ <Text style={{ color: p.foreground, fontSize: 18, fontWeight: "700" }}>
286
+ Connection
287
+ </Text>
288
+ <Pressable onPress={onClose} hitSlop={10} style={{ padding: 4 }}>
289
+ <X size={20} color={p.mutedForeground} />
290
+ </Pressable>
291
+ </View>
292
+ </View>
293
+ </GestureDetector>
294
+
295
+ <ScrollView
296
+ style={{ maxHeight: win.height * 0.66 }}
297
+ contentContainerStyle={{ gap: 16 }}
298
+ keyboardShouldPersistTaps="handled"
299
+ showsVerticalScrollIndicator={false}
300
+ >
301
+ {/* mode segmented */}
302
+ <View
303
+ style={{
304
+ backgroundColor: p.muted,
305
+ borderRadius: 14,
306
+ padding: 4,
307
+ flexDirection: "row",
308
+ gap: 4,
309
+ }}
310
+ >
311
+ <ModePill
312
+ label="Cloud"
313
+ active={localMode === "cloud"}
314
+ onPress={() => setLocalMode("cloud")}
315
+ palette={p}
316
+ />
317
+ <ModePill
318
+ label="Standalone"
319
+ active={localMode === "standalone"}
320
+ disabled={!allowStandalone}
321
+ onPress={() => setLocalMode("standalone")}
322
+ palette={p}
323
+ />
324
+ </View>
325
+
326
+ {localMode === "standalone" ? (
327
+ <Text style={{ color: p.mutedForeground, fontSize: 13, lineHeight: 19 }}>
328
+ {allowStandalone
329
+ ? "Private by default. Your vault stays on this device — no account, no server."
330
+ : "On-device, account-free mode is coming soon. For now, sign in to your Entropy or self-hosted server."}
331
+ </Text>
332
+ ) : (
333
+ <View style={{ gap: 12 }}>
334
+ <Text style={{ color: p.mutedForeground, fontSize: 13, lineHeight: 19 }}>
335
+ Sign in to sync across devices. Use Entropy&apos;s hosted server or point at
336
+ one you run yourself.
337
+ </Text>
338
+
339
+ {/* endpoint selector — dropdown floats as an overlay so the
340
+ dialog never resizes when it opens. */}
341
+ <View style={{ position: "relative", zIndex: open ? 100 : 1 }}>
342
+ <Text
343
+ style={{
344
+ color: p.mutedForeground,
345
+ fontSize: 13,
346
+ fontWeight: "500",
347
+ marginBottom: 6,
348
+ }}
349
+ >
350
+ Server
351
+ </Text>
352
+ <Pressable
353
+ ref={selRef}
354
+ onPress={toggleMenu}
355
+ style={{
356
+ height: 52,
357
+ borderRadius: 14,
358
+ backgroundColor: p.background,
359
+ borderWidth: 1,
360
+ borderColor: open ? p.primary : p.border,
361
+ flexDirection: "row",
362
+ alignItems: "center",
363
+ paddingHorizontal: 14,
364
+ }}
365
+ >
366
+ <View style={{ flex: 1 }}>
367
+ <Text style={{ color: p.foreground, fontSize: 15, fontWeight: "500" }}>
368
+ {endpoint === "entropy" ? "Entropy Cloud" : "Custom server"}
369
+ </Text>
370
+ <Text style={{ color: p.mutedForeground, fontSize: 11, marginTop: 1 }}>
371
+ {endpoint === "entropy" ? entropyUrl : "DNS / IP / host:port"}
372
+ </Text>
373
+ </View>
374
+ <Animated.View
375
+ style={{ transform: [{ rotate: open ? "180deg" : "0deg" }] }}
376
+ >
377
+ <ChevronDown size={18} color={p.mutedForeground} />
378
+ </Animated.View>
379
+ </Pressable>
380
+ </View>
381
+
382
+ {endpoint === "custom" ? (
383
+ <Input
384
+ label="Address"
385
+ placeholder="vault.example.com · 192.168.1.10:8080"
386
+ autoCapitalize="none"
387
+ autoCorrect={false}
388
+ keyboardType="url"
389
+ value={custom}
390
+ onChangeText={setCustom}
391
+ />
392
+ ) : null}
393
+
394
+ {/* live connection status — fixed height, never shifts layout */}
395
+ <Pressable
396
+ onPress={() => void runTest(resolvedUrl)}
397
+ style={{ height: 22, flexDirection: "row", alignItems: "center", gap: 7 }}
398
+ accessibilityRole="button"
399
+ accessibilityLabel="Test connection"
400
+ >
401
+ {test === "testing" ? (
402
+ <>
403
+ <ActivityIndicator size="small" color={p.mutedForeground} />
404
+ <Text style={{ color: p.mutedForeground, fontSize: 13 }}>
405
+ Checking connection…
406
+ </Text>
407
+ </>
408
+ ) : test === "ok" ? (
409
+ <>
410
+ <Dot color={SUCCESS} />
411
+ <Text style={{ color: SUCCESS, fontSize: 13, fontWeight: "500" }}>
412
+ {testMsg}
413
+ </Text>
414
+ </>
415
+ ) : test === "fail" ? (
416
+ <>
417
+ <Dot color={p.destructive} />
418
+ <Text
419
+ numberOfLines={1}
420
+ style={{ color: p.destructive, fontSize: 13, flex: 1 }}
421
+ >
422
+ {testMsg} · tap to retry
423
+ </Text>
424
+ </>
425
+ ) : (
426
+ <>
427
+ <Dot color={p.mutedForeground} />
428
+ <Text style={{ color: p.mutedForeground, fontSize: 13 }}>
429
+ Not checked
430
+ </Text>
431
+ </>
432
+ )}
433
+ </Pressable>
434
+ </View>
435
+ )}
436
+
437
+ <View style={{ position: "relative", zIndex: 0 }}>
438
+ <Button
439
+ className="w-full"
440
+ disabled={!canSave}
441
+ onPress={() => onSave(localMode, localMode === "cloud" ? resolvedUrl : "")}
442
+ accessibilityLabel="Save connection settings"
443
+ >
444
+ Save
445
+ </Button>
446
+ </View>
447
+ </ScrollView>
448
+
449
+ {/* Server dropdown — rendered last so it paints above everything,
450
+ anchored under the selector via measurement. */}
451
+ {open && menu ? (
452
+ <>
453
+ <Pressable
454
+ onPress={() => setOpen(false)}
455
+ style={{ position: "absolute", top: 0, left: 0, right: 0, bottom: 0 }}
456
+ />
457
+ <Animated.View
458
+ entering={FadeIn.duration(120)}
459
+ style={{
460
+ position: "absolute",
461
+ top: menu.top,
462
+ left: menu.left,
463
+ width: menu.width,
464
+ borderRadius: 14,
465
+ borderWidth: 1,
466
+ borderColor: p.border,
467
+ backgroundColor: p.background,
468
+ overflow: "hidden",
469
+ boxShadow: "0px 10px 18px rgba(0,0,0,0.5)",
470
+ elevation: 12,
471
+ }}
472
+ >
473
+ <Option
474
+ label="Entropy Cloud"
475
+ sub={entropyUrl}
476
+ active={endpoint === "entropy"}
477
+ onPress={() => {
478
+ setEndpoint("entropy");
479
+ setOpen(false);
480
+ }}
481
+ palette={p}
482
+ />
483
+ <Option
484
+ label="Custom server"
485
+ sub="DNS / IP / host:port"
486
+ active={endpoint === "custom"}
487
+ onPress={() => {
488
+ setEndpoint("custom");
489
+ setOpen(false);
490
+ }}
491
+ palette={p}
492
+ />
493
+ </Animated.View>
494
+ </>
495
+ ) : null}
496
+ </Pressable>
497
+ </Animated.View>
498
+ </Pressable>
499
+ </Animated.View>
500
+ </GestureHandlerRootView>
501
+ </Modal>
502
+ );
503
+ }
504
+
505
+ type Pal = ReturnType<typeof usePalette>["palette"];
506
+
507
+ function Dot({ color }: { color: string }) {
508
+ return <View style={{ width: 8, height: 8, borderRadius: 999, backgroundColor: color }} />;
509
+ }
510
+
511
+ function ModePill({
512
+ label,
513
+ active,
514
+ disabled,
515
+ onPress,
516
+ palette: p,
517
+ }: {
518
+ label: string;
519
+ active: boolean;
520
+ disabled?: boolean;
521
+ onPress: () => void;
522
+ palette: Pal;
523
+ }) {
524
+ return (
525
+ <Pressable
526
+ onPress={() => !disabled && onPress()}
527
+ disabled={disabled}
528
+ style={{
529
+ flex: 1,
530
+ paddingVertical: 9,
531
+ borderRadius: 10,
532
+ alignItems: "center",
533
+ backgroundColor: active ? p.primary : "transparent",
534
+ opacity: disabled ? 0.45 : 1,
535
+ }}
536
+ >
537
+ <Text
538
+ style={{
539
+ fontSize: 14,
540
+ fontWeight: active ? "700" : "500",
541
+ color: active ? p.primaryForeground : p.foreground,
542
+ }}
543
+ >
544
+ {label}
545
+ </Text>
546
+ </Pressable>
547
+ );
548
+ }
549
+
550
+ function Option({
551
+ label,
552
+ sub,
553
+ active,
554
+ onPress,
555
+ palette: p,
556
+ }: {
557
+ label: string;
558
+ sub: string;
559
+ active: boolean;
560
+ onPress: () => void;
561
+ palette: Pal;
562
+ }) {
563
+ return (
564
+ <Pressable
565
+ onPress={onPress}
566
+ style={{
567
+ flexDirection: "row",
568
+ alignItems: "center",
569
+ paddingHorizontal: 14,
570
+ paddingVertical: 11,
571
+ backgroundColor: active ? p.muted : p.background,
572
+ }}
573
+ >
574
+ <View style={{ flex: 1 }}>
575
+ <Text style={{ color: p.foreground, fontSize: 14, fontWeight: "500" }}>{label}</Text>
576
+ <Text style={{ color: p.mutedForeground, fontSize: 11, marginTop: 1 }}>{sub}</Text>
577
+ </View>
578
+ {active ? <Check size={16} color={p.primary} /> : null}
579
+ </Pressable>
580
+ );
581
+ }
582
+
583
+ export default ConnectionSettings;
@@ -0,0 +1,95 @@
1
+ import React from "react";
2
+ import * as ContextMenuPrimitive from "@rn-primitives/context-menu";
3
+ import { cn } from "../utils/cn";
4
+ import { useColorScheme } from "nativewind";
5
+
6
+ const ContextMenu = ContextMenuPrimitive.Root;
7
+ const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
8
+
9
+ const ContextMenuContent = React.forwardRef<
10
+ React.ElementRef<typeof ContextMenuPrimitive.Content>,
11
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> & { className?: string }
12
+ >(({ className, ...props }, ref) => {
13
+ const { colorScheme } = useColorScheme();
14
+ const isDark = colorScheme === "dark";
15
+
16
+ return (
17
+ <ContextMenuPrimitive.Portal>
18
+ <ContextMenuPrimitive.Overlay>
19
+ <ContextMenuPrimitive.Content
20
+ ref={ref}
21
+ className={cn(
22
+ "z-50 min-w-[160px] rounded-lg border border-border p-1",
23
+ isDark ? "bg-neutral-900" : "bg-white",
24
+ "shadow-lg",
25
+ className
26
+ )}
27
+ {...props}
28
+ />
29
+ </ContextMenuPrimitive.Overlay>
30
+ </ContextMenuPrimitive.Portal>
31
+ );
32
+ });
33
+ ContextMenuContent.displayName = "ContextMenuContent";
34
+
35
+ const ContextMenuItem = React.forwardRef<
36
+ React.ElementRef<typeof ContextMenuPrimitive.Item>,
37
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
38
+ className?: string;
39
+ inset?: boolean;
40
+ variant?: "default" | "destructive";
41
+ }
42
+ >(({ className, inset, variant, ...props }, ref) => (
43
+ <ContextMenuPrimitive.Item
44
+ ref={ref}
45
+ className={cn(
46
+ "flex-row items-center rounded-md px-2 py-1.5",
47
+ "active:bg-neutral-200 dark:active:bg-neutral-700",
48
+ inset && "pl-8",
49
+ variant === "destructive" && "active:bg-red-100 dark:active:bg-red-900/30",
50
+ className
51
+ )}
52
+ {...props}
53
+ />
54
+ ));
55
+ ContextMenuItem.displayName = "ContextMenuItem";
56
+
57
+ const ContextMenuSeparator = React.forwardRef<
58
+ React.ElementRef<typeof ContextMenuPrimitive.Separator>,
59
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator> & { className?: string }
60
+ >(({ className, ...props }, ref) => (
61
+ <ContextMenuPrimitive.Separator
62
+ ref={ref}
63
+ className={cn("my-1 h-px bg-border dark:bg-neutral-700", className)}
64
+ {...props}
65
+ />
66
+ ));
67
+ ContextMenuSeparator.displayName = "ContextMenuSeparator";
68
+
69
+ const ContextMenuLabel = React.forwardRef<
70
+ React.ElementRef<typeof ContextMenuPrimitive.Label>,
71
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
72
+ className?: string;
73
+ inset?: boolean;
74
+ }
75
+ >(({ className, inset, ...props }, ref) => (
76
+ <ContextMenuPrimitive.Label
77
+ ref={ref}
78
+ className={cn(
79
+ "px-2 py-1.5 text-xs font-semibold text-foreground dark:text-white",
80
+ inset && "pl-8",
81
+ className
82
+ )}
83
+ {...props}
84
+ />
85
+ ));
86
+ ContextMenuLabel.displayName = "ContextMenuLabel";
87
+
88
+ export {
89
+ ContextMenu,
90
+ ContextMenuTrigger,
91
+ ContextMenuContent,
92
+ ContextMenuItem,
93
+ ContextMenuSeparator,
94
+ ContextMenuLabel,
95
+ };