@entropy-softworks/ui 2026.8.35 → 2026.8.37

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 (54) hide show
  1. package/lib/components/auth/PasswordStrengthMeter.d.ts +9 -0
  2. package/lib/components/auth/PasswordStrengthMeter.d.ts.map +1 -1
  3. package/lib/components/auth/PasswordStrengthMeter.jsx +10 -4
  4. package/lib/components/auth/PasswordStrengthMeter.jsx.map +1 -1
  5. package/lib/components/index.d.ts +1 -0
  6. package/lib/components/index.d.ts.map +1 -1
  7. package/lib/components/index.js +1 -0
  8. package/lib/components/index.js.map +1 -1
  9. package/lib/components/profile/SecurityTab.d.ts +5 -1
  10. package/lib/components/profile/SecurityTab.d.ts.map +1 -1
  11. package/lib/components/profile/SecurityTab.jsx.map +1 -1
  12. package/lib/components/segmented-toggle.d.ts +9 -0
  13. package/lib/components/segmented-toggle.d.ts.map +1 -1
  14. package/lib/components/segmented-toggle.jsx +4 -0
  15. package/lib/components/segmented-toggle.jsx.map +1 -1
  16. package/lib/components/social-connections.d.ts +22 -5
  17. package/lib/components/social-connections.d.ts.map +1 -1
  18. package/lib/components/social-connections.jsx +37 -11
  19. package/lib/components/social-connections.jsx.map +1 -1
  20. package/lib/components/update-overlay.d.ts +41 -0
  21. package/lib/components/update-overlay.d.ts.map +1 -0
  22. package/lib/components/update-overlay.jsx +132 -0
  23. package/lib/components/update-overlay.jsx.map +1 -0
  24. package/lib/context/AppConfigContext.d.ts +27 -0
  25. package/lib/context/AppConfigContext.d.ts.map +1 -1
  26. package/lib/context/AppConfigContext.jsx.map +1 -1
  27. package/lib/demos/extension-popup.d.ts +212 -0
  28. package/lib/demos/extension-popup.d.ts.map +1 -0
  29. package/lib/demos/extension-popup.jsx +1172 -0
  30. package/lib/demos/extension-popup.jsx.map +1 -0
  31. package/lib/demos/index.d.ts +11 -0
  32. package/lib/demos/index.d.ts.map +1 -0
  33. package/lib/demos/index.js +14 -0
  34. package/lib/demos/index.js.map +1 -0
  35. package/lib/screens/auth/AuthScreen.d.ts +75 -1
  36. package/lib/screens/auth/AuthScreen.d.ts.map +1 -1
  37. package/lib/screens/auth/AuthScreen.jsx +356 -122
  38. package/lib/screens/auth/AuthScreen.jsx.map +1 -1
  39. package/lib/services/auth.service.d.ts +5 -1
  40. package/lib/services/auth.service.d.ts.map +1 -1
  41. package/lib/services/auth.service.js +5 -1
  42. package/lib/services/auth.service.js.map +1 -1
  43. package/package.json +7 -1
  44. package/src/components/auth/PasswordStrengthMeter.tsx +25 -3
  45. package/src/components/index.ts +1 -0
  46. package/src/components/profile/SecurityTab.tsx +11 -1
  47. package/src/components/segmented-toggle.tsx +13 -0
  48. package/src/components/social-connections.tsx +66 -20
  49. package/src/components/update-overlay.tsx +206 -0
  50. package/src/context/AppConfigContext.tsx +27 -0
  51. package/src/demos/extension-popup.tsx +2051 -0
  52. package/src/demos/index.ts +38 -0
  53. package/src/screens/auth/AuthScreen.tsx +701 -312
  54. package/src/services/auth.service.ts +13 -2
@@ -0,0 +1,206 @@
1
+ // Full-screen overlay shown while an EAS Update downloads and applies.
2
+ //
3
+ // It exists because of what expo-updates does by DEFAULT, which is nothing
4
+ // visible. `fallbackToCacheTimeout: 0` means the app launches from the cached
5
+ // bundle immediately, fetches any new update in the background, and applies it
6
+ // on the NEXT cold start — so a tester relaunches once, sees the old build,
7
+ // relaunches again, and sees the new one. Every app here reacts to the pending
8
+ // update by reloading straight away instead; this component is what turns the
9
+ // resulting pause into a visible, explained wait rather than the app appearing
10
+ // to hang.
11
+ //
12
+ // # Why this is in the shared package
13
+ //
14
+ // It was written in vault, ported by hand into wheatley and glados, and then
15
+ // FIXED in the ports while vault kept the bug (see the colour note below).
16
+ // Three copies, one of which was wrong, of a component whose entire job is to
17
+ // render correctly in the worst conditions the app ever has. Sharing it is how
18
+ // the next fix reaches all three.
19
+ //
20
+ // # Every colour is a literal, and that is the fix
21
+ //
22
+ // This renders while the app is NOT ready — during a cold start whose update
23
+ // is still downloading, and during the reload that applies it. NativeWind's
24
+ // `dark:` variant and theme tokens like `text-foreground` only resolve once the
25
+ // provider tree has mounted and run its effects, which by definition has not
26
+ // happened yet. Asking the theme produced light-mode ink on a dark ground: an
27
+ // invisible label on what the user reads as a black screen. Observed for real
28
+ // on Android in both wheatley (2026-08-23) and vault (2026-08-29).
29
+ //
30
+ // The values ARE the palettes' own — this matches the theme, it just does not
31
+ // ask it. An overlay whose whole job is to render before the app is ready
32
+ // cannot depend on app state to be legible.
33
+ //
34
+ // reanimated rather than RN's `Animated`, matching the animation convention in
35
+ // every consumer.
36
+ import * as React from "react";
37
+ import { StyleSheet, Text, View, useColorScheme } from "react-native";
38
+ import Animated, {
39
+ Easing,
40
+ useAnimatedStyle,
41
+ useSharedValue,
42
+ withRepeat,
43
+ withTiming,
44
+ } from "react-native-reanimated";
45
+
46
+ import { EntropyMark } from "./entropy-mark";
47
+
48
+ export interface UpdateOverlayProps {
49
+ /** "downloading" shows a progress-aware label; "restarting" is the moment
50
+ * between a finished download and the reload call that applies it. */
51
+ phase: "downloading" | "restarting";
52
+ /** 0-1, and only meaningful while downloading. Undefined when the update
53
+ * server sent no Content-Length for expo-updates to compute it from, which
54
+ * is why the percentage renders conditionally rather than defaulting to 0 —
55
+ * a "0%" that never moves reads as a stall. */
56
+ progress?: number;
57
+ /**
58
+ * Glyph shown inside the ring. Defaults to the Entropy mark.
59
+ *
60
+ * A slot rather than a name or an icon set, because the consumers genuinely
61
+ * differ: vault shows its own product mark, wheatley and glados show the
62
+ * shared one. Whatever is passed must not derive its colour from the theme,
63
+ * for the reason in the header — pass an explicit ink.
64
+ */
65
+ mark?: React.ReactNode;
66
+ /**
67
+ * Force a colour scheme instead of following the system.
68
+ *
69
+ * Dark-only apps pass `"dark"`: without it the overlay would follow a light
70
+ * system setting and render a white flash before a dark app.
71
+ */
72
+ scheme?: "light" | "dark";
73
+ /**
74
+ * Ground colour override.
75
+ *
76
+ * Defaults to the palette's own background. Worth setting when the native
77
+ * splash and window background use a specific value — matching it makes the
78
+ * overlay read as a continuation of the launch rather than a second screen.
79
+ */
80
+ background?: string;
81
+ /** Label under the ring while downloading. */
82
+ downloadingLabel?: string;
83
+ /** Label under the ring while restarting. */
84
+ restartingLabel?: string;
85
+ }
86
+
87
+ /** How long one full turn takes. Slow enough to read as deliberate, fast
88
+ * enough to look like work rather than a frozen frame. */
89
+ const SPIN_MS = 1100;
90
+
91
+ const PALETTE = {
92
+ dark: {
93
+ ground: "#000000",
94
+ ink: "#EBEFEB",
95
+ muted: "#A3A3A3",
96
+ track: "#262626",
97
+ arc: "#FFFFFF",
98
+ },
99
+ light: {
100
+ ground: "#FFFFFF",
101
+ ink: "#0A0A0A",
102
+ muted: "#737373",
103
+ track: "#E5E5E5",
104
+ arc: "#000000",
105
+ },
106
+ } as const;
107
+
108
+ export function UpdateOverlay({
109
+ phase,
110
+ progress,
111
+ mark,
112
+ scheme,
113
+ background,
114
+ downloadingLabel = "Installing update…",
115
+ restartingLabel = "Restarting…",
116
+ }: UpdateOverlayProps) {
117
+ // RN's own hook, not NativeWind's: this has to work before the NativeWind
118
+ // provider has done anything, which is the entire premise of the component.
119
+ const system = useColorScheme();
120
+ const isDark = (scheme ?? system) !== "light";
121
+ const c = isDark ? PALETTE.dark : PALETTE.light;
122
+
123
+ const rotation = useSharedValue(0);
124
+
125
+ // Started on mount rather than gated on a `visible` prop: the caller renders
126
+ // this conditionally, so mounting IS becoming visible, and a prop would be a
127
+ // second source of truth for the same fact.
128
+ React.useEffect(() => {
129
+ rotation.value = withRepeat(
130
+ withTiming(360, { duration: SPIN_MS, easing: Easing.linear }),
131
+ -1,
132
+ false
133
+ );
134
+ }, [rotation]);
135
+
136
+ const ringStyle = useAnimatedStyle(() => ({
137
+ transform: [{ rotate: `${rotation.value}deg` }],
138
+ }));
139
+
140
+ return (
141
+ <View
142
+ style={[StyleSheet.absoluteFill, styles.ground, { backgroundColor: background ?? c.ground }]}
143
+ // Announced as one modal thing rather than as its parts: a screen reader
144
+ // reading "Installing update" then "43%" as separate nodes during a
145
+ // two-second wait is noise, and the percentage is decoration on top of
146
+ // the label rather than information of its own.
147
+ accessibilityViewIsModal
148
+ accessibilityLabel={phase === "downloading" ? downloadingLabel : restartingLabel}
149
+ >
150
+ <View style={styles.markWell}>
151
+ <Animated.View
152
+ style={[
153
+ ringStyle,
154
+ styles.ring,
155
+ // The track, then one lit arc. A single contrasting border-top is
156
+ // what makes a plain rotation read as a spinner — a uniform ring
157
+ // rotating is indistinguishable from a static one.
158
+ { borderColor: c.track, borderTopColor: c.arc },
159
+ ]}
160
+ />
161
+ {mark ?? <EntropyMark size={32} variant="chroma" />}
162
+ </View>
163
+ <Text style={[styles.label, { color: c.ink }]}>
164
+ {phase === "downloading" ? downloadingLabel : restartingLabel}
165
+ </Text>
166
+ {phase === "downloading" && typeof progress === "number" ? (
167
+ <Text style={[styles.progress, { color: c.muted }]}>{Math.round(progress * 100)}%</Text>
168
+ ) : null}
169
+ </View>
170
+ );
171
+ }
172
+
173
+ // StyleSheet rather than className throughout, deliberately: NativeWind classes
174
+ // resolve through the same provider this component cannot wait for. Plain RN
175
+ // styles are the only thing guaranteed to apply here. The ring also needs
176
+ // concrete numbers that agree with each other (a 72px box, a 36px radius, one
177
+ // 3px border), and its transform comes from an animated style anyway.
178
+ const styles = StyleSheet.create({
179
+ ground: {
180
+ alignItems: "center",
181
+ justifyContent: "center",
182
+ // Both, not either: zIndex orders within the JS view hierarchy and
183
+ // elevation is what Android's native compositor honours. A high zIndex
184
+ // alone can still be drawn under an elevated sibling.
185
+ zIndex: 10000,
186
+ elevation: 10000,
187
+ },
188
+ markWell: {
189
+ width: 72,
190
+ height: 72,
191
+ alignItems: "center",
192
+ justifyContent: "center",
193
+ },
194
+ ring: {
195
+ position: "absolute",
196
+ width: 72,
197
+ height: 72,
198
+ borderRadius: 36,
199
+ borderWidth: 3,
200
+ },
201
+ // No font family: the overlay can render before the app's fonts have loaded,
202
+ // and naming one that is not there yet gives a blank label rather than a
203
+ // system-font fallback.
204
+ label: { marginTop: 20, fontSize: 16, fontWeight: "500" },
205
+ progress: { marginTop: 4, fontSize: 12 },
206
+ });
@@ -34,6 +34,17 @@ export type LogoComponent = React.ComponentType<{
34
34
  */
35
35
  color?: string;
36
36
  style?: StyleProp<ViewStyle> | StyleProp<ImageStyle>;
37
+ /**
38
+ * Render the mark flush to its box, without whatever internal padding it
39
+ * normally carries.
40
+ *
41
+ * A mark usually bakes in breathing room so it drops into arbitrary layouts
42
+ * safely. In a lockup that padding fights the layout instead: the caller is
43
+ * already controlling the gap, and the baked margin makes the mark read as
44
+ * smaller than the text it is supposed to match. Logos that have no internal
45
+ * padding can ignore this.
46
+ */
47
+ bleed?: boolean;
37
48
  }>;
38
49
 
39
50
  export interface AppConfig {
@@ -48,6 +59,22 @@ export interface AppConfig {
48
59
  * a generic "An Entropy Softworks platform".
49
60
  */
50
61
  tagline?: string;
62
+ /**
63
+ * How to set the tagline against the app name. Omit for the default: stacked
64
+ * centred underneath, no rule.
65
+ *
66
+ * "inline" — same line, pipe-separated: `Vault | Password Manager`
67
+ * "ruled" — under the name, hairline rule between the two
68
+ * "lockup" — logo to the LEFT of the name and tagline stacked, with the
69
+ * mark sized to the height of those two lines together. The
70
+ * conventional app lockup, and the tidiest option when the
71
+ * tagline is a short category label.
72
+ *
73
+ * `"inline"` and `"lockup"` both need an app-name text to set against, so a
74
+ * `logoIsWordmark` mark — which renders alone at its natural width — falls
75
+ * back to `"ruled"`.
76
+ */
77
+ taglineLayout?: "inline" | "ruled" | "lockup";
51
78
  /**
52
79
  * Optional terms-of-service markdown / plain text. Welcome screen and
53
80
  * the dedicated terms screen render this. When omitted, the screens