@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,126 @@
1
+ import React from "react";
2
+ import { Pressable, Switch, View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ import { useColorScheme } from "nativewind";
5
+ import { cn } from "../utils/cn";
6
+ import { MutedText, Text } from "./text";
7
+
8
+ export interface SettingsRowProps {
9
+ icon: keyof typeof Ionicons.glyphMap;
10
+ label: string;
11
+ subtitle?: string;
12
+ onPress?: () => void;
13
+ showChevron?: boolean;
14
+ toggle?: boolean;
15
+ value?: boolean;
16
+ onValueChange?: (value: boolean) => void;
17
+ destructive?: boolean;
18
+ }
19
+
20
+ /**
21
+ * Horizontal row used inside settings dialogs / panels. Combines an icon
22
+ * tile, label + optional subtitle, and a trailing switch *or* chevron.
23
+ * `destructive` recolors the label + icon to the destructive token.
24
+ */
25
+ export function SettingsRow({
26
+ icon,
27
+ label,
28
+ subtitle,
29
+ onPress,
30
+ showChevron,
31
+ toggle,
32
+ value,
33
+ onValueChange,
34
+ destructive,
35
+ }: SettingsRowProps) {
36
+ const { colorScheme } = useColorScheme();
37
+ const isDark = colorScheme === "dark";
38
+
39
+ const Wrapper = onPress ? Pressable : View;
40
+ // Toggle-mode rows tap-to-switch and also surface the switch
41
+ // itself — both surfaces need the label as their accessible name
42
+ // so a screen reader doesn't announce "Switch, on" with no
43
+ // context. The Switch carries `accessibilityState` so AT can
44
+ // announce the on/off state.
45
+ const wrapperProps = onPress
46
+ ? ({
47
+ onPress,
48
+ accessibilityRole: "button" as const,
49
+ accessibilityLabel: label,
50
+ ...(subtitle ? { accessibilityHint: subtitle } : {}),
51
+ } as const)
52
+ : {};
53
+
54
+ let iconColor: string;
55
+ if (destructive) {
56
+ iconColor = "#EF4444";
57
+ } else if (isDark) {
58
+ iconColor = "#FFFFFF";
59
+ } else {
60
+ iconColor = "#000000";
61
+ }
62
+
63
+ return (
64
+ <Wrapper
65
+ className="flex-row items-center border-b border-border px-3 py-3 last:border-b-0 dark:border-neutral-800"
66
+ {...wrapperProps}
67
+ >
68
+ <View className="mr-3 h-8 w-8 items-center justify-center rounded-md bg-secondary dark:bg-neutral-800">
69
+ <Ionicons name={icon} size={16} color={iconColor} />
70
+ </View>
71
+ <View className="flex-1">
72
+ <Text
73
+ className={cn(
74
+ "text-sm font-medium",
75
+ destructive ? "text-destructive" : "text-foreground"
76
+ )}
77
+ >
78
+ {label}
79
+ </Text>
80
+ {subtitle ? <MutedText className="text-xs">{subtitle}</MutedText> : null}
81
+ </View>
82
+ {toggle ? (
83
+ <Switch
84
+ value={value}
85
+ onValueChange={onValueChange}
86
+ accessibilityLabel={label}
87
+ accessibilityState={{ checked: value === true }}
88
+ trackColor={{
89
+ false: isDark ? "#404040" : "#D4D4D4",
90
+ true: isDark ? "#FFFFFF" : "#000000",
91
+ }}
92
+ thumbColor={isDark ? "#000000" : "#FFFFFF"}
93
+ />
94
+ ) : null}
95
+ {showChevron ? (
96
+ <Ionicons name="chevron-forward" size={16} color={isDark ? "#525252" : "#A3A3A3"} />
97
+ ) : null}
98
+ </Wrapper>
99
+ );
100
+ }
101
+
102
+ export interface SettingsSectionProps {
103
+ title: string;
104
+ destructive?: boolean;
105
+ children: React.ReactNode;
106
+ }
107
+
108
+ /** Group container for `SettingsRow`s. Renders the section title + a
109
+ * bordered card around its children. */
110
+ export function SettingsSection({ title, destructive, children }: SettingsSectionProps) {
111
+ return (
112
+ <View>
113
+ <Text
114
+ className={cn(
115
+ "mb-2 text-xs font-semibold tracking-wider",
116
+ destructive ? "text-destructive" : "text-muted-foreground"
117
+ )}
118
+ >
119
+ {title}
120
+ </Text>
121
+ <View className="rounded-lg border border-border bg-card dark:border-neutral-800 dark:bg-neutral-900/50">
122
+ {children}
123
+ </View>
124
+ </View>
125
+ );
126
+ }
@@ -0,0 +1,48 @@
1
+ import React, { useEffect } from "react";
2
+ import { type ViewProps } from "react-native";
3
+ import Animated, {
4
+ cancelAnimation,
5
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withRepeat,
8
+ withSequence,
9
+ withTiming,
10
+ } from "react-native-reanimated";
11
+ import { useColorScheme } from "nativewind";
12
+ import { cn } from "../utils/cn";
13
+
14
+ interface SkeletonProps extends ViewProps {
15
+ className?: string;
16
+ }
17
+
18
+ export const Skeleton = React.memo(function Skeleton({ className, ...props }: SkeletonProps) {
19
+ const { colorScheme } = useColorScheme();
20
+ const isDark = colorScheme === "dark";
21
+ const opacity = useSharedValue(0.4);
22
+
23
+ useEffect(() => {
24
+ opacity.value = withRepeat(
25
+ withSequence(withTiming(1, { duration: 750 }), withTiming(0.4, { duration: 750 })),
26
+ -1
27
+ );
28
+ // Cancel the infinite repeat on unmount. Without this the worklet
29
+ // keeps running until the shared value is GC'd, which in long
30
+ // FlashList re-cycles (skeleton → loaded item → skeleton) leaves
31
+ // orphan animations alive.
32
+ return () => {
33
+ cancelAnimation(opacity);
34
+ };
35
+ }, [opacity]);
36
+
37
+ const animatedStyle = useAnimatedStyle(() => ({
38
+ opacity: opacity.value,
39
+ }));
40
+
41
+ return (
42
+ <Animated.View
43
+ className={cn("rounded-md", isDark ? "bg-neutral-800" : "bg-neutral-200", className)}
44
+ style={animatedStyle}
45
+ {...props}
46
+ />
47
+ );
48
+ });
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+ import { Pressable, View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ import { useColorScheme } from "nativewind";
5
+
6
+ export interface SocialConnectionsProps {
7
+ onApple?: () => void;
8
+ onGoogle?: () => void;
9
+ onMicrosoft?: () => void;
10
+ }
11
+
12
+ const PROVIDERS = [
13
+ { key: "apple", icon: "logo-apple" as const },
14
+ { key: "google", icon: "logo-google" as const },
15
+ { key: "microsoft", icon: "logo-microsoft" as const },
16
+ ] as const;
17
+
18
+ /**
19
+ * Three-up button row for federated sign-in (Apple / Google / Microsoft).
20
+ * Each handler is optional — providers with no handler still render but
21
+ * fire no action; this matches the "coming soon" UX the consumer apps
22
+ * use during the staged rollout.
23
+ */
24
+ export function SocialConnections({
25
+ onApple,
26
+ onGoogle,
27
+ onMicrosoft,
28
+ }: SocialConnectionsProps): React.JSX.Element {
29
+ const { colorScheme } = useColorScheme();
30
+ const isDark = colorScheme === "dark";
31
+
32
+ const handlers: Record<string, (() => void) | undefined> = {
33
+ apple: onApple,
34
+ google: onGoogle,
35
+ microsoft: onMicrosoft,
36
+ };
37
+
38
+ return (
39
+ <View className="w-full flex-row items-center justify-center gap-3">
40
+ {PROVIDERS.map(({ key, icon }) => (
41
+ <Pressable
42
+ key={key}
43
+ onPress={handlers[key]}
44
+ className="h-12 w-12 items-center justify-center rounded-lg border border-border dark:border-neutral-600 active:opacity-70"
45
+ accessibilityRole="button"
46
+ accessibilityLabel={`Sign in with ${key}`}
47
+ >
48
+ <Ionicons name={icon} size={22} color={isDark ? "#FFFFFF" : "#000000"} />
49
+ </Pressable>
50
+ ))}
51
+ </View>
52
+ );
53
+ }
54
+
55
+ export default SocialConnections;
@@ -0,0 +1,64 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { cn } from "../utils/cn";
4
+ import { Card } from "./card";
5
+ import { MutedText, Text } from "./text";
6
+
7
+ /** Direction of a stat's delta — drives the trend colour. */
8
+ export type StatTrend = "up" | "down" | "neutral";
9
+
10
+ export interface StatCardProps {
11
+ /** Short label describing the metric (e.g. "Active users"). */
12
+ label: string;
13
+ /** The headline value. Numbers are rendered as-is; format upstream. */
14
+ value: string | number;
15
+ /** Optional leading icon slot (e.g. a lucide / Ionicons element). */
16
+ icon?: React.ReactNode;
17
+ /** Optional change indicator text (e.g. "+12% this week"). */
18
+ delta?: string;
19
+ /** Trend direction — colours the delta. Defaults to "neutral". */
20
+ trend?: StatTrend;
21
+ className?: string;
22
+ }
23
+
24
+ // Trend → delta text colour. Tokens only (success/destructive/muted) so each
25
+ // consuming app's palette flows through; no hardcoded hex.
26
+ const trendClass: Record<StatTrend, string> = {
27
+ up: "text-success",
28
+ down: "text-destructive",
29
+ neutral: "text-muted-foreground dark:text-neutral-400",
30
+ };
31
+
32
+ /**
33
+ * Map a trend direction to its delta text colour class. Pure so the
34
+ * trend→colour contract is unit-testable without a render host.
35
+ */
36
+ export function trendColorClass(trend: StatTrend): string {
37
+ return trendClass[trend];
38
+ }
39
+
40
+ /**
41
+ * Dashboard stat tile: a label, a prominent value, an optional leading icon,
42
+ * and an optional trend/delta line. Composes `Card` + `Text` so the surface,
43
+ * border, and typography stay in lockstep with the rest of the system rather
44
+ * than duplicating styles.
45
+ */
46
+ export function StatCard({
47
+ label,
48
+ value,
49
+ icon,
50
+ delta,
51
+ trend = "neutral",
52
+ className,
53
+ }: StatCardProps) {
54
+ return (
55
+ <Card className={cn("p-5", className)} accessibilityRole="summary">
56
+ <View className="flex-row items-start justify-between">
57
+ <MutedText className="text-sm">{label}</MutedText>
58
+ {icon ? <View className="ml-2">{icon}</View> : null}
59
+ </View>
60
+ <Text className="mt-2 text-3xl font-bold text-foreground dark:text-white">{value}</Text>
61
+ {delta ? <Text className={cn("mt-1 text-xs", trendColorClass(trend))}>{delta}</Text> : null}
62
+ </Card>
63
+ );
64
+ }
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+ import { Pressable, View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ import { useColorScheme } from "nativewind";
5
+ import { Text } from "./text";
6
+
7
+ export interface TagChipProps {
8
+ name: string;
9
+ /** Accent color for the tag. Accepts `rgb(r, g, b)` or `#rrggbb`. */
10
+ color?: string | null;
11
+ selected?: boolean;
12
+ onPress?: () => void;
13
+ size?: "sm" | "md";
14
+ }
15
+
16
+ /**
17
+ * Compact chip used to display a tag — icon + name with a soft accent
18
+ * border. When `selected` is true the border + background pick up the
19
+ * accent at full opacity / 18% fill respectively.
20
+ */
21
+ export function TagChip({ name, color, selected, onPress, size = "sm" }: TagChipProps) {
22
+ const { colorScheme } = useColorScheme();
23
+ const isDark = colorScheme === "dark";
24
+ const accent = color ?? (isDark ? "rgb(163, 163, 163)" : "rgb(115, 115, 115)");
25
+ const dims =
26
+ size === "sm"
27
+ ? { paddingH: 8, paddingV: 3, fontSize: 11, iconSize: 10 }
28
+ : { paddingH: 10, paddingV: 5, fontSize: 12, iconSize: 12 };
29
+
30
+ const inner = (
31
+ <View
32
+ style={{
33
+ flexDirection: "row",
34
+ alignItems: "center",
35
+ gap: 4,
36
+ paddingHorizontal: dims.paddingH,
37
+ paddingVertical: dims.paddingV,
38
+ borderRadius: 6,
39
+ borderWidth: 1,
40
+ borderColor: selected ? accent : withAlpha(accent, 0.33),
41
+ backgroundColor: selected ? withAlpha(accent, 0.18) : "transparent",
42
+ }}
43
+ >
44
+ <Ionicons name="pricetag-outline" size={dims.iconSize} color={accent} />
45
+ <Text className="text-foreground" style={{ fontSize: dims.fontSize }}>
46
+ {name}
47
+ </Text>
48
+ </View>
49
+ );
50
+
51
+ if (!onPress) {
52
+ return inner;
53
+ }
54
+
55
+ return (
56
+ <Pressable
57
+ onPress={onPress}
58
+ hitSlop={4}
59
+ style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1 })}
60
+ >
61
+ {inner}
62
+ </Pressable>
63
+ );
64
+ }
65
+
66
+ /** Tints an `rgb(...)` or `#rrggbb` color with the given alpha 0..1. */
67
+ export function withAlpha(color: string, alpha: number): string {
68
+ const rgb = color.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i);
69
+ if (rgb) {
70
+ return `rgba(${rgb[1]}, ${rgb[2]}, ${rgb[3]}, ${alpha})`;
71
+ }
72
+ const hex = color.match(/^#([0-9a-f]{6})$/i);
73
+ if (hex) {
74
+ const h = hex[1] ?? "";
75
+ return `rgba(${parseInt(h.slice(0, 2), 16)}, ${parseInt(h.slice(2, 4), 16)}, ${parseInt(h.slice(4, 6), 16)}, ${alpha})`;
76
+ }
77
+ return color;
78
+ }
@@ -0,0 +1,136 @@
1
+ import React, { useMemo, useState } from "react";
2
+ import { Pressable, ScrollView, View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ import { useColorScheme } from "nativewind";
5
+ import { MutedText, Text } from "./text";
6
+ import { SearchInput } from "./input";
7
+
8
+ export interface TagOption {
9
+ id: string;
10
+ name: string;
11
+ /** Accent color used for the chip border + icon (defaults to muted). */
12
+ color?: string | null;
13
+ }
14
+
15
+ export interface TagMultiSelectProps<T extends TagOption> {
16
+ tags: T[];
17
+ selectedIds: string[];
18
+ onToggle: (tagId: string) => void;
19
+ /** Resolves the Ionicons glyph for a tag. Defaults to `pricetag`. */
20
+ iconFor?: (tag: T) => keyof typeof Ionicons.glyphMap;
21
+ /** Pixel cap on the scrollable list height. Defaults to 240. */
22
+ listMaxHeight?: number;
23
+ }
24
+
25
+ /**
26
+ * Searchable multi-select picker for tags / labels. Designed for lists
27
+ * that can be in the thousands — the list scrolls, fuzzy filters by
28
+ * name, and selected items appear as removable chips above the search.
29
+ *
30
+ * Generic over the tag shape; pass `iconFor` to map your tag's icon
31
+ * value onto an Ionicons glyph.
32
+ */
33
+ export function TagMultiSelect<T extends TagOption>({
34
+ tags,
35
+ selectedIds,
36
+ onToggle,
37
+ iconFor,
38
+ listMaxHeight = 240,
39
+ }: TagMultiSelectProps<T>) {
40
+ const { colorScheme } = useColorScheme();
41
+ const isDark = colorScheme === "dark";
42
+ const mutedColor = isDark ? "#A3A3A3" : "#737373";
43
+
44
+ const [query, setQuery] = useState("");
45
+
46
+ const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);
47
+
48
+ const selectedTags = useMemo(
49
+ () => selectedIds.map((id) => tags.find((t) => t.id === id)).filter((t): t is T => !!t),
50
+ [selectedIds, tags]
51
+ );
52
+
53
+ const filteredTags = useMemo(() => {
54
+ const q = query.trim().toLowerCase();
55
+ if (!q) {
56
+ return tags;
57
+ }
58
+ return tags.filter((t) => t.name.toLowerCase().includes(q));
59
+ }, [query, tags]);
60
+
61
+ const resolveIcon = (tag: T): keyof typeof Ionicons.glyphMap =>
62
+ iconFor ? iconFor(tag) : "pricetag";
63
+
64
+ return (
65
+ <View className="gap-2">
66
+ {selectedTags.length > 0 && (
67
+ <View className="flex-row flex-wrap gap-1.5">
68
+ {selectedTags.map((tag) => {
69
+ const accent = tag.color ?? mutedColor;
70
+ return (
71
+ <Pressable
72
+ key={tag.id}
73
+ onPress={() => onToggle(tag.id)}
74
+ className="flex-row items-center gap-1 rounded-md border px-2 py-1 active:opacity-70"
75
+ style={{ borderColor: `${accent}55` }}
76
+ accessibilityLabel={`Remove tag ${tag.name}`}
77
+ accessibilityRole="button"
78
+ >
79
+ <Ionicons name={resolveIcon(tag)} size={12} color={accent} />
80
+ <Text className="text-xs text-foreground">{tag.name}</Text>
81
+ <Ionicons name="close-circle" size={14} color={mutedColor} />
82
+ </Pressable>
83
+ );
84
+ })}
85
+ </View>
86
+ )}
87
+
88
+ <SearchInput
89
+ value={query}
90
+ onChangeText={setQuery}
91
+ placeholder={`Search ${tags.length} tag${tags.length === 1 ? "" : "s"}...`}
92
+ accessibilityLabel="Search tags"
93
+ />
94
+
95
+ <View
96
+ className="rounded-lg border border-border dark:border-neutral-800"
97
+ style={{ maxHeight: listMaxHeight }}
98
+ >
99
+ {filteredTags.length === 0 ? (
100
+ <View className="p-4">
101
+ <MutedText className="text-center text-sm italic">
102
+ {query ? `No tags match "${query}"` : "No tags yet"}
103
+ </MutedText>
104
+ </View>
105
+ ) : (
106
+ <ScrollView keyboardShouldPersistTaps="handled">
107
+ {filteredTags.map((tag) => {
108
+ const selected = selectedSet.has(tag.id);
109
+ const accent = tag.color ?? mutedColor;
110
+ const selectedColor = isDark ? "#FFFFFF" : "#000000";
111
+ const checkboxColor = selected ? selectedColor : mutedColor;
112
+ return (
113
+ <Pressable
114
+ key={tag.id}
115
+ onPress={() => onToggle(tag.id)}
116
+ accessibilityLabel={`${selected ? "Deselect" : "Select"} tag ${tag.name}`}
117
+ accessibilityRole="checkbox"
118
+ accessibilityState={{ checked: selected }}
119
+ className="flex-row items-center gap-2 px-3 py-2 active:bg-neutral-100 dark:active:bg-neutral-800"
120
+ >
121
+ <Ionicons
122
+ name={selected ? "checkbox" : "square-outline"}
123
+ size={18}
124
+ color={checkboxColor}
125
+ />
126
+ <Ionicons name={resolveIcon(tag)} size={14} color={accent} />
127
+ <Text className="flex-1 text-sm text-foreground">{tag.name}</Text>
128
+ </Pressable>
129
+ );
130
+ })}
131
+ </ScrollView>
132
+ )}
133
+ </View>
134
+ </View>
135
+ );
136
+ }
@@ -0,0 +1,77 @@
1
+ import React from "react";
2
+ import { Text as RNText, type TextProps as RNTextProps } from "react-native";
3
+ import { cn } from "../utils/cn";
4
+
5
+ export interface TextProps extends RNTextProps {
6
+ className?: string;
7
+ children?: React.ReactNode;
8
+ }
9
+
10
+ /**
11
+ * Text component with NativeWind styling. Provides consistent typography
12
+ * across consumer apps with dark-mode support.
13
+ */
14
+ export function Text({ className, children, ...props }: TextProps) {
15
+ return (
16
+ <RNText className={cn("text-base text-foreground dark:text-white", className)} {...props}>
17
+ {children}
18
+ </RNText>
19
+ );
20
+ }
21
+
22
+ /** Heading variant with larger size and bold weight. */
23
+ export function Heading({ className, children, ...props }: TextProps) {
24
+ return (
25
+ <RNText
26
+ className={cn("text-2xl font-bold text-foreground dark:text-white", className)}
27
+ {...props}
28
+ >
29
+ {children}
30
+ </RNText>
31
+ );
32
+ }
33
+
34
+ /** Label variant for form labels and small text. */
35
+ export function Label({ className, children, ...props }: TextProps) {
36
+ return (
37
+ <RNText
38
+ className={cn("text-sm text-muted-foreground dark:text-neutral-400", className)}
39
+ {...props}
40
+ >
41
+ {children}
42
+ </RNText>
43
+ );
44
+ }
45
+
46
+ /** Muted text variant for less prominent content. */
47
+ export function MutedText({ className, children, ...props }: TextProps) {
48
+ return (
49
+ <RNText
50
+ className={cn("text-base text-muted-foreground dark:text-neutral-400", className)}
51
+ {...props}
52
+ >
53
+ {children}
54
+ </RNText>
55
+ );
56
+ }
57
+
58
+ /** Monospace text variant for codes, passwords, and technical content. */
59
+ export function MonoText({ className, children, ...props }: TextProps) {
60
+ return (
61
+ <RNText
62
+ className={cn("font-mono text-base text-foreground dark:text-white", className)}
63
+ {...props}
64
+ >
65
+ {children}
66
+ </RNText>
67
+ );
68
+ }
69
+
70
+ /** Error text variant for validation messages. */
71
+ export function ErrorText({ className, children, ...props }: TextProps) {
72
+ return (
73
+ <RNText className={cn("text-sm text-destructive dark:text-red-400", className)} {...props}>
74
+ {children}
75
+ </RNText>
76
+ );
77
+ }
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import Toast from "react-native-toast-message";
3
+ import { toastConfig } from "./toast-config";
4
+
5
+ /**
6
+ * Native toast portal — renders `Toast` directly in the component tree.
7
+ * Toast uses absolute positioning + a high zIndex to float above most
8
+ * content. Note: Toast will not appear above a React Native `Modal`
9
+ * (dialogs); wrapping in a Modal to force that would block all touches
10
+ * on the screen.
11
+ */
12
+ export function ToastPortal(): React.JSX.Element {
13
+ return <Toast config={toastConfig} position="top" visibilityTime={3000} topOffset={50} />;
14
+ }
15
+
16
+ export default ToastPortal;
@@ -0,0 +1,47 @@
1
+ import React, { useEffect, useRef } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import Toast from "react-native-toast-message";
4
+ import { toastConfig } from "./toast-config";
5
+
6
+ /**
7
+ * Web toast portal — mounts `Toast` into a div appended directly to
8
+ * `document.body` so it renders above React Native Web's `Modal`
9
+ * portals (which also target body).
10
+ *
11
+ * The container div uses `pointer-events:none` so the transparent
12
+ * overlay never intercepts clicks. HTML `pointer-events` does NOT
13
+ * inherit to children (unlike SVG), so the visible toast pill itself
14
+ * remains fully interactive via its own event handlers.
15
+ */
16
+ export function ToastPortal(): React.JSX.Element | null {
17
+ const containerRef = useRef<HTMLDivElement | null>(null);
18
+
19
+ if (!containerRef.current) {
20
+ const div = document.createElement("div");
21
+ div.style.cssText =
22
+ "position:fixed;top:0;bottom:0;right:0;left:var(--sidebar-width,0px);z-index:999999;pointer-events:none;";
23
+ containerRef.current = div;
24
+ }
25
+
26
+ useEffect(() => {
27
+ const container = containerRef.current;
28
+ if (!container) {
29
+ return;
30
+ }
31
+ document.body.appendChild(container);
32
+ return () => {
33
+ document.body.removeChild(container);
34
+ };
35
+ }, []);
36
+
37
+ if (!containerRef.current) {
38
+ return null;
39
+ }
40
+
41
+ return createPortal(
42
+ <Toast config={toastConfig} position="top" visibilityTime={3000} topOffset={50} />,
43
+ containerRef.current
44
+ );
45
+ }
46
+
47
+ export default ToastPortal;
@@ -0,0 +1,2 @@
1
+ export { ToastPortal } from "./ToastPortal";
2
+ export { toastConfig } from "./toast-config";
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import type { BaseToastProps } from "react-native-toast-message";
4
+ import { Text } from "../text";
5
+
6
+ /**
7
+ * Shared toast renderer config for `react-native-toast-message`. Three
8
+ * variants (success / error / info) rendered as pill cards in the
9
+ * package's standard token palette. Used by `ToastPortal` on both
10
+ * native and web.
11
+ */
12
+ export const toastConfig = {
13
+ success: ({ text1, text2 }: BaseToastProps) => (
14
+ <View className="self-center bg-green-500 dark:bg-green-600 px-4 py-3 rounded-lg mx-4 mt-4">
15
+ <Text className="text-white font-semibold">{text1}</Text>
16
+ {text2 && <Text className="text-white/80 text-sm mt-1">{text2}</Text>}
17
+ </View>
18
+ ),
19
+ error: ({ text1, text2 }: BaseToastProps) => (
20
+ <View className="self-center bg-destructive dark:bg-red-600 px-4 py-3 rounded-lg mx-4 mt-4">
21
+ <Text className="text-white font-semibold">{text1}</Text>
22
+ {text2 && <Text className="text-white/80 text-sm mt-1">{text2}</Text>}
23
+ </View>
24
+ ),
25
+ info: ({ text1, text2 }: BaseToastProps) => (
26
+ <View className="self-center bg-blue-500 dark:bg-blue-600 px-4 py-3 rounded-lg mx-4 mt-4">
27
+ <Text className="text-white font-semibold">{text1}</Text>
28
+ {text2 && <Text className="text-white/80 text-sm mt-1">{text2}</Text>}
29
+ </View>
30
+ ),
31
+ };