@entropy-softworks/ui 2026.9.10 → 2026.910.1

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 (67) hide show
  1. package/README.md +2 -2
  2. package/lib/components/error-boundary.d.ts.map +1 -1
  3. package/lib/components/error-boundary.jsx +3 -1
  4. package/lib/components/error-boundary.jsx.map +1 -1
  5. package/lib/components/password-generator.d.ts.map +1 -1
  6. package/lib/components/password-generator.jsx +15 -5
  7. package/lib/components/password-generator.jsx.map +1 -1
  8. package/lib/components/settings-row.d.ts.map +1 -1
  9. package/lib/components/settings-row.jsx +3 -1
  10. package/lib/components/settings-row.jsx.map +1 -1
  11. package/lib/hooks/usePasswordGenerator.d.ts.map +1 -1
  12. package/lib/hooks/usePasswordGenerator.js +1 -1
  13. package/lib/hooks/usePasswordGenerator.js.map +1 -1
  14. package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -1
  15. package/lib/screens/auth/AccountPickerScreen.jsx +1 -3
  16. package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -1
  17. package/lib/screens/auth/AuthScreen.d.ts +18 -1
  18. package/lib/screens/auth/AuthScreen.d.ts.map +1 -1
  19. package/lib/screens/auth/AuthScreen.jsx +30 -1
  20. package/lib/screens/auth/AuthScreen.jsx.map +1 -1
  21. package/lib/screens/auth/ConsentScreen.jsx.map +1 -1
  22. package/lib/screens/auth/ErrorScreen.jsx.map +1 -1
  23. package/lib/screens/auth/ExpiredLinkScreen.d.ts.map +1 -1
  24. package/lib/screens/auth/ExpiredLinkScreen.jsx +1 -1
  25. package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -1
  26. package/lib/screens/auth/ForgotPasswordScreen.d.ts.map +1 -1
  27. package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -1
  28. package/lib/screens/auth/ImportDataScreen.jsx.map +1 -1
  29. package/lib/screens/auth/LoginScreen.d.ts.map +1 -1
  30. package/lib/screens/auth/LoginScreen.jsx.map +1 -1
  31. package/lib/screens/auth/MfaChallengeScreen.d.ts.map +1 -1
  32. package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -1
  33. package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -1
  34. package/lib/screens/auth/PasskeyEnrollScreen.jsx +3 -5
  35. package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -1
  36. package/lib/screens/auth/PrivacyPolicyScreen.d.ts.map +1 -1
  37. package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -1
  38. package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -1
  39. package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -1
  40. package/lib/screens/auth/ServerConfigScreen.d.ts.map +1 -1
  41. package/lib/screens/auth/ServerConfigScreen.jsx +15 -5
  42. package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -1
  43. package/lib/screens/auth/TermsOfServiceScreen.d.ts.map +1 -1
  44. package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -1
  45. package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -1
  46. package/lib/screens/auth/WelcomeScreen.jsx.map +1 -1
  47. package/package.json +1 -1
  48. package/src/components/error-boundary.tsx +3 -1
  49. package/src/components/password-generator.tsx +15 -5
  50. package/src/components/settings-row.tsx +3 -1
  51. package/src/hooks/usePasswordGenerator.ts +8 -1
  52. package/src/screens/auth/AccountPickerScreen.tsx +2 -4
  53. package/src/screens/auth/AuthScreen.tsx +52 -0
  54. package/src/screens/auth/ConsentScreen.tsx +2 -2
  55. package/src/screens/auth/ErrorScreen.tsx +2 -2
  56. package/src/screens/auth/ExpiredLinkScreen.tsx +3 -9
  57. package/src/screens/auth/ForgotPasswordScreen.tsx +6 -4
  58. package/src/screens/auth/ImportDataScreen.tsx +1 -1
  59. package/src/screens/auth/LoginScreen.tsx +13 -6
  60. package/src/screens/auth/MfaChallengeScreen.tsx +4 -1
  61. package/src/screens/auth/PasskeyEnrollScreen.tsx +5 -7
  62. package/src/screens/auth/PrivacyPolicyScreen.tsx +9 -3
  63. package/src/screens/auth/ResetPasswordScreen.tsx +6 -4
  64. package/src/screens/auth/ServerConfigScreen.tsx +19 -6
  65. package/src/screens/auth/TermsOfServiceScreen.tsx +9 -3
  66. package/src/screens/auth/WelcomeScreen.tsx +11 -4
  67. package/src/services/telemetry.ts +3 -3
@@ -6,7 +6,14 @@
6
6
  // layout — see components/password-generator.tsx's own doc comment for
7
7
  // why that stayed a separate render rather than one component branching
8
8
  // on Platform.OS internally. User-requested, 2026-08-17.
9
- import { type Dispatch, type SetStateAction, useCallback, useEffect, useRef, useState } from "react";
9
+ import {
10
+ type Dispatch,
11
+ type SetStateAction,
12
+ useCallback,
13
+ useEffect,
14
+ useRef,
15
+ useState,
16
+ } from "react";
10
17
  import { Platform } from "react-native";
11
18
  import * as Haptics from "expo-haptics";
12
19
  import Toast from "react-native-toast-message";
@@ -75,9 +75,7 @@ export function AccountPickerScreen({
75
75
  accessibilityLabel={`Continue as ${a.username}`}
76
76
  className="rounded-md border border-border px-3 py-3"
77
77
  >
78
- <Text className="text-sm text-foreground">
79
- {a.displayName || a.username}
80
- </Text>
78
+ <Text className="text-sm text-foreground">{a.displayName || a.username}</Text>
81
79
  <MutedText className="text-xs">{a.email}</MutedText>
82
80
  </Pressable>
83
81
  ))
@@ -89,7 +87,7 @@ export function AccountPickerScreen({
89
87
  className="w-full"
90
88
  variant="outline"
91
89
  telemetryId="auth.account_picker.use_another"
92
- onPress={onUseAnother}
90
+ onPress={onUseAnother}
93
91
  accessibilityLabel="Use another account"
94
92
  accessibilityRole="button"
95
93
  >
@@ -279,6 +279,23 @@ export interface AuthScreenProps {
279
279
  * hidden too, since there is no stage behind it.
280
280
  */
281
281
  showWelcome?: boolean;
282
+ /**
283
+ * Page heading, rendered ABOVE the card, e.g.
284
+ * "Log into your Entropy account to continue."
285
+ *
286
+ * Undefined renders no heading, which is the right default for an app whose
287
+ * own login the user reached deliberately -- the surrounding brand already
288
+ * says where they are. Set it on an identity provider, where the user is
289
+ * frequently redirected in from a relying application and this is the only
290
+ * thing on screen that can say whose credentials to type.
291
+ *
292
+ * It used to render inside the card header, where it sat over the
293
+ * Sign In / Sign Up toggle and read as a label for the selected mode. Above
294
+ * the card it is the page's heading and the card is purely the form.
295
+ */
296
+ title?: string;
297
+ /** Optional line under `title`. Ignored unless `title` is set. */
298
+ subtitle?: string;
282
299
  }
283
300
 
284
301
  /**
@@ -314,6 +331,8 @@ export function AuthScreen({
314
331
  onSignUp,
315
332
  signUpSuccessMessage = "Account created. Check your email to verify the address, then sign in.",
316
333
  showWelcome = true,
334
+ title,
335
+ subtitle,
317
336
  }: AuthScreenProps = {}) {
318
337
  const {
319
338
  login: authLogin,
@@ -1058,6 +1077,34 @@ export function AuthScreen({
1058
1077
  {cardBackdrop}
1059
1078
  </View>
1060
1079
  ) : null}
1080
+ {/* ── WHOSE login this is, ABOVE the card ──────────────
1081
+ Without it the card is a pair of unlabelled fields under
1082
+ a Sign In / Sign Up toggle: fine when the user arrived
1083
+ from the app they are signing into, actively confusing
1084
+ when an OIDC redirect dropped them here from somewhere
1085
+ else -- the case an identity provider is FOR.
1086
+
1087
+ Above the card rather than in its header, which is where
1088
+ this started. In the header it sat directly over the
1089
+ mode toggle and read as a label for the selected mode
1090
+ ("Log in to Entropy" above a Sign In / Sign Up switch
1091
+ restates the switch). Out here it is unambiguously the
1092
+ page's own heading, the card below it is purely the
1093
+ form, and the brand backdrop has something to sit
1094
+ behind.
1095
+
1096
+ Width is pinned to the card's 360px so the two are one
1097
+ column, and the text is centred to match. */}
1098
+ {title ? (
1099
+ <View className="w-full max-w-[360px] gap-1 pb-5">
1100
+ <Heading className="text-center text-xl">{title}</Heading>
1101
+ {subtitle ? (
1102
+ <MutedText className="text-center text-sm leading-5">
1103
+ {subtitle}
1104
+ </MutedText>
1105
+ ) : null}
1106
+ </View>
1107
+ ) : null}
1061
1108
  <Card className="w-full max-w-[360px]">
1062
1109
  {displayStage === "forgot" ? (
1063
1110
  <>
@@ -1139,6 +1186,11 @@ export function AuthScreen({
1139
1186
  that otherwise all collects above the footer.
1140
1187
  Applies to both modes — the header is shared. */}
1141
1188
  <CardHeader className="gap-5 pb-3 pt-5">
1189
+ {/* The heading moved OUT of the card -- see the
1190
+ block above <Card>. Inside here it read as a
1191
+ label for whichever toggle mode was selected;
1192
+ above the card it reads as the page saying whose
1193
+ login this is, which is what it is for. */}
1142
1194
  {signUpEnabled ? (
1143
1195
  <SegmentedToggle<Mode>
1144
1196
  value={mode}
@@ -93,7 +93,7 @@ export function ConsentScreen({
93
93
  <Button
94
94
  className="w-full"
95
95
  telemetryId="auth.consent.allow"
96
- onPress={onAllow}
96
+ onPress={onAllow}
97
97
  isLoading={isLoading}
98
98
  accessibilityLabel={`Allow ${clientName}`}
99
99
  accessibilityRole="button"
@@ -104,7 +104,7 @@ export function ConsentScreen({
104
104
  className="w-full"
105
105
  variant="outline"
106
106
  telemetryId="auth.consent.deny"
107
- onPress={onDeny}
107
+ onPress={onDeny}
108
108
  accessibilityLabel="Deny access"
109
109
  accessibilityRole="button"
110
110
  >
@@ -68,7 +68,7 @@ export function ErrorScreen({
68
68
  <Button
69
69
  className="w-full"
70
70
  telemetryId="auth.error.retry"
71
- onPress={onRetry}
71
+ onPress={onRetry}
72
72
  accessibilityLabel="Try again"
73
73
  accessibilityRole="button"
74
74
  >
@@ -80,7 +80,7 @@ export function ErrorScreen({
80
80
  className="w-full"
81
81
  variant="outline"
82
82
  telemetryId="auth.error.home"
83
- onPress={onHome}
83
+ onPress={onHome}
84
84
  accessibilityLabel={homeLabel}
85
85
  accessibilityRole="button"
86
86
  >
@@ -4,13 +4,7 @@ import { SafeAreaView } from "react-native-safe-area-context";
4
4
  import { StatusBar } from "expo-status-bar";
5
5
  import { useColorScheme } from "nativewind";
6
6
  import { Button } from "../../components/button";
7
- import {
8
- Card,
9
- CardDescription,
10
- CardFooter,
11
- CardHeader,
12
- CardTitle,
13
- } from "../../components/card";
7
+ import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "../../components/card";
14
8
 
15
9
  export interface ExpiredLinkScreenProps {
16
10
  title?: string;
@@ -59,7 +53,7 @@ export function ExpiredLinkScreen({
59
53
  <Button
60
54
  className="w-full"
61
55
  telemetryId="auth.expired_link.request_new"
62
- onPress={onRequestNew}
56
+ onPress={onRequestNew}
63
57
  accessibilityLabel={requestLabel}
64
58
  accessibilityRole="button"
65
59
  >
@@ -71,7 +65,7 @@ export function ExpiredLinkScreen({
71
65
  className="w-full"
72
66
  variant="outline"
73
67
  telemetryId="auth.expired_link.home"
74
- onPress={onHome}
68
+ onPress={onHome}
75
69
  accessibilityLabel={homeLabel}
76
70
  accessibilityRole="button"
77
71
  >
@@ -81,7 +81,7 @@ export function ForgotPasswordScreen() {
81
81
  <IconButton
82
82
  variant="ghost"
83
83
  telemetryId="auth.forgot_password.back"
84
- onPress={() => router.back()}
84
+ onPress={() => router.back()}
85
85
  accessibilityLabel="Go back"
86
86
  accessibilityRole="button"
87
87
  >
@@ -123,7 +123,7 @@ export function ForgotPasswordScreen() {
123
123
  <Button
124
124
  className="w-full"
125
125
  telemetryId="auth.forgot_password.submit"
126
- onPress={handleSubmit}
126
+ onPress={handleSubmit}
127
127
  isLoading={isLoading}
128
128
  accessibilityLabel="Reset your password"
129
129
  accessibilityRole="button"
@@ -135,7 +135,9 @@ export function ForgotPasswordScreen() {
135
135
  Remember your password?{" "}
136
136
  <Text
137
137
  className="text-sm text-foreground underline"
138
- onPress={withClickTracking("auth.forgot_password.push", () => router.push(routes.login))}
138
+ onPress={withClickTracking("auth.forgot_password.push", () =>
139
+ router.push(routes.login)
140
+ )}
139
141
  accessibilityRole="link"
140
142
  accessibilityLabel="Sign in"
141
143
  >
@@ -149,7 +151,7 @@ export function ForgotPasswordScreen() {
149
151
  <Button
150
152
  className="w-full"
151
153
  telemetryId="auth.forgot_password.push_2"
152
- onPress={() =>
154
+ onPress={() =>
153
155
  router.push({
154
156
  pathname: routes.resetPassword,
155
157
  params: { email },
@@ -96,7 +96,7 @@ export function ImportDataScreen({ renderImport }: ImportDataScreenProps = {}) {
96
96
  <Button
97
97
  className="w-full"
98
98
  telemetryId="auth.import_data.complete_and_navigate"
99
- onPress={completeAndNavigate}
99
+ onPress={completeAndNavigate}
100
100
  accessibilityLabel={`Continue to ${appName}`}
101
101
  accessibilityRole="button"
102
102
  testID="import-continue"
@@ -282,7 +282,7 @@ export function LoginScreen({
282
282
  <IconButton
283
283
  variant="ghost"
284
284
  telemetryId="auth.login.push"
285
- onPress={() => router.push(routes.welcome)}
285
+ onPress={() => router.push(routes.welcome)}
286
286
  className="self-start"
287
287
  accessibilityLabel="Go back"
288
288
  accessibilityRole="button"
@@ -318,7 +318,7 @@ export function LoginScreen({
318
318
  variant="outline"
319
319
  className="w-full"
320
320
  telemetryId="auth.login.passkey_login"
321
- onPress={handlePasskeyLogin}
321
+ onPress={handlePasskeyLogin}
322
322
  isLoading={passkeyStep === "passkey-loading"}
323
323
  accessibilityLabel="Sign in with passkey"
324
324
  accessibilityRole="button"
@@ -378,7 +378,9 @@ export function LoginScreen({
378
378
  <View className="items-end">
379
379
  <Text
380
380
  className="font-mono text-sm underline"
381
- onPress={withClickTracking("auth.login.push_2", () => router.push(routes.forgotPassword))}
381
+ onPress={withClickTracking("auth.login.push_2", () =>
382
+ router.push(routes.forgotPassword)
383
+ )}
382
384
  accessibilityRole="link"
383
385
  accessibilityLabel="Forgot password"
384
386
  >
@@ -392,7 +394,9 @@ export function LoginScreen({
392
394
  Don&apos;t have an account?{" "}
393
395
  <Text
394
396
  className="text-sm text-foreground underline"
395
- onPress={withClickTracking("auth.login.push_3", () => router.push(routes.register))}
397
+ onPress={withClickTracking("auth.login.push_3", () =>
398
+ router.push(routes.register)
399
+ )}
396
400
  accessibilityRole="link"
397
401
  accessibilityLabel="Sign up"
398
402
  >
@@ -405,7 +409,7 @@ export function LoginScreen({
405
409
  <Button
406
410
  className="w-full"
407
411
  telemetryId="auth.login.login"
408
- onPress={handleLogin}
412
+ onPress={handleLogin}
409
413
  isLoading={isLoading}
410
414
  accessibilityLabel="Sign in"
411
415
  accessibilityRole="button"
@@ -473,7 +477,10 @@ export function LoginScreen({
473
477
  <DialogFooter style={{ justifyContent: "center" }}>
474
478
  <DialogButton
475
479
  variant="primary"
476
- onPress={withClickTracking("auth.login.action", isLoading ? () => {} : handleUnlockAfterPasskey)}
480
+ onPress={withClickTracking(
481
+ "auth.login.action",
482
+ isLoading ? () => {} : handleUnlockAfterPasskey
483
+ )}
477
484
  accessibilityLabel="Unlock"
478
485
  accessibilityRole="button"
479
486
  >
@@ -195,7 +195,10 @@ export function MfaChallengeScreen() {
195
195
  </Pressable>
196
196
 
197
197
  <Pressable
198
- onPress={withClickTracking("auth.mfa_challenge.toggle_recovery_mode", toggleRecoveryMode)}
198
+ onPress={withClickTracking(
199
+ "auth.mfa_challenge.toggle_recovery_mode",
200
+ toggleRecoveryMode
201
+ )}
199
202
  className="mt-5 items-center"
200
203
  hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
201
204
  accessibilityRole="button"
@@ -50,9 +50,8 @@ export function PasskeyEnrollScreen({
50
50
  <CardHeader>
51
51
  <CardTitle>Add a passkey</CardTitle>
52
52
  <CardDescription>
53
- Sign in faster and more securely next time with a passkey — your
54
- device unlocks it with your fingerprint, face, or PIN. No password
55
- to remember or phish.
53
+ Sign in faster and more securely next time with a passkey — your device unlocks it
54
+ with your fingerprint, face, or PIN. No password to remember or phish.
56
55
  </CardDescription>
57
56
  </CardHeader>
58
57
 
@@ -61,8 +60,7 @@ export function PasskeyEnrollScreen({
61
60
  <Text className="text-sm text-destructive dark:text-red-900">{error}</Text>
62
61
  ) : (
63
62
  <MutedText className="text-sm">
64
- You can always add or remove passkeys later in your account
65
- settings.
63
+ You can always add or remove passkeys later in your account settings.
66
64
  </MutedText>
67
65
  )}
68
66
  </CardContent>
@@ -71,7 +69,7 @@ export function PasskeyEnrollScreen({
71
69
  <Button
72
70
  className="w-full"
73
71
  telemetryId="auth.passkey_enroll.enroll"
74
- onPress={onEnroll}
72
+ onPress={onEnroll}
75
73
  isLoading={isLoading}
76
74
  accessibilityLabel="Add a passkey"
77
75
  accessibilityRole="button"
@@ -82,7 +80,7 @@ export function PasskeyEnrollScreen({
82
80
  className="w-full"
83
81
  variant="ghost"
84
82
  telemetryId="auth.passkey_enroll.skip"
85
- onPress={onSkip}
83
+ onPress={onSkip}
86
84
  accessibilityLabel="Not now"
87
85
  accessibilityRole="button"
88
86
  >
@@ -62,8 +62,12 @@ export function PrivacyPolicyScreen({
62
62
  <View className="flex-1" style={{ backgroundColor: isDark ? "#000000" : "#FFFFFF" }}>
63
63
  <View className="px-6 pt-4 pb-6">
64
64
  <View className="flex-row items-center justify-between mb-6">
65
- <IconButton variant="ghost" size="sm" telemetryId="auth.privacy_policy.back"
66
- onPress={() => router.back()}>
65
+ <IconButton
66
+ variant="ghost"
67
+ size="sm"
68
+ telemetryId="auth.privacy_policy.back"
69
+ onPress={() => router.back()}
70
+ >
67
71
  <Ionicons name="arrow-back" size={20} color={isDark ? "#FFFFFF" : "#000000"} />
68
72
  </IconButton>
69
73
 
@@ -191,7 +195,9 @@ export function PrivacyPolicyScreen({
191
195
  <Text
192
196
  className="font-mono text-sm underline"
193
197
  style={{ color: isDark ? "#6BA3E5" : "#4A90E2" }}
194
- onPress={withClickTracking("auth.privacy_policy.open_u_r_l", () => Linking.openURL(`mailto:${supportEmail}`))}
198
+ onPress={withClickTracking("auth.privacy_policy.open_u_r_l", () =>
199
+ Linking.openURL(`mailto:${supportEmail}`)
200
+ )}
195
201
  >
196
202
  {supportEmail}
197
203
  </Text>
@@ -230,7 +230,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
230
230
  <IconButton
231
231
  variant="ghost"
232
232
  telemetryId="auth.reset_password.step"
233
- onPress={() => (step === "password" ? setStep("code") : router.back())}
233
+ onPress={() => (step === "password" ? setStep("code") : router.back())}
234
234
  accessibilityLabel="Go back"
235
235
  accessibilityRole="button"
236
236
  >
@@ -273,7 +273,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
273
273
  <Button
274
274
  className="w-full"
275
275
  telemetryId="auth.reset_password.code_submit"
276
- onPress={handleCodeSubmit}
276
+ onPress={handleCodeSubmit}
277
277
  isLoading={isLoading}
278
278
  accessibilityLabel="Continue"
279
279
  accessibilityRole="button"
@@ -285,7 +285,9 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
285
285
  Back to{" "}
286
286
  <Text
287
287
  className="text-sm text-foreground underline"
288
- onPress={withClickTracking("auth.reset_password.push", () => router.push(routes.login))}
288
+ onPress={withClickTracking("auth.reset_password.push", () =>
289
+ router.push(routes.login)
290
+ )}
289
291
  accessibilityRole="link"
290
292
  accessibilityLabel="Sign in"
291
293
  >
@@ -351,7 +353,7 @@ export function ResetPasswordScreen({ encryptionWarning }: ResetPasswordScreenPr
351
353
  <Button
352
354
  className="w-full"
353
355
  telemetryId="auth.reset_password.password_submit"
354
- onPress={handlePasswordSubmit}
356
+ onPress={handlePasswordSubmit}
355
357
  isLoading={isLoading}
356
358
  accessibilityLabel="Reset password"
357
359
  accessibilityRole="button"
@@ -251,7 +251,9 @@ export function ServerConfigScreen({
251
251
  >
252
252
  <Ionicons name="close" size={24} color={isDark ? "white" : "black"} />
253
253
  </Pressable>
254
- <Text className="text-lg font-semibold text-foreground dark:text-white">Server Configuration</Text>
254
+ <Text className="text-lg font-semibold text-foreground dark:text-white">
255
+ Server Configuration
256
+ </Text>
255
257
  <View className="h-10 w-10" />
256
258
  </View>
257
259
 
@@ -275,7 +277,9 @@ export function ServerConfigScreen({
275
277
  </View>
276
278
 
277
279
  <View className="mb-6">
278
- <Text className="mb-2 text-sm font-medium text-foreground dark:text-white">Server URL</Text>
280
+ <Text className="mb-2 text-sm font-medium text-foreground dark:text-white">
281
+ Server URL
282
+ </Text>
279
283
  <View className="flex-row items-center rounded-lg border border-border dark:border-neutral-800 bg-background dark:bg-black px-3 py-3">
280
284
  <Ionicons
281
285
  name="globe-outline"
@@ -345,7 +349,10 @@ export function ServerConfigScreen({
345
349
  </Pressable>
346
350
 
347
351
  <Pressable
348
- onPress={withClickTracking("auth.server_config.clear_configuration", handleClearConfiguration)}
352
+ onPress={withClickTracking(
353
+ "auth.server_config.clear_configuration",
354
+ handleClearConfiguration
355
+ )}
349
356
  accessibilityRole="button"
350
357
  accessibilityLabel="Clear all settings"
351
358
  className="active:scale-98 mb-3 w-full rounded-lg border border-destructive dark:border-red-900 bg-background dark:bg-black px-6 py-3"
@@ -370,12 +377,18 @@ export function ServerConfigScreen({
370
377
  </Pressable>
371
378
 
372
379
  <View className="rounded-lg bg-card dark:bg-neutral-900/50 p-4">
373
- <Text className="mb-3 text-sm font-semibold text-foreground dark:text-white">Need Help?</Text>
380
+ <Text className="mb-3 text-sm font-semibold text-foreground dark:text-white">
381
+ Need Help?
382
+ </Text>
374
383
  <View className="space-y-2">
375
384
  {bullets.map((bullet, idx) => (
376
385
  <View key={idx} className="flex-row items-start">
377
- <Text className="mr-2 text-sm text-muted-foreground dark:text-neutral-400">•</Text>
378
- <Text className="flex-1 text-sm text-muted-foreground dark:text-neutral-400">{bullet}</Text>
386
+ <Text className="mr-2 text-sm text-muted-foreground dark:text-neutral-400">
387
+
388
+ </Text>
389
+ <Text className="flex-1 text-sm text-muted-foreground dark:text-neutral-400">
390
+ {bullet}
391
+ </Text>
379
392
  </View>
380
393
  ))}
381
394
  </View>
@@ -74,8 +74,12 @@ export function TermsOfServiceScreen({
74
74
  <View className="flex-1" style={{ backgroundColor: isDark ? "#000000" : "#FFFFFF" }}>
75
75
  <View className="px-6 pt-4 pb-6">
76
76
  <View className="flex-row items-center justify-between mb-6">
77
- <IconButton variant="ghost" size="sm" telemetryId="auth.terms_of_service.back"
78
- onPress={() => router.back()}>
77
+ <IconButton
78
+ variant="ghost"
79
+ size="sm"
80
+ telemetryId="auth.terms_of_service.back"
81
+ onPress={() => router.back()}
82
+ >
79
83
  <Ionicons name="arrow-back" size={20} color={isDark ? "#FFFFFF" : "#000000"} />
80
84
  </IconButton>
81
85
 
@@ -168,7 +172,9 @@ export function TermsOfServiceScreen({
168
172
  <Text
169
173
  className="font-mono text-sm underline"
170
174
  style={{ color: isDark ? "#6BA3E5" : "#4A90E2" }}
171
- onPress={withClickTracking("auth.terms_of_service.open_u_r_l", () => Linking.openURL(`mailto:${supportEmail}`))}
175
+ onPress={withClickTracking("auth.terms_of_service.open_u_r_l", () =>
176
+ Linking.openURL(`mailto:${supportEmail}`)
177
+ )}
172
178
  >
173
179
  {supportEmail}
174
180
  </Text>
@@ -50,12 +50,19 @@ export function WelcomeScreen({ decoration, topAccessory }: WelcomeScreenProps)
50
50
  ) : null}
51
51
 
52
52
  <View className="w-full max-w-[360px] gap-3">
53
- <Button className="w-full" telemetryId="auth.welcome.push"
54
- onPress={() => router.push(routes.login)}>
53
+ <Button
54
+ className="w-full"
55
+ telemetryId="auth.welcome.push"
56
+ onPress={() => router.push(routes.login)}
57
+ >
55
58
  Sign In
56
59
  </Button>
57
- <Button variant="outline" className="w-full" telemetryId="auth.welcome.push_2"
58
- onPress={() => router.push(routes.register)}>
60
+ <Button
61
+ variant="outline"
62
+ className="w-full"
63
+ telemetryId="auth.welcome.push_2"
64
+ onPress={() => router.push(routes.register)}
65
+ >
59
66
  Create Account
60
67
  </Button>
61
68
  </View>
@@ -70,11 +70,11 @@ export function trackClick(element: string, route?: string): void {
70
70
  */
71
71
  export function withClickTracking<F extends (...args: never[]) => unknown>(
72
72
  element: string | undefined,
73
- handler: F,
73
+ handler: F
74
74
  ): F;
75
75
  export function withClickTracking<F extends (...args: never[]) => unknown>(
76
76
  element: string | undefined,
77
- handler: F | null | undefined,
77
+ handler: F | null | undefined
78
78
  ): F | undefined;
79
79
  /**
80
80
  * Wrap a press handler so activating the element is reported first.
@@ -88,7 +88,7 @@ export function withClickTracking<F extends (...args: never[]) => unknown>(
88
88
  */
89
89
  export function withClickTracking(
90
90
  element: string | undefined,
91
- handler: ((...args: never[]) => unknown) | null | undefined,
91
+ handler: ((...args: never[]) => unknown) | null | undefined
92
92
  ): ((...args: never[]) => unknown) | undefined {
93
93
  // Untagged controls keep their original handler rather than gaining a
94
94
  // closure per render for nothing.