@neofaceid/web-sdk 1.17.0 → 1.21.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.
|
@@ -3428,7 +3428,18 @@ const CALIBRATION = {
|
|
|
3428
3428
|
YAW_LEFT_THRESHOLD: 15,
|
|
3429
3429
|
YAW_RIGHT_THRESHOLD: -15,
|
|
3430
3430
|
CAPTURE_HOLD_TIME: 1500,
|
|
3431
|
-
DETECTION_INTERVAL:
|
|
3431
|
+
DETECTION_INTERVAL: 150,
|
|
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
|
|
3441
|
+
STEP_TIMEOUT: 2e4
|
|
3442
|
+
// 20 segundos para realizar cada movimento
|
|
3432
3443
|
};
|
|
3433
3444
|
function BiometricRegistrationModal({
|
|
3434
3445
|
personData,
|
|
@@ -3446,11 +3457,13 @@ function BiometricRegistrationModal({
|
|
|
3446
3457
|
const isCapturingRef = useRef(false);
|
|
3447
3458
|
const currentStepRef = useRef("center");
|
|
3448
3459
|
const watchdogTimerRef = useRef(null);
|
|
3460
|
+
const stepTimeoutTimerRef = useRef(null);
|
|
3449
3461
|
const detectionStartedRef = useRef(false);
|
|
3450
3462
|
const captureStateRef = useRef(null);
|
|
3451
3463
|
const [state, dispatch] = useReducer(modalReducer, initialState);
|
|
3452
3464
|
const [faceDetected, setFaceDetected] = useState(false);
|
|
3453
|
-
const [
|
|
3465
|
+
const [faceDistance, setFaceDistance] = useState("ok");
|
|
3466
|
+
const [cameraReady, setCameraReady] = useState(false);
|
|
3454
3467
|
useEffect(() => {
|
|
3455
3468
|
if (state.status === "liveness") {
|
|
3456
3469
|
const newStep = state.step;
|
|
@@ -3472,6 +3485,18 @@ function BiometricRegistrationModal({
|
|
|
3472
3485
|
currentStepIndex: state.currentStepIndex,
|
|
3473
3486
|
capturedPhotos: state.capturedPhotos
|
|
3474
3487
|
};
|
|
3488
|
+
if (stepTimeoutTimerRef.current) {
|
|
3489
|
+
clearTimeout(stepTimeoutTimerRef.current);
|
|
3490
|
+
}
|
|
3491
|
+
if (state.step !== "complete") {
|
|
3492
|
+
stepTimeoutTimerRef.current = window.setTimeout(() => {
|
|
3493
|
+
dispatch({
|
|
3494
|
+
type: "ERROR",
|
|
3495
|
+
message: "Tempo limite atingido para o movimento. Por favor, tente novamente em um ambiente bem iluminado.",
|
|
3496
|
+
retryable: true
|
|
3497
|
+
});
|
|
3498
|
+
}, CALIBRATION.STEP_TIMEOUT);
|
|
3499
|
+
}
|
|
3475
3500
|
}
|
|
3476
3501
|
}, [
|
|
3477
3502
|
state.status,
|
|
@@ -3512,6 +3537,18 @@ function BiometricRegistrationModal({
|
|
|
3512
3537
|
return null;
|
|
3513
3538
|
}
|
|
3514
3539
|
};
|
|
3540
|
+
const checkFacePosition = (box, videoWidth, videoHeight) => {
|
|
3541
|
+
if (box.width < CALIBRATION.MIN_FACE_WIDTH) return "too-far";
|
|
3542
|
+
if (box.width > CALIBRATION.MAX_FACE_WIDTH) return "too-close";
|
|
3543
|
+
const faceCenterX = box.x + box.width / 2;
|
|
3544
|
+
const faceCenterY = box.y + box.height / 2;
|
|
3545
|
+
const deviationX = Math.abs(faceCenterX - videoWidth / 2) / videoWidth;
|
|
3546
|
+
const deviationY = Math.abs(faceCenterY - videoHeight / 2) / videoHeight;
|
|
3547
|
+
if (deviationX > CALIBRATION.CENTER_X_THRESHOLD || deviationY > CALIBRATION.CENTER_Y_THRESHOLD) {
|
|
3548
|
+
return "not-centered";
|
|
3549
|
+
}
|
|
3550
|
+
return "ok";
|
|
3551
|
+
};
|
|
3515
3552
|
const checkPositionMatch = (pitch, yaw, step) => {
|
|
3516
3553
|
switch (step) {
|
|
3517
3554
|
case "center":
|
|
@@ -3605,6 +3642,10 @@ function BiometricRegistrationModal({
|
|
|
3605
3642
|
clearInterval(progressIntervalRef.current);
|
|
3606
3643
|
progressIntervalRef.current = null;
|
|
3607
3644
|
}
|
|
3645
|
+
if (stepTimeoutTimerRef.current) {
|
|
3646
|
+
clearTimeout(stepTimeoutTimerRef.current);
|
|
3647
|
+
stepTimeoutTimerRef.current = null;
|
|
3648
|
+
}
|
|
3608
3649
|
};
|
|
3609
3650
|
}, []);
|
|
3610
3651
|
useEffect(() => {
|
|
@@ -3652,7 +3693,11 @@ function BiometricRegistrationModal({
|
|
|
3652
3693
|
}
|
|
3653
3694
|
video.srcObject = stream;
|
|
3654
3695
|
streamRef.current = stream;
|
|
3655
|
-
|
|
3696
|
+
video.onloadedmetadata = () => {
|
|
3697
|
+
video.play().then(() => {
|
|
3698
|
+
if (mounted) setCameraReady(true);
|
|
3699
|
+
});
|
|
3700
|
+
};
|
|
3656
3701
|
} catch (error) {
|
|
3657
3702
|
if (!mounted) return;
|
|
3658
3703
|
if (watchdogTimerRef.current) {
|
|
@@ -3714,22 +3759,27 @@ function BiometricRegistrationModal({
|
|
|
3714
3759
|
const detection = await faceapi.detectSingleFace(
|
|
3715
3760
|
videoRef.current,
|
|
3716
3761
|
new faceapi.TinyFaceDetectorOptions({
|
|
3717
|
-
inputSize:
|
|
3718
|
-
|
|
3762
|
+
inputSize: 224,
|
|
3763
|
+
// Aumentado para detectar rostos menores/mais distantes
|
|
3764
|
+
scoreThreshold: 0.35
|
|
3765
|
+
// Ligeiramente mais sensível
|
|
3719
3766
|
})
|
|
3720
3767
|
).withFaceLandmarks();
|
|
3721
3768
|
if (!isRunning) return;
|
|
3722
3769
|
const hasFace = !!detection;
|
|
3723
3770
|
setFaceDetected(hasFace);
|
|
3724
3771
|
if (hasFace && detection.detection) {
|
|
3725
|
-
const
|
|
3726
|
-
const
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3772
|
+
const video2 = videoRef.current;
|
|
3773
|
+
const positionStatus = checkFacePosition(
|
|
3774
|
+
detection.detection.box,
|
|
3775
|
+
video2.videoWidth,
|
|
3776
|
+
video2.videoHeight
|
|
3777
|
+
);
|
|
3778
|
+
setFaceDistance(positionStatus);
|
|
3779
|
+
if (positionStatus !== "ok") {
|
|
3780
|
+
manageHoldAndCapture(false);
|
|
3781
|
+
requestAnimationFrame(detectFace2);
|
|
3782
|
+
return;
|
|
3733
3783
|
}
|
|
3734
3784
|
}
|
|
3735
3785
|
if (hasFace && detection.landmarks) {
|
|
@@ -3911,26 +3961,25 @@ function BiometricRegistrationModal({
|
|
|
3911
3961
|
const stepProgress = state.stepProgress;
|
|
3912
3962
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "liveness-container", children: [
|
|
3913
3963
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "progress-indicator", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "progress-text", children: progress }) }),
|
|
3914
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
3964
|
+
faceDetected && faceDistance !== "ok" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `status-badge ${faceDistance === "not-centered" ? "warning" : "error"}`, children: [
|
|
3965
|
+
faceDistance === "too-far" && "Aproxime-se mais",
|
|
3966
|
+
faceDistance === "too-close" && "Afaste-se um pouco",
|
|
3967
|
+
faceDistance === "not-centered" && "Centralize seu rosto"
|
|
3968
|
+
] }),
|
|
3969
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "video-wrapper", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3915
3970
|
"div",
|
|
3916
3971
|
{
|
|
3917
|
-
className: `video-circle ${faceDetected ? "face-detected" : ""} ${stepProgress > 0 ? "capturing" : ""}`,
|
|
3918
|
-
children:
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
strokeLinejoin: "round"
|
|
3929
|
-
}
|
|
3930
|
-
) }),
|
|
3931
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Rosto detectado" })
|
|
3932
|
-
] })
|
|
3933
|
-
]
|
|
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
|
+
)
|
|
3934
3983
|
}
|
|
3935
3984
|
) }),
|
|
3936
3985
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "instruction-box", children: [
|
|
@@ -4387,8 +4436,9 @@ function BiometricRegistrationModal({
|
|
|
4387
4436
|
border-radius: 50%;
|
|
4388
4437
|
overflow: hidden;
|
|
4389
4438
|
position: relative;
|
|
4390
|
-
|
|
4439
|
+
/* Removida transição 'all' para evitar salto de zoom no início */
|
|
4391
4440
|
box-shadow: 0 0 0 4px rgba(124, 58, 237, 0.3);
|
|
4441
|
+
background: #000;
|
|
4392
4442
|
}
|
|
4393
4443
|
|
|
4394
4444
|
.video-circle.face-detected {
|
|
@@ -4405,14 +4455,23 @@ function BiometricRegistrationModal({
|
|
|
4405
4455
|
height: 100%;
|
|
4406
4456
|
object-fit: cover;
|
|
4407
4457
|
transform: scaleX(-1);
|
|
4458
|
+
opacity: 0;
|
|
4459
|
+
transition: opacity 0.4s ease;
|
|
4460
|
+
}
|
|
4461
|
+
|
|
4462
|
+
.video-feed.ready {
|
|
4463
|
+
opacity: 1;
|
|
4408
4464
|
}
|
|
4409
4465
|
|
|
4410
4466
|
.face-badge {
|
|
4467
|
+
display: none; /* Removido por ser redundante com o oval verde */
|
|
4468
|
+
}
|
|
4469
|
+
|
|
4470
|
+
.status-badge {
|
|
4411
4471
|
position: absolute;
|
|
4412
|
-
top:
|
|
4472
|
+
top: 72px; /* Logo abaixo do progress-indicator */
|
|
4413
4473
|
left: 50%;
|
|
4414
4474
|
transform: translateX(-50%);
|
|
4415
|
-
background: rgba(16, 185, 129, 0.95);
|
|
4416
4475
|
backdrop-filter: blur(10px);
|
|
4417
4476
|
border-radius: 20px;
|
|
4418
4477
|
padding: 8px 16px;
|
|
@@ -4423,22 +4482,30 @@ function BiometricRegistrationModal({
|
|
|
4423
4482
|
font-size: 13px;
|
|
4424
4483
|
font-weight: 600;
|
|
4425
4484
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
4426
|
-
box-shadow: 0 4px 12px rgba(
|
|
4485
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
4427
4486
|
animation: slideDown 0.3s ease-out;
|
|
4487
|
+
z-index: 10;
|
|
4488
|
+
white-space: nowrap;
|
|
4489
|
+
}
|
|
4490
|
+
|
|
4491
|
+
.status-badge.info {
|
|
4492
|
+
background: rgba(255, 255, 255, 0.15);
|
|
4493
|
+
color: white;
|
|
4494
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
4428
4495
|
}
|
|
4429
4496
|
|
|
4430
|
-
.
|
|
4497
|
+
.status-badge.warning {
|
|
4431
4498
|
background: rgba(251, 191, 36, 0.95);
|
|
4432
|
-
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
4433
|
-
box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3);
|
|
4434
4499
|
color: #78350f;
|
|
4500
|
+
border-color: rgba(0, 0, 0, 0.1);
|
|
4501
|
+
box-shadow: 0 4px 15px rgba(251, 191, 36, 0.2);
|
|
4435
4502
|
}
|
|
4436
4503
|
|
|
4437
|
-
.
|
|
4504
|
+
.status-badge.error {
|
|
4438
4505
|
background: rgba(239, 68, 68, 0.95);
|
|
4439
|
-
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
4440
|
-
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
|
4441
4506
|
color: white;
|
|
4507
|
+
border-color: rgba(255, 255, 255, 0.2);
|
|
4508
|
+
box-shadow: 0 4px 15px rgba(239, 68, 68, 0.2);
|
|
4442
4509
|
}
|
|
4443
4510
|
|
|
4444
4511
|
.instruction-box {
|
|
@@ -6835,7 +6902,7 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
|
|
|
6835
6902
|
initializeBiometricDetection,
|
|
6836
6903
|
isAdvancedDetectionAvailable
|
|
6837
6904
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
6838
|
-
const VERSION = "1.
|
|
6905
|
+
const VERSION = "1.21.0";
|
|
6839
6906
|
const RELEASE_DATE = "2026-03-13";
|
|
6840
6907
|
class OnboardingCaptureModal {
|
|
6841
6908
|
constructor(options) {
|