vesant-sdk 1.7.1-dev.2a5fd70 → 1.7.1-dev.2b64301
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.
- package/dist/index.js +23 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +23 -7
- package/dist/index.mjs.map +1 -1
- package/dist/kyc/core.js +23 -7
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +23 -7
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +28 -10
- package/dist/kyc/index.d.ts +28 -10
- package/dist/kyc/index.js +23 -7
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +23 -7
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.js +149 -26
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +149 -26
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -924,19 +924,19 @@ var Close = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0id
|
|
|
924
924
|
var FACE_MESSAGES = {
|
|
925
925
|
idle: "Initializing camera...",
|
|
926
926
|
loading: "Loading face detection...",
|
|
927
|
-
scanning: "Position your face in the
|
|
927
|
+
scanning: "Position your face in the oval",
|
|
928
928
|
too_far: "Face too far. Please move closer to the camera",
|
|
929
929
|
too_close: "Too close. Please move back a little",
|
|
930
|
-
off_center: "Center your face in the
|
|
930
|
+
off_center: "Center your face in the oval",
|
|
931
931
|
ok: "Looking great! Tap the button to capture"
|
|
932
932
|
};
|
|
933
933
|
var FACE_COLORS = {
|
|
934
934
|
idle: "#9ca3af",
|
|
935
|
-
loading: "#
|
|
936
|
-
scanning: "#
|
|
935
|
+
loading: "#ef4444",
|
|
936
|
+
scanning: "#ef4444",
|
|
937
937
|
too_far: "#ef4444",
|
|
938
|
-
too_close: "#
|
|
939
|
-
off_center: "#
|
|
938
|
+
too_close: "#ef4444",
|
|
939
|
+
off_center: "#ef4444",
|
|
940
940
|
ok: "#10b981"
|
|
941
941
|
};
|
|
942
942
|
var MEDIAPIPE_BASE = "https://cdn.jsdelivr.net/npm/@mediapipe/face_detection";
|
|
@@ -1001,11 +1001,32 @@ function loadMediaPipeFaceDetection(assetBasePath) {
|
|
|
1001
1001
|
return loader;
|
|
1002
1002
|
}
|
|
1003
1003
|
|
|
1004
|
+
// src/kyc/hostedJourney.ts
|
|
1005
|
+
var REUSE_KYC_SESSION_EXPIRY_MS = 10 * 60 * 1e3;
|
|
1006
|
+
function tryStartOperation(guard) {
|
|
1007
|
+
if (guard.current) return false;
|
|
1008
|
+
guard.current = true;
|
|
1009
|
+
return true;
|
|
1010
|
+
}
|
|
1011
|
+
function releaseOperation(guard) {
|
|
1012
|
+
guard.current = false;
|
|
1013
|
+
}
|
|
1014
|
+
function retryTarget(hasHostedJourney) {
|
|
1015
|
+
return hasHostedJourney ? "journey" : "capture";
|
|
1016
|
+
}
|
|
1017
|
+
function modalMaxWidth(hasHostedJourney) {
|
|
1018
|
+
return hasHostedJourney ? 680 : 448;
|
|
1019
|
+
}
|
|
1020
|
+
function resultForModalClose(stageKind, result) {
|
|
1021
|
+
const terminal = stageKind === "accepted" || stageKind === "declined" || stageKind === "max_attempts";
|
|
1022
|
+
return terminal && result !== void 0 ? result : null;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1004
1025
|
// src/kyc/FaceCaptureModal.tsx
|
|
1005
1026
|
var MOBILE_UA = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
|
1006
|
-
var
|
|
1027
|
+
var ACCEPTED_AUTO_CLOSE_MS = 1200;
|
|
1007
1028
|
var LIVENESS_MESSAGES = [
|
|
1008
|
-
"Position your face inside the
|
|
1029
|
+
"Position your face inside the oval",
|
|
1009
1030
|
"Make sure your face is well lit",
|
|
1010
1031
|
"Remove glasses or hats if possible",
|
|
1011
1032
|
"Look straight at the camera, then tap Capture"
|
|
@@ -1014,6 +1035,8 @@ function headerSubtitle(stageKind) {
|
|
|
1014
1035
|
switch (stageKind) {
|
|
1015
1036
|
case "qr":
|
|
1016
1037
|
return "Use your phone to continue";
|
|
1038
|
+
case "journey":
|
|
1039
|
+
return "Follow the steps to verify your identity";
|
|
1017
1040
|
case "accepted":
|
|
1018
1041
|
return "All set";
|
|
1019
1042
|
case "declined":
|
|
@@ -1039,7 +1062,9 @@ function FaceCaptureModal({
|
|
|
1039
1062
|
}) {
|
|
1040
1063
|
const isMobile = typeof navigator !== "undefined" && MOBILE_UA.test(navigator.userAgent);
|
|
1041
1064
|
const initialChoice = defaultDevice ?? (isMobile ? "this" : "ask");
|
|
1065
|
+
const journeyMode = Boolean(session.verification_url);
|
|
1042
1066
|
const initialStage = (() => {
|
|
1067
|
+
if (journeyMode) return { kind: "journey", url: session.verification_url ?? "" };
|
|
1043
1068
|
if (initialChoice === "this") return { kind: "capture" };
|
|
1044
1069
|
if (initialChoice === "mobile") return { kind: "qr", mobileConnected: false };
|
|
1045
1070
|
return { kind: "choose" };
|
|
@@ -1057,19 +1082,20 @@ function FaceCaptureModal({
|
|
|
1057
1082
|
const qrPayload = session.link || `vesant://reuse-kyc?token=${encodeURIComponent(session.token)}`;
|
|
1058
1083
|
const cancelledRef = useRef(false);
|
|
1059
1084
|
const submittingRef = useRef(false);
|
|
1085
|
+
const journeyRetryingRef = useRef(false);
|
|
1060
1086
|
useEffect(() => {
|
|
1061
1087
|
const timer = setTimeout(() => {
|
|
1062
1088
|
setStage(
|
|
1063
|
-
(prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" ? { kind: "error", message: "Session expired" } : prev
|
|
1089
|
+
(prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" || prev.kind === "journey" ? { kind: "error", message: "Session expired" } : prev
|
|
1064
1090
|
);
|
|
1065
|
-
},
|
|
1091
|
+
}, REUSE_KYC_SESSION_EXPIRY_MS);
|
|
1066
1092
|
return () => clearTimeout(timer);
|
|
1067
1093
|
}, [session.token]);
|
|
1068
1094
|
useEffect(() => {
|
|
1069
1095
|
if (stage.kind !== "qr") return;
|
|
1070
1096
|
let stopped = false;
|
|
1071
1097
|
const intervalMs = 2e3;
|
|
1072
|
-
const deadline = Date.now() +
|
|
1098
|
+
const deadline = Date.now() + REUSE_KYC_SESSION_EXPIRY_MS;
|
|
1073
1099
|
let mobileSeen = stage.mobileConnected;
|
|
1074
1100
|
const tick = async () => {
|
|
1075
1101
|
if (stopped || cancelledRef.current) return;
|
|
@@ -1110,6 +1136,64 @@ function FaceCaptureModal({
|
|
|
1110
1136
|
stopped = true;
|
|
1111
1137
|
};
|
|
1112
1138
|
}, [stage.kind, stage.kind === "qr" ? stage.mobileConnected : false]);
|
|
1139
|
+
useEffect(() => {
|
|
1140
|
+
if (stage.kind !== "journey" || !stage.url) return;
|
|
1141
|
+
let stopped = false;
|
|
1142
|
+
const checkOnce = async () => {
|
|
1143
|
+
try {
|
|
1144
|
+
const result = await client.getEventBasedFaceVerificationSessionStatus(session.token);
|
|
1145
|
+
if (applyResult(result)) {
|
|
1146
|
+
stopped = true;
|
|
1147
|
+
return true;
|
|
1148
|
+
}
|
|
1149
|
+
} catch {
|
|
1150
|
+
}
|
|
1151
|
+
return false;
|
|
1152
|
+
};
|
|
1153
|
+
const tick = async () => {
|
|
1154
|
+
if (stopped || cancelledRef.current) return;
|
|
1155
|
+
await checkOnce();
|
|
1156
|
+
if (!stopped && !cancelledRef.current) {
|
|
1157
|
+
setTimeout(tick, 2500);
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
void tick();
|
|
1161
|
+
let frameOrigin = "";
|
|
1162
|
+
try {
|
|
1163
|
+
frameOrigin = new URL(stage.url).origin;
|
|
1164
|
+
} catch {
|
|
1165
|
+
frameOrigin = "";
|
|
1166
|
+
}
|
|
1167
|
+
const onMessage = (e) => {
|
|
1168
|
+
if (!frameOrigin || e.origin !== frameOrigin) return;
|
|
1169
|
+
const status = e.data?.verification_status;
|
|
1170
|
+
if (status) void checkOnce();
|
|
1171
|
+
};
|
|
1172
|
+
window.addEventListener("message", onMessage);
|
|
1173
|
+
return () => {
|
|
1174
|
+
stopped = true;
|
|
1175
|
+
window.removeEventListener("message", onMessage);
|
|
1176
|
+
};
|
|
1177
|
+
}, [stage.kind, stage.kind === "journey" ? stage.url : ""]);
|
|
1178
|
+
const handleJourneyRetry = async () => {
|
|
1179
|
+
if (!tryStartOperation(journeyRetryingRef)) return;
|
|
1180
|
+
setStage({ kind: "journey", url: "" });
|
|
1181
|
+
try {
|
|
1182
|
+
const refreshed = await client.retryEventBasedFaceVerificationJourney(session.token);
|
|
1183
|
+
if (refreshed.verification_url) {
|
|
1184
|
+
setStage({ kind: "journey", url: refreshed.verification_url });
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
setStage({ kind: "error", message: "Unable to start a new attempt. Please try again." });
|
|
1188
|
+
} catch (err) {
|
|
1189
|
+
setStage({
|
|
1190
|
+
kind: "error",
|
|
1191
|
+
message: err instanceof Error ? err.message : "Unable to start a new attempt. Please try again."
|
|
1192
|
+
});
|
|
1193
|
+
} finally {
|
|
1194
|
+
releaseOperation(journeyRetryingRef);
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1113
1197
|
const applyResult = (result) => {
|
|
1114
1198
|
if (result.status === "accepted") {
|
|
1115
1199
|
setStage({ kind: "accepted", result });
|
|
@@ -1237,12 +1321,22 @@ function FaceCaptureModal({
|
|
|
1237
1321
|
}, 3e3);
|
|
1238
1322
|
return () => clearInterval(interval);
|
|
1239
1323
|
}, [captureMode]);
|
|
1240
|
-
const close = (result) => {
|
|
1324
|
+
const close = useCallback((result) => {
|
|
1241
1325
|
cancelledRef.current = true;
|
|
1242
1326
|
if (result === null && onCancel) {
|
|
1243
1327
|
onCancel();
|
|
1244
1328
|
}
|
|
1245
1329
|
onComplete(result);
|
|
1330
|
+
}, [onCancel, onComplete]);
|
|
1331
|
+
useEffect(() => {
|
|
1332
|
+
if (stage.kind !== "accepted") return;
|
|
1333
|
+
const result = stage.result;
|
|
1334
|
+
const timer = setTimeout(() => close(result), ACCEPTED_AUTO_CLOSE_MS);
|
|
1335
|
+
return () => clearTimeout(timer);
|
|
1336
|
+
}, [close, stage]);
|
|
1337
|
+
const handleHeaderClose = () => {
|
|
1338
|
+
const result = "result" in stage ? stage.result : void 0;
|
|
1339
|
+
close(resultForModalClose(stage.kind, result));
|
|
1246
1340
|
};
|
|
1247
1341
|
const renderChoose = () => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React.createElement("div", { style: circleStyle }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })))), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "How would you like to verify?"), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement(
|
|
1248
1342
|
"button",
|
|
@@ -1301,12 +1395,28 @@ function FaceCaptureModal({
|
|
|
1301
1395
|
};
|
|
1302
1396
|
const renderSubmitting = () => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Verifying your photo\u2026"), /* @__PURE__ */ React.createElement("p", { style: { ...attemptsTextStyle, marginTop: 8 } }, "This usually takes a few seconds."));
|
|
1303
1397
|
const renderAccepted = (result) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: successCircleStyle }, /* @__PURE__ */ React.createElement("img", { src: Done, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Verified"), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Your identity has been confirmed. You can continue."), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Continue")));
|
|
1304
|
-
const renderDeclined = (result) => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, "Retake")), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
|
|
1398
|
+
const renderDeclined = (result) => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, journeyMode ? /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => void handleJourneyRetry() }, /* @__PURE__ */ React.createElement("span", null, "Try Again")) : /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, "Retake")), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
|
|
1305
1399
|
const renderMaxAttempts = (result) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Maximum Attempts Reached"), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, result.declined_reason ?? "We couldn't verify your identity after several attempts."), result.data?.freeze_account && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "For your security, your account has been temporarily restricted", result.data.freeze_duration_minutes ? ` for ${result.data.freeze_duration_minutes} minutes` : "", ". Please contact support if you need immediate help."), result.data?.enforce_logout && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "You will be signed out of your session."), result.data?.block && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "This action has been blocked for security reasons."), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Close")));
|
|
1306
1400
|
const renderError = (message) => {
|
|
1307
1401
|
const sessionExpired = /session\s+expired/i.test(message);
|
|
1308
|
-
return /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, sessionExpired ? "Session expired" : "Something Went Wrong"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, sessionExpired ? "Your verification session is no longer valid. Please start a new verification from the beginning." : message), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, !sessionExpired && /* @__PURE__ */ React.createElement(
|
|
1402
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, sessionExpired ? "Session expired" : "Something Went Wrong"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, sessionExpired ? "Your verification session is no longer valid. Please start a new verification from the beginning." : message), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, !sessionExpired && /* @__PURE__ */ React.createElement(
|
|
1403
|
+
"button",
|
|
1404
|
+
{
|
|
1405
|
+
style: primaryButtonStyle,
|
|
1406
|
+
onClick: () => retryTarget(journeyMode) === "journey" ? void handleJourneyRetry() : setStage({ kind: "capture" })
|
|
1407
|
+
},
|
|
1408
|
+
"Try Again"
|
|
1409
|
+
), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, sessionExpired ? "Close" : "Cancel")));
|
|
1309
1410
|
};
|
|
1411
|
+
const renderJourney = (url) => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, url ? /* @__PURE__ */ React.createElement(
|
|
1412
|
+
"iframe",
|
|
1413
|
+
{
|
|
1414
|
+
src: url,
|
|
1415
|
+
title: "Identity verification",
|
|
1416
|
+
allow: "camera; microphone",
|
|
1417
|
+
style: journeyFrameStyle
|
|
1418
|
+
}
|
|
1419
|
+
) : /* @__PURE__ */ React.createElement("div", { style: { padding: 24, textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Preparing your next attempt\u2026")), /* @__PURE__ */ React.createElement("p", { style: { ...attemptsTextStyle, textAlign: "center" } }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
|
|
1310
1420
|
const body = (() => {
|
|
1311
1421
|
switch (stage.kind) {
|
|
1312
1422
|
case "choose":
|
|
@@ -1315,6 +1425,8 @@ function FaceCaptureModal({
|
|
|
1315
1425
|
return renderQRStage(stage.mobileConnected);
|
|
1316
1426
|
case "capture":
|
|
1317
1427
|
return renderCapture(stage.declinedReason);
|
|
1428
|
+
case "journey":
|
|
1429
|
+
return renderJourney(stage.url);
|
|
1318
1430
|
case "submitting":
|
|
1319
1431
|
return renderSubmitting();
|
|
1320
1432
|
case "accepted":
|
|
@@ -1327,10 +1439,10 @@ function FaceCaptureModal({
|
|
|
1327
1439
|
return renderError(stage.message);
|
|
1328
1440
|
}
|
|
1329
1441
|
})();
|
|
1330
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("style", null, keyframes), /* @__PURE__ */ React.createElement("div", { style: overlayStyle }, /* @__PURE__ */ React.createElement("div", { style: modalStyle }, /* @__PURE__ */ React.createElement("div", { style: headerStyle }, /* @__PURE__ */ React.createElement(
|
|
1442
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("style", null, keyframes), /* @__PURE__ */ React.createElement("div", { style: overlayStyle }, /* @__PURE__ */ React.createElement("div", { style: { ...modalStyle, maxWidth: modalMaxWidth(journeyMode) } }, /* @__PURE__ */ React.createElement("div", { style: headerStyle }, /* @__PURE__ */ React.createElement(
|
|
1331
1443
|
"button",
|
|
1332
1444
|
{
|
|
1333
|
-
onClick:
|
|
1445
|
+
onClick: handleHeaderClose,
|
|
1334
1446
|
style: closeButtonStyle,
|
|
1335
1447
|
"aria-label": "Close"
|
|
1336
1448
|
},
|
|
@@ -1360,7 +1472,8 @@ var modalStyle = {
|
|
|
1360
1472
|
borderRadius: 16,
|
|
1361
1473
|
boxShadow: "0 25px 50px -12px rgba(0,0,0,.25)",
|
|
1362
1474
|
width: "100%",
|
|
1363
|
-
|
|
1475
|
+
maxHeight: "calc(100vh - 32px)",
|
|
1476
|
+
overflowY: "auto",
|
|
1364
1477
|
animation: "zoomIn .2s ease-out"
|
|
1365
1478
|
};
|
|
1366
1479
|
var headerStyle = {
|
|
@@ -1386,6 +1499,13 @@ var contentStyle = {
|
|
|
1386
1499
|
flexDirection: "column",
|
|
1387
1500
|
gap: 16
|
|
1388
1501
|
};
|
|
1502
|
+
var journeyFrameStyle = {
|
|
1503
|
+
width: "100%",
|
|
1504
|
+
height: "min(520px, 60vh)",
|
|
1505
|
+
border: 0,
|
|
1506
|
+
borderRadius: 12,
|
|
1507
|
+
background: "#f9fafb"
|
|
1508
|
+
};
|
|
1389
1509
|
var visualGuideContainerStyle = { display: "flex", justifyContent: "center" };
|
|
1390
1510
|
var visualGuideStyle = { position: "relative" };
|
|
1391
1511
|
var circleStyle = {
|
|
@@ -1652,9 +1772,10 @@ function LiveCamera({ onCapture, onCancel, message, assetBasePath }) {
|
|
|
1652
1772
|
if (error) {
|
|
1653
1773
|
return /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center", padding: 0 } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Camera unavailable"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, error), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: secondaryButtonStyle, onClick: onCancel }, "Back")));
|
|
1654
1774
|
}
|
|
1655
|
-
const
|
|
1656
|
-
const
|
|
1657
|
-
const
|
|
1775
|
+
const detectionAvailable = !faceDetectionFailed;
|
|
1776
|
+
const detectionActive = ready && detectionAvailable;
|
|
1777
|
+
const liveMessage = detectionAvailable ? FACE_MESSAGES[faceStatus] : message;
|
|
1778
|
+
const liveFaceColor = detectionAvailable ? FACE_COLORS[faceStatus] : void 0;
|
|
1658
1779
|
const captureDisabled = isCaptureDisabled(ready, detectionActive, faceStatus);
|
|
1659
1780
|
const ringPulse = detectionActive && (faceStatus === "too_far" || faceStatus === "too_close") ? "pulse 1.5s ease-in-out infinite" : void 0;
|
|
1660
1781
|
const dynamicCircleStyle = {
|
|
@@ -1770,20 +1891,22 @@ var faceOverlayContainerStyle = {
|
|
|
1770
1891
|
};
|
|
1771
1892
|
var faceRingWrapStyle = {
|
|
1772
1893
|
position: "relative",
|
|
1773
|
-
|
|
1774
|
-
aspectRatio: "
|
|
1894
|
+
height: "64%",
|
|
1895
|
+
aspectRatio: "3 / 4"
|
|
1775
1896
|
};
|
|
1776
1897
|
var faceDashedRingStyle = {
|
|
1777
1898
|
position: "absolute",
|
|
1778
1899
|
inset: -6,
|
|
1779
|
-
borderRadius:
|
|
1780
|
-
border: "6px dashed rgba(255, 255, 255, 0.4)"
|
|
1781
|
-
animation: "spin 10s linear infinite"
|
|
1900
|
+
borderRadius: 9999,
|
|
1901
|
+
border: "6px dashed rgba(255, 255, 255, 0.4)"
|
|
1782
1902
|
};
|
|
1783
1903
|
var faceCircleStyle = {
|
|
1784
1904
|
position: "absolute",
|
|
1785
1905
|
inset: 0,
|
|
1786
|
-
|
|
1906
|
+
// Tailwind's `rounded-full` resolves to a very large fixed radius. On a
|
|
1907
|
+
// 3:4 box that creates the normal KYC capsule/head guide with straighter
|
|
1908
|
+
// sides; 50% would instead produce a mathematically perfect ellipse.
|
|
1909
|
+
borderRadius: 9999,
|
|
1787
1910
|
border: "4px solid #00bc7d",
|
|
1788
1911
|
boxShadow: "0 0 0 1000px rgba(0, 0, 0, 0.5)"
|
|
1789
1912
|
};
|