face-validator-sdk 1.3.0 → 1.3.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.1] – 2026-02-23
9
+
10
+ ### Added
11
+
12
+ - **Container-first API**: New `container`, `ui`, `autoStart`, `mirror`, and `videoConstraints` options to let the SDK manage media elements and UI automatically.
13
+ - **Managed UI styling**: Default UI now injects its own minimal styles when `ui: 'default'` is used.
14
+ - **Lifecycle cleanup**: New `destroy()` method to release resources, stop camera streams, and clean up managed DOM elements.
15
+
16
+ ### Changed
17
+
18
+ - **Callbacks optional**: `onStatusUpdate`, `onCaptureSuccess`, and `onError` are now optional.
19
+ - **Camera initialization**: Internal camera setup now awaits readiness before starting detection loops.
20
+ - **ReactSelfieCapture**: Uses SDK-managed camera lifecycle and `destroy()` for cleanup.
21
+
8
22
  ## [1.2.1] – 2026-02-18
9
23
 
10
24
  ### Fixed
package/README.md CHANGED
@@ -41,33 +41,26 @@ npm install face-validator-sdk
41
41
  ```typescript
42
42
  import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
43
43
 
44
- const video = document.querySelector('video');
45
- const canvas = document.querySelector('canvas');
46
-
47
44
  const validator = new FaceValidator({
48
- videoElement: video,
49
- overlayCanvasElement: canvas,
50
- locale: 'pt-BR', // 'pt-BR' | 'en' | 'es'
45
+ container: '#selfieContainer',
46
+ ui: 'default',
47
+ locale: 'pt-BR',
51
48
  debugMode: false,
52
-
49
+ mirror: true,
53
50
  onStatusUpdate: (status, message) => {
54
51
  console.log(status, message);
55
- // Update UI with validation status
56
52
  },
57
-
58
53
  onCaptureSuccess: (blob) => {
59
- // Upload or preview the captured selfie
60
54
  const url = URL.createObjectURL(blob);
61
- document.querySelector('img').src = url;
55
+ document.querySelector('img')!.src = url;
62
56
  },
63
-
64
57
  onError: (errorType, error) => {
65
58
  console.error(errorType, error);
66
59
  }
67
60
  });
68
61
 
69
- // Validator starts automatically
70
- // To stop: validator.stop();
62
+ // The validator starts automatically.
63
+ // To stop and release resources: validator.destroy();
71
64
  ```
72
65
 
73
66
  ## 📊 Validation Status
@@ -93,34 +86,124 @@ const validator = new FaceValidator({
93
86
 
94
87
  ```typescript
95
88
  interface FaceValidatorOptions {
96
- // Required
97
- videoElement: HTMLVideoElement;
98
- onStatusUpdate: (status: ValidationStatus, message: string) => void;
99
- onCaptureSuccess: (imageBlob: Blob) => void;
100
- onError: (errorType: ValidationStatus, error: Error) => void;
101
-
102
- // Optional
103
- overlayCanvasElement?: HTMLCanvasElement;
104
- locale?: 'pt-BR' | 'en' | 'es'; // Default: 'en'
105
- debugMode?: boolean; // Default: false
106
-
107
- // Validation thresholds
108
- minDetectionConfidence?: number; // Default: 0.5
109
- minIlluminationThreshold?: number; // Default: 70 (0-255)
110
- minFaceSizeFactor?: number; // Default: 0.25
111
- maxFaceSizeFactor?: number; // Default: 0.65
112
- stabilizationTimeThreshold?: number; // Default: 1000ms
113
- stabilityMovementThreshold?: number; // Default: 5px
114
- minFaceVisibilityScore?: number; // Default: 0.5
115
- maxHeadTiltDegrees?: number; // Default: 28°
116
- maxHandFaceDistance?: number; // Default: 0.15 (normalized)
117
-
118
- // Advanced
119
- modelPath?: string; // MediaPipe WASM path (auto-detected from CDN)
89
+ // UI structure
90
+ container?: HTMLElement | string; // Element or selector to auto-render video/canvas and status
91
+ ui?: 'default' | 'none'; // Default: 'default'
92
+ autoStart?: boolean; // Default: true
93
+ mirror?: boolean; // Default: true
94
+
95
+ // Camera
96
+ videoElement?: HTMLVideoElement; // Use if you want to control the video manually
97
+ overlayCanvasElement?: HTMLCanvasElement | null;
98
+ videoConstraints?: MediaTrackConstraints;
99
+ videoWidth?: number;
100
+ videoHeight?: number;
101
+
102
+ // Idioma e debug
103
+ locale?: 'pt-BR' | 'en' | 'es';
104
+ debugMode?: boolean;
120
105
  customMessages?: Partial<Record<ValidationStatus, string>>;
106
+
107
+ // Callbacks
108
+ onStatusUpdate?: (status: ValidationStatus, message: string) => void;
109
+ onCaptureSuccess?: (imageBlob: Blob) => void;
110
+ onError?: (errorType: ValidationStatus, error: Error) => void;
111
+
112
+ // Thresholds de validacao
113
+ minDetectionConfidence?: number;
114
+ minIlluminationThreshold?: number;
115
+ minFaceSizeFactor?: number;
116
+ maxFaceSizeFactor?: number;
117
+ stabilizationTimeThreshold?: number;
118
+ stabilityMovementThreshold?: number;
119
+ minFaceVisibilityScore?: number;
120
+ maxHeadTiltDegrees?: number;
121
+ maxHandFaceDistance?: number;
122
+
123
+ // Advanced
124
+ modelPath?: string; // Path to MediaPipe WASM (auto-detected via CDN)
121
125
  }
122
126
  ```
123
127
 
128
+ ## ✅ Usage with React (ReactSelfieCapture)
129
+
130
+ ```tsx
131
+ import { ReactSelfieCapture } from 'face-validator-sdk';
132
+
133
+ export function SelfieModal() {
134
+ const handleCapture = (imageBase64: string | null) => {
135
+ if (!imageBase64) return;
136
+ // Send the selfie to your API
137
+ };
138
+
139
+ return (
140
+ <ReactSelfieCapture
141
+ locale="pt-BR"
142
+ onCapture={handleCapture}
143
+ onDismiss={() => console.log('Modal closed')}
144
+ debugMode={false}
145
+ labels={{
146
+ previewQuestion: 'Check your selfie before saving',
147
+ savePhoto: 'Confirm selfie'
148
+ }}
149
+ />
150
+ );
151
+ }
152
+ ```
153
+
154
+ - `locale`: defines the language for messages and labels displayed.
155
+ - `onCapture`: main callback; receives the base64 image or `null` if the user cancels.
156
+ - `onDismiss`: used to close the modal/dialog when the user clicks cancel.
157
+ - `debugMode`: enables landmark visualization for debugging.
158
+ - `labels`: overrides default texts (e.g., preview title and button label).
159
+
160
+ ## ✅ Usage with Angular (Core API)
161
+
162
+ ```ts
163
+ import { AfterViewInit, Component, OnDestroy } from '@angular/core';
164
+ import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
165
+
166
+ @Component({
167
+ selector: 'app-selfie-dialog',
168
+ template: '<div id="selfieContainer"></div>'
169
+ })
170
+ export class SelfieDialogComponent implements AfterViewInit, OnDestroy {
171
+ private validator: FaceValidator | null = null;
172
+
173
+ ngAfterViewInit(): void {
174
+ this.validator = new FaceValidator({
175
+ container: '#selfieContainer',
176
+ ui: 'none',
177
+ locale: 'pt-BR',
178
+ debugMode: false,
179
+ mirror: true,
180
+ onStatusUpdate: (status: ValidationStatus, message: string) => {
181
+ console.log(status, message);
182
+ },
183
+ onCaptureSuccess: (blob: Blob) => {
184
+ console.log('Selfie capturada', blob);
185
+ },
186
+ onError: (errorType: ValidationStatus, error: Error) => {
187
+ console.error(errorType, error);
188
+ }
189
+ });
190
+ }
191
+
192
+ ngOnDestroy(): void {
193
+ this.validator?.destroy();
194
+ }
195
+ }
196
+ ```
197
+
198
+ - `container`: target element where the SDK automatically renders video and canvas.
199
+ - `ui`: use `none` to render your own UI (status, buttons, etc.).
200
+ - `locale`: defines the language for SDK messages.
201
+ - `debugMode`: displays landmarks for debugging during development.
202
+ - `mirror`: mirrors the camera (selfie default).
203
+ - `onStatusUpdate`: receives the status and message for you to update the UI.
204
+ - `onCaptureSuccess`: receives the `Blob` of the captured selfie for upload/preview.
205
+ - `onError`: captures camera or model loading errors.
206
+
124
207
  ---
125
208
 
126
209
  ## 🧩 React Component: `ReactSelfieCapture`
@@ -1,2 +1,2 @@
1
- (()=>{"use strict";var e={d:(t,n)=>{for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{FaceValidator:()=>T,ValidationStatus:()=>a,default:()=>S,getLoadingModelsMessage:()=>l,getMessage:()=>r,getValidationMessages:()=>s});const n=require("@mediapipe/tasks-vision");var a;!function(e){e.INITIALIZING="INITIALIZING",e.NO_FACE_DETECTED="NO_FACE_DETECTED",e.FACE_DETECTED="FACE_DETECTED",e.TOO_CLOSE="TOO_CLOSE",e.TOO_FAR="TOO_FAR",e.OFF_CENTER="OFF_CENTER",e.FACE_OBSTRUCTED="FACE_OBSTRUCTED",e.HEAD_NOT_STRAIGHT="HEAD_NOT_STRAIGHT",e.MULTIPLE_FACES="MULTIPLE_FACES",e.POOR_ILLUMINATION="POOR_ILLUMINATION",e.NOT_NEUTRAL_EXPRESSION="NOT_NEUTRAL_EXPRESSION",e.DARK_GLASSES="DARK_GLASSES",e.STAY_STILL="STAY_STILL",e.CAPTURING="CAPTURING",e.SUCCESS="SUCCESS",e.ERROR="ERROR"}(a||(a={}));const i={"pt-BR":{[a.INITIALIZING]:"Inicializando câmera e detector...",[a.NO_FACE_DETECTED]:"Posicione seu rosto no centro do oval.",[a.FACE_DETECTED]:"Analisando...",[a.TOO_CLOSE]:"Afaste-se um pouco",[a.TOO_FAR]:"Aproxime-se da câmera",[a.OFF_CENTER]:"Centralize o rosto no centro do oval",[a.FACE_OBSTRUCTED]:"Mantenha o rosto totalmente visível. Remova as mãos do rosto.",[a.HEAD_NOT_STRAIGHT]:"Olhe diretamente para a câmera e mantenha a cabeça reta.",[a.MULTIPLE_FACES]:"Mantenha apenas uma pessoa no quadro.",[a.POOR_ILLUMINATION]:"Procure um ambiente com boa iluminação.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenha expressão neutra: boca fechada, sem sorrir e olhos abertos.",[a.DARK_GLASSES]:"Remova os óculos escuros. Óculos de grau são permitidos.",[a.STAY_STILL]:"Fique imóvel para capturar a foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"Captura realizada!",[a.ERROR]:"Ocorreu um erro."},en:{[a.INITIALIZING]:"Initializing camera and detector...",[a.NO_FACE_DETECTED]:"Position your face in the center of the oval.",[a.FACE_DETECTED]:"Analyzing...",[a.TOO_CLOSE]:"Move back a little",[a.TOO_FAR]:"Move closer to the camera",[a.OFF_CENTER]:"Center your face in the center of the oval",[a.FACE_OBSTRUCTED]:"Keep your face fully visible. Remove your hands from your face.",[a.HEAD_NOT_STRAIGHT]:"Look directly at the camera and keep your head straight.",[a.MULTIPLE_FACES]:"Keep only one person in the frame.",[a.POOR_ILLUMINATION]:"Find a well-lit environment and center your face in the oval.",[a.NOT_NEUTRAL_EXPRESSION]:"Keep a neutral expression: mouth closed, no smiling, and eyes open.",[a.DARK_GLASSES]:"Remove sunglasses. Prescription glasses are allowed.",[a.STAY_STILL]:"Stay still to capture the photo",[a.CAPTURING]:"Capturing...",[a.SUCCESS]:"Capture complete!",[a.ERROR]:"An error occurred."},es:{[a.INITIALIZING]:"Inicializando cámara y detector...",[a.NO_FACE_DETECTED]:"Coloque su rostro en el centro del óvalo.",[a.FACE_DETECTED]:"Analizando...",[a.TOO_CLOSE]:"Aléjese un poco",[a.TOO_FAR]:"Acérquese a la cámara",[a.OFF_CENTER]:"Centre el rostro en el centro del óvalo",[a.FACE_OBSTRUCTED]:"Mantenga el rostro totalmente visible. Quite las manos del rostro.",[a.HEAD_NOT_STRAIGHT]:"Mire directamente a la cámara y mantenga la cabeza recta.",[a.MULTIPLE_FACES]:"Mantenga solo una persona en el encuadre.",[a.POOR_ILLUMINATION]:"Busque un ambiente con buena iluminación y centre su rostro en el óvalo.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenga expresión neutra: boca cerrada, sin sonreír y ojos abiertos.",[a.DARK_GLASSES]:"Quite las gafas de sol. Las gafas graduadas están permitidas.",[a.STAY_STILL]:"Permanezca quieto para capturar la foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"¡Captura realizada!",[a.ERROR]:"Ocurrió un error."}},o={"pt-BR":"Status desconhecido.",en:"Unknown status.",es:"Estado desconhecido."};function s(e){return Object.assign({},i[e])}function r(e,t){var n;return null!==(n=i[t][e])&&void 0!==n?n:o[t]}function l(e){return{"pt-BR":"Carregando...",en:"Loading...",es:"Cargando..."}[e]}function c(e){const t=e.data;let n=0;for(let e=0;e<t.length;e+=4)n+=.2126*t[e]+.7152*t[e+1]+.0722*t[e+2];return n/(t.length/4)}const h=[33,133,159,145],d=[263,362,386,374],u=[61,291,0,17,39,269,270,409],m=.34;var E=function(e,t,n,a){return new(n||(n=Promise))(function(i,o){function s(e){try{l(a.next(e))}catch(e){o(e)}}function r(e){try{l(a.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(s,r)}l((a=a.apply(e,t||[])).next())})};const g={overlayCanvasElement:void 0,videoWidth:512,videoHeight:384,minDetectionConfidence:.4,minIlluminationThreshold:50,minFaceSizeFactor:.15,maxFaceSizeFactor:.75,stabilizationTimeThreshold:1e3,stabilityMovementThreshold:5,minFaceVisibilityScore:.4,maxHeadTiltDegrees:30,maxHandFaceDistance:.15,debugMode:!1,locale:"en",customMessages:{}};class T{constructor(e){this.faceLandmarker=null,this.handLandmarker=null,this.animationFrameId=null,this.lastDetection=null,this.stableSince=null,this.isCapturing=!1,this.options=this.resolveOptions(e),this.setStatus(a.INITIALIZING),this.init()}resolveOptions(e){const t=e.modelPath||"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm";return Object.assign(Object.assign(Object.assign({},g),e),{modelPath:t,locale:e.locale||"en",customMessages:e.customMessages||{}})}init(){return E(this,void 0,void 0,function*(){try{const e=l(this.options.locale);this.setStatus(a.INITIALIZING,void 0,e);const t=yield n.FilesetResolver.forVisionTasks(this.options.modelPath);this.faceLandmarker=yield n.FaceLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numFaces:2,minFaceDetectionConfidence:this.options.minDetectionConfidence,minFacePresenceConfidence:this.options.minFaceVisibilityScore,minTrackingConfidence:this.options.minFaceVisibilityScore}),this.handLandmarker=yield n.HandLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5}),this.startDetectionLoop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}})}getMessageForStatus(e,t){return t||(this.options.customMessages[e]?this.options.customMessages[e]:r(e,this.options.locale))}setStatus(e,t,n){const i=this.getMessageForStatus(e,n);this.options.onStatusUpdate(e,i),e===a.ERROR&&t&&this.options.onError(e,t)}startDetectionLoop(){const e=this.options.videoElement,t=this.options.videoWidth||640,n=this.options.videoHeight||480,i=()=>E(this,void 0,void 0,function*(){var o;if(this.faceLandmarker&&this.handLandmarker&&e.videoWidth){try{const i=performance.now();let s=a.NO_FACE_DETECTED,r=null,l=[];const E=this.faceLandmarker.detectForVideo(e,i),g=this.handLandmarker.detectForVideo(e,i);if(g.landmarks&&g.landmarks.length>0&&(l=g.landmarks.map((e,t)=>{var n,a,i;return{landmarks:e,handedness:(null===(i=null===(a=null===(n=g.handednesses)||void 0===n?void 0:n[t])||void 0===a?void 0:a[0])||void 0===i?void 0:i.categoryName)||"Unknown"}})),E.faceLandmarks&&E.faceLandmarks.length>1){s=a.MULTIPLE_FACES,this.stableSince=null;const e=E.faceLandmarks[0],t=(null===(o=E.faceBlendshapes)||void 0===o?void 0:o[0])?this.estimateBoundingBox(e):null;t&&(r={boundingBox:t,landmarks:e,timestamp:i})}else if(E.faceLandmarks&&1===E.faceLandmarks.length){const o=E.faceLandmarks[0],g=this.estimateBoundingBox(o);r={boundingBox:g,landmarks:o,timestamp:i};const T=function(e,t=.18,n=.7){const a=e.width;return a<t?"TOO_FAR":a>n?"TOO_CLOSE":"OK"}(g,this.options.minFaceSizeFactor,this.options.maxFaceSizeFactor);if("OK"!==T)s="TOO_CLOSE"===T?a.TOO_CLOSE:a.TOO_FAR,this.stableSince=null;else{const E=o[4],T=function(e,t,n,a){const i=(e*n-n/2)/(.2*n),o=(t*a-a/2)/(a*m);return i*i+o*o<=.6}(E.x,E.y,t,n),S=function(e,t,n){const a=t/2,i=n/2,o=.2*t,s=n*m,r=e.xMin*t,l=(e.xMin+e.width)*t,c=e.yMin*n,h=(e.yMin+e.height)*n,d=((r+l)/2-a)/o,u=((c+h)/2-i)/s;if(d*d+u*u>1)return!1;const E=[{x:r,y:c},{x:l,y:c},{x:r,y:h},{x:l,y:h}];let g=0;for(const e of E){const t=(e.x-a)/o,n=(e.y-i)/s;t*t+n*n>1&&g++}return 0===g}(g,t,n);if(T&&S)if(function(e,t){if(e.length<478)return!1;const n=e[4],a=u.map(t=>e[t]),i=a.reduce((e,t)=>e+t.y,0)/a.length,o=Math.min(...a.map(e=>e.y)),s=Math.max(...a.map(e=>e.y))-o,r=t.height;return!(i<n.y-.01||i-n.y<.06*r||s<.02*r)}(o,g))if(function(e,t=25){if(e.length<478)return!1;const n=e[h[0]],a=e[d[0]],i=e[4],o=e[13],s=e[14],r=e[152],l=e[10],c=Math.abs(n.y-a.y),u=Math.abs(n.x-a.x);if(u<.01)return!1;const m=c/u;if(Math.atan(m)*(180/Math.PI)>t)return!1;const E=(n.x+a.x)/2,g=i.x-E,T=Math.abs(n.x-a.x);if(T<.01)return!1;const S=Math.abs(g)/T;if(Math.atan(S)*(180/Math.PI)>t)return!1;if(!function(e){if(e.length<478)return!1;const t=e[234],n=e[454],a=e[4],i=Math.abs(t.x-a.x),o=Math.abs(n.x-a.x);return!((i>.01&&o>.01?Math.max(i,o)/Math.min(i,o):1)>1.4||void 0!==t.z&&void 0!==n.z&&Math.abs(t.z-n.z)>.05)}(e))return!1;const f=(n.y+a.y)/2,O=(o.y+s.y)/2,p=r.y-l.y;if(p<.1)return!1;if(l.y>f+.02)return!1;if(f>i.y+.02)return!1;if(i.y>O+.02)return!1;if(O>=r.y)return!1;const I=(f-l.y)/p,y=(i.y-f)/p,C=(O-i.y)/p,A=(r.y-O)/p;return!(I<.06||I>.38||y<.03||y>.3||C<.02||C>.25||A<.04||A>.38)}(o,this.options.maxHeadTiltDegrees))if(l.length>0&&function(e,t,n=.15){const a=t.xMin+t.width/2,i=t.yMin+t.height/2;for(const t of e.landmarks){const e=t.x-a,o=t.y-i;if(Math.sqrt(e*e+o*o)<n)return!0}return!1}(l[0],g,this.options.maxHandFaceDistance))s=a.FACE_OBSTRUCTED,this.stableSince=null;else if(function(e){if(e.length<478)return!1;const t=e[159],n=e[144],a=e[386],i=e[373],o=Math.abs(t.y-n.y),s=Math.abs(a.y-i.y);if(o<.01||s<.01)return!1;const r=e[13],l=e[14];if(Math.abs(r.y-l.y)>.025)return!1;const c=e[61],h=e[291],d=e[4];return!((c.y+h.y)/2-d.y<.05)}(o))if(function(e,t){if(t.length<478)return!1;try{const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)return!1;const i=e.videoWidth,o=e.videoHeight,s=[t[33],t[133],t[159],t[144],t[145]],r=[t[263],t[362],t[386],t[373],t[374]],l=e=>{const t=e.map(e=>e.x*i),n=e.map(e=>e.y*o),a=Math.max(0,Math.min(...t)-5),s=Math.min(i,Math.max(...t)+5),r=Math.max(0,Math.min(...n)-5);return{x:a,y:r,width:s-a,height:Math.min(o,Math.max(...n)+5)-r}},h=t=>(n.width=t.width,n.height=t.height,a.drawImage(e,t.x,t.y,t.width,t.height,0,0,t.width,t.height),c(a.getImageData(0,0,t.width,t.height))),d=l(s),u=l(r);return(h(d)+h(u))/2<35}catch(e){return console.warn("Erro ao detectar óculos escuros:",e),!1}}(e,o))s=a.DARK_GLASSES,this.stableSince=null;else{const o=document.createElement("canvas"),l=g.xMin*e.videoWidth,h=g.yMin*e.videoHeight,d=g.width*e.videoWidth,u=g.height*e.videoHeight;o.width=d,o.height=u;const m=o.getContext("2d",{willReadFrequently:!0});if(m){m.drawImage(e,l,h,d,u,0,0,d,u);c(m.getImageData(0,0,o.width,o.height))<this.options.minIlluminationThreshold?(s=a.POOR_ILLUMINATION,this.stableSince=null):function(e,t,n=5,a=512,i=384){if(!e||!t)return!1;const o=(e.boundingBox.xMin+e.boundingBox.width/2)*a,s=(e.boundingBox.yMin+e.boundingBox.height/2)*i,r=(t.boundingBox.xMin+t.boundingBox.width/2)*a,l=(t.boundingBox.yMin+t.boundingBox.height/2)*i,c=Math.abs(o-r),h=Math.abs(s-l),d=Math.abs(e.boundingBox.width-t.boundingBox.width)*a,u=Math.abs(e.boundingBox.height-t.boundingBox.height)*i;return c<=n&&h<=n&&d<=2*n&&u<=2*n}(r,this.lastDetection,this.options.stabilityMovementThreshold,t,n)?(this.stableSince||(this.stableSince=i),s=i-this.stableSince>=this.options.stabilizationTimeThreshold?a.CAPTURING:a.STAY_STILL):(this.stableSince=null,s=a.STAY_STILL)}else s=a.FACE_DETECTED,this.stableSince=null}else s=a.NOT_NEUTRAL_EXPRESSION,this.stableSince=null;else s=a.HEAD_NOT_STRAIGHT,this.stableSince=null;else s=a.FACE_OBSTRUCTED,this.stableSince=null;else s=a.OFF_CENTER,this.stableSince=null}}else this.lastDetection=null,this.stableSince=null;if(this.lastDetection=r,this.setStatus(s),this.options.overlayCanvasElement&&function(e,t,n,i,o){const s=e.getContext("2d");if(!s)return;const r=e.width,l=e.height,c=r/2,u=l/2;s.clearRect(0,0,r,l);const E=.2*r,g=l*m;if(s.fillStyle="rgba(255, 255, 255, 0.35)",s.fillRect(0,0,r,l),s.save(),s.beginPath(),s.ellipse(c,u,E,g,0,0,2*Math.PI),s.closePath(),s.globalCompositeOperation="destination-out",s.fill(),s.restore(),s.strokeStyle="rgba(255, 255, 255, 0.9)",s.lineWidth=3,s.beginPath(),s.ellipse(c,u,E,g,0,0,2*Math.PI),s.stroke(),s.strokeStyle="rgba(255, 255, 255, 0.45)",s.lineWidth=1,s.beginPath(),s.moveTo(c-6,u),s.lineTo(c+6,u),s.moveTo(c,u-6),s.lineTo(c,u+6),s.stroke(),t&&i){const e=i.landmarks;if(e.length>=478){const t=e[10],i=e[152],o=e[234],c=e[454],u=e.map(e=>e.x),m=e.map(e=>e.y),E=Math.min(...u),g=Math.max(...u),T=Math.min(...m),S=g-E,f=Math.max(...m)-T,O=.08,p=(E-S*O)*r,I=(T-f*O)*l,y=S*(1+2*O)*r,C=f*(1+2*O)*l;let A="red";n===a.STAY_STILL||n===a.CAPTURING?A="lime":n===a.FACE_DETECTED&&(A="yellow"),s.strokeStyle=A,s.lineWidth=3,s.strokeRect(p,I,y,C);const _=e[4];e[h[0]],e[d[0]],s.fillStyle="cyan",s.beginPath(),s.arc(_.x*r,_.y*l,5,0,2*Math.PI),s.fill(),s.fillStyle="magenta",s.beginPath(),s.arc(t.x*r,t.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="lime",s.beginPath(),s.arc(i.x*r,i.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="yellow",[e[33],e[133],e[159],e[144],e[145]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="yellow",[e[263],e[362],e[386],e[373],e[374]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="purple",s.beginPath(),s.arc(o.x*r,o.y*l,3,0,2*Math.PI),s.fill(),s.beginPath(),s.arc(c.x*r,c.y*l,3,0,2*Math.PI),s.fill()}}t&&o&&o.length>0&&o.forEach(e=>{s.fillStyle="orange",e.landmarks.forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()})})}(this.options.overlayCanvasElement,this.options.debugMode||!1,s,r||void 0,l.length>0?l:void 0),s===a.CAPTURING&&!this.isCapturing)return this.isCapturing=!0,yield this.captureImage(),this.setStatus(a.SUCCESS),void this.stop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}this.animationFrameId=requestAnimationFrame(i)}else this.animationFrameId=requestAnimationFrame(i)});this.animationFrameId=requestAnimationFrame(i)}estimateBoundingBox(e){const t=e.map(e=>e.x),n=e.map(e=>e.y),a=Math.min(...t),i=Math.max(...t),o=Math.min(...n);return{xMin:a,yMin:o,width:i-a,height:Math.max(...n)-o}}captureImage(){return E(this,void 0,void 0,function*(){const e=this.options.videoElement,t=document.createElement("canvas");t.width=e.videoWidth,t.height=e.videoHeight;const n=t.getContext("2d");n?(n.drawImage(e,0,0,t.width,t.height),t.toBlob(e=>{e?this.options.onCaptureSuccess(e):this.setStatus(a.ERROR,new Error("Failed to generate image blob"))},"image/jpeg",.95)):this.setStatus(a.ERROR,new Error("Failed to get canvas context"))})}stop(){null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.faceLandmarker&&this.faceLandmarker.close(),this.handLandmarker&&this.handLandmarker.close()}}const S=T;module.exports=t})();
1
+ (()=>{"use strict";var e={d:(t,n)=>{for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{FaceValidator:()=>g,ValidationStatus:()=>a,default:()=>p,getLoadingModelsMessage:()=>l,getMessage:()=>r,getValidationMessages:()=>s});const n=require("@mediapipe/tasks-vision");var a;!function(e){e.INITIALIZING="INITIALIZING",e.NO_FACE_DETECTED="NO_FACE_DETECTED",e.FACE_DETECTED="FACE_DETECTED",e.TOO_CLOSE="TOO_CLOSE",e.TOO_FAR="TOO_FAR",e.OFF_CENTER="OFF_CENTER",e.FACE_OBSTRUCTED="FACE_OBSTRUCTED",e.HEAD_NOT_STRAIGHT="HEAD_NOT_STRAIGHT",e.MULTIPLE_FACES="MULTIPLE_FACES",e.POOR_ILLUMINATION="POOR_ILLUMINATION",e.NOT_NEUTRAL_EXPRESSION="NOT_NEUTRAL_EXPRESSION",e.DARK_GLASSES="DARK_GLASSES",e.STAY_STILL="STAY_STILL",e.CAPTURING="CAPTURING",e.SUCCESS="SUCCESS",e.ERROR="ERROR"}(a||(a={}));const i={"pt-BR":{[a.INITIALIZING]:"Inicializando câmera e detector...",[a.NO_FACE_DETECTED]:"Posicione seu rosto no centro do oval.",[a.FACE_DETECTED]:"Analisando...",[a.TOO_CLOSE]:"Afaste-se um pouco",[a.TOO_FAR]:"Aproxime-se da câmera",[a.OFF_CENTER]:"Centralize o rosto no centro do oval",[a.FACE_OBSTRUCTED]:"Mantenha o rosto totalmente visível. Remova as mãos do rosto.",[a.HEAD_NOT_STRAIGHT]:"Olhe diretamente para a câmera e mantenha a cabeça reta.",[a.MULTIPLE_FACES]:"Mantenha apenas uma pessoa no quadro.",[a.POOR_ILLUMINATION]:"Procure um ambiente com boa iluminação.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenha expressão neutra: boca fechada, sem sorrir e olhos abertos.",[a.DARK_GLASSES]:"Remova os óculos escuros. Óculos de grau são permitidos.",[a.STAY_STILL]:"Fique imóvel para capturar a foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"Captura realizada!",[a.ERROR]:"Ocorreu um erro."},en:{[a.INITIALIZING]:"Initializing camera and detector...",[a.NO_FACE_DETECTED]:"Position your face in the center of the oval.",[a.FACE_DETECTED]:"Analyzing...",[a.TOO_CLOSE]:"Move back a little",[a.TOO_FAR]:"Move closer to the camera",[a.OFF_CENTER]:"Center your face in the center of the oval",[a.FACE_OBSTRUCTED]:"Keep your face fully visible. Remove your hands from your face.",[a.HEAD_NOT_STRAIGHT]:"Look directly at the camera and keep your head straight.",[a.MULTIPLE_FACES]:"Keep only one person in the frame.",[a.POOR_ILLUMINATION]:"Find a well-lit environment and center your face in the oval.",[a.NOT_NEUTRAL_EXPRESSION]:"Keep a neutral expression: mouth closed, no smiling, and eyes open.",[a.DARK_GLASSES]:"Remove sunglasses. Prescription glasses are allowed.",[a.STAY_STILL]:"Stay still to capture the photo",[a.CAPTURING]:"Capturing...",[a.SUCCESS]:"Capture complete!",[a.ERROR]:"An error occurred."},es:{[a.INITIALIZING]:"Inicializando cámara y detector...",[a.NO_FACE_DETECTED]:"Coloque su rostro en el centro del óvalo.",[a.FACE_DETECTED]:"Analizando...",[a.TOO_CLOSE]:"Aléjese un poco",[a.TOO_FAR]:"Acérquese a la cámara",[a.OFF_CENTER]:"Centre el rostro en el centro del óvalo",[a.FACE_OBSTRUCTED]:"Mantenga el rostro totalmente visible. Quite las manos del rostro.",[a.HEAD_NOT_STRAIGHT]:"Mire directamente a la cámara y mantenga la cabeza recta.",[a.MULTIPLE_FACES]:"Mantenga solo una persona en el encuadre.",[a.POOR_ILLUMINATION]:"Busque un ambiente con buena iluminación y centre su rostro en el óvalo.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenga expresión neutra: boca cerrada, sin sonreír y ojos abiertos.",[a.DARK_GLASSES]:"Quite las gafas de sol. Las gafas graduadas están permitidas.",[a.STAY_STILL]:"Permanezca quieto para capturar la foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"¡Captura realizada!",[a.ERROR]:"Ocurrió un error."}},o={"pt-BR":"Status desconhecido.",en:"Unknown status.",es:"Estado desconhecido."};function s(e){return Object.assign({},i[e])}function r(e,t){var n;return null!==(n=i[t][e])&&void 0!==n?n:o[t]}function l(e){return{"pt-BR":"Carregando...",en:"Loading...",es:"Cargando..."}[e]}function d(e){const t=e.data;let n=0;for(let e=0;e<t.length;e+=4)n+=.2126*t[e]+.7152*t[e+1]+.0722*t[e+2];return n/(t.length/4)}const c=[33,133,159,145],h=[263,362,386,374],m=[61,291,0,17,39,269,270,409],u=.34;var E=function(e,t,n,a){return new(n||(n=Promise))(function(i,o){function s(e){try{l(a.next(e))}catch(e){o(e)}}function r(e){try{l(a.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(s,r)}l((a=a.apply(e,t||[])).next())})};const f={container:void 0,ui:"default",autoStart:!0,mirror:!0,videoConstraints:void 0,overlayCanvasElement:void 0,videoWidth:512,videoHeight:384,minDetectionConfidence:.4,minIlluminationThreshold:50,minFaceSizeFactor:.15,maxFaceSizeFactor:.75,stabilizationTimeThreshold:1e3,stabilityMovementThreshold:5,minFaceVisibilityScore:.4,maxHeadTiltDegrees:30,maxHandFaceDistance:.15,debugMode:!1,locale:"en",customMessages:{},onStatusUpdate:void 0,onCaptureSuccess:void 0,onError:void 0};class g{constructor(e){this.faceLandmarker=null,this.handLandmarker=null,this.animationFrameId=null,this.lastDetection=null,this.stableSince=null,this.isCapturing=!1,this.containerElement=null,this.statusElement=null,this.uiRootElement=null,this.cameraStream=null,this.cameraReadyPromise=null,this.managedElements=!1,this.managedCamera=!1,this.injectedStyleElement=null,this.options=this.resolveOptions(e),this.setupElements(),this.setStatus(a.INITIALIZING),this.cameraReadyPromise=this.options.autoStart?this.initCamera():Promise.resolve(),this.init()}resolveOptions(e){const t=e.modelPath||"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm";return Object.assign(Object.assign(Object.assign({},f),e),{modelPath:t,locale:e.locale||"en",customMessages:e.customMessages||{},onStatusUpdate:e.onStatusUpdate||(()=>{}),onCaptureSuccess:e.onCaptureSuccess||(()=>{}),onError:e.onError||(()=>{}),videoConstraints:e.videoConstraints||{width:{ideal:e.videoWidth||f.videoWidth},height:{ideal:e.videoHeight||f.videoHeight},facingMode:"user"}})}setupElements(){const e=this.resolveContainer(this.options.container);if(this.containerElement=e,"default"===this.options.ui&&e&&this.ensureDefaultUI(e),this.options.videoElement){if(!this.options.overlayCanvasElement&&e){const t=document.createElement("canvas");t.className="fv-sdk-canvas",this.attachMediaElements(e,this.options.videoElement,t),this.options.overlayCanvasElement=t,this.managedElements=!0}}else{if(!e)throw new Error("FaceValidator requires either videoElement or container.");const t=document.createElement("video");t.autoplay=!0,t.playsInline=!0,t.muted=!0,t.className="fv-sdk-video";const n=document.createElement("canvas");n.className="fv-sdk-canvas",this.attachMediaElements(e,t,n),this.options.videoElement=t,this.options.overlayCanvasElement=n,this.managedElements=!0}this.options.mirror&&this.applyMirrorStyles()}resolveContainer(e){return e?"string"==typeof e?document.querySelector(e):e:null}ensureDefaultUI(e){e.innerHTML="",e.classList.add("fv-sdk-root");const t=document.createElement("div");t.className="fv-sdk-media",e.appendChild(t);const n=document.createElement("div");n.className="fv-sdk-status",e.appendChild(n),this.statusElement=n,this.uiRootElement=e,this.injectDefaultStyles()}attachMediaElements(e,t,n){const a=e.querySelector(".fv-sdk-media");if(a)return a.appendChild(t),void a.appendChild(n);const i=document.createElement("div");i.className="fv-sdk-media",i.appendChild(t),i.appendChild(n),e.appendChild(i)}injectDefaultStyles(){if(this.injectedStyleElement||document.querySelector('style[data-fv-sdk="true"]'))return;const e=document.createElement("style");e.setAttribute("data-fv-sdk","true"),e.textContent="\n .fv-sdk-root { display: flex; flex-direction: column; gap: 12px; width: 100%; }\n .fv-sdk-media { position: relative; width: 100%; max-width: 512px; height: 384px; margin: 0 auto; background: #000; border-radius: 10px; overflow: hidden; }\n .fv-sdk-video, .fv-sdk-canvas { width: 100%; height: 100%; display: block; object-fit: contain; }\n .fv-sdk-canvas { position: absolute; top: 0; left: 0; }\n .fv-sdk-status { text-align: center; display: flex; align-items: center; justify-content: center; font-size: 14px; padding: 10px 12px; border-radius: 8px; font-weight: 600; background: #f8f9fa; color: #555; }\n .fv-sdk-status.success { background: #d4edda; color: #155724; }\n .fv-sdk-status.error { background: #f8d7da; color: #721c24; }\n .fv-sdk-status.warning { background: #fff3cd; color: #856404; }\n ",document.head.appendChild(e),this.injectedStyleElement=e}applyMirrorStyles(){const e=this.options.videoElement,t=this.options.overlayCanvasElement;e&&(e.style.transform="scaleX(-1)"),t&&(t.style.transform="scaleX(-1)")}init(){return E(this,void 0,void 0,function*(){try{const e=l(this.options.locale);this.setStatus(a.INITIALIZING,void 0,e);const t=yield n.FilesetResolver.forVisionTasks(this.options.modelPath);this.faceLandmarker=yield n.FaceLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numFaces:2,minFaceDetectionConfidence:this.options.minDetectionConfidence,minFacePresenceConfidence:this.options.minFaceVisibilityScore,minTrackingConfidence:this.options.minFaceVisibilityScore}),this.handLandmarker=yield n.HandLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5}),this.cameraReadyPromise&&(yield this.cameraReadyPromise),this.startDetectionLoop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}})}getMessageForStatus(e,t){return t||(this.options.customMessages[e]?this.options.customMessages[e]:r(e,this.options.locale))}setStatus(e,t,n){const i=this.getMessageForStatus(e,n);this.updateStatusUI(e,i),this.options.onStatusUpdate(e,i),e===a.ERROR&&t&&this.options.onError(e,t)}updateStatusUI(e,t){if(!this.statusElement)return;this.statusElement.textContent=t,this.statusElement.classList.remove("success","warning","error");const n=this.getStatusClass(e);n&&this.statusElement.classList.add(n)}getStatusClass(e){return e===a.SUCCESS?"success":e===a.ERROR?"error":[a.NO_FACE_DETECTED,a.MULTIPLE_FACES,a.TOO_CLOSE,a.TOO_FAR,a.OFF_CENTER,a.HEAD_NOT_STRAIGHT,a.FACE_OBSTRUCTED,a.POOR_ILLUMINATION,a.NOT_NEUTRAL_EXPRESSION,a.DARK_GLASSES,a.STAY_STILL,a.CAPTURING].includes(e)?"warning":""}initCamera(){return E(this,void 0,void 0,function*(){const e=this.options.videoElement;if(e&&!e.srcObject)try{const t=yield navigator.mediaDevices.getUserMedia({video:this.options.videoConstraints});this.cameraStream=t,this.managedCamera=!0,e.srcObject=t,yield this.waitForVideoReady(e),yield e.play();const n=this.options.overlayCanvasElement;n&&(n.width=e.videoWidth||this.options.videoWidth||f.videoWidth,n.height=e.videoHeight||this.options.videoHeight||f.videoHeight)}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}})}waitForVideoReady(e){return E(this,void 0,void 0,function*(){e.readyState>=2||(yield new Promise((t,n)=>{const a=()=>{e.removeEventListener("loadedmetadata",a),t()},i=t=>{e.removeEventListener("error",i),n(t)};e.addEventListener("loadedmetadata",a),e.addEventListener("error",i),setTimeout(()=>{e.removeEventListener("loadedmetadata",a),e.removeEventListener("error",i),t()},5e3)}))})}startDetectionLoop(){const e=this.getVideoElement(),t=this.options.videoWidth||640,n=this.options.videoHeight||480,i=()=>E(this,void 0,void 0,function*(){var o;if(this.faceLandmarker&&this.handLandmarker&&e.videoWidth){try{const i=performance.now();let s=a.NO_FACE_DETECTED,r=null,l=[];const E=this.faceLandmarker.detectForVideo(e,i),f=this.handLandmarker.detectForVideo(e,i);if(f.landmarks&&f.landmarks.length>0&&(l=f.landmarks.map((e,t)=>{var n,a,i;return{landmarks:e,handedness:(null===(i=null===(a=null===(n=f.handednesses)||void 0===n?void 0:n[t])||void 0===a?void 0:a[0])||void 0===i?void 0:i.categoryName)||"Unknown"}})),E.faceLandmarks&&E.faceLandmarks.length>1){s=a.MULTIPLE_FACES,this.stableSince=null;const e=E.faceLandmarks[0],t=(null===(o=E.faceBlendshapes)||void 0===o?void 0:o[0])?this.estimateBoundingBox(e):null;t&&(r={boundingBox:t,landmarks:e,timestamp:i})}else if(E.faceLandmarks&&1===E.faceLandmarks.length){const o=E.faceLandmarks[0],f=this.estimateBoundingBox(o);r={boundingBox:f,landmarks:o,timestamp:i};const g=function(e,t=.18,n=.7){const a=e.width;return a<t?"TOO_FAR":a>n?"TOO_CLOSE":"OK"}(f,this.options.minFaceSizeFactor,this.options.maxFaceSizeFactor);if("OK"!==g)s="TOO_CLOSE"===g?a.TOO_CLOSE:a.TOO_FAR,this.stableSince=null;else{const E=o[4],g=function(e,t,n,a){const i=(e*n-n/2)/(.2*n),o=(t*a-a/2)/(a*u);return i*i+o*o<=.6}(E.x,E.y,t,n),p=function(e,t,n){const a=t/2,i=n/2,o=.2*t,s=n*u,r=e.xMin*t,l=(e.xMin+e.width)*t,d=e.yMin*n,c=(e.yMin+e.height)*n,h=((r+l)/2-a)/o,m=((d+c)/2-i)/s;if(h*h+m*m>1)return!1;const E=[{x:r,y:d},{x:l,y:d},{x:r,y:c},{x:l,y:c}];let f=0;for(const e of E){const t=(e.x-a)/o,n=(e.y-i)/s;t*t+n*n>1&&f++}return 0===f}(f,t,n);if(g&&p)if(function(e,t){if(e.length<478)return!1;const n=e[4],a=m.map(t=>e[t]),i=a.reduce((e,t)=>e+t.y,0)/a.length,o=Math.min(...a.map(e=>e.y)),s=Math.max(...a.map(e=>e.y))-o,r=t.height;return!(i<n.y-.01||i-n.y<.06*r||s<.02*r)}(o,f))if(function(e,t=25){if(e.length<478)return!1;const n=e[c[0]],a=e[h[0]],i=e[4],o=e[13],s=e[14],r=e[152],l=e[10],d=Math.abs(n.y-a.y),m=Math.abs(n.x-a.x);if(m<.01)return!1;const u=d/m;if(Math.atan(u)*(180/Math.PI)>t)return!1;const E=(n.x+a.x)/2,f=i.x-E,g=Math.abs(n.x-a.x);if(g<.01)return!1;const p=Math.abs(f)/g;if(Math.atan(p)*(180/Math.PI)>t)return!1;if(!function(e){if(e.length<478)return!1;const t=e[234],n=e[454],a=e[4],i=Math.abs(t.x-a.x),o=Math.abs(n.x-a.x);return!((i>.01&&o>.01?Math.max(i,o)/Math.min(i,o):1)>1.4||void 0!==t.z&&void 0!==n.z&&Math.abs(t.z-n.z)>.05)}(e))return!1;const S=(n.y+a.y)/2,v=(o.y+s.y)/2,T=r.y-l.y;if(T<.1)return!1;if(l.y>S+.02)return!1;if(S>i.y+.02)return!1;if(i.y>v+.02)return!1;if(v>=r.y)return!1;const y=(S-l.y)/T,C=(i.y-S)/T,O=(v-i.y)/T,I=(r.y-v)/T;return!(y<.06||y>.38||C<.03||C>.3||O<.02||O>.25||I<.04||I>.38)}(o,this.options.maxHeadTiltDegrees))if(l.length>0&&function(e,t,n=.15){const a=t.xMin+t.width/2,i=t.yMin+t.height/2;for(const t of e.landmarks){const e=t.x-a,o=t.y-i;if(Math.sqrt(e*e+o*o)<n)return!0}return!1}(l[0],f,this.options.maxHandFaceDistance))s=a.FACE_OBSTRUCTED,this.stableSince=null;else if(function(e){if(e.length<478)return!1;const t=e[159],n=e[144],a=e[386],i=e[373],o=Math.abs(t.y-n.y),s=Math.abs(a.y-i.y);if(o<.01||s<.01)return!1;const r=e[13],l=e[14];if(Math.abs(r.y-l.y)>.025)return!1;const d=e[61],c=e[291],h=e[4];return!((d.y+c.y)/2-h.y<.05)}(o))if(function(e,t){if(t.length<478)return!1;try{const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)return!1;const i=e.videoWidth,o=e.videoHeight,s=[t[33],t[133],t[159],t[144],t[145]],r=[t[263],t[362],t[386],t[373],t[374]],l=e=>{const t=e.map(e=>e.x*i),n=e.map(e=>e.y*o),a=Math.max(0,Math.min(...t)-5),s=Math.min(i,Math.max(...t)+5),r=Math.max(0,Math.min(...n)-5);return{x:a,y:r,width:s-a,height:Math.min(o,Math.max(...n)+5)-r}},c=t=>(n.width=t.width,n.height=t.height,a.drawImage(e,t.x,t.y,t.width,t.height,0,0,t.width,t.height),d(a.getImageData(0,0,t.width,t.height))),h=l(s),m=l(r);return(c(h)+c(m))/2<35}catch(e){return console.warn("Erro ao detectar óculos escuros:",e),!1}}(e,o))s=a.DARK_GLASSES,this.stableSince=null;else{const o=document.createElement("canvas"),l=f.xMin*e.videoWidth,c=f.yMin*e.videoHeight,h=f.width*e.videoWidth,m=f.height*e.videoHeight;o.width=h,o.height=m;const u=o.getContext("2d",{willReadFrequently:!0});if(u){u.drawImage(e,l,c,h,m,0,0,h,m);d(u.getImageData(0,0,o.width,o.height))<this.options.minIlluminationThreshold?(s=a.POOR_ILLUMINATION,this.stableSince=null):function(e,t,n=5,a=512,i=384){if(!e||!t)return!1;const o=(e.boundingBox.xMin+e.boundingBox.width/2)*a,s=(e.boundingBox.yMin+e.boundingBox.height/2)*i,r=(t.boundingBox.xMin+t.boundingBox.width/2)*a,l=(t.boundingBox.yMin+t.boundingBox.height/2)*i,d=Math.abs(o-r),c=Math.abs(s-l),h=Math.abs(e.boundingBox.width-t.boundingBox.width)*a,m=Math.abs(e.boundingBox.height-t.boundingBox.height)*i;return d<=n&&c<=n&&h<=2*n&&m<=2*n}(r,this.lastDetection,this.options.stabilityMovementThreshold,t,n)?(this.stableSince||(this.stableSince=i),s=i-this.stableSince>=this.options.stabilizationTimeThreshold?a.CAPTURING:a.STAY_STILL):(this.stableSince=null,s=a.STAY_STILL)}else s=a.FACE_DETECTED,this.stableSince=null}else s=a.NOT_NEUTRAL_EXPRESSION,this.stableSince=null;else s=a.HEAD_NOT_STRAIGHT,this.stableSince=null;else s=a.FACE_OBSTRUCTED,this.stableSince=null;else s=a.OFF_CENTER,this.stableSince=null}}else this.lastDetection=null,this.stableSince=null;if(this.lastDetection=r,this.setStatus(s),this.options.overlayCanvasElement&&function(e,t,n,i,o){const s=e.getContext("2d");if(!s)return;const r=e.width,l=e.height,d=r/2,m=l/2;s.clearRect(0,0,r,l);const E=.2*r,f=l*u;if(s.fillStyle="rgba(255, 255, 255, 0.35)",s.fillRect(0,0,r,l),s.save(),s.beginPath(),s.ellipse(d,m,E,f,0,0,2*Math.PI),s.closePath(),s.globalCompositeOperation="destination-out",s.fill(),s.restore(),s.strokeStyle="rgba(255, 255, 255, 0.9)",s.lineWidth=3,s.beginPath(),s.ellipse(d,m,E,f,0,0,2*Math.PI),s.stroke(),s.strokeStyle="rgba(255, 255, 255, 0.45)",s.lineWidth=1,s.beginPath(),s.moveTo(d-6,m),s.lineTo(d+6,m),s.moveTo(d,m-6),s.lineTo(d,m+6),s.stroke(),t&&i){const e=i.landmarks;if(e.length>=478){const t=e[10],i=e[152],o=e[234],d=e[454],m=e.map(e=>e.x),u=e.map(e=>e.y),E=Math.min(...m),f=Math.max(...m),g=Math.min(...u),p=f-E,S=Math.max(...u)-g,v=.08,T=(E-p*v)*r,y=(g-S*v)*l,C=p*(1+2*v)*r,O=S*(1+2*v)*l;let I="red";n===a.STAY_STILL||n===a.CAPTURING?I="lime":n===a.FACE_DETECTED&&(I="yellow"),s.strokeStyle=I,s.lineWidth=3,s.strokeRect(T,y,C,O);const R=e[4];e[c[0]],e[h[0]],s.fillStyle="cyan",s.beginPath(),s.arc(R.x*r,R.y*l,5,0,2*Math.PI),s.fill(),s.fillStyle="magenta",s.beginPath(),s.arc(t.x*r,t.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="lime",s.beginPath(),s.arc(i.x*r,i.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="yellow",[e[33],e[133],e[159],e[144],e[145]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="yellow",[e[263],e[362],e[386],e[373],e[374]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="purple",s.beginPath(),s.arc(o.x*r,o.y*l,3,0,2*Math.PI),s.fill(),s.beginPath(),s.arc(d.x*r,d.y*l,3,0,2*Math.PI),s.fill()}}t&&o&&o.length>0&&o.forEach(e=>{s.fillStyle="orange",e.landmarks.forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()})})}(this.options.overlayCanvasElement,this.options.debugMode||!1,s,r||void 0,l.length>0?l:void 0),s===a.CAPTURING&&!this.isCapturing)return this.isCapturing=!0,yield this.captureImage(),this.setStatus(a.SUCCESS),void this.stop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}this.animationFrameId=requestAnimationFrame(i)}else this.animationFrameId=requestAnimationFrame(i)});this.animationFrameId=requestAnimationFrame(i)}estimateBoundingBox(e){const t=e.map(e=>e.x),n=e.map(e=>e.y),a=Math.min(...t),i=Math.max(...t),o=Math.min(...n);return{xMin:a,yMin:o,width:i-a,height:Math.max(...n)-o}}captureImage(){return E(this,void 0,void 0,function*(){const e=this.getVideoElement(),t=document.createElement("canvas");t.width=e.videoWidth,t.height=e.videoHeight;const n=t.getContext("2d");n?(n.drawImage(e,0,0,t.width,t.height),t.toBlob(e=>{e?this.options.onCaptureSuccess(e):this.setStatus(a.ERROR,new Error("Failed to generate image blob"))},"image/jpeg",.95)):this.setStatus(a.ERROR,new Error("Failed to get canvas context"))})}stop(){null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.faceLandmarker&&this.faceLandmarker.close(),this.handLandmarker&&this.handLandmarker.close(),this.managedCamera&&this.stopCamera()}destroy(){this.stop(),this.managedElements&&this.containerElement&&(this.containerElement.innerHTML=""),this.statusElement=null,this.uiRootElement=null}stopCamera(){this.cameraStream&&(this.cameraStream.getTracks().forEach(e=>e.stop()),this.cameraStream=null)}getVideoElement(){if(!this.options.videoElement)throw new Error("Video element is not available. Provide videoElement or container.");return this.options.videoElement}}const p=g;module.exports=t})();
2
2
  //# sourceMappingURL=face-validator-sdk-core.cjs.js.map