@jaak.ai/stamps 2.0.0-beta.4 → 2.0.0-beta.6
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 +660 -510
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +241 -41
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/my-component/my-component.css +54 -24
- package/dist/collection/components/my-component/my-component.js +91 -5
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/CameraService.js +158 -21
- package/dist/collection/services/CameraService.js.map +1 -1
- package/dist/collection/services/DetectionService.js +34 -16
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/components/jaak-stamps.js +244 -41
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/jaak-stamps-webcomponent.js +1 -1
- package/dist/esm/jaak-stamps.entry.js +241 -41
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-65990bdd.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-65990bdd.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +6 -0
- package/dist/types/components.d.ts +18 -2
- package/dist/types/services/CameraService.d.ts +1 -0
- package/package.json +2 -2
- package/dist/jaak-stamps-webcomponent/p-10ee2dab.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-10ee2dab.entry.js.map +0 -1
|
@@ -18,7 +18,7 @@ var patchBrowser = () => {
|
|
|
18
18
|
|
|
19
19
|
patchBrowser().then(async (options) => {
|
|
20
20
|
await index.globalScripts();
|
|
21
|
-
return index.bootstrapLazy([["jaak-stamps.cjs",[[1,"jaak-stamps",{"debug":[4],"alignmentTolerance":[2,"alignment-tolerance"],"maskSize":[2,"mask-size"],"cropMargin":[2,"crop-margin"],"useDocumentClassification":[4,"use-document-classification"],"preferredCamera":[1,"preferred-camera"],"captureDelay":[2,"capture-delay"],"detectionBoxes":[32],"sideAlignment":[32],"isMaskReady":[32],"shouldMirrorVideo":[32],"showCameraSelector":[32],"isSwitchingCamera":[32],"hasDocumentDetected":[32],"currentStatus":[32],"performanceData":[32],"getCapturedImages":[64],"isProcessCompleted":[64],"startCapture":[64],"stopCapture":[64],"resetCapture":[64],"skipBackCapture":[64],"getStatus":[64],"preloadModel":[64],"getCameraInfo":[64],"setPreferredCamera":[64],"setCaptureDelay":[64],"getCaptureDelay":[64],"setTorchEnabled":[64],"focusAtPoint":[64]}]]]], options);
|
|
21
|
+
return index.bootstrapLazy([["jaak-stamps.cjs",[[1,"jaak-stamps",{"debug":[4],"alignmentTolerance":[2,"alignment-tolerance"],"maskSize":[2,"mask-size"],"cropMargin":[2,"crop-margin"],"useDocumentClassification":[4,"use-document-classification"],"preferredCamera":[1,"preferred-camera"],"captureDelay":[2,"capture-delay"],"enableBackDocumentTimer":[4,"enable-back-document-timer"],"backDocumentTimerDuration":[2,"back-document-timer-duration"],"detectionBoxes":[32],"sideAlignment":[32],"isMaskReady":[32],"shouldMirrorVideo":[32],"showCameraSelector":[32],"isSwitchingCamera":[32],"hasDocumentDetected":[32],"currentStatus":[32],"performanceData":[32],"backDocumentTimerRemaining":[32],"getCapturedImages":[64],"isProcessCompleted":[64],"startCapture":[64],"stopCapture":[64],"resetCapture":[64],"skipBackCapture":[64],"getStatus":[64],"preloadModel":[64],"getCameraInfo":[64],"setPreferredCamera":[64],"setCaptureDelay":[64],"getCaptureDelay":[64],"setTorchEnabled":[64],"focusAtPoint":[64]}]]]], options);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
exports.setNonce = index.setNonce;
|
|
@@ -201,6 +201,7 @@ class CameraService {
|
|
|
201
201
|
deviceType = 'desktop';
|
|
202
202
|
preferredCameraFacing = null;
|
|
203
203
|
preferredCamera = 'auto';
|
|
204
|
+
isManuallySelected = false;
|
|
204
205
|
constructor(logger, eventBus, preferredCamera = 'auto') {
|
|
205
206
|
this.logger = logger;
|
|
206
207
|
this.eventBus = eventBus;
|
|
@@ -247,6 +248,11 @@ class CameraService {
|
|
|
247
248
|
}))
|
|
248
249
|
});
|
|
249
250
|
this.setInitialCameraPreference();
|
|
251
|
+
this.logger.state('PREFERENCIA_INICIAL_APLICADA', {
|
|
252
|
+
selectedCameraId: this.selectedCameraId,
|
|
253
|
+
preferredCameraFacing: this.preferredCameraFacing,
|
|
254
|
+
preferredCamera: this.preferredCamera
|
|
255
|
+
});
|
|
250
256
|
this.eventBus.emit('camera-changed', this.selectedCameraId);
|
|
251
257
|
return this.availableCameras;
|
|
252
258
|
}
|
|
@@ -272,6 +278,7 @@ class CameraService {
|
|
|
272
278
|
}
|
|
273
279
|
this.selectedCameraId = cameraId;
|
|
274
280
|
this.updatePreferredFacing(camera);
|
|
281
|
+
this.isManuallySelected = true; // Mark as manually selected
|
|
275
282
|
this.savePreference();
|
|
276
283
|
this.eventBus.emit('camera-changed', cameraId);
|
|
277
284
|
}
|
|
@@ -279,12 +286,31 @@ class CameraService {
|
|
|
279
286
|
return this.preferredCameraFacing;
|
|
280
287
|
}
|
|
281
288
|
async setupCamera(constraints) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
289
|
+
try {
|
|
290
|
+
const finalConstraints = constraints || await this.getMaxResolution();
|
|
291
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
292
|
+
video: finalConstraints,
|
|
293
|
+
audio: false
|
|
294
|
+
});
|
|
295
|
+
return stream;
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
this.logger.error('Error en setupCamera, intentando con restricciones básicas:', error);
|
|
299
|
+
// Fallback to basic constraints if getMaxResolution fails
|
|
300
|
+
const basicConstraints = {
|
|
301
|
+
width: { ideal: 1280 },
|
|
302
|
+
height: { ideal: 720 }
|
|
303
|
+
};
|
|
304
|
+
// Only add facingMode if not on desktop to avoid OverconstrainedError
|
|
305
|
+
if (this.deviceType !== 'desktop' && this.preferredCameraFacing) {
|
|
306
|
+
basicConstraints.facingMode = this.preferredCameraFacing;
|
|
307
|
+
}
|
|
308
|
+
this.logger.state('USANDO_RESTRICCIONES_BASICAS', { constraints: basicConstraints });
|
|
309
|
+
return await navigator.mediaDevices.getUserMedia({
|
|
310
|
+
video: basicConstraints,
|
|
311
|
+
audio: false
|
|
312
|
+
});
|
|
313
|
+
}
|
|
288
314
|
}
|
|
289
315
|
async switchCamera(cameraId) {
|
|
290
316
|
const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);
|
|
@@ -296,7 +322,8 @@ class CameraService {
|
|
|
296
322
|
await this.setSelectedCamera(cameraId);
|
|
297
323
|
this.logger.state('CAMARA_CAMBIADA', {
|
|
298
324
|
label: selectedCamera.label,
|
|
299
|
-
deviceId: selectedCamera.deviceId
|
|
325
|
+
deviceId: selectedCamera.deviceId,
|
|
326
|
+
isManuallySelected: this.isManuallySelected
|
|
300
327
|
});
|
|
301
328
|
}
|
|
302
329
|
async flipToNextCamera() {
|
|
@@ -378,6 +405,13 @@ class CameraService {
|
|
|
378
405
|
setInitialCameraPreference() {
|
|
379
406
|
if (this.availableCameras.length === 0)
|
|
380
407
|
return;
|
|
408
|
+
// Clear any existing localStorage preferences to ensure fresh start
|
|
409
|
+
localStorage.removeItem('jaak-stamps-camera-preference');
|
|
410
|
+
this.logger.state('APLICANDO_PREFERENCIA_INICIAL', {
|
|
411
|
+
preferredCamera: this.preferredCamera,
|
|
412
|
+
availableCamerasCount: this.availableCameras.length
|
|
413
|
+
});
|
|
414
|
+
this.isManuallySelected = false; // Reset manual selection flag
|
|
381
415
|
if (this.preferredCamera === 'front') {
|
|
382
416
|
this.selectFrontCamera();
|
|
383
417
|
}
|
|
@@ -390,23 +424,56 @@ class CameraService {
|
|
|
390
424
|
}
|
|
391
425
|
selectFrontCamera() {
|
|
392
426
|
this.preferredCameraFacing = 'user';
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
427
|
+
this.logger.state('BUSCANDO_CAMARA_FRONTAL', {
|
|
428
|
+
availableCameras: this.availableCameras.map(cam => ({
|
|
429
|
+
id: cam.deviceId,
|
|
430
|
+
label: cam.label
|
|
431
|
+
}))
|
|
432
|
+
});
|
|
433
|
+
const frontCamera = this.availableCameras.find(camera => {
|
|
434
|
+
const label = camera.label.toLowerCase();
|
|
435
|
+
return label.includes('front') ||
|
|
436
|
+
label.includes('user') ||
|
|
437
|
+
label.includes('selfie') ||
|
|
438
|
+
label.includes('frontal') ||
|
|
439
|
+
(!label.includes('back') && !label.includes('rear') && !label.includes('environment'));
|
|
440
|
+
});
|
|
441
|
+
// If not found, use the first camera (usually front on mobile)
|
|
397
442
|
this.selectedCameraId = frontCamera ? frontCamera.deviceId : this.availableCameras[0].deviceId;
|
|
398
443
|
this.logger.state('CAMARA_FRONTAL_SELECCIONADA', {
|
|
444
|
+
found: !!frontCamera,
|
|
399
445
|
label: frontCamera?.label || this.availableCameras[0].label,
|
|
400
446
|
deviceId: this.selectedCameraId
|
|
401
447
|
});
|
|
402
448
|
}
|
|
403
449
|
selectBackCamera() {
|
|
404
450
|
this.preferredCameraFacing = 'environment';
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
451
|
+
this.logger.state('BUSCANDO_CAMARA_TRASERA', {
|
|
452
|
+
availableCameras: this.availableCameras.map(cam => ({
|
|
453
|
+
id: cam.deviceId,
|
|
454
|
+
label: cam.label
|
|
455
|
+
}))
|
|
456
|
+
});
|
|
457
|
+
// Try to find rear camera with multiple detection methods
|
|
458
|
+
let backCamera = this.availableCameras.find(camera => {
|
|
459
|
+
const label = camera.label.toLowerCase();
|
|
460
|
+
return label.includes('back') ||
|
|
461
|
+
label.includes('rear') ||
|
|
462
|
+
label.includes('environment') ||
|
|
463
|
+
label.includes('trasera') ||
|
|
464
|
+
label.includes('posterior');
|
|
465
|
+
});
|
|
466
|
+
// If not found by label, try to use the last camera (usually rear on mobile)
|
|
467
|
+
if (!backCamera && this.availableCameras.length > 1) {
|
|
468
|
+
backCamera = this.availableCameras[this.availableCameras.length - 1];
|
|
469
|
+
this.logger.state('USANDO_ULTIMA_CAMARA_COMO_TRASERA', {
|
|
470
|
+
label: backCamera.label,
|
|
471
|
+
deviceId: backCamera.deviceId
|
|
472
|
+
});
|
|
473
|
+
}
|
|
408
474
|
this.selectedCameraId = backCamera ? backCamera.deviceId : this.availableCameras[0].deviceId;
|
|
409
475
|
this.logger.state('CAMARA_TRASERA_SELECCIONADA', {
|
|
476
|
+
found: !!backCamera,
|
|
410
477
|
label: backCamera?.label || this.availableCameras[0].label,
|
|
411
478
|
deviceId: this.selectedCameraId
|
|
412
479
|
});
|
|
@@ -417,8 +484,10 @@ class CameraService {
|
|
|
417
484
|
this.logger.state('CAMARA_AUTO_SELECCIONADA_MOBILE', { type: 'rear' });
|
|
418
485
|
}
|
|
419
486
|
else {
|
|
420
|
-
|
|
487
|
+
// For desktop, prefer front camera (usually built-in webcam)
|
|
488
|
+
this.selectFrontCamera();
|
|
421
489
|
this.logger.state('CAMARA_AUTO_SELECCIONADA_DESKTOP', {
|
|
490
|
+
type: 'front',
|
|
422
491
|
label: this.availableCameras[0].label,
|
|
423
492
|
deviceId: this.selectedCameraId
|
|
424
493
|
});
|
|
@@ -432,15 +501,60 @@ class CameraService {
|
|
|
432
501
|
}
|
|
433
502
|
async getMaxResolution() {
|
|
434
503
|
try {
|
|
504
|
+
// Ensure device type is detected before determining constraints
|
|
505
|
+
if (!this.deviceType || this.deviceType === 'desktop') {
|
|
506
|
+
await this.detectDeviceType();
|
|
507
|
+
}
|
|
435
508
|
const videoConstraints = {};
|
|
436
|
-
|
|
509
|
+
this.logger.state('CONFIGURANDO_CONSTRAINS_CAMARA', {
|
|
510
|
+
selectedCameraId: this.selectedCameraId,
|
|
511
|
+
preferredCameraFacing: this.preferredCameraFacing,
|
|
512
|
+
preferredCamera: this.preferredCamera,
|
|
513
|
+
deviceType: this.deviceType,
|
|
514
|
+
isManuallySelected: this.isManuallySelected
|
|
515
|
+
});
|
|
516
|
+
if (this.isManuallySelected && this.selectedCameraId) {
|
|
517
|
+
// When manually selected, use deviceId for exact camera selection
|
|
437
518
|
videoConstraints.deviceId = { exact: this.selectedCameraId };
|
|
519
|
+
this.logger.state('USANDO_CAMARA_MANUAL', {
|
|
520
|
+
deviceId: this.selectedCameraId
|
|
521
|
+
});
|
|
438
522
|
}
|
|
439
523
|
else if (this.preferredCameraFacing) {
|
|
440
|
-
|
|
524
|
+
// Use facingMode for initial preference-based selection
|
|
525
|
+
// Use 'ideal' instead of 'exact' to avoid OverconstrainedError on desktop
|
|
526
|
+
const facingConstraint = this.deviceType === 'desktop'
|
|
527
|
+
? { ideal: this.preferredCameraFacing }
|
|
528
|
+
: { exact: this.preferredCameraFacing };
|
|
529
|
+
videoConstraints.facingMode = facingConstraint;
|
|
530
|
+
this.logger.state('USANDO_FACING_CONSTRAINT', {
|
|
531
|
+
facingMode: this.preferredCameraFacing,
|
|
532
|
+
constraintType: this.deviceType === 'desktop' ? 'ideal' : 'exact'
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
else if (this.selectedCameraId) {
|
|
536
|
+
videoConstraints.deviceId = { exact: this.selectedCameraId };
|
|
537
|
+
this.logger.state('USANDO_CAMARA_SELECCIONADA', {
|
|
538
|
+
deviceId: this.selectedCameraId
|
|
539
|
+
});
|
|
441
540
|
}
|
|
442
541
|
else {
|
|
443
|
-
|
|
542
|
+
// Use preferredCamera setting to determine default facing mode
|
|
543
|
+
if (this.preferredCamera === 'front') {
|
|
544
|
+
videoConstraints.facingMode = "user";
|
|
545
|
+
}
|
|
546
|
+
else if (this.preferredCamera === 'back') {
|
|
547
|
+
videoConstraints.facingMode = "environment";
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
// Auto mode: use environment for mobile/tablet, user for desktop
|
|
551
|
+
videoConstraints.facingMode = (this.deviceType === 'mobile' || this.deviceType === 'tablet') ? "environment" : "user";
|
|
552
|
+
}
|
|
553
|
+
this.logger.state('USANDO_PREFERENCIA_CONFIGURADA', {
|
|
554
|
+
facingMode: videoConstraints.facingMode,
|
|
555
|
+
preferredCamera: this.preferredCamera,
|
|
556
|
+
deviceType: this.deviceType
|
|
557
|
+
});
|
|
444
558
|
}
|
|
445
559
|
const tempStream = await navigator.mediaDevices.getUserMedia({
|
|
446
560
|
video: videoConstraints
|
|
@@ -472,6 +586,10 @@ class CameraService {
|
|
|
472
586
|
}
|
|
473
587
|
catch (err) {
|
|
474
588
|
this.logger.warn('No se pudieron obtener capacidades de cámara, usando configuración de respaldo');
|
|
589
|
+
// Ensure device type is detected before determining constraints
|
|
590
|
+
if (!this.deviceType || this.deviceType === 'desktop') {
|
|
591
|
+
await this.detectDeviceType();
|
|
592
|
+
}
|
|
475
593
|
const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
|
|
476
594
|
const fallbackConstraints = {
|
|
477
595
|
width: { ideal: isTablet ? 1280 : 1920 },
|
|
@@ -479,14 +597,33 @@ class CameraService {
|
|
|
479
597
|
};
|
|
480
598
|
// Apply basic focus settings even for fallback
|
|
481
599
|
this.applyBasicFocusSettings(fallbackConstraints);
|
|
482
|
-
if (this.selectedCameraId) {
|
|
600
|
+
if (this.isManuallySelected && this.selectedCameraId) {
|
|
601
|
+
// When manually selected, use deviceId for exact camera selection
|
|
483
602
|
fallbackConstraints.deviceId = { exact: this.selectedCameraId };
|
|
484
603
|
}
|
|
485
604
|
else if (this.preferredCameraFacing) {
|
|
486
|
-
|
|
605
|
+
// Use facingMode for initial preference-based selection
|
|
606
|
+
// Use 'ideal' instead of 'exact' to avoid OverconstrainedError on desktop
|
|
607
|
+
const facingConstraint = this.deviceType === 'desktop'
|
|
608
|
+
? { ideal: this.preferredCameraFacing }
|
|
609
|
+
: { exact: this.preferredCameraFacing };
|
|
610
|
+
fallbackConstraints.facingMode = facingConstraint;
|
|
611
|
+
}
|
|
612
|
+
else if (this.selectedCameraId) {
|
|
613
|
+
fallbackConstraints.deviceId = { exact: this.selectedCameraId };
|
|
487
614
|
}
|
|
488
615
|
else {
|
|
489
|
-
|
|
616
|
+
// Use preferredCamera setting to determine default facing mode
|
|
617
|
+
if (this.preferredCamera === 'front') {
|
|
618
|
+
fallbackConstraints.facingMode = "user";
|
|
619
|
+
}
|
|
620
|
+
else if (this.preferredCamera === 'back') {
|
|
621
|
+
fallbackConstraints.facingMode = "environment";
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
// Auto mode: use environment for mobile/tablet, user for desktop
|
|
625
|
+
fallbackConstraints.facingMode = (this.deviceType === 'mobile' || this.deviceType === 'tablet') ? "environment" : "user";
|
|
626
|
+
}
|
|
490
627
|
}
|
|
491
628
|
return fallbackConstraints;
|
|
492
629
|
}
|
|
@@ -730,7 +867,7 @@ class DetectionService {
|
|
|
730
867
|
modelLoaded = false;
|
|
731
868
|
deviceStrategy;
|
|
732
869
|
MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/ddmyp-v2.onnx";
|
|
733
|
-
MOBILENET_MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/
|
|
870
|
+
MOBILENET_MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/squeezenet_new_fp32.onnx";
|
|
734
871
|
MOBILENET_CLASSES_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.json";
|
|
735
872
|
INPUT_SIZE = 320;
|
|
736
873
|
CONFIDENCE_THRESHOLD = 0.6;
|
|
@@ -792,15 +929,27 @@ class DetectionService {
|
|
|
792
929
|
}
|
|
793
930
|
try {
|
|
794
931
|
this.logger.state('CARGANDO_MODELO_MOBILENET', { path: this.MOBILENET_MODEL_PATH });
|
|
795
|
-
//
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
932
|
+
// Try to load class map (optional - SqueezeNet may not need it for basic classification)
|
|
933
|
+
try {
|
|
934
|
+
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH);
|
|
935
|
+
if (classResponse.ok) {
|
|
936
|
+
this.mobileNetClassMap = await classResponse.json();
|
|
937
|
+
this.logger.state('CLASES_MOBILENET_CARGADAS', {
|
|
938
|
+
classCount: Object.keys(this.mobileNetClassMap).length
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
catch (error) {
|
|
943
|
+
this.logger.warn('No se pudo cargar el mapa de clases de MobileNet, usando clasificación básica');
|
|
944
|
+
// Create a basic class map for document types
|
|
945
|
+
this.mobileNetClassMap = {
|
|
946
|
+
"0": "document",
|
|
947
|
+
"1": "id_card",
|
|
948
|
+
"2": "passport",
|
|
949
|
+
"3": "license",
|
|
950
|
+
"4": "other"
|
|
951
|
+
};
|
|
799
952
|
}
|
|
800
|
-
this.mobileNetClassMap = await classResponse.json();
|
|
801
|
-
this.logger.state('CLASES_MOBILENET_CARGADAS', {
|
|
802
|
-
classCount: Object.keys(this.mobileNetClassMap).length
|
|
803
|
-
});
|
|
804
953
|
// Load model
|
|
805
954
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
806
955
|
try {
|
|
@@ -889,7 +1038,7 @@ class DetectionService {
|
|
|
889
1038
|
try {
|
|
890
1039
|
this.logger.state('CLASIFICANDO_DOCUMENTO', { timestamp: Date.now() });
|
|
891
1040
|
const inputTensor = this.preprocessMobileNet(canvas);
|
|
892
|
-
const feeds = {
|
|
1041
|
+
const feeds = { [this.mobileNetSession.inputNames[0]]: inputTensor };
|
|
893
1042
|
const results = await this.mobileNetSession.run(feeds);
|
|
894
1043
|
const output = results[Object.keys(results)[0]].data;
|
|
895
1044
|
const maxIdx = output.reduce((bestIdx, val, idx, arr) => val > arr[bestIdx] ? idx : bestIdx, 0);
|
|
@@ -899,7 +1048,8 @@ class DetectionService {
|
|
|
899
1048
|
class: className,
|
|
900
1049
|
confidence: confidence.toFixed(3),
|
|
901
1050
|
classIndex: maxIdx,
|
|
902
|
-
timestamp: Date.now()
|
|
1051
|
+
timestamp: Date.now(),
|
|
1052
|
+
results
|
|
903
1053
|
});
|
|
904
1054
|
return {
|
|
905
1055
|
class: className,
|
|
@@ -938,7 +1088,7 @@ class DetectionService {
|
|
|
938
1088
|
displayedVideoHeight = INPUT_SIZE / videoAspectRatio;
|
|
939
1089
|
}
|
|
940
1090
|
else {
|
|
941
|
-
// Video is taller - pillarboxed in display
|
|
1091
|
+
// Video is taller - pillarboxed in display
|
|
942
1092
|
displayedVideoHeight = INPUT_SIZE;
|
|
943
1093
|
displayedVideoWidth = INPUT_SIZE * videoAspectRatio;
|
|
944
1094
|
}
|
|
@@ -989,7 +1139,7 @@ class DetectionService {
|
|
|
989
1139
|
const leftAligned = Math.abs(docLeft - maskLeft) <= tolerance;
|
|
990
1140
|
return {
|
|
991
1141
|
top: topAligned && leftAligned, // Esquina superior izquierda: borde top Y left alineados
|
|
992
|
-
right: topAligned && rightAligned, // Esquina superior derecha: borde top Y right alineados
|
|
1142
|
+
right: topAligned && rightAligned, // Esquina superior derecha: borde top Y right alineados
|
|
993
1143
|
bottom: bottomAligned && leftAligned, // Esquina inferior izquierda: borde bottom Y left alineados
|
|
994
1144
|
left: bottomAligned && rightAligned // Esquina inferior derecha: borde bottom Y right alineados
|
|
995
1145
|
};
|
|
@@ -1074,15 +1224,20 @@ class DetectionService {
|
|
|
1074
1224
|
tempCanvas.width = 224;
|
|
1075
1225
|
tempCanvas.height = 224;
|
|
1076
1226
|
const tempCtx = tempCanvas.getContext('2d');
|
|
1227
|
+
// Resize image to 224x224 for SqueezeNet (using MobileNet interface)
|
|
1077
1228
|
tempCtx.drawImage(canvas, 0, 0, 224, 224);
|
|
1078
1229
|
const imageData = tempCtx.getImageData(0, 0, 224, 224);
|
|
1079
1230
|
const data = imageData.data;
|
|
1080
1231
|
const hw = 224 * 224;
|
|
1081
1232
|
const arr = new Float32Array(3 * hw);
|
|
1233
|
+
// SqueezeNet preprocessing: normalize to [0,1] range and arrange in CHW format
|
|
1082
1234
|
for (let i = 0; i < hw; i++) {
|
|
1083
|
-
|
|
1084
|
-
arr[
|
|
1085
|
-
|
|
1235
|
+
// Red channel
|
|
1236
|
+
arr[i] = data[i * 4 + 0] / 255.0;
|
|
1237
|
+
// Green channel
|
|
1238
|
+
arr[hw + i] = data[i * 4 + 1] / 255.0;
|
|
1239
|
+
// Blue channel
|
|
1240
|
+
arr[2 * hw + i] = data[i * 4 + 2] / 255.0;
|
|
1086
1241
|
}
|
|
1087
1242
|
return new window.ort.Tensor('float32', arr, [1, 3, 224, 224]);
|
|
1088
1243
|
}
|
|
@@ -1143,7 +1298,7 @@ class ServiceContainer {
|
|
|
1143
1298
|
}
|
|
1144
1299
|
}
|
|
1145
1300
|
|
|
1146
|
-
const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);color:#fff;font-size:14px;font-weight:600;text-align:center;white-space:normal;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;z-index:20}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-button{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateX(-50%) translateY(0)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(255, 255, 255, 0.3);border-top:2px solid #ffffff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:space-between;align-items:center;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:8px;font-weight:400}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}";
|
|
1301
|
+
const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);color:#fff;font-size:14px;font-weight:600;text-align:center;white-space:normal;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);padding:12px 20px;border-radius:8px;max-width:300px;z-index:20;border:1px solid rgba(255, 255, 255, 0.1)}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(255, 255, 255, 0.3);border-top:2px solid #ffffff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:space-between;align-items:center;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:8px;font-weight:400}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}";
|
|
1147
1302
|
|
|
1148
1303
|
const JaakStamps = class {
|
|
1149
1304
|
constructor(hostRef) {
|
|
@@ -1154,11 +1309,13 @@ const JaakStamps = class {
|
|
|
1154
1309
|
get el() { return index.getElement(this); }
|
|
1155
1310
|
debug = false;
|
|
1156
1311
|
alignmentTolerance = 15;
|
|
1157
|
-
maskSize =
|
|
1312
|
+
maskSize = 90;
|
|
1158
1313
|
cropMargin = 20;
|
|
1159
1314
|
useDocumentClassification = false;
|
|
1160
1315
|
preferredCamera = 'auto';
|
|
1161
1316
|
captureDelay = 1500;
|
|
1317
|
+
enableBackDocumentTimer = false;
|
|
1318
|
+
backDocumentTimerDuration = 20;
|
|
1162
1319
|
captureCompleted;
|
|
1163
1320
|
isReady;
|
|
1164
1321
|
// State derived from services
|
|
@@ -1187,6 +1344,7 @@ const JaakStamps = class {
|
|
|
1187
1344
|
successfulDetections: 0,
|
|
1188
1345
|
detectionRate: 0
|
|
1189
1346
|
};
|
|
1347
|
+
backDocumentTimerRemaining = 0;
|
|
1190
1348
|
// Services
|
|
1191
1349
|
serviceContainer;
|
|
1192
1350
|
logger;
|
|
@@ -1205,6 +1363,7 @@ const JaakStamps = class {
|
|
|
1205
1363
|
hasScreenshotTaken = false;
|
|
1206
1364
|
alignmentStartTime;
|
|
1207
1365
|
alignmentTimer;
|
|
1366
|
+
backDocumentTimer;
|
|
1208
1367
|
// Performance monitoring
|
|
1209
1368
|
performanceMetrics = {
|
|
1210
1369
|
fps: 0,
|
|
@@ -1670,7 +1829,13 @@ const JaakStamps = class {
|
|
|
1670
1829
|
});
|
|
1671
1830
|
}
|
|
1672
1831
|
// Update document detection state
|
|
1832
|
+
const wasDocumentDetected = this.hasDocumentDetected;
|
|
1673
1833
|
this.hasDocumentDetected = detections.length > 0;
|
|
1834
|
+
// Restart timer if document detected during back capture
|
|
1835
|
+
if (!wasDocumentDetected && this.hasDocumentDetected &&
|
|
1836
|
+
captureState.step === 'back' && this.enableBackDocumentTimer) {
|
|
1837
|
+
this.startBackDocumentTimer();
|
|
1838
|
+
}
|
|
1674
1839
|
// Adaptive frame rate
|
|
1675
1840
|
if (detections.length === 0) {
|
|
1676
1841
|
this.consecutiveFailures++;
|
|
@@ -1747,7 +1912,7 @@ const JaakStamps = class {
|
|
|
1747
1912
|
availableCameras: [],
|
|
1748
1913
|
selectedCameraId: null,
|
|
1749
1914
|
deviceType: 'desktop'};
|
|
1750
|
-
return (index.h("div", { key: '
|
|
1915
|
+
return (index.h("div", { key: '9d4d77042218ab3d0bc4fb1ce879db09dc3e78b4', class: "detector-container" }, index.h("div", { key: '51ac8ccb1275637282b038ad8364d19f14ef61b1', class: "video-container" }, index.h("video", { key: 'a34f0481da20ec5b7b62b21aa275ec5f6dfec5d1', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), index.h("div", { key: '43b71dfe3eaf20ffa66a4139e24b1af6a2074631', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index$1) => (index.h("div", { key: index$1, class: "detection-box", style: {
|
|
1751
1916
|
position: 'absolute',
|
|
1752
1917
|
left: `${box.x}px`,
|
|
1753
1918
|
top: `${box.y}px`,
|
|
@@ -1756,7 +1921,9 @@ const JaakStamps = class {
|
|
|
1756
1921
|
border: '2px solid #32406C',
|
|
1757
1922
|
pointerEvents: 'none',
|
|
1758
1923
|
boxSizing: 'border-box'
|
|
1759
|
-
} })))), this.isMaskReady && (index.h("div", { key: '
|
|
1924
|
+
} })))), this.isMaskReady && (index.h("div", { key: 'c2596f8cabb227e3ea3b082495f52b141cb1690f', class: "overlay-mask" }, index.h("div", { key: 'ba1ea39b5df1cd312a9bd9410b7225a29cfb8a29', class: "card-outline" }, index.h("div", { key: '6ce4d4d969d5f3fbcf75a4eb285f61d434c5633b', class: "side side-top" }), index.h("div", { key: '36653ecc4fcda25dfc84bc4d1f4c4c5dfb71ced7', class: "side side-right" }), index.h("div", { key: '8b108fa8fb370b229a0d1a77cb1ff044319a8f75', class: "side side-bottom" }), index.h("div", { key: 'a7aa3b45c065a50445aafd5c3ccefb57b0c9c769', class: "side side-left" }), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: 'b94b92273e12a019238e11ae57f21e140f13c3f1', class: "guide-text" }, "Alinee su identificaci\u00F3n con el marco"))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '144c3741003ec0cc148bb6ce92012d7b3cd110c8', class: "skip-section" }, index.h("div", { key: 'f0a984a6b75afa374c5d47da9d53e39f98071da0', class: "skip-explanation" }, "Si tu documento no tiene lado trasero, da clic en el bot\u00F3n para continuar con el proceso"), index.h("button", { key: '57ac30d776e30709aab6e0a621cef889da3cc677', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
1925
|
+
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
1926
|
+
: 'Saltar reverso'))), captureState.isVideoActive && (index.h("div", { key: 'bd45628707e0169317ed3dd4247e1056d7046104', class: "camera-controls" }, index.h("button", { key: 'e750fbf4d5d76d9e9596823bb2b86801ef19e485', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (index.h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (index.h("div", { key: '40ada780a8ea13aeeaee9cb39bc6496f69f2679d', class: "camera-selector-dropdown" }, index.h("div", { key: '3a920bb02ba024f0359513f10544bf0528ecee6d', class: "camera-selector-header" }, index.h("span", { key: 'e5efd478a602b13821ba53cb9a7a59d1b4a71178' }, "Seleccionar C\u00E1mara"), index.h("button", { key: '2df6cc11e162be74862029e33815927e3fa8f2f8', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: '06934642dff134c9e1a6872f935a5892a9c7b835', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (index.h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`), cameraInfo.selectedCameraId === camera.id && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: '8d5ede287c2efb0ebe418bee8dc7fb310443c991', class: "device-info" }, index.h("small", { key: 'b7dd10565113694b09f01ab28ffa29262ae1d2bc' }, "Dispositivo: ", cameraInfo.deviceType)))))), captureState.isCapturing && (index.h("div", { key: '2f9e549e6c0c56d4db5835e58361fb17bba6cf90', class: "capture-animation" })), captureState.showFlipAnimation && (index.h("div", { key: 'a6ef0ed127f23e9d764896ed743ca8d8454851a6', class: "flip-animation" }, index.h("div", { key: '0b04411f88a634bb3243b00ca2d5a45fd6962dea', class: "id-card-icon" }), index.h("div", { key: '9327be58879b407cea87f455989406e58302e9d1', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (index.h("div", { key: 'bd268578b5003e99256e3340b4a9a6fb7656be64', class: "success-animation" }, index.h("div", { key: '064e0a5b9851de14bc6829f028e7ea7161fd1633', class: "check-icon" }), index.h("div", { key: 'bdcd94a8806399e396e218a359c10b11d05f38d6', class: "success-text" }, "\u00A1Proceso completado!"))), index.h("div", { key: '3d9af617376c16e44e065873919c3d90ff46110a', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (index.h("div", { key: 'be7b56decfd6f12dcca46b2a9456480e3d5ac00e', class: "status-spinner" })), index.h("div", { key: '5fe9850883cb749cebaa18c3e9c13ca21325e373', class: "status-content" }, index.h("div", { key: '43fdb50691e4b98e56126af1a129f625061128bf', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (index.h("div", { key: 'b76cbfa4a0349421fcd535d3fd8bf40c89ca3aef', class: "status-description" }, this.currentStatus.description)))), this.debug && (index.h("div", { key: 'eddefb464af0487ab9ab2a0f76dccbb6a2092e51', class: "performance-monitor" }, index.h("div", { key: '7b7d93b56cb4cd4fbb1bdde89c6150b282fa805e', class: "performance-expanded" }, index.h("div", { key: 'f302e8b9d985fb8cad53c4c4e0d37a3bf5a63261', class: "metrics-row" }, index.h("div", { key: 'e8d8f2420ee8643a7f454d38065c75b5be598f77', class: "metric-compact" }, index.h("span", { key: '6e52eb5bb06e0df57e61774de38c31349f45e22b', class: "metric-label" }, "FPS"), index.h("span", { key: '6636f9cea20c49b49c17db86188e2f6b214c45c3', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), index.h("div", { key: 'afa1d9dec05b14256a1b1fcec8638b8c21a56e37', class: "metric-compact" }, index.h("span", { key: '9c55e30f5d01582648bacc1fb38ca1990485a591', class: "metric-label" }, "MEM"), index.h("span", { key: 'ca263601e8715027f95ffb2d5413a643dc570cf2', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), index.h("div", { key: '03caf19961fd01b7781484d49d8121b1e78c161f', class: "metrics-row" }, index.h("div", { key: 'bffb08f01f703411f7c8605e4f8d95b1c37b5c0f', class: "metric-compact" }, index.h("span", { key: '9b92279b3253cc669412733facb3751f4344d69f', class: "metric-label" }, "INF"), index.h("span", { key: '3234d4150e2e1479e3b552767af8123e69911d6c', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), index.h("div", { key: '77082d2669db53f19fb50b3bbfa1ebefb0b86ff4', class: "metric-compact" }, index.h("span", { key: 'cc6ef96c72c800ba36dab2a388f1de863b2dacd5', class: "metric-label" }, "FRAME"), index.h("span", { key: 'e2bb4b7351869a76e810f8bbf20d805154495e35', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), index.h("div", { key: '5700d7ef839fa77686299d79445203090dc8079f', class: "metrics-row" }, index.h("div", { key: 'a2999a035fccb7f250c01b084075490678513c87', class: "metric-compact" }, index.h("span", { key: '3677d58665996fa5969507dbaff9e956b2ea54ee', class: "metric-label" }, "DET"), index.h("span", { key: 'c584312438b339c82a3bdc33f00fb985ff2a6f55', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), index.h("div", { key: '58866219f213bf8607083f30be2d5ca5d0515cea', class: "metric-compact" }, index.h("span", { key: 'd90b450051331347f219b6c0f197536d0dcee9b7', class: "metric-label" }, "RATE"), index.h("span", { key: 'f6e6cd7948e06014e5a0416e4e81394fa36ec61d', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), index.h("div", { key: 'c1d0194f181a0082cc1aa5452de382908da61b65', class: "watermark" }, index.h("img", { key: 'e04d35f3c79a2b4a3922ab5b0f102220171fa7c0', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
1760
1927
|
}
|
|
1761
1928
|
// Utility methods
|
|
1762
1929
|
updateDetectionBoxes(boxes) {
|
|
@@ -1825,6 +1992,15 @@ const JaakStamps = class {
|
|
|
1825
1992
|
bottomSide?.classList.toggle('aligned', currentAlignment.bottom);
|
|
1826
1993
|
leftSide?.classList.toggle('aligned', currentAlignment.left);
|
|
1827
1994
|
const allSidesAligned = this.detectionService.areAllSidesAligned(currentAlignment);
|
|
1995
|
+
// Restart back document timer if any border is aligned during back capture
|
|
1996
|
+
const captureState = this.stateManager.getCaptureState();
|
|
1997
|
+
if (captureState.step === 'back' && this.enableBackDocumentTimer && bestBox) {
|
|
1998
|
+
const anyBorderAligned = currentAlignment.top || currentAlignment.right ||
|
|
1999
|
+
currentAlignment.bottom || currentAlignment.left;
|
|
2000
|
+
if (anyBorderAligned) {
|
|
2001
|
+
this.startBackDocumentTimer();
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
1828
2004
|
if (allSidesAligned && bestBox) {
|
|
1829
2005
|
cardOutline?.classList.add('perfect-match');
|
|
1830
2006
|
corners?.forEach(corner => corner.classList.add('perfect-match'));
|
|
@@ -1939,6 +2115,7 @@ const JaakStamps = class {
|
|
|
1939
2115
|
showFlipAnimation: false,
|
|
1940
2116
|
isDetectionPaused: false
|
|
1941
2117
|
});
|
|
2118
|
+
this.startBackDocumentTimer();
|
|
1942
2119
|
}, 3000);
|
|
1943
2120
|
}
|
|
1944
2121
|
else if (captureState.step === 'back') {
|
|
@@ -1989,9 +2166,32 @@ const JaakStamps = class {
|
|
|
1989
2166
|
cancelAnimationFrame(this.animationId);
|
|
1990
2167
|
this.animationId = undefined;
|
|
1991
2168
|
}
|
|
2169
|
+
this.clearBackDocumentTimer();
|
|
1992
2170
|
this.detectionBoxes = [];
|
|
1993
2171
|
this.logger.state('DETECTOR_DETENIDO', { timestamp: Date.now() });
|
|
1994
2172
|
}
|
|
2173
|
+
startBackDocumentTimer() {
|
|
2174
|
+
if (!this.enableBackDocumentTimer)
|
|
2175
|
+
return;
|
|
2176
|
+
if (this.backDocumentTimer) {
|
|
2177
|
+
clearInterval(this.backDocumentTimer);
|
|
2178
|
+
}
|
|
2179
|
+
this.backDocumentTimerRemaining = this.backDocumentTimerDuration;
|
|
2180
|
+
this.backDocumentTimer = window.setInterval(() => {
|
|
2181
|
+
this.backDocumentTimerRemaining--;
|
|
2182
|
+
if (this.backDocumentTimerRemaining <= 0) {
|
|
2183
|
+
this.clearBackDocumentTimer();
|
|
2184
|
+
this.skipBackCapture();
|
|
2185
|
+
}
|
|
2186
|
+
}, 1000);
|
|
2187
|
+
}
|
|
2188
|
+
clearBackDocumentTimer() {
|
|
2189
|
+
if (this.backDocumentTimer) {
|
|
2190
|
+
clearInterval(this.backDocumentTimer);
|
|
2191
|
+
this.backDocumentTimer = undefined;
|
|
2192
|
+
}
|
|
2193
|
+
this.backDocumentTimerRemaining = 0;
|
|
2194
|
+
}
|
|
1995
2195
|
toggleCameraSelector() {
|
|
1996
2196
|
if (this.isSwitchingCamera)
|
|
1997
2197
|
return; // Don't toggle if switching camera
|