vesant-sdk 1.7.0-dev.e0ee6d5 → 1.7.0-dev.f9faca4

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/react.d.ts CHANGED
@@ -2,7 +2,7 @@ import { D as DeviceFingerprintRequest, G as GeolocationClient, i as CipherTextO
2
2
  import { e as LoginVerificationResponse, L as LoginVerificationRequest, f as RegistrationVerificationResponse, R as RegistrationVerificationRequest, h as TransactionVerificationResponse, g as TransactionVerificationRequest, C as ComplianceClient } from './client-C3DCmGe9.js';
3
3
  import { RiskProfileClient } from './risk-profile/index.js';
4
4
  import { C as CustomerProfile } from './types-UGyDl1fd.js';
5
- import { KycClient, UseKycAlertsOptions, UseKycAlertsResult, UseKycOverviewOptions, UseKycOverviewResult, UseKycPreferencesResult, UseKycRequestsOptions, UseKycRequestsResult, UseKycSubmissionOptions, UseKycSubmissionResult, CreateReuseKycSessionRequest, CreateReuseKycSessionResponse, ReuseKycCallback } from './kyc/index.js';
5
+ import { KycClient, CreateEventBasedFaceVerificationSessionRequest, CreateEventBasedFaceVerificationSessionResponse, EventBasedFaceVerificationCallback, UseKycAlertsOptions, UseKycAlertsResult, UseKycOverviewOptions, UseKycOverviewResult, UseKycPreferencesResult, UseKycRequestsOptions, UseKycRequestsResult, UseKycSubmissionOptions, UseKycSubmissionResult } from './kyc/index.js';
6
6
  import './client-BolQlL5e.js';
7
7
  import './types-CBQRNL-l.js';
8
8
 
@@ -508,7 +508,7 @@ declare function getStatusColor(status: string): string;
508
508
  */
509
509
  declare function getRiskColor(risk: string): string;
510
510
  /**
511
- * Hook for the Re-Use KYC face-capture flow.
511
+ * Hook for the Event-Based Face Verification face-capture flow.
512
512
  *
513
513
  * Returns helpers that cover both mobile (direct capture) and desktop
514
514
  * (QR + polling) modes. Device detection happens here on the client —
@@ -524,26 +524,28 @@ declare function getRiskColor(risk: string): string;
524
524
  * - `runFlow(req, opts?)` — convenience wrapper: `startSession` then
525
525
  * `verifyFace`, with a short-circuit when `is_required` is false.
526
526
  */
527
- declare function useReuseKYCSubmission(client: KycClient): {
528
- startSession: (request: CreateReuseKycSessionRequest) => Promise<CreateReuseKycSessionResponse>;
529
- verifyFace: (session: CreateReuseKycSessionResponse, opts?: {
527
+ declare function useEventBasedFaceVerificationSubmission(client: KycClient): {
528
+ startSession: (request: CreateEventBasedFaceVerificationSessionRequest) => Promise<CreateEventBasedFaceVerificationSessionResponse>;
529
+ verifyFace: (session: CreateEventBasedFaceVerificationSessionResponse, opts?: {
530
530
  defaultDevice?: "ask" | "this" | "mobile";
531
531
  renderQR?: (payload: string) => React.ReactNode;
532
- }) => Promise<ReuseKycCallback | null>;
533
- runFlow: (request: CreateReuseKycSessionRequest, opts?: {
532
+ onCancel?: () => void;
533
+ }) => Promise<EventBasedFaceVerificationCallback | null>;
534
+ runFlow: (request: CreateEventBasedFaceVerificationSessionRequest, opts?: {
534
535
  defaultDevice?: "ask" | "this" | "mobile";
535
536
  renderQR?: (payload: string) => React.ReactNode;
537
+ onCancel?: () => void;
536
538
  }) => Promise<{
537
539
  kind: "not_required";
538
- session: CreateReuseKycSessionResponse;
540
+ session: CreateEventBasedFaceVerificationSessionResponse;
539
541
  } | {
540
542
  kind: "cancelled";
541
- session: CreateReuseKycSessionResponse;
543
+ session: CreateEventBasedFaceVerificationSessionResponse;
542
544
  } | {
543
545
  kind: "completed";
544
- session: CreateReuseKycSessionResponse;
545
- result: ReuseKycCallback;
546
+ session: CreateEventBasedFaceVerificationSessionResponse;
547
+ result: EventBasedFaceVerificationCallback;
546
548
  }>;
547
549
  };
548
550
 
549
- export { type UseCustomerProfileOptions, type UseCustomerProfileResult, UseGeolocationOptions, UseGeolocationResult, UseKycAlertsOptions, UseKycAlertsResult, UseKycOverviewOptions, UseKycOverviewResult, UseKycPreferencesResult, UseKycRequestsOptions, UseKycRequestsResult, UseKycSubmissionOptions, UseKycSubmissionResult, UseLocationCaptureOptions, UseLocationCaptureResult, UseLocationRequestsOptions, UseLocationRequestsResult, type UseLoginVerificationOptions, type UseLoginVerificationResult, type UseRegistrationOptions, type UseRegistrationResult, type UseTransactionVerificationOptions, type UseTransactionVerificationResult, createDeviceFingerprint, fileToBase64, formatKycStatus, getBrowserInfo, getRiskColor, getStatusColor, isValidFileSize, isValidFileType, useCipherText, useCustomerProfile, useGeolocation, useKycAlerts, useKycOverview, useKycPreferences, useKycRequests, useKycSubmission, useLocationCapture, useLocationRequests, useLoginVerification, useRegistration, useReuseKYCSubmission, useTransactionVerification };
551
+ export { type UseCustomerProfileOptions, type UseCustomerProfileResult, UseGeolocationOptions, UseGeolocationResult, UseKycAlertsOptions, UseKycAlertsResult, UseKycOverviewOptions, UseKycOverviewResult, UseKycPreferencesResult, UseKycRequestsOptions, UseKycRequestsResult, UseKycSubmissionOptions, UseKycSubmissionResult, UseLocationCaptureOptions, UseLocationCaptureResult, UseLocationRequestsOptions, UseLocationRequestsResult, type UseLoginVerificationOptions, type UseLoginVerificationResult, type UseRegistrationOptions, type UseRegistrationResult, type UseTransactionVerificationOptions, type UseTransactionVerificationResult, createDeviceFingerprint, fileToBase64, formatKycStatus, getBrowserInfo, getRiskColor, getStatusColor, isValidFileSize, isValidFileType, useCipherText, useCustomerProfile, useEventBasedFaceVerificationSubmission, useGeolocation, useKycAlerts, useKycOverview, useKycPreferences, useKycRequests, useKycSubmission, useLocationCapture, useLocationRequests, useLoginVerification, useRegistration, useTransactionVerification };
package/dist/react.js CHANGED
@@ -888,10 +888,10 @@ function useCustomerProfile(client, customerId, options = {}) {
888
888
  var Camera = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjAvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvMjAwMS9SRUMtU1ZHLTIwMDEwOTA0L0RURC9zdmcxMC5kdGQiPg0KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4NCjxzdmcgdmVyc2lvbj0iMS4wIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgDQoJIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDY0IDY0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2NCA2NCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8cGF0aCBmaWxsPSIjMjMxRjIwIiBkPSJNNjAsMTBINDkuNjU2bC02LjgyOC02LjgyOEM0Mi4wNzgsMi40MjIsNDEuMDYyLDIsNDAsMkgyNGMtMS4wNjIsMC0yLjA3OCwwLjQyMi0yLjgyOCwxLjE3MkwxNC4zNDQsMTBINA0KCQljLTIuMjExLDAtNCwxLjc4OS00LDR2NDRjMCwyLjIxMSwxLjc4OSw0LDQsNGg1NmMyLjIxMSwwLDQtMS43ODksNC00VjE0QzY0LDExLjc4OSw2Mi4yMTEsMTAsNjAsMTB6IE0zMiw1MA0KCQljLTguODM2LDAtMTYtNy4xNjQtMTYtMTZzNy4xNjQtMTYsMTYtMTZzMTYsNy4xNjQsMTYsMTZTNDAuODM2LDUwLDMyLDUweiIvPg0KCTxjaXJjbGUgZmlsbD0iIzIzMUYyMCIgY3g9IjMyIiBjeT0iMzQiIHI9IjgiLz4NCjwvZz4NCjwvc3ZnPg==";
889
889
  var Done = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik04LjUgMTIuNUwxMC41IDE0LjVMMTUuNSA5LjUiIHN0cm9rZT0iIzFDMjc0QyIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPg0KPHBhdGggZD0iTTcgMy4zMzc4MkM4LjQ3MDg3IDIuNDg2OTcgMTAuMTc4NiAyIDEyIDJDMTcuNTIyOCAyIDIyIDYuNDc3MTUgMjIgMTJDMjIgMTcuNTIyOCAxNy41MjI4IDIyIDEyIDIyQzYuNDc3MTUgMjIgMiAxNy41MjI4IDIgMTJDMiAxMC4xNzg2IDIuNDg2OTcgOC40NzA4NyAzLjMzNzgyIDciIHN0cm9rZT0iIzFDMjc0QyIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPg0KPC9zdmc+";
890
890
  var Close = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0xNC41IDkuNTAwMDJMOS41IDE0LjVNOS40OTk5OCA5LjVMMTQuNSAxNC41IiBzdHJva2U9IiMxQzI3NEMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4NCjxwYXRoIGQ9Ik03IDMuMzM3ODJDOC40NzA4NyAyLjQ4Njk3IDEwLjE3ODYgMiAxMiAyQzE3LjUyMjggMiAyMiA2LjQ3NzE1IDIyIDEyQzIyIDE3LjUyMjggMTcuNTIyOCAyMiAxMiAyMkM2LjQ3NzE1IDIyIDIgMTcuNTIyOCAyIDEyQzIgMTAuMTc4NiAyLjQ4Njk3IDguNDcwODcgMy4zMzc4MiA3IiBzdHJva2U9IiMxQzI3NEMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4NCjwvc3ZnPg==";
891
- var Upload = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0xNyAxN0gxNy4wMU0xNS42IDE0SDE4QzE4LjkzMTkgMTQgMTkuMzk3OCAxNCAxOS43NjU0IDE0LjE1MjJDMjAuMjU1NCAxNC4zNTUyIDIwLjY0NDggMTQuNzQ0NiAyMC44NDc4IDE1LjIzNDZDMjEgMTUuNjAyMiAyMSAxNi4wNjgxIDIxIDE3QzIxIDE3LjkzMTkgMjEgMTguMzk3OCAyMC44NDc4IDE4Ljc2NTRDMjAuNjQ0OCAxOS4yNTU0IDIwLjI1NTQgMTkuNjQ0OCAxOS43NjU0IDE5Ljg0NzhDMTkuMzk3OCAyMCAxOC45MzE5IDIwIDE4IDIwSDZDNS4wNjgxMiAyMCA0LjYwMjE4IDIwIDQuMjM0NjMgMTkuODQ3OEMzLjc0NDU4IDE5LjY0NDggMy4zNTUyMyAxOS4yNTU0IDMuMTUyMjQgMTguNzY1NEMzIDE4LjM5NzggMyAxNy45MzE5IDMgMTdDMyAxNi4wNjgxIDMgMTUuNjAyMiAzLjE1MjI0IDE1LjIzNDZDMy4zNTUyMyAxNC43NDQ2IDMuNzQ0NTggMTQuMzU1MiA0LjIzNDYzIDE0LjE1MjJDNC42MDIxOCAxNCA1LjA2ODEyIDE0IDYgMTRIOC40TTEyIDE1VjRNMTIgNEwxNSA3TTEyIDRMOSA3IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+DQo8L3N2Zz4=";
892
891
 
893
892
  // src/kyc/FaceCaptureModal.tsx
894
893
  var MOBILE_UA = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
894
+ var SESSION_EXPIRY_MS = 15 * 60 * 1e3;
895
895
  var LIVENESS_MESSAGES = [
896
896
  "Position your face inside the circle",
897
897
  "Make sure your face is well lit",
@@ -905,9 +905,9 @@ function headerSubtitle(stageKind) {
905
905
  case "accepted":
906
906
  return "All set";
907
907
  case "declined":
908
- return "Verification didn't match, try once more";
908
+ return "Verification Declined";
909
909
  case "max_attempts":
910
- return "We couldn't verify you";
910
+ return "We couldn't verify your identity";
911
911
  default:
912
912
  return "Please capture a clear photo of your face";
913
913
  }
@@ -920,16 +920,18 @@ function FaceCaptureModal({
920
920
  client,
921
921
  session,
922
922
  onComplete,
923
+ onCancel,
923
924
  defaultDevice,
924
925
  renderQR
925
926
  }) {
926
927
  const isMobile = typeof navigator !== "undefined" && MOBILE_UA.test(navigator.userAgent);
928
+ const initialChoice = defaultDevice ?? (isMobile ? "this" : "ask");
927
929
  const initialStage = (() => {
928
- const choice = defaultDevice ?? (isMobile ? "this" : "ask");
929
- if (choice === "this") return { kind: "capture" };
930
- if (choice === "mobile") return { kind: "qr", mobileConnected: false };
930
+ if (initialChoice === "this") return { kind: "capture" };
931
+ if (initialChoice === "mobile") return { kind: "qr", mobileConnected: false };
931
932
  return { kind: "choose" };
932
933
  })();
934
+ const hasMethodChoice = initialChoice === "ask";
933
935
  const [stage, setStage] = React.useState(initialStage);
934
936
  const [attempts, setAttempts] = React.useState(session.attempts);
935
937
  const maxAttempts = session.max_attempts || 1;
@@ -941,17 +943,25 @@ function FaceCaptureModal({
941
943
  const qrPayload = session.link || `vesant://reuse-kyc?token=${encodeURIComponent(session.token)}`;
942
944
  const cancelledRef = React.useRef(false);
943
945
  const submittingRef = React.useRef(false);
946
+ React.useEffect(() => {
947
+ const timer = setTimeout(() => {
948
+ setStage(
949
+ (prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" ? { kind: "error", message: "Session expired" } : prev
950
+ );
951
+ }, SESSION_EXPIRY_MS);
952
+ return () => clearTimeout(timer);
953
+ }, [session.token]);
944
954
  React.useEffect(() => {
945
955
  if (stage.kind !== "qr") return;
946
956
  let stopped = false;
947
957
  const intervalMs = 2e3;
948
- const deadline = Date.now() + 15 * 60 * 1e3;
958
+ const deadline = Date.now() + SESSION_EXPIRY_MS;
949
959
  let mobileSeen = stage.mobileConnected;
950
960
  const tick = async () => {
951
961
  if (stopped || cancelledRef.current) return;
952
962
  try {
953
963
  if (mobileSeen) {
954
- const result = await client.getReuseKycSessionStatus(session.reference);
964
+ const result = await client.getEventBasedFaceVerificationSessionStatus(session.token);
955
965
  if (result.status === "accepted") {
956
966
  setStage({ kind: "accepted", result });
957
967
  stopped = true;
@@ -1023,7 +1033,7 @@ function FaceCaptureModal({
1023
1033
  try {
1024
1034
  const postPromise = (async () => {
1025
1035
  try {
1026
- const data = await client.submitReuseKycSession({
1036
+ const data = await client.submitEventBasedFaceVerificationSession({
1027
1037
  token: session.token,
1028
1038
  reference: ref,
1029
1039
  proof
@@ -1039,7 +1049,7 @@ function FaceCaptureModal({
1039
1049
  while (Date.now() < deadline) {
1040
1050
  if (settled || cancelledRef.current) return null;
1041
1051
  try {
1042
- const r = await client.getReuseKycSessionStatus(ref);
1052
+ const r = await client.getEventBasedFaceVerificationSessionStatus(session.token);
1043
1053
  if (isFreshPollResult(r)) return r;
1044
1054
  } catch {
1045
1055
  }
@@ -1110,6 +1120,9 @@ function FaceCaptureModal({
1110
1120
  }, [captureMode]);
1111
1121
  const close = (result) => {
1112
1122
  cancelledRef.current = true;
1123
+ if (result === null && onCancel) {
1124
+ onCancel();
1125
+ }
1113
1126
  onComplete(result);
1114
1127
  };
1115
1128
  const renderChoose = () => /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: circleStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })))), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, "How would you like to verify?"), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement(
@@ -1118,7 +1131,7 @@ function FaceCaptureModal({
1118
1131
  style: primaryButtonStyle,
1119
1132
  onClick: () => setStage({ kind: "capture" })
1120
1133
  },
1121
- /* @__PURE__ */ React__default.default.createElement("img", { src: isMobile ? Camera : Upload, alt: "", width: 20, height: 20 }),
1134
+ /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }),
1122
1135
  /* @__PURE__ */ React__default.default.createElement("span", null, "Continue on this device")
1123
1136
  ), /* @__PURE__ */ React__default.default.createElement(
1124
1137
  "button",
@@ -1135,6 +1148,13 @@ function FaceCaptureModal({
1135
1148
  onClick: () => setStage({ kind: "capture" })
1136
1149
  },
1137
1150
  "Use this device instead"
1151
+ ), hasMethodChoice && /* @__PURE__ */ React__default.default.createElement(
1152
+ "button",
1153
+ {
1154
+ style: secondaryButtonStyle,
1155
+ onClick: () => setStage({ kind: "choose" })
1156
+ },
1157
+ "Change verification method"
1138
1158
  ), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")));
1139
1159
  const renderCapture = (declinedReason) => {
1140
1160
  if (captureMode === "live") {
@@ -1150,13 +1170,23 @@ function FaceCaptureModal({
1150
1170
  if (captureMode === "preview" && capturedPreview) {
1151
1171
  return /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, declinedReason && /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, "Looks good? Submit this selfie or retake it."), /* @__PURE__ */ React__default.default.createElement("div", { style: previewBoxStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: capturedPreview, alt: "Captured selfie preview", style: previewImgStyle })), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleConfirmSubmit }, /* @__PURE__ */ React__default.default.createElement("img", { src: Done, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, "Submit")), /* @__PURE__ */ React__default.default.createElement("button", { style: secondaryButtonStyle, onClick: handleRetake }, "Retake"), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1152
1172
  }
1153
- return /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: circleStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React__default.default.createElement("div", { style: badgeStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Done, alt: "", width: 16, height: 16 })))), declinedReason && /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React__default.default.createElement("div", { style: instructionsBoxStyle }, /* @__PURE__ */ React__default.default.createElement("h3", { style: instructionsTitleStyle }, "Tips for best results:"), /* @__PURE__ */ React__default.default.createElement("ul", { style: instructionsListStyle }, /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Ensure good lighting on your face")), /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Remove glasses or accessories if possible")), /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Look directly at the camera")))), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleOpenCamera }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, declinedReason ? "Try again" : "Open Camera")), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1173
+ return /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: circleStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React__default.default.createElement("div", { style: badgeStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Done, alt: "", width: 16, height: 16 })))), declinedReason && /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React__default.default.createElement("div", { style: instructionsBoxStyle }, /* @__PURE__ */ React__default.default.createElement("h3", { style: instructionsTitleStyle }, "Tips for best results:"), /* @__PURE__ */ React__default.default.createElement("ul", { style: instructionsListStyle }, /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Ensure good lighting on your face")), /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Remove glasses or accessories if possible")), /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Look directly at the camera")))), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleOpenCamera }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, declinedReason ? "Try Again" : "Open Camera")), hasMethodChoice && /* @__PURE__ */ React__default.default.createElement(
1174
+ "button",
1175
+ {
1176
+ style: secondaryButtonStyle,
1177
+ onClick: () => setStage({ kind: "choose" })
1178
+ },
1179
+ "Change verification method"
1180
+ ), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1154
1181
  };
1155
1182
  const renderSubmitting = () => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, "Verifying your photo\u2026"), /* @__PURE__ */ React__default.default.createElement("p", { style: { ...attemptsTextStyle, marginTop: 8 } }, "This usually takes a few seconds."));
1156
1183
  const renderAccepted = (result) => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: successCircleStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Done, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Verified"), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, "Your identity has been confirmed. You can continue."), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Continue")));
1157
- const renderDeclined = (result) => /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, "Retake")), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
1158
- const renderMaxAttempts = (result) => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Maximum attempts reached"), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, result.declined_reason ?? "We couldn't verify your identity after several attempts."), result.data?.freeze_account && /* @__PURE__ */ React__default.default.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__default.default.createElement("p", { style: alertTextStyle }, "You will be signed out of your session."), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Close")));
1159
- const renderError = (message) => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Something went wrong"), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, message), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => setStage({ kind: "capture" }) }, "Try again"), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")));
1184
+ const renderDeclined = (result) => /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, "Retake")), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
1185
+ const renderMaxAttempts = (result) => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Maximum Attempts Reached"), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, result.declined_reason ?? "We couldn't verify your identity after several attempts."), result.data?.freeze_account && /* @__PURE__ */ React__default.default.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__default.default.createElement("p", { style: alertTextStyle }, "You will be signed out of your session."), result.data?.block && /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, "This action has been blocked for security reasons."), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Close")));
1186
+ const renderError = (message) => {
1187
+ const sessionExpired = /session\s+expired/i.test(message);
1188
+ return /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, sessionExpired ? "Session expired" : "Something Went Wrong"), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, sessionExpired ? "Your verification session is no longer valid. Please start a new verification from the beginning." : message), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, !sessionExpired && /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => setStage({ kind: "capture" }) }, "Try Again"), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, sessionExpired ? "Close" : "Cancel")));
1189
+ };
1160
1190
  const body = (() => {
1161
1191
  switch (stage.kind) {
1162
1192
  case "choose":
@@ -1364,11 +1394,63 @@ var attemptsTextStyle = {
1364
1394
  };
1365
1395
  var footerStyle = { padding: "12px 24px 16px", borderTop: "1px solid #f3f4f6" };
1366
1396
  var footerTextStyle = { margin: 0, fontSize: 12, color: "#9ca3af", textAlign: "center" };
1397
+ var FACE_MESSAGES = {
1398
+ idle: "Initializing camera...",
1399
+ loading: "Loading face detection...",
1400
+ scanning: "Position your face in the circle",
1401
+ too_far: "Face too far. Please move closer to the camera",
1402
+ too_close: "Too close. Please move back a little",
1403
+ off_center: "Center your face in the circle",
1404
+ ok: "Looking great! Tap the button to capture"
1405
+ };
1406
+ var FACE_COLORS = {
1407
+ idle: "#9ca3af",
1408
+ loading: "#3b82f6",
1409
+ scanning: "#3b82f6",
1410
+ too_far: "#ef4444",
1411
+ too_close: "#f59e0b",
1412
+ off_center: "#f59e0b",
1413
+ ok: "#10b981"
1414
+ };
1415
+ var MEDIAPIPE_BASE = "https://cdn.jsdelivr.net/npm/@mediapipe/face_detection";
1416
+ var mediapipeLoaderPromise = null;
1417
+ function loadMediaPipeFaceDetection() {
1418
+ if (typeof window === "undefined" || typeof document === "undefined") {
1419
+ return Promise.reject(new Error("MediaPipe requires a browser environment"));
1420
+ }
1421
+ const w = window;
1422
+ if (w.FaceDetection) {
1423
+ return Promise.resolve(w.FaceDetection);
1424
+ }
1425
+ if (mediapipeLoaderPromise) return mediapipeLoaderPromise;
1426
+ mediapipeLoaderPromise = new Promise((resolve, reject) => {
1427
+ const script = document.createElement("script");
1428
+ script.src = `${MEDIAPIPE_BASE}/face_detection.js`;
1429
+ script.async = true;
1430
+ script.crossOrigin = "anonymous";
1431
+ script.onload = () => {
1432
+ if (w.FaceDetection) {
1433
+ resolve(w.FaceDetection);
1434
+ } else {
1435
+ mediapipeLoaderPromise = null;
1436
+ reject(new Error("MediaPipe loaded but FaceDetection global is missing"));
1437
+ }
1438
+ };
1439
+ script.onerror = () => {
1440
+ mediapipeLoaderPromise = null;
1441
+ reject(new Error("Failed to load MediaPipe face_detection script"));
1442
+ };
1443
+ document.head.appendChild(script);
1444
+ });
1445
+ return mediapipeLoaderPromise;
1446
+ }
1367
1447
  function LiveCamera({ onCapture, onCancel, message }) {
1368
1448
  const videoRef = React.useRef(null);
1369
1449
  const streamRef = React.useRef(null);
1370
1450
  const [ready, setReady] = React.useState(false);
1371
1451
  const [error, setError] = React.useState(null);
1452
+ const [faceStatus, setFaceStatus] = React.useState("idle");
1453
+ const [faceDetectionFailed, setFaceDetectionFailed] = React.useState(false);
1372
1454
  React.useEffect(() => {
1373
1455
  let cancelled = false;
1374
1456
  async function init() {
@@ -1409,6 +1491,77 @@ function LiveCamera({ onCapture, onCancel, message }) {
1409
1491
  streamRef.current = null;
1410
1492
  };
1411
1493
  }, []);
1494
+ React.useEffect(() => {
1495
+ if (!ready) {
1496
+ setFaceStatus("idle");
1497
+ return;
1498
+ }
1499
+ let cancelled = false;
1500
+ let rafId = null;
1501
+ let detector = null;
1502
+ setFaceStatus("loading");
1503
+ setFaceDetectionFailed(false);
1504
+ (async () => {
1505
+ try {
1506
+ const FaceDetection = await loadMediaPipeFaceDetection();
1507
+ if (cancelled) return;
1508
+ const d = new FaceDetection({
1509
+ locateFile: (f) => `${MEDIAPIPE_BASE}/${f}`
1510
+ });
1511
+ d.setOptions({ model: "short", minDetectionConfidence: 0.5 });
1512
+ d.onResults((results) => {
1513
+ if (cancelled) return;
1514
+ const detections = results.detections ?? [];
1515
+ let next;
1516
+ if (detections.length === 0) {
1517
+ next = "scanning";
1518
+ } else {
1519
+ const bb = detections[0].boundingBox;
1520
+ if (bb.width < 0.18) next = "too_far";
1521
+ else if (bb.width > 0.72) next = "too_close";
1522
+ else if (bb.xCenter < 0.28 || bb.xCenter > 0.72 || bb.yCenter < 0.28 || bb.yCenter > 0.72) {
1523
+ next = "off_center";
1524
+ } else {
1525
+ next = "ok";
1526
+ }
1527
+ }
1528
+ setFaceStatus(next);
1529
+ });
1530
+ await d.initialize();
1531
+ if (cancelled) return;
1532
+ detector = d;
1533
+ setFaceStatus("scanning");
1534
+ const tick = async () => {
1535
+ if (cancelled) return;
1536
+ const v = videoRef.current;
1537
+ if (v?.readyState === 4 && detector) {
1538
+ try {
1539
+ await detector.send({ image: v });
1540
+ } catch {
1541
+ }
1542
+ }
1543
+ if (!cancelled) {
1544
+ rafId = requestAnimationFrame(tick);
1545
+ }
1546
+ };
1547
+ tick();
1548
+ } catch (err) {
1549
+ console.error("Face detection failed to initialize:", err);
1550
+ if (!cancelled) {
1551
+ setFaceDetectionFailed(true);
1552
+ setFaceStatus("scanning");
1553
+ }
1554
+ }
1555
+ })();
1556
+ return () => {
1557
+ cancelled = true;
1558
+ if (rafId !== null) cancelAnimationFrame(rafId);
1559
+ try {
1560
+ detector?.close?.();
1561
+ } catch {
1562
+ }
1563
+ };
1564
+ }, [ready]);
1412
1565
  const capture = () => {
1413
1566
  const video = videoRef.current;
1414
1567
  if (!video || !video.videoWidth) return;
@@ -1430,7 +1583,17 @@ function LiveCamera({ onCapture, onCancel, message }) {
1430
1583
  if (error) {
1431
1584
  return /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center", padding: 0 } }, /* @__PURE__ */ React__default.default.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Camera unavailable"), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, error), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: secondaryButtonStyle, onClick: onCancel }, "Back")));
1432
1585
  }
1433
- return /* @__PURE__ */ React__default.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React__default.default.createElement("div", { style: videoFrameStyle }, /* @__PURE__ */ React__default.default.createElement(
1586
+ const detectionActive = ready && !faceDetectionFailed;
1587
+ const liveMessage = detectionActive ? FACE_MESSAGES[faceStatus] : message;
1588
+ const liveFaceColor = detectionActive ? FACE_COLORS[faceStatus] : void 0;
1589
+ const captureDisabled = !ready || detectionActive && faceStatus !== "ok";
1590
+ const ringPulse = detectionActive && (faceStatus === "too_far" || faceStatus === "too_close") ? "pulse 1.5s ease-in-out infinite" : void 0;
1591
+ const dynamicCircleStyle = {
1592
+ ...faceCircleStyle,
1593
+ ...liveFaceColor ? { borderColor: liveFaceColor } : null,
1594
+ ...ringPulse ? { animation: ringPulse } : null
1595
+ };
1596
+ return /* @__PURE__ */ React__default.default.createElement("div", { style: videoFrameStyle }, /* @__PURE__ */ React__default.default.createElement(
1434
1597
  "video",
1435
1598
  {
1436
1599
  ref: videoRef,
@@ -1439,24 +1602,87 @@ function LiveCamera({ onCapture, onCancel, message }) {
1439
1602
  autoPlay: true,
1440
1603
  style: videoStyle
1441
1604
  }
1442
- ), /* @__PURE__ */ React__default.default.createElement("div", { style: faceOverlayContainerStyle, "aria-hidden": "true" }, /* @__PURE__ */ React__default.default.createElement("div", { style: faceRingWrapStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: faceDashedRingStyle }), /* @__PURE__ */ React__default.default.createElement("div", { style: faceCircleStyle }))), message && /* @__PURE__ */ React__default.default.createElement("div", { style: liveMessagePillContainerStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: liveMessagePillStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: liveMessagePillTextStyle }, message))), !ready && /* @__PURE__ */ React__default.default.createElement("div", { style: videoLoadingStyle }, "Starting camera\u2026")), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement(
1605
+ ), /* @__PURE__ */ React__default.default.createElement("div", { style: faceOverlayContainerStyle, "aria-hidden": "true" }, /* @__PURE__ */ React__default.default.createElement("div", { style: faceRingWrapStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: faceDashedRingStyle }), /* @__PURE__ */ React__default.default.createElement("div", { style: dynamicCircleStyle }))), liveMessage && /* @__PURE__ */ React__default.default.createElement("div", { style: liveMessagePillContainerStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: liveMessagePillStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: liveMessagePillTextStyle }, liveMessage))), /* @__PURE__ */ React__default.default.createElement(
1606
+ "button",
1607
+ {
1608
+ type: "button",
1609
+ onClick: onCancel,
1610
+ "aria-label": "Close camera",
1611
+ style: overlayCloseButtonStyle
1612
+ },
1613
+ /* @__PURE__ */ React__default.default.createElement("img", { src: Close, alt: "", width: 16, height: 16 })
1614
+ ), !ready && /* @__PURE__ */ React__default.default.createElement("div", { style: videoLoadingStyle }, "Starting camera\u2026"), /* @__PURE__ */ React__default.default.createElement("div", { style: bottomGradientStyle }, /* @__PURE__ */ React__default.default.createElement(
1443
1615
  "button",
1444
1616
  {
1445
- style: ready ? primaryButtonStyle : { ...primaryButtonStyle, opacity: 0.5, cursor: "not-allowed" },
1617
+ type: "button",
1446
1618
  onClick: capture,
1447
- disabled: !ready
1619
+ disabled: captureDisabled,
1620
+ "aria-label": "Capture",
1621
+ style: captureDisabled ? { ...shutterButtonStyle, opacity: 0.5, cursor: "not-allowed" } : shutterButtonStyle
1448
1622
  },
1449
- /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }),
1450
- /* @__PURE__ */ React__default.default.createElement("span", null, "Capture")
1451
- ), /* @__PURE__ */ React__default.default.createElement("button", { style: secondaryButtonStyle, onClick: onCancel }, "Cancel")));
1623
+ /* @__PURE__ */ React__default.default.createElement("span", { style: shutterInnerStyle })
1624
+ )));
1452
1625
  }
1453
1626
  var videoFrameStyle = {
1454
1627
  position: "relative",
1455
1628
  width: "100%",
1456
- aspectRatio: "1 / 1",
1457
- borderRadius: 12,
1629
+ aspectRatio: "3 / 4",
1630
+ borderRadius: 24,
1458
1631
  overflow: "hidden",
1459
- background: "#0f172a"
1632
+ background: "#000000",
1633
+ border: "4px solid rgba(255, 255, 255, 0.08)",
1634
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)"
1635
+ };
1636
+ var overlayCloseButtonStyle = {
1637
+ position: "absolute",
1638
+ top: 12,
1639
+ right: 12,
1640
+ width: 32,
1641
+ height: 32,
1642
+ display: "inline-flex",
1643
+ alignItems: "center",
1644
+ justifyContent: "center",
1645
+ padding: 0,
1646
+ border: "none",
1647
+ borderRadius: "50%",
1648
+ background: "rgba(0, 0, 0, 0.4)",
1649
+ backdropFilter: "blur(6px)",
1650
+ color: "#ffffff",
1651
+ cursor: "pointer",
1652
+ zIndex: 10
1653
+ };
1654
+ var bottomGradientStyle = {
1655
+ position: "absolute",
1656
+ bottom: 0,
1657
+ left: 0,
1658
+ right: 0,
1659
+ padding: "16px",
1660
+ background: "linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%)",
1661
+ display: "flex",
1662
+ alignItems: "center",
1663
+ justifyContent: "center",
1664
+ zIndex: 10
1665
+ };
1666
+ var shutterButtonStyle = {
1667
+ width: 64,
1668
+ height: 64,
1669
+ borderRadius: "50%",
1670
+ background: "rgba(255, 255, 255, 0.15)",
1671
+ backdropFilter: "blur(6px)",
1672
+ border: "2px solid rgba(255, 255, 255, 0.7)",
1673
+ display: "inline-flex",
1674
+ alignItems: "center",
1675
+ justifyContent: "center",
1676
+ cursor: "pointer",
1677
+ padding: 0,
1678
+ transition: "transform 120ms ease-out, background 120ms"
1679
+ };
1680
+ var shutterInnerStyle = {
1681
+ display: "block",
1682
+ width: 44,
1683
+ height: 44,
1684
+ borderRadius: "50%",
1685
+ background: "rgba(255, 255, 255, 0.85)"
1460
1686
  };
1461
1687
  var videoStyle = {
1462
1688
  width: "100%",
@@ -1500,7 +1726,9 @@ var videoLoadingStyle = {
1500
1726
  justifyContent: "center",
1501
1727
  color: "#e5e7eb",
1502
1728
  fontSize: 14,
1503
- fontWeight: 500
1729
+ fontWeight: 500,
1730
+ background: "rgba(0, 0, 0, 0.4)",
1731
+ zIndex: 5
1504
1732
  };
1505
1733
  var liveMessagePillContainerStyle = {
1506
1734
  position: "absolute",
@@ -1865,9 +2093,9 @@ function getRiskColor(risk) {
1865
2093
  };
1866
2094
  return colorMap[risk] || "#6b7280";
1867
2095
  }
1868
- function useReuseKYCSubmission(client$1) {
2096
+ function useEventBasedFaceVerificationSubmission(client$1) {
1869
2097
  const startSession = React.useCallback(
1870
- (request) => client$1.createReuseKycSession(request),
2098
+ (request) => client$1.createEventBasedFaceVerificationSession(request),
1871
2099
  [client$1]
1872
2100
  );
1873
2101
  const verifyFace = React.useCallback(
@@ -1884,6 +2112,7 @@ function useReuseKYCSubmission(client$1) {
1884
2112
  session,
1885
2113
  defaultDevice: opts.defaultDevice,
1886
2114
  renderQR: opts.renderQR,
2115
+ onCancel: opts.onCancel,
1887
2116
  onComplete: (result) => {
1888
2117
  cleanup();
1889
2118
  resolve(result);
@@ -1920,6 +2149,7 @@ exports.isValidFileSize = isValidFileSize;
1920
2149
  exports.isValidFileType = isValidFileType;
1921
2150
  exports.useCipherText = useCipherText;
1922
2151
  exports.useCustomerProfile = useCustomerProfile;
2152
+ exports.useEventBasedFaceVerificationSubmission = useEventBasedFaceVerificationSubmission;
1923
2153
  exports.useGeolocation = useGeolocation;
1924
2154
  exports.useKycAlerts = useKycAlerts;
1925
2155
  exports.useKycOverview = useKycOverview;
@@ -1930,7 +2160,6 @@ exports.useLocationCapture = useLocationCapture;
1930
2160
  exports.useLocationRequests = useLocationRequests;
1931
2161
  exports.useLoginVerification = useLoginVerification;
1932
2162
  exports.useRegistration = useRegistration;
1933
- exports.useReuseKYCSubmission = useReuseKYCSubmission;
1934
2163
  exports.useTransactionVerification = useTransactionVerification;
1935
2164
  //# sourceMappingURL=react.js.map
1936
2165
  //# sourceMappingURL=react.js.map