face-validator-sdk 1.1.1 → 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
@@ -26,15 +26,17 @@ Real-time selfie validation SDK with face detection, powered by **MediaPipe**. D
26
26
  - 📦 **Multiple builds**: ESM, CJS, UMD
27
27
  - 🚀 **GPU accelerated**: Powered by MediaPipe with GPU support
28
28
 
29
- ## 📦 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**:
30
32
 
31
33
  ```bash
32
- npm install face-validator-sdk @mediapipe/tasks-vision
34
+ npm install face-validator-sdk
33
35
  ```
34
36
 
35
- **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`.
36
38
 
37
- ## 🚀 Quick Start
39
+ ## 🚀 Quick Start (Core API)
38
40
 
39
41
  ```typescript
40
42
  import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
@@ -119,35 +121,131 @@ interface FaceValidatorOptions {
119
121
  }
120
122
  ```
121
123
 
122
- ## 🎭 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:
129
+
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
123
135
 
124
- ### Online Demo
125
- Visit: **[https://face-validator-sdk.vercel.app](https://face-validator-sdk.vercel.app)**
136
+ ### Installation (React project)
126
137
 
127
- ### Local Development
138
+ Your React app should already have `react` and `react-dom` installed. Then:
128
139
 
129
140
  ```bash
130
- # Clone the repository
131
- git clone https://github.com/rwmsousa/face-validator-sdk.git
132
- cd face-validator-sdk
141
+ npm install face-validator-sdk
142
+ ```
133
143
 
134
- # Install dependencies
135
- 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
+ ```
136
169
 
137
- # Run local demo (http://localhost:8081)
138
- 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
+ }
139
207
  ```
140
208
 
141
- ### Build Demo for Production
209
+ ### Labels and i18n
142
210
 
143
- ```bash
144
- # Build SDK + Demo
145
- npm run build
146
- npm run build:demo
211
+ By default, the component renders UI labels in **Portuguese (pt-BR)**, **English (en)** or **Spanish (es)**:
147
212
 
148
- # Demo files output to: demo/dist/
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
+ />
149
227
  ```
150
228
 
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
+ />;
244
+ ```
245
+
246
+ > Tip: you can wrap `ReactSelfieCapture` in your own modal/dialog and pass `onDismiss` to close it from the **Cancel** button.
247
+
248
+
151
249
  ## 🏗️ Architecture
152
250
 
153
251
  ### MediaPipe Integration
@@ -157,28 +255,6 @@ The SDK uses two MediaPipe models running in parallel:
157
255
  1. **FaceLandmarker**: 478 facial landmarks + face detection
158
256
  2. **HandLandmarker**: 21 hand landmarks per hand
159
257
 
160
- ```
161
- ┌─────────────────────────────────────────┐
162
- │ FaceValidator │
163
- ├─────────────────────────────────────────┤
164
- │ ┌─────────────────┐ ┌──────────────┐ │
165
- │ │ FaceLandmarker │ │ HandLandmarker│ │
166
- │ │ (478 points) │ │ (21 pts/hand) │ │
167
- │ └─────────────────┘ └──────────────┘ │
168
- │ ↓ ↓ │
169
- │ ┌──────────────────────────────────┐ │
170
- │ │ Validation Pipeline │ │
171
- │ │ 1. Distance │ │
172
- │ │ 2. Centering │ │
173
- │ │ 3. Face geometry │ │
174
- │ │ 4. Head pose │ │
175
- │ │ 5. Hand proximity ⭐NEW │ │
176
- │ │ 6. Illumination │ │
177
- │ │ 7. Stability │ │
178
- │ └──────────────────────────────────┘ │
179
- └─────────────────────────────────────────┘
180
- ```
181
-
182
258
  ## 📚 Why MediaPipe?
183
259
 
184
260
  Migrated from face-api.js (discontinued 2021) to MediaPipe (Google):
@@ -192,64 +268,6 @@ Migrated from face-api.js (discontinued 2021) to MediaPipe (Google):
192
268
  | Accuracy | ~60-70% | ✅ **~90-95%** |
193
269
  | Model size | ~8MB | ~15MB |
194
270
 
195
- ## 🔧 Development
196
-
197
- ### Scripts
198
-
199
- ```bash
200
- npm run dev # Start local dev server (webpack)
201
- npm run build # Build SDK (CJS, ESM, UMD)
202
- npm run build:demo # Build production demo
203
- npm run lint # Run ESLint
204
- npm run format # Format code with Prettier
205
- npm run test # Run tests (Jest)
206
- ```
207
-
208
- ### Project Structure
209
-
210
- ```
211
- face-validator-sdk/
212
- ├── src/
213
- │ ├── FaceValidator.ts # Main validator class
214
- │ ├── types.ts # TypeScript types
215
- │ ├── utils.ts # Validation functions
216
- │ ├── i18n.ts # Internationalization
217
- │ └── index.ts # Public API exports
218
- ├── demo/
219
- │ ├── demo.ts # Local dev demo
220
- │ ├── demo-standalone.ts # Production demo
221
- │ └── public/
222
- │ └── index.html # Demo HTML
223
- ├── dist/ # SDK build output
224
- │ ├── face-validator-sdk.esm.js
225
- │ ├── face-validator-sdk.cjs.js
226
- │ ├── face-validator-sdk.umd.js
227
- │ └── types/ # TypeScript declarations
228
- ├── .github/
229
- │ └── workflows/
230
- │ ├── ci.yml # CI/CD pipeline
231
- │ └── deploy-vercel.yml # Vercel deployment
232
- └── vercel.json # Vercel configuration
233
- ```
234
-
235
- ## 🚀 Deployment
236
-
237
- ### Vercel (Automatic)
238
-
239
- 1. Connect repository to Vercel
240
- 2. Add secrets to GitHub:
241
- - `VERCEL_TOKEN`
242
- - `VERCEL_ORG_ID`
243
- - `VERCEL_PROJECT_ID`
244
- 3. Push to `main` branch → auto-deploy
245
-
246
- ### Manual Deployment
247
-
248
- ```bash
249
- npm run build:demo
250
- # Deploy demo/dist/ to any static host
251
- ```
252
-
253
271
  ## 🤝 Contributing
254
272
 
255
273
  Contributions are welcome! Please:
@@ -260,17 +278,6 @@ Contributions are welcome! Please:
260
278
  4. Push to branch: `git push origin feature/amazing-feature`
261
279
  5. Open a Pull Request
262
280
 
263
- ### Commit Convention
264
-
265
- We use [Conventional Commits](https://www.conventionalcommits.org/):
266
-
267
- - `feat:` New feature
268
- - `fix:` Bug fix
269
- - `docs:` Documentation changes
270
- - `chore:` Maintenance tasks
271
- - `refactor:` Code refactoring
272
- - `test:` Add/update tests
273
-
274
281
  ## 📄 License
275
282
 
276
283
  MIT License - see [LICENSE](LICENSE) file for details.
@@ -288,4 +295,3 @@ MIT License - see [LICENSE](LICENSE) file for details.
288
295
 
289
296
  ---
290
297
 
291
- 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
+ */