face-validator-sdk 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -2,10 +2,7 @@
2
2
 
3
3
  Real-time selfie validation SDK with face detection, powered by **MediaPipe**. Detects faces, hands, and validates pose, lighting, and occlusions in real-time.
4
4
 
5
- 🎭 **[Live Demo](https://face-validator-sdk.vercel.app)** | 📦 [NPM Package](#installation) | 📖 [Documentation](#usage) | 🤝 [Contributing](#contributing)
6
-
7
- [![CI](https://github.com/rwmsousa/face-validator-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/rwmsousa/face-validator-sdk/actions/workflows/ci.yml)
8
- [![Deploy](https://github.com/rwmsousa/face-validator-sdk/actions/workflows/deploy-vercel.yml/badge.svg)](https://github.com/rwmsousa/face-validator-sdk/actions/workflows/deploy-vercel.yml)
5
+ 🎭 **[Live Demo](https://face-validator-sdk.vercel.app)**
9
6
 
10
7
  ## ✨ Features
11
8
 
@@ -29,15 +26,17 @@ Real-time selfie validation SDK with face detection, powered by **MediaPipe**. D
29
26
  - 📦 **Multiple builds**: ESM, CJS, UMD
30
27
  - 🚀 **GPU accelerated**: Powered by MediaPipe with GPU support
31
28
 
32
- ## 📦 Installation
29
+ ## 📦 Installation (Core SDK)
30
+
31
+ For any web application (React, Angular, Vue, vanilla JS, Java backend with JS frontend, etc.) that wants to use the **core validator**:
33
32
 
34
33
  ```bash
35
- npm install face-validator-sdk @mediapipe/tasks-vision
34
+ npm install face-validator-sdk
36
35
  ```
37
36
 
38
- **Peer dependency**: `@mediapipe/tasks-vision` (^0.10.15)
37
+ > The SDK declares `@mediapipe/tasks-vision` (^0.10.15) as a regular dependency, so it is installed automatically when you install `face-validator-sdk`.
39
38
 
40
- ## 🚀 Quick Start
39
+ ## 🚀 Quick Start (Core API)
41
40
 
42
41
  ```typescript
43
42
  import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
@@ -122,35 +121,131 @@ interface FaceValidatorOptions {
122
121
  }
123
122
  ```
124
123
 
125
- ## 🎭 Live Demo
124
+ ---
125
+
126
+ ## 🧩 React Component: `ReactSelfieCapture`
127
+
128
+ If you are building a **React** application and want a **ready‑to‑use selfie capture UI**, the SDK exposes an optional React component that encapsulates:
126
129
 
127
- ### Online Demo
128
- Visit: **[https://face-validator-sdk.vercel.app](https://face-validator-sdk.vercel.app)**
130
+ - Camera access (`getUserMedia`)
131
+ - Validation loop (`FaceValidator`)
132
+ - Overlay drawing (oval + feedback)
133
+ - Preview step (photo + buttons)
134
+ - i18n for pt-BR, en, es
129
135
 
130
- ### Local Development
136
+ ### Installation (React project)
137
+
138
+ Your React app should already have `react` and `react-dom` installed. Then:
131
139
 
132
140
  ```bash
133
- # Clone the repository
134
- git clone https://github.com/rwmsousa/face-validator-sdk.git
135
- cd face-validator-sdk
141
+ npm install face-validator-sdk
142
+ ```
136
143
 
137
- # Install dependencies
138
- npm install
144
+ > `@mediapipe/tasks-vision` is installed automatically as a dependency of the SDK.
145
+ > `react` and `react-dom` are declared as **peerDependencies** – they are **not** bundled inside the package.
146
+ > Non‑React applications should use the **core `FaceValidator` API** shown above instead of `ReactSelfieCapture`.
147
+
148
+ ### Basic usage
149
+
150
+ ```tsx
151
+ import { ReactSelfieCapture } from 'face-validator-sdk';
152
+
153
+ function SelfieExample() {
154
+ const handleCapture = (imageBase64: string | null) => {
155
+ if (imageBase64) {
156
+ // send to API, store, etc.
157
+ }
158
+ };
159
+
160
+ return (
161
+ <ReactSelfieCapture
162
+ locale={navigator.language} // 'pt-BR' | 'en' | 'es' (auto-normalized)
163
+ onCapture={handleCapture}
164
+ onDismiss={() => console.log('Modal closed')}
165
+ />
166
+ );
167
+ }
168
+ ```
139
169
 
140
- # Run local demo (http://localhost:8081)
141
- npm run dev
170
+ ### Props
171
+
172
+ ```ts
173
+ type SupportedLocale = 'pt-BR' | 'en' | 'es';
174
+
175
+ type SelfieCaptureStyles = {
176
+ container?: React.CSSProperties;
177
+ media?: React.CSSProperties;
178
+ messageBanner?: React.CSSProperties;
179
+ primaryButton?: React.CSSProperties;
180
+ secondaryButton?: React.CSSProperties;
181
+ };
182
+
183
+ type SelfieCaptureUILabelOverrides = Partial<{
184
+ previewQuestion: string;
185
+ savePhoto: string;
186
+ tryAgain: string;
187
+ cancel: string;
188
+ }>;
189
+
190
+ interface ReactSelfieCaptureProps {
191
+ onCapture: (image: string | null) => void; // base64 data URL or null on cancel
192
+ onDismiss?: () => void;
193
+
194
+ // Behaviour
195
+ locale?: SupportedLocale | string; // Default: 'pt-BR' (auto-normalized)
196
+ videoWidth?: number; // Default: 512
197
+ videoHeight?: number; // Default: 384
198
+ debugMode?: boolean; // Default: false
199
+ modelPath?: string; // Optional MediaPipe WASM path; if omitted, uses internal CDN default
200
+
201
+ // Visual customization (inline styles)
202
+ styles?: SelfieCaptureStyles;
203
+
204
+ // Optional UI labels override (per-locale defaults exist)
205
+ labels?: SelfieCaptureUILabelOverrides;
206
+ }
142
207
  ```
143
208
 
144
- ### Build Demo for Production
209
+ ### Labels and i18n
145
210
 
146
- ```bash
147
- # Build SDK + Demo
148
- npm run build
149
- npm run build:demo
211
+ By default, the component renders UI labels in **Portuguese (pt-BR)**, **English (en)** or **Spanish (es)**:
212
+
213
+ - Preview question (“O que você achou?” / “What do you think?” / “¿Qué te pareció?”)
214
+ - Buttons (“Salvar foto”, “Tentar novamente”, “Cancelar”, etc.)
215
+
216
+ You can override any of these without having to set up an external i18n layer:
217
+
218
+ ```tsx
219
+ <ReactSelfieCapture
220
+ locale="pt-BR"
221
+ onCapture={handleCapture}
222
+ labels={{
223
+ previewQuestion: 'Confira sua selfie antes de salvar',
224
+ savePhoto: 'Confirmar selfie',
225
+ }}
226
+ />
227
+ ```
150
228
 
151
- # Demo files output to: demo/dist/
229
+ ### Styling
230
+
231
+ The component ships with a sensible default layout, but you can tweak it via the `styles` prop:
232
+
233
+ ```tsx
234
+ <ReactSelfieCapture
235
+ onCapture={handleCapture}
236
+ styles={{
237
+ container: { borderRadius: 24 },
238
+ media: { borderRadius: 16 },
239
+ messageBanner: { backgroundColor: '#f0f9ff', color: '#0369a1' },
240
+ primaryButton: { backgroundColor: '#2563eb', borderColor: '#2563eb' },
241
+ secondaryButton: { borderRadius: 9999 },
242
+ }}
243
+ />;
152
244
  ```
153
245
 
246
+ > Tip: you can wrap `ReactSelfieCapture` in your own modal/dialog and pass `onDismiss` to close it from the **Cancel** button.
247
+
248
+
154
249
  ## 🏗️ Architecture
155
250
 
156
251
  ### MediaPipe Integration
@@ -160,28 +255,6 @@ The SDK uses two MediaPipe models running in parallel:
160
255
  1. **FaceLandmarker**: 478 facial landmarks + face detection
161
256
  2. **HandLandmarker**: 21 hand landmarks per hand
162
257
 
163
- ```
164
- ┌─────────────────────────────────────────┐
165
- │ FaceValidator │
166
- ├─────────────────────────────────────────┤
167
- │ ┌─────────────────┐ ┌──────────────┐ │
168
- │ │ FaceLandmarker │ │ HandLandmarker│ │
169
- │ │ (478 points) │ │ (21 pts/hand) │ │
170
- │ └─────────────────┘ └──────────────┘ │
171
- │ ↓ ↓ │
172
- │ ┌──────────────────────────────────┐ │
173
- │ │ Validation Pipeline │ │
174
- │ │ 1. Distance │ │
175
- │ │ 2. Centering │ │
176
- │ │ 3. Face geometry │ │
177
- │ │ 4. Head pose │ │
178
- │ │ 5. Hand proximity ⭐NEW │ │
179
- │ │ 6. Illumination │ │
180
- │ │ 7. Stability │ │
181
- │ └──────────────────────────────────┘ │
182
- └─────────────────────────────────────────┘
183
- ```
184
-
185
258
  ## 📚 Why MediaPipe?
186
259
 
187
260
  Migrated from face-api.js (discontinued 2021) to MediaPipe (Google):
@@ -195,64 +268,6 @@ Migrated from face-api.js (discontinued 2021) to MediaPipe (Google):
195
268
  | Accuracy | ~60-70% | ✅ **~90-95%** |
196
269
  | Model size | ~8MB | ~15MB |
197
270
 
198
- ## 🔧 Development
199
-
200
- ### Scripts
201
-
202
- ```bash
203
- npm run dev # Start local dev server (webpack)
204
- npm run build # Build SDK (CJS, ESM, UMD)
205
- npm run build:demo # Build production demo
206
- npm run lint # Run ESLint
207
- npm run format # Format code with Prettier
208
- npm run test # Run tests (Jest)
209
- ```
210
-
211
- ### Project Structure
212
-
213
- ```
214
- face-validator-sdk/
215
- ├── src/
216
- │ ├── FaceValidator.ts # Main validator class
217
- │ ├── types.ts # TypeScript types
218
- │ ├── utils.ts # Validation functions
219
- │ ├── i18n.ts # Internationalization
220
- │ └── index.ts # Public API exports
221
- ├── demo/
222
- │ ├── demo.ts # Local dev demo
223
- │ ├── demo-standalone.ts # Production demo
224
- │ └── public/
225
- │ └── index.html # Demo HTML
226
- ├── dist/ # SDK build output
227
- │ ├── face-validator-sdk.esm.js
228
- │ ├── face-validator-sdk.cjs.js
229
- │ ├── face-validator-sdk.umd.js
230
- │ └── types/ # TypeScript declarations
231
- ├── .github/
232
- │ └── workflows/
233
- │ ├── ci.yml # CI/CD pipeline
234
- │ └── deploy-vercel.yml # Vercel deployment
235
- └── vercel.json # Vercel configuration
236
- ```
237
-
238
- ## 🚀 Deployment
239
-
240
- ### Vercel (Automatic)
241
-
242
- 1. Connect repository to Vercel
243
- 2. Add secrets to GitHub:
244
- - `VERCEL_TOKEN`
245
- - `VERCEL_ORG_ID`
246
- - `VERCEL_PROJECT_ID`
247
- 3. Push to `main` branch → auto-deploy
248
-
249
- ### Manual Deployment
250
-
251
- ```bash
252
- npm run build:demo
253
- # Deploy demo/dist/ to any static host
254
- ```
255
-
256
271
  ## 🤝 Contributing
257
272
 
258
273
  Contributions are welcome! Please:
@@ -263,17 +278,6 @@ Contributions are welcome! Please:
263
278
  4. Push to branch: `git push origin feature/amazing-feature`
264
279
  5. Open a Pull Request
265
280
 
266
- ### Commit Convention
267
-
268
- We use [Conventional Commits](https://www.conventionalcommits.org/):
269
-
270
- - `feat:` New feature
271
- - `fix:` Bug fix
272
- - `docs:` Documentation changes
273
- - `chore:` Maintenance tasks
274
- - `refactor:` Code refactoring
275
- - `test:` Add/update tests
276
-
277
281
  ## 📄 License
278
282
 
279
283
  MIT License - see [LICENSE](LICENSE) file for details.
@@ -291,4 +295,3 @@ MIT License - see [LICENSE](LICENSE) file for details.
291
295
 
292
296
  ---
293
297
 
294
- Made with ❤️ using MediaPipe
@@ -1,2 +1,3 @@
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 e centralize seu rosto no centro do oval.",[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<=1}(E.x,E.y,t,n);if(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}];for(const e of E){const t=(e.x-a)/o,n=(e.y-i)/s;t*t+n*n>1.2&&0}}(g,t,n),T)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
+ /*! For license information please see face-validator-sdk.cjs.js.LICENSE.txt */
2
+ (()=>{"use strict";var e={20(e,t,n){var a=n(953),i=Symbol.for("react.element"),o=Symbol.for("react.fragment"),s=Object.prototype.hasOwnProperty,r=a.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,n){var a,o={},c=null,d=null;for(a in void 0!==n&&(c=""+n),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(d=t.ref),t)s.call(t,a)&&!l.hasOwnProperty(a)&&(o[a]=t[a]);if(e&&e.defaultProps)for(a in t=e.defaultProps)void 0===o[a]&&(o[a]=t[a]);return{$$typeof:i,type:e,key:c,ref:d,props:o,_owner:r.current}}t.Fragment=o,t.jsx=c,t.jsxs=c},848(e,t,n){e.exports=n(20)},953(e){e.exports=require("react")}},t={};function n(a){var i=t[a];if(void 0!==i)return i.exports;var o=t[a]={exports:{}};return e[a](o,o.exports,n),o.exports}n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};n.r(a),n.d(a,{FaceValidator:()=>S,ReactSelfieCapture:()=>O,ValidationStatus:()=>o,default:()=>v,getLoadingModelsMessage:()=>d,getMessage:()=>c,getValidationMessages:()=>l});const i=require("@mediapipe/tasks-vision");var o;!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"}(o||(o={}));const s={"pt-BR":{[o.INITIALIZING]:"Inicializando câmera e detector...",[o.NO_FACE_DETECTED]:"Posicione seu rosto no centro do oval.",[o.FACE_DETECTED]:"Analisando...",[o.TOO_CLOSE]:"Afaste-se um pouco",[o.TOO_FAR]:"Aproxime-se da câmera",[o.OFF_CENTER]:"Centralize o rosto no centro do oval",[o.FACE_OBSTRUCTED]:"Mantenha o rosto totalmente visível. Remova as mãos do rosto.",[o.HEAD_NOT_STRAIGHT]:"Olhe diretamente para a câmera e mantenha a cabeça reta.",[o.MULTIPLE_FACES]:"Mantenha apenas uma pessoa no quadro.",[o.POOR_ILLUMINATION]:"Procure um ambiente com boa iluminação.",[o.NOT_NEUTRAL_EXPRESSION]:"Mantenha expressão neutra: boca fechada, sem sorrir e olhos abertos.",[o.DARK_GLASSES]:"Remova os óculos escuros. Óculos de grau são permitidos.",[o.STAY_STILL]:"Fique imóvel para capturar a foto",[o.CAPTURING]:"Capturando...",[o.SUCCESS]:"Captura realizada!",[o.ERROR]:"Ocorreu um erro."},en:{[o.INITIALIZING]:"Initializing camera and detector...",[o.NO_FACE_DETECTED]:"Position your face in the center of the oval.",[o.FACE_DETECTED]:"Analyzing...",[o.TOO_CLOSE]:"Move back a little",[o.TOO_FAR]:"Move closer to the camera",[o.OFF_CENTER]:"Center your face in the center of the oval",[o.FACE_OBSTRUCTED]:"Keep your face fully visible. Remove your hands from your face.",[o.HEAD_NOT_STRAIGHT]:"Look directly at the camera and keep your head straight.",[o.MULTIPLE_FACES]:"Keep only one person in the frame.",[o.POOR_ILLUMINATION]:"Find a well-lit environment and center your face in the oval.",[o.NOT_NEUTRAL_EXPRESSION]:"Keep a neutral expression: mouth closed, no smiling, and eyes open.",[o.DARK_GLASSES]:"Remove sunglasses. Prescription glasses are allowed.",[o.STAY_STILL]:"Stay still to capture the photo",[o.CAPTURING]:"Capturing...",[o.SUCCESS]:"Capture complete!",[o.ERROR]:"An error occurred."},es:{[o.INITIALIZING]:"Inicializando cámara y detector...",[o.NO_FACE_DETECTED]:"Coloque su rostro en el centro del óvalo.",[o.FACE_DETECTED]:"Analizando...",[o.TOO_CLOSE]:"Aléjese un poco",[o.TOO_FAR]:"Acérquese a la cámara",[o.OFF_CENTER]:"Centre el rostro en el centro del óvalo",[o.FACE_OBSTRUCTED]:"Mantenga el rostro totalmente visible. Quite las manos del rostro.",[o.HEAD_NOT_STRAIGHT]:"Mire directamente a la cámara y mantenga la cabeza recta.",[o.MULTIPLE_FACES]:"Mantenga solo una persona en el encuadre.",[o.POOR_ILLUMINATION]:"Busque un ambiente con buena iluminación y centre su rostro en el óvalo.",[o.NOT_NEUTRAL_EXPRESSION]:"Mantenga expresión neutra: boca cerrada, sin sonreír y ojos abiertos.",[o.DARK_GLASSES]:"Quite las gafas de sol. Las gafas graduadas están permitidas.",[o.STAY_STILL]:"Permanezca quieto para capturar la foto",[o.CAPTURING]:"Capturando...",[o.SUCCESS]:"¡Captura realizada!",[o.ERROR]:"Ocurrió un error."}},r={"pt-BR":"Status desconhecido.",en:"Unknown status.",es:"Estado desconhecido."};function l(e){return Object.assign({},s[e])}function c(e,t){var n;return null!==(n=s[t][e])&&void 0!==n?n:r[t]}function d(e){return{"pt-BR":"Carregando...",en:"Loading...",es:"Cargando..."}[e]}function h(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 u=[33,133,159,145],m=[263,362,386,374],f=[61,291,0,17,39,269,270,409],g=.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 p={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 S{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(o.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({},p),e),{modelPath:t,locale:e.locale||"en",customMessages:e.customMessages||{}})}init(){return E(this,void 0,void 0,function*(){try{const e=d(this.options.locale);this.setStatus(o.INITIALIZING,void 0,e);const t=yield i.FilesetResolver.forVisionTasks(this.options.modelPath);this.faceLandmarker=yield i.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 i.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(o.ERROR,t)}})}getMessageForStatus(e,t){return t||(this.options.customMessages[e]?this.options.customMessages[e]:c(e,this.options.locale))}setStatus(e,t,n){const a=this.getMessageForStatus(e,n);this.options.onStatusUpdate(e,a),e===o.ERROR&&t&&this.options.onError(e,t)}startDetectionLoop(){const e=this.options.videoElement,t=this.options.videoWidth||640,n=this.options.videoHeight||480,a=()=>E(this,void 0,void 0,function*(){var i;if(this.faceLandmarker&&this.handLandmarker&&e.videoWidth){try{const a=performance.now();let s=o.NO_FACE_DETECTED,r=null,l=[];const c=this.faceLandmarker.detectForVideo(e,a),d=this.handLandmarker.detectForVideo(e,a);if(d.landmarks&&d.landmarks.length>0&&(l=d.landmarks.map((e,t)=>{var n,a,i;return{landmarks:e,handedness:(null===(i=null===(a=null===(n=d.handednesses)||void 0===n?void 0:n[t])||void 0===a?void 0:a[0])||void 0===i?void 0:i.categoryName)||"Unknown"}})),c.faceLandmarks&&c.faceLandmarks.length>1){s=o.MULTIPLE_FACES,this.stableSince=null;const e=c.faceLandmarks[0],t=(null===(i=c.faceBlendshapes)||void 0===i?void 0:i[0])?this.estimateBoundingBox(e):null;t&&(r={boundingBox:t,landmarks:e,timestamp:a})}else if(c.faceLandmarks&&1===c.faceLandmarks.length){const i=c.faceLandmarks[0],d=this.estimateBoundingBox(i);r={boundingBox:d,landmarks:i,timestamp:a};const E=function(e,t=.18,n=.7){const a=e.width;return a<t?"TOO_FAR":a>n?"TOO_CLOSE":"OK"}(d,this.options.minFaceSizeFactor,this.options.maxFaceSizeFactor);if("OK"!==E)s="TOO_CLOSE"===E?o.TOO_CLOSE:o.TOO_FAR,this.stableSince=null;else{const c=i[4],E=function(e,t,n,a){const i=(e*n-n/2)/(.2*n),o=(t*a-a/2)/(a*g);return i*i+o*o<=.6}(c.x,c.y,t,n),p=function(e,t,n){const a=t/2,i=n/2,o=.2*t,s=n*g,r=e.xMin*t,l=(e.xMin+e.width)*t,c=e.yMin*n,d=(e.yMin+e.height)*n,h=((r+l)/2-a)/o,u=((c+d)/2-i)/s;if(h*h+u*u>1)return!1;const m=[{x:r,y:c},{x:l,y:c},{x:r,y:d},{x:l,y:d}];let f=0;for(const e of m){const t=(e.x-a)/o,n=(e.y-i)/s;t*t+n*n>1&&f++}return 0===f}(d,t,n);if(E&&p)if(function(e,t){if(e.length<478)return!1;const n=e[4],a=f.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)}(i,d))if(function(e,t=25){if(e.length<478)return!1;const n=e[u[0]],a=e[m[0]],i=e[4],o=e[13],s=e[14],r=e[152],l=e[10],c=Math.abs(n.y-a.y),d=Math.abs(n.x-a.x);if(d<.01)return!1;const h=c/d;if(Math.atan(h)*(180/Math.PI)>t)return!1;const f=(n.x+a.x)/2,g=i.x-f,E=Math.abs(n.x-a.x);if(E<.01)return!1;const p=Math.abs(g)/E;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,y=(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>y+.02)return!1;if(y>=r.y)return!1;const b=(S-l.y)/T,O=(i.y-S)/T,v=(y-i.y)/T,I=(r.y-y)/T;return!(b<.06||b>.38||O<.03||O>.3||v<.02||v>.25||I<.04||I>.38)}(i,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],d,this.options.maxHandFaceDistance))s=o.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],d=e[291],h=e[4];return!((c.y+d.y)/2-h.y<.05)}(i))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),h(a.getImageData(0,0,t.width,t.height))),d=l(s),u=l(r);return(c(d)+c(u))/2<35}catch(e){return console.warn("Erro ao detectar óculos escuros:",e),!1}}(e,i))s=o.DARK_GLASSES,this.stableSince=null;else{const i=document.createElement("canvas"),l=d.xMin*e.videoWidth,c=d.yMin*e.videoHeight,u=d.width*e.videoWidth,m=d.height*e.videoHeight;i.width=u,i.height=m;const f=i.getContext("2d",{willReadFrequently:!0});if(f){f.drawImage(e,l,c,u,m,0,0,u,m);h(f.getImageData(0,0,i.width,i.height))<this.options.minIlluminationThreshold?(s=o.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),d=Math.abs(s-l),h=Math.abs(e.boundingBox.width-t.boundingBox.width)*a,u=Math.abs(e.boundingBox.height-t.boundingBox.height)*i;return c<=n&&d<=n&&h<=2*n&&u<=2*n}(r,this.lastDetection,this.options.stabilityMovementThreshold,t,n)?(this.stableSince||(this.stableSince=a),s=a-this.stableSince>=this.options.stabilizationTimeThreshold?o.CAPTURING:o.STAY_STILL):(this.stableSince=null,s=o.STAY_STILL)}else s=o.FACE_DETECTED,this.stableSince=null}else s=o.NOT_NEUTRAL_EXPRESSION,this.stableSince=null;else s=o.HEAD_NOT_STRAIGHT,this.stableSince=null;else s=o.FACE_OBSTRUCTED,this.stableSince=null;else s=o.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,a,i){const s=e.getContext("2d");if(!s)return;const r=e.width,l=e.height,c=r/2,d=l/2;s.clearRect(0,0,r,l);const h=.2*r,f=l*g;if(s.fillStyle="rgba(255, 255, 255, 0.35)",s.fillRect(0,0,r,l),s.save(),s.beginPath(),s.ellipse(c,d,h,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(c,d,h,f,0,0,2*Math.PI),s.stroke(),s.strokeStyle="rgba(255, 255, 255, 0.45)",s.lineWidth=1,s.beginPath(),s.moveTo(c-6,d),s.lineTo(c+6,d),s.moveTo(c,d-6),s.lineTo(c,d+6),s.stroke(),t&&a){const e=a.landmarks;if(e.length>=478){const t=e[10],a=e[152],i=e[234],c=e[454],d=e.map(e=>e.x),h=e.map(e=>e.y),f=Math.min(...d),g=Math.max(...d),E=Math.min(...h),p=g-f,S=Math.max(...h)-E,y=.08,T=(f-p*y)*r,b=(E-S*y)*l,O=p*(1+2*y)*r,v=S*(1+2*y)*l;let I="red";n===o.STAY_STILL||n===o.CAPTURING?I="lime":n===o.FACE_DETECTED&&(I="yellow"),s.strokeStyle=I,s.lineWidth=3,s.strokeRect(T,b,O,v);const C=e[4];e[u[0]],e[m[0]],s.fillStyle="cyan",s.beginPath(),s.arc(C.x*r,C.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(a.x*r,a.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(i.x*r,i.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&&i&&i.length>0&&i.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===o.CAPTURING&&!this.isCapturing)return this.isCapturing=!0,yield this.captureImage(),this.setStatus(o.SUCCESS),void this.stop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(o.ERROR,t)}this.animationFrameId=requestAnimationFrame(a)}else this.animationFrameId=requestAnimationFrame(a)});this.animationFrameId=requestAnimationFrame(a)}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(o.ERROR,new Error("Failed to generate image blob"))},"image/jpeg",.95)):this.setStatus(o.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()}}var y=n(848),T=n(953);const b={"pt-BR":{previewQuestion:"O que você achou?",savePhoto:"Salvar foto",tryAgain:"Tentar novamente",cancel:"Cancelar"},en:{previewQuestion:"What do you think?",savePhoto:"Save photo",tryAgain:"Try again",cancel:"Cancel"},es:{previewQuestion:"¿Qué te pareció?",savePhoto:"Guardar foto",tryAgain:"Intentar de nuevo",cancel:"Cancelar"}},O=({onCapture:e,onDismiss:t,locale:n,videoWidth:a=512,videoHeight:i=384,debugMode:s=!1,modelPath:r,styles:l,labels:d})=>{const h=(e=>{if(!e)return"pt-BR";const t=e.toLowerCase();return t.startsWith("en")?"en":t.startsWith("es")?"es":"pt-BR"})(n),u=b[h],m=Object.assign(Object.assign({},u),null!=d?d:{}),f=(0,T.useRef)(null),g=(0,T.useRef)(null),E=(0,T.useRef)(null),[p,O]=(0,T.useState)(o.INITIALIZING),[v,I]=(0,T.useState)(c(o.INITIALIZING,h)),[C,R]=(0,T.useState)(!0),[_,x]=(0,T.useState)(null),A=(0,T.useCallback)((e,t)=>{O(e),I(t)},[]),M=(0,T.useCallback)(e=>{var t;O(o.SUCCESS),I(c(o.SUCCESS,h)),(t=e,new Promise((e,n)=>{const a=new FileReader;a.onloadend=()=>e(a.result),a.onerror=n,a.readAsDataURL(t)})).then(e=>{x(e),R(!1)})},[h]),L=(0,T.useCallback)(()=>{e(null!=_?_:null),null==t||t()},[e,t,_]),F=(0,T.useCallback)(()=>{x(null),O(o.INITIALIZING),I(c(o.INITIALIZING,h)),R(!0)},[h]),N=(0,T.useCallback)((e,t)=>{I(t.message),O(o.ERROR)},[]);(0,T.useEffect)(()=>{const e=f.current;if(!C)return E.current&&(E.current.stop(),E.current=null),void(e&&e.srcObject&&(e.srcObject.getTracks().forEach(e=>e.stop()),e.srcObject=null));if(!e)return;let t=!1;return(n=void 0,l=void 0,c=void 0,d=function*(){var n;try{if(!e.srcObject){const n=yield navigator.mediaDevices.getUserMedia({video:{width:a,height:i}});if(t)return;e.srcObject=n,yield e.play()}const o=g.current;if(o){const t=e.videoWidth||a,n=e.videoHeight||i;o.width=t,o.height=n}if(E.current||t)return;const l={videoElement:e,overlayCanvasElement:null!==(n=g.current)&&void 0!==n?n:void 0,locale:h,debugMode:s,onStatusUpdate:A,onCaptureSuccess:M,onError:N,videoWidth:a,videoHeight:i};r&&(l.modelPath=r);const c=new S(l);E.current=c}catch(e){if(t)return;I(e.message),O(o.ERROR)}},new(c||(c=Promise))(function(e,t){function a(e){try{o(d.next(e))}catch(e){t(e)}}function i(e){try{o(d.throw(e))}catch(e){t(e)}}function o(t){var n;t.done?e(t.value):(n=t.value,n instanceof c?n:new c(function(e){e(n)})).then(a,i)}o((d=d.apply(n,l||[])).next())})).catch(()=>{}),()=>{t=!0,E.current&&(E.current.stop(),E.current=null),e&&e.srcObject&&(e.srcObject.getTracks().forEach(e=>e.stop()),e.srcObject=null)};var n,l,c,d},[C,h,a,i,s,r,A,M,N]);const P=!C&&Boolean(_),k=C,w=Object.assign({width:"100%",maxWidth:640,boxSizing:"border-box"},null==l?void 0:l.container),D=Object.assign({textAlign:"center",display:"flex",alignItems:"center",justifyContent:"center",fontSize:16,padding:"10px 12px",marginBottom:10,borderRadius:10,fontWeight:600,marginTop:30,height:52,boxSizing:"border-box"},null==l?void 0:l.messageBanner),j=Object.assign(Object.assign({},D),{backgroundColor:"#fff7df",color:"#b67219"});k&&(p===o.SUCCESS?(j.backgroundColor="#e0ffdf",j.color="#26c026"):p===o.ERROR&&(j.backgroundColor="#ffdfdf",j.color="#c02626"));const U=Object.assign({width:"100%",maxWidth:512,height:384,margin:"0 auto",borderRadius:10,overflow:"hidden",position:"relative",backgroundColor:"#1a1a1a"},null==l?void 0:l.media),B={display:"block",width:"100%",padding:"8px 12px",borderRadius:4,border:"1px solid #d9d9d9",backgroundColor:"#ffffff",cursor:"pointer",fontSize:14},G=Object.assign(Object.assign(Object.assign({},B),{backgroundColor:"#1dbe32",color:"#ffffff",borderColor:"#1dbe32"}),null==l?void 0:l.primaryButton),H=Object.assign(Object.assign({},B),null==l?void 0:l.secondaryButton);return(0,y.jsxs)("div",{style:w,children:[(0,y.jsx)("div",{style:j,children:P?m.previewQuestion:v}),(0,y.jsx)("div",{style:U,children:k?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)("video",{ref:f,autoPlay:!0,playsInline:!0,muted:!0,style:{width:"100%",height:"100%",objectFit:"cover",transform:"scaleX(-1)"}}),(0,y.jsx)("canvas",{ref:g,style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",pointerEvents:"none",transform:"scaleX(-1)"}})]}):P&&_?(0,y.jsx)("img",{src:_,alt:"Selfie preview",style:{display:"block",width:"100%",height:"100%",objectFit:"cover",transform:"scaleX(-1)",backgroundColor:"transparent",border:"none"}}):(0,y.jsx)("div",{"aria-hidden":!0})}),(0,y.jsx)("div",{style:{marginTop:10},children:P?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)("button",{type:"button",onClick:F,style:H,children:m.tryAgain}),(0,y.jsx)("div",{style:{height:8}}),(0,y.jsx)("button",{type:"button",onClick:L,disabled:!_,style:Object.assign(Object.assign({},G),{opacity:_?1:.6,cursor:_?"pointer":"not-allowed"}),children:m.savePhoto})]}):(0,y.jsx)("button",{type:"button",onClick:()=>{null==t||t(),e(null),R(!1)},style:H,children:m.cancel})})]})},v=S;module.exports=a})();
2
3
  //# sourceMappingURL=face-validator-sdk.cjs.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license React
3
+ * react-jsx-runtime.production.min.js
4
+ *
5
+ * Copyright (c) Facebook, Inc. and its affiliates.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */