@entropy-softworks/ui 2026.8.20 → 2026.8.21
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.
|
@@ -9,6 +9,21 @@ interface LockOverlayProps {
|
|
|
9
9
|
onSignOut: () => void;
|
|
10
10
|
/** Optional success-toast message. Defaults to `${appName} unlocked`. */
|
|
11
11
|
successMessage?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Optional biometric/passkey unlock, shown as a prominent button above
|
|
14
|
+
* the password field when provided. This overlay has no opinion on
|
|
15
|
+
* *how* that happens (passkey PRF, platform secure-storage, etc.) —
|
|
16
|
+
* the caller owns the whole ceremony and is expected to swallow
|
|
17
|
+
* expected failure modes (user cancel, nothing enrolled) rather than
|
|
18
|
+
* throw; a throw here surfaces the same generic "unlock failed" error
|
|
19
|
+
* the password path does, which is the wrong message for "you tapped
|
|
20
|
+
* cancel on the Face ID prompt".
|
|
21
|
+
*/
|
|
22
|
+
onBiometricUnlock?: () => Promise<void> | void;
|
|
23
|
+
/** Button label. Defaults to "Unlock with Biometrics" — pass
|
|
24
|
+
* something more specific ("Unlock with Face ID") when the caller
|
|
25
|
+
* can detect which authentication type is actually available. */
|
|
26
|
+
biometricLabel?: string;
|
|
12
27
|
}
|
|
13
28
|
/**
|
|
14
29
|
* Unlock form for session-locked flows (e.g. vault re-lock after idle).
|
|
@@ -17,6 +32,6 @@ interface LockOverlayProps {
|
|
|
17
32
|
* unlock work begins so the fiber tree no longer references the
|
|
18
33
|
* plaintext during PBKDF2 / WebCrypto derivation.
|
|
19
34
|
*/
|
|
20
|
-
export declare function LockOverlay({ onUnlock, onSignOut, successMessage }: LockOverlayProps): React.JSX.Element;
|
|
35
|
+
export declare function LockOverlay({ onUnlock, onSignOut, successMessage, onBiometricUnlock, biometricLabel, }: LockOverlayProps): React.JSX.Element;
|
|
21
36
|
export {};
|
|
22
37
|
//# sourceMappingURL=LockOverlay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LockOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/auth/LockOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAQxC,UAAU,gBAAgB;IACxB;;;OAGG;IACH,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACrD,4CAA4C;IAC5C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"LockOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/auth/LockOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAQxC,UAAU,gBAAgB;IACxB;;;OAGG;IACH,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACrD,4CAA4C;IAC5C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/C;;sEAEkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,SAAS,EACT,cAAc,EACd,iBAAiB,EACjB,cAAc,GACf,EAAE,gBAAgB,qBAkIlB"}
|
|
@@ -12,11 +12,21 @@ import { useAppConfig } from "../../context/AppConfigContext";
|
|
|
12
12
|
* unlock work begins so the fiber tree no longer references the
|
|
13
13
|
* plaintext during PBKDF2 / WebCrypto derivation.
|
|
14
14
|
*/
|
|
15
|
-
export function LockOverlay({ onUnlock, onSignOut, successMessage }) {
|
|
15
|
+
export function LockOverlay({ onUnlock, onSignOut, successMessage, onBiometricUnlock, biometricLabel, }) {
|
|
16
16
|
const { appName, Logo } = useAppConfig();
|
|
17
17
|
const [password, setPassword] = useState("");
|
|
18
18
|
const [loading, setLoading] = useState(false);
|
|
19
|
+
const [biometricLoading, setBiometricLoading] = useState(false);
|
|
19
20
|
const [error, setError] = useState("");
|
|
21
|
+
const handleBiometricUnlock = async () => {
|
|
22
|
+
setBiometricLoading(true);
|
|
23
|
+
try {
|
|
24
|
+
await onBiometricUnlock?.();
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
setBiometricLoading(false);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
20
30
|
const handleUnlock = async () => {
|
|
21
31
|
const captured = password;
|
|
22
32
|
if (!captured.trim()) {
|
|
@@ -63,6 +73,16 @@ export function LockOverlay({ onUnlock, onSignOut, successMessage }) {
|
|
|
63
73
|
Enter your master password to unlock
|
|
64
74
|
</MutedText>
|
|
65
75
|
<View className="w-full gap-3">
|
|
76
|
+
{onBiometricUnlock ? (<>
|
|
77
|
+
<Button className="w-full" onPress={handleBiometricUnlock} isLoading={biometricLoading} accessibilityLabel={biometricLabel ?? "Unlock with Biometrics"} accessibilityRole="button" testID="lock-biometric-button">
|
|
78
|
+
{biometricLabel ?? "Unlock with Biometrics"}
|
|
79
|
+
</Button>
|
|
80
|
+
<View className="flex-row items-center gap-3 py-1">
|
|
81
|
+
<View className="h-px flex-1 bg-border dark:bg-neutral-800"/>
|
|
82
|
+
<MutedText className="text-xs">or use your password</MutedText>
|
|
83
|
+
<View className="h-px flex-1 bg-border dark:bg-neutral-800"/>
|
|
84
|
+
</View>
|
|
85
|
+
</>) : null}
|
|
66
86
|
<PasswordInput value={password} onChangeText={(text) => {
|
|
67
87
|
setPassword(text);
|
|
68
88
|
if (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LockOverlay.jsx","sourceRoot":"","sources":["../../../src/components/auth/LockOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,MAAM,4BAA4B,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"LockOverlay.jsx","sourceRoot":"","sources":["../../../src/components/auth/LockOverlay.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -16,6 +16,21 @@ interface LockOverlayProps {
|
|
|
16
16
|
onSignOut: () => void;
|
|
17
17
|
/** Optional success-toast message. Defaults to `${appName} unlocked`. */
|
|
18
18
|
successMessage?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Optional biometric/passkey unlock, shown as a prominent button above
|
|
21
|
+
* the password field when provided. This overlay has no opinion on
|
|
22
|
+
* *how* that happens (passkey PRF, platform secure-storage, etc.) —
|
|
23
|
+
* the caller owns the whole ceremony and is expected to swallow
|
|
24
|
+
* expected failure modes (user cancel, nothing enrolled) rather than
|
|
25
|
+
* throw; a throw here surfaces the same generic "unlock failed" error
|
|
26
|
+
* the password path does, which is the wrong message for "you tapped
|
|
27
|
+
* cancel on the Face ID prompt".
|
|
28
|
+
*/
|
|
29
|
+
onBiometricUnlock?: () => Promise<void> | void;
|
|
30
|
+
/** Button label. Defaults to "Unlock with Biometrics" — pass
|
|
31
|
+
* something more specific ("Unlock with Face ID") when the caller
|
|
32
|
+
* can detect which authentication type is actually available. */
|
|
33
|
+
biometricLabel?: string;
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
/**
|
|
@@ -25,12 +40,28 @@ interface LockOverlayProps {
|
|
|
25
40
|
* unlock work begins so the fiber tree no longer references the
|
|
26
41
|
* plaintext during PBKDF2 / WebCrypto derivation.
|
|
27
42
|
*/
|
|
28
|
-
export function LockOverlay({
|
|
43
|
+
export function LockOverlay({
|
|
44
|
+
onUnlock,
|
|
45
|
+
onSignOut,
|
|
46
|
+
successMessage,
|
|
47
|
+
onBiometricUnlock,
|
|
48
|
+
biometricLabel,
|
|
49
|
+
}: LockOverlayProps) {
|
|
29
50
|
const { appName, Logo } = useAppConfig();
|
|
30
51
|
const [password, setPassword] = useState("");
|
|
31
52
|
const [loading, setLoading] = useState(false);
|
|
53
|
+
const [biometricLoading, setBiometricLoading] = useState(false);
|
|
32
54
|
const [error, setError] = useState("");
|
|
33
55
|
|
|
56
|
+
const handleBiometricUnlock = async () => {
|
|
57
|
+
setBiometricLoading(true);
|
|
58
|
+
try {
|
|
59
|
+
await onBiometricUnlock?.();
|
|
60
|
+
} finally {
|
|
61
|
+
setBiometricLoading(false);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
34
65
|
const handleUnlock = async () => {
|
|
35
66
|
const captured = password;
|
|
36
67
|
if (!captured.trim()) {
|
|
@@ -80,6 +111,25 @@ export function LockOverlay({ onUnlock, onSignOut, successMessage }: LockOverlay
|
|
|
80
111
|
Enter your master password to unlock
|
|
81
112
|
</MutedText>
|
|
82
113
|
<View className="w-full gap-3">
|
|
114
|
+
{onBiometricUnlock ? (
|
|
115
|
+
<>
|
|
116
|
+
<Button
|
|
117
|
+
className="w-full"
|
|
118
|
+
onPress={handleBiometricUnlock}
|
|
119
|
+
isLoading={biometricLoading}
|
|
120
|
+
accessibilityLabel={biometricLabel ?? "Unlock with Biometrics"}
|
|
121
|
+
accessibilityRole="button"
|
|
122
|
+
testID="lock-biometric-button"
|
|
123
|
+
>
|
|
124
|
+
{biometricLabel ?? "Unlock with Biometrics"}
|
|
125
|
+
</Button>
|
|
126
|
+
<View className="flex-row items-center gap-3 py-1">
|
|
127
|
+
<View className="h-px flex-1 bg-border dark:bg-neutral-800" />
|
|
128
|
+
<MutedText className="text-xs">or use your password</MutedText>
|
|
129
|
+
<View className="h-px flex-1 bg-border dark:bg-neutral-800" />
|
|
130
|
+
</View>
|
|
131
|
+
</>
|
|
132
|
+
) : null}
|
|
83
133
|
<PasswordInput
|
|
84
134
|
value={password}
|
|
85
135
|
onChangeText={(text) => {
|