@neofaceid/web-sdk 1.19.0 → 1.23.0

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.
@@ -3430,14 +3430,14 @@ const CALIBRATION = {
3430
3430
  CAPTURE_HOLD_TIME: 1500,
3431
3431
  DETECTION_INTERVAL: 150,
3432
3432
  // Um pouco mais rápido para melhor resposta
3433
- MIN_FACE_WIDTH: 160,
3434
- // Rosto deve ocupar boa parte do oval (era ~120 implícito)
3435
- MAX_FACE_WIDTH: 280,
3436
- // Evita rosto "colado" na câmera
3437
- CENTER_X_THRESHOLD: 0.15,
3438
- // Rosto deve estar centralizado (desvio max 15%)
3439
- CENTER_Y_THRESHOLD: 0.2
3440
- // Rosto deve estar centralizado verticalmente
3433
+ MIN_FACE_PERCENT: 0.35,
3434
+ // Rosto deve ocupar pelo menos 35% da largura do vídeo
3435
+ MAX_FACE_PERCENT: 0.65,
3436
+ // Rosto pode ocupar até 65% da largura do vídeo
3437
+ CENTER_X_THRESHOLD: 0.18,
3438
+ // Ligeiramente mais tolerante na centralização
3439
+ CENTER_Y_THRESHOLD: 0.22,
3440
+ STEP_TIMEOUT: 2e4
3441
3441
  };
3442
3442
  function BiometricRegistrationModal({
3443
3443
  personData,
@@ -3455,6 +3455,7 @@ function BiometricRegistrationModal({
3455
3455
  const isCapturingRef = useRef(false);
3456
3456
  const currentStepRef = useRef("center");
3457
3457
  const watchdogTimerRef = useRef(null);
3458
+ const stepTimeoutTimerRef = useRef(null);
3458
3459
  const detectionStartedRef = useRef(false);
3459
3460
  const captureStateRef = useRef(null);
3460
3461
  const [state, dispatch] = useReducer(modalReducer, initialState);
@@ -3482,6 +3483,18 @@ function BiometricRegistrationModal({
3482
3483
  currentStepIndex: state.currentStepIndex,
3483
3484
  capturedPhotos: state.capturedPhotos
3484
3485
  };
3486
+ if (stepTimeoutTimerRef.current) {
3487
+ clearTimeout(stepTimeoutTimerRef.current);
3488
+ }
3489
+ if (state.step !== "complete") {
3490
+ stepTimeoutTimerRef.current = window.setTimeout(() => {
3491
+ dispatch({
3492
+ type: "ERROR",
3493
+ message: "Tempo limite atingido para o movimento. Por favor, tente novamente em um ambiente bem iluminado.",
3494
+ retryable: true
3495
+ });
3496
+ }, CALIBRATION.STEP_TIMEOUT);
3497
+ }
3485
3498
  }
3486
3499
  }, [
3487
3500
  state.status,
@@ -3522,9 +3535,14 @@ function BiometricRegistrationModal({
3522
3535
  return null;
3523
3536
  }
3524
3537
  };
3525
- const checkFacePosition = (box, videoWidth, videoHeight) => {
3526
- if (box.width < CALIBRATION.MIN_FACE_WIDTH) return "too-far";
3527
- if (box.width > CALIBRATION.MAX_FACE_WIDTH) return "too-close";
3538
+ const checkFacePosition = (detection) => {
3539
+ if (!detection || !videoRef.current) return "not-detected";
3540
+ const { box } = detection.detection;
3541
+ const videoWidth = videoRef.current.videoWidth || 640;
3542
+ const videoHeight = videoRef.current.videoHeight || 480;
3543
+ const faceWidthRatio = box.width / videoWidth;
3544
+ if (faceWidthRatio < CALIBRATION.MIN_FACE_PERCENT) return "too-far";
3545
+ if (faceWidthRatio > CALIBRATION.MAX_FACE_PERCENT) return "too-close";
3528
3546
  const faceCenterX = box.x + box.width / 2;
3529
3547
  const faceCenterY = box.y + box.height / 2;
3530
3548
  const deviationX = Math.abs(faceCenterX - videoWidth / 2) / videoWidth;
@@ -3627,6 +3645,10 @@ function BiometricRegistrationModal({
3627
3645
  clearInterval(progressIntervalRef.current);
3628
3646
  progressIntervalRef.current = null;
3629
3647
  }
3648
+ if (stepTimeoutTimerRef.current) {
3649
+ clearTimeout(stepTimeoutTimerRef.current);
3650
+ stepTimeoutTimerRef.current = null;
3651
+ }
3630
3652
  };
3631
3653
  }, []);
3632
3654
  useEffect(() => {
@@ -3740,21 +3762,20 @@ function BiometricRegistrationModal({
3740
3762
  const detection = await faceapi.detectSingleFace(
3741
3763
  videoRef.current,
3742
3764
  new faceapi.TinyFaceDetectorOptions({
3743
- inputSize: 160,
3744
- scoreThreshold: 0.4
3765
+ inputSize: 224,
3766
+ // Aumentado para detectar rostos menores/mais distantes
3767
+ scoreThreshold: 0.35
3768
+ // Ligeiramente mais sensível
3745
3769
  })
3746
3770
  ).withFaceLandmarks();
3747
3771
  if (!isRunning) return;
3748
3772
  const hasFace = !!detection;
3749
3773
  setFaceDetected(hasFace);
3750
3774
  if (hasFace && detection.detection) {
3751
- const video2 = videoRef.current;
3752
- const positionStatus = checkFacePosition(
3753
- detection.detection.box,
3754
- video2.videoWidth,
3755
- video2.videoHeight
3756
- );
3757
- setFaceDistance(positionStatus);
3775
+ const positionStatus = checkFacePosition(detection);
3776
+ if (positionStatus !== "not-detected") {
3777
+ setFaceDistance(positionStatus);
3778
+ }
3758
3779
  if (positionStatus !== "ok") {
3759
3780
  manageHoldAndCapture(false);
3760
3781
  requestAnimationFrame(detectFace2);
@@ -3940,40 +3961,25 @@ function BiometricRegistrationModal({
3940
3961
  const stepProgress = state.stepProgress;
3941
3962
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "liveness-container", children: [
3942
3963
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "progress-indicator", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "progress-text", children: progress }) }),
3943
- faceDetected && faceDistance !== "ok" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `face-badge ${faceDistance === "not-centered" ? "warning" : "error"}`, children: [
3964
+ faceDetected && faceDistance !== "ok" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `status-badge ${faceDistance === "not-centered" ? "warning" : "error"}`, children: [
3944
3965
  faceDistance === "too-far" && "Aproxime-se mais",
3945
- faceDistance === "too-close" && "Rosto muito perto",
3966
+ faceDistance === "too-close" && "Afaste-se um pouco",
3946
3967
  faceDistance === "not-centered" && "Centralize seu rosto"
3947
3968
  ] }),
3948
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "video-wrapper", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3969
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "video-wrapper", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3949
3970
  "div",
3950
3971
  {
3951
- className: `video-circle ${faceDetected ? "face-detected" : ""} ${stepProgress > 0 ? "capturing" : ""}`,
3952
- children: [
3953
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3954
- "video",
3955
- {
3956
- ref: videoRef,
3957
- className: `video-feed ${cameraReady ? "ready" : ""}`,
3958
- autoPlay: true,
3959
- muted: true,
3960
- playsInline: true
3961
- }
3962
- ),
3963
- faceDetected && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "face-badge", children: [
3964
- /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3965
- "path",
3966
- {
3967
- d: "M5 10 L8 13 L15 6",
3968
- stroke: "currentColor",
3969
- strokeWidth: "2.5",
3970
- strokeLinecap: "round",
3971
- strokeLinejoin: "round"
3972
- }
3973
- ) }),
3974
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Rosto detectado" })
3975
- ] })
3976
- ]
3972
+ className: `video-circle ${faceDetected && faceDistance === "ok" ? "face-detected" : ""} ${stepProgress > 0 ? "capturing" : ""}`,
3973
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3974
+ "video",
3975
+ {
3976
+ ref: videoRef,
3977
+ className: `video-feed ${cameraReady ? "ready" : ""}`,
3978
+ autoPlay: true,
3979
+ muted: true,
3980
+ playsInline: true
3981
+ }
3982
+ )
3977
3983
  }
3978
3984
  ) }),
3979
3985
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "instruction-box", children: [
@@ -4458,11 +4464,14 @@ function BiometricRegistrationModal({
4458
4464
  }
4459
4465
 
4460
4466
  .face-badge {
4467
+ display: none; /* Removido por ser redundante com o oval verde */
4468
+ }
4469
+
4470
+ .status-badge {
4461
4471
  position: absolute;
4462
- top: 20px;
4472
+ top: 72px; /* Logo abaixo do progress-indicator */
4463
4473
  left: 50%;
4464
4474
  transform: translateX(-50%);
4465
- background: rgba(16, 185, 129, 0.95);
4466
4475
  backdrop-filter: blur(10px);
4467
4476
  border-radius: 20px;
4468
4477
  padding: 8px 16px;
@@ -4473,22 +4482,30 @@ function BiometricRegistrationModal({
4473
4482
  font-size: 13px;
4474
4483
  font-weight: 600;
4475
4484
  border: 1px solid rgba(255, 255, 255, 0.3);
4476
- box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
4485
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
4477
4486
  animation: slideDown 0.3s ease-out;
4487
+ z-index: 10;
4488
+ white-space: nowrap;
4478
4489
  }
4479
4490
 
4480
- .face-badge.warning {
4491
+ .status-badge.info {
4492
+ background: rgba(255, 255, 255, 0.15);
4493
+ color: white;
4494
+ border-color: rgba(255, 255, 255, 0.1);
4495
+ }
4496
+
4497
+ .status-badge.warning {
4481
4498
  background: rgba(251, 191, 36, 0.95);
4482
- border: 1px solid rgba(255, 255, 255, 0.3);
4483
- box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3);
4484
4499
  color: #78350f;
4500
+ border-color: rgba(0, 0, 0, 0.1);
4501
+ box-shadow: 0 4px 15px rgba(251, 191, 36, 0.2);
4485
4502
  }
4486
4503
 
4487
- .face-badge.error {
4504
+ .status-badge.error {
4488
4505
  background: rgba(239, 68, 68, 0.95);
4489
- border: 1px solid rgba(255, 255, 255, 0.3);
4490
- box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
4491
4506
  color: white;
4507
+ border-color: rgba(255, 255, 255, 0.2);
4508
+ box-shadow: 0 4px 15px rgba(239, 68, 68, 0.2);
4492
4509
  }
4493
4510
 
4494
4511
  .instruction-box {
@@ -6885,7 +6902,7 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
6885
6902
  initializeBiometricDetection,
6886
6903
  isAdvancedDetectionAvailable
6887
6904
  }, Symbol.toStringTag, { value: "Module" }));
6888
- const VERSION = "1.19.0";
6905
+ const VERSION = "1.23.0";
6889
6906
  const RELEASE_DATE = "2026-03-13";
6890
6907
  class OnboardingCaptureModal {
6891
6908
  constructor(options) {