@jaak.ai/stamps 2.1.0-dev.5 → 2.1.0-dev.7
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/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +270 -25
- 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 +36 -11
- package/dist/collection/components/my-component/my-component.js +240 -21
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/DetectionService.js +32 -4
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/components/jaak-stamps.js +272 -25
- 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 +270 -25
- 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-be6b6ca3.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-be6b6ca3.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +7 -0
- package/package.json +1 -1
- package/dist/jaak-stamps-webcomponent/p-c08802a9.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-c08802a9.entry.js.map +0 -1
package/dist/cjs/loader.cjs.js
CHANGED
|
@@ -5,7 +5,7 @@ var index = require('./index-BfhtOB0D.js');
|
|
|
5
5
|
const defineCustomElements = async (win, options) => {
|
|
6
6
|
if (typeof window === 'undefined') return undefined;
|
|
7
7
|
await index.globalScripts();
|
|
8
|
-
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"],"useDocumentDetector":[4,"use-document-detector"],"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],"cameraInfoWithAutofocus":[32],"currentStatus":[32],"performanceData":[32],"backDocumentTimerRemaining":[32],"showManualCaptureButton":[32],"performanceDegradedMode":[32],"showPerformanceMessage":[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]}]]]], options);
|
|
8
|
+
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"],"useDocumentDetector":[4,"use-document-detector"],"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],"cameraInfoWithAutofocus":[32],"currentStatus":[32],"performanceData":[32],"backDocumentTimerRemaining":[32],"showManualCaptureButton":[32],"performanceDegradedMode":[32],"showPerformanceMessage":[32],"captureStateVersion":[32],"processingButton":[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]}]]]], options);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
exports.setNonce = index.setNonce;
|
|
@@ -571,11 +571,12 @@ video {
|
|
|
571
571
|
transform: translateY(1px);
|
|
572
572
|
}
|
|
573
573
|
|
|
574
|
-
.capture-button:disabled
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
574
|
+
.capture-button:disabled,
|
|
575
|
+
.capture-button.primary-button:disabled {
|
|
576
|
+
cursor: not-allowed !important;
|
|
577
|
+
transform: none !important;
|
|
578
|
+
opacity: 0.7 !important;
|
|
579
|
+
transition: none !important;
|
|
579
580
|
}
|
|
580
581
|
|
|
581
582
|
/* Responsive para móviles */
|
|
@@ -599,6 +600,14 @@ video {
|
|
|
599
600
|
padding: 10px 20px;
|
|
600
601
|
font-size: 13px;
|
|
601
602
|
}
|
|
603
|
+
|
|
604
|
+
/* Ensure disabled styles work on mobile too */
|
|
605
|
+
.capture-button:disabled,
|
|
606
|
+
.capture-button.primary-button:disabled,
|
|
607
|
+
.skip-button:disabled {
|
|
608
|
+
opacity: 0.7 !important;
|
|
609
|
+
cursor: not-allowed !important;
|
|
610
|
+
}
|
|
602
611
|
}
|
|
603
612
|
|
|
604
613
|
.skip-explanation {
|
|
@@ -663,6 +672,13 @@ video {
|
|
|
663
672
|
transform: translateY(1px);
|
|
664
673
|
}
|
|
665
674
|
|
|
675
|
+
.skip-button:disabled {
|
|
676
|
+
cursor: not-allowed !important;
|
|
677
|
+
transform: none !important;
|
|
678
|
+
opacity: 0.7 !important;
|
|
679
|
+
transition: none !important;
|
|
680
|
+
}
|
|
681
|
+
|
|
666
682
|
/* Camera controls */
|
|
667
683
|
.camera-controls {
|
|
668
684
|
position: absolute;
|
|
@@ -724,10 +740,13 @@ video {
|
|
|
724
740
|
.button-spinner {
|
|
725
741
|
width: 16px;
|
|
726
742
|
height: 16px;
|
|
727
|
-
border: 2px solid rgba(
|
|
728
|
-
border-top: 2px solid #
|
|
743
|
+
border: 2px solid rgba(0, 0, 0, 0.2);
|
|
744
|
+
border-top: 2px solid #333333;
|
|
729
745
|
border-radius: 50%;
|
|
730
746
|
animation: spin 1s linear infinite;
|
|
747
|
+
display: inline-block;
|
|
748
|
+
margin-right: 8px;
|
|
749
|
+
vertical-align: middle;
|
|
731
750
|
}
|
|
732
751
|
|
|
733
752
|
@keyframes spin {
|
|
@@ -1282,10 +1301,10 @@ video {
|
|
|
1282
1301
|
}
|
|
1283
1302
|
|
|
1284
1303
|
.manual-capture-button:disabled {
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1304
|
+
cursor: not-allowed !important;
|
|
1305
|
+
transform: none !important;
|
|
1306
|
+
opacity: 0.7 !important;
|
|
1307
|
+
transition: none !important;
|
|
1289
1308
|
}
|
|
1290
1309
|
|
|
1291
1310
|
/* Responsive para móviles */
|
|
@@ -1300,4 +1319,10 @@ video {
|
|
|
1300
1319
|
padding: 10px 20px;
|
|
1301
1320
|
font-size: 13px;
|
|
1302
1321
|
}
|
|
1322
|
+
|
|
1323
|
+
/* Ensure disabled styles work on mobile too */
|
|
1324
|
+
.manual-capture-button:disabled {
|
|
1325
|
+
opacity: 0.7 !important;
|
|
1326
|
+
cursor: not-allowed !important;
|
|
1327
|
+
}
|
|
1303
1328
|
}
|
|
@@ -51,6 +51,8 @@ export class JaakStamps {
|
|
|
51
51
|
showManualCaptureButton = false;
|
|
52
52
|
performanceDegradedMode = false; // Auto-switched to manual mode due to performance
|
|
53
53
|
showPerformanceMessage = false; // Show performance message temporarily
|
|
54
|
+
captureStateVersion = 0; // Triggers re-render when StateManager changes
|
|
55
|
+
processingButton = null; // Track which button is processing
|
|
54
56
|
// Services
|
|
55
57
|
serviceContainer;
|
|
56
58
|
eventBus;
|
|
@@ -102,14 +104,19 @@ export class JaakStamps {
|
|
|
102
104
|
canvasPool = [];
|
|
103
105
|
MAX_CANVAS_POOL_SIZE = 3;
|
|
104
106
|
async componentDidLoad() {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.
|
|
107
|
+
try {
|
|
108
|
+
this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
|
|
109
|
+
await this.initializeServices();
|
|
110
|
+
this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
|
|
111
|
+
await this.setupEventListeners();
|
|
112
|
+
this.updateStatus('Configurando cámara...', 'Detectando dispositivos disponibles', 'initializing');
|
|
113
|
+
await this.initializeComponent();
|
|
114
|
+
if (this.debug) {
|
|
115
|
+
this.initializePerformanceMonitor();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
this.updateStatus('Error de inicialización', error.message, 'error');
|
|
113
120
|
}
|
|
114
121
|
}
|
|
115
122
|
async initializeServices() {
|
|
@@ -256,6 +263,9 @@ export class JaakStamps {
|
|
|
256
263
|
this.updateMaskDimensions(rect);
|
|
257
264
|
}
|
|
258
265
|
}
|
|
266
|
+
triggerRerender() {
|
|
267
|
+
this.captureStateVersion = this.captureStateVersion + 1;
|
|
268
|
+
}
|
|
259
269
|
getGuideText(step) {
|
|
260
270
|
if (step === 'completed') {
|
|
261
271
|
return 'Proceso completado';
|
|
@@ -421,8 +431,14 @@ export class JaakStamps {
|
|
|
421
431
|
}
|
|
422
432
|
}
|
|
423
433
|
async skipBackCapture() {
|
|
434
|
+
console.log('🟡 Botón clicked: Saltar reverso');
|
|
435
|
+
// Set processing state immediately
|
|
436
|
+
this.processingButton = 'skip-back';
|
|
437
|
+
// Add small delay to show spinner
|
|
438
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
424
439
|
const readyCheck = this.isComponentReady();
|
|
425
440
|
if (!readyCheck.ready) {
|
|
441
|
+
this.processingButton = null;
|
|
426
442
|
return { success: false, error: readyCheck.message };
|
|
427
443
|
}
|
|
428
444
|
try {
|
|
@@ -439,6 +455,10 @@ export class JaakStamps {
|
|
|
439
455
|
catch (error) {
|
|
440
456
|
return { success: false, error: error.message };
|
|
441
457
|
}
|
|
458
|
+
finally {
|
|
459
|
+
// Always clear processing state when done
|
|
460
|
+
this.processingButton = null;
|
|
461
|
+
}
|
|
442
462
|
}
|
|
443
463
|
async getStatus() {
|
|
444
464
|
const readyCheck = this.isComponentReady();
|
|
@@ -616,9 +636,53 @@ export class JaakStamps {
|
|
|
616
636
|
// Always allow getting capture delay, even during initialization
|
|
617
637
|
return this.captureDelay;
|
|
618
638
|
}
|
|
639
|
+
// Memory and device capability detection
|
|
640
|
+
checkDeviceCapabilities() {
|
|
641
|
+
const deviceMemory = navigator.deviceMemory;
|
|
642
|
+
const hardwareConcurrency = navigator.hardwareConcurrency || 1;
|
|
643
|
+
const isLowEndDevice = hardwareConcurrency <= 2;
|
|
644
|
+
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
645
|
+
const isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
|
|
646
|
+
// Si tenemos info de memoria del dispositivo
|
|
647
|
+
if (deviceMemory && deviceMemory < 4) {
|
|
648
|
+
return {
|
|
649
|
+
canUseML: false,
|
|
650
|
+
reason: `Memoria del dispositivo (${deviceMemory}GB) insuficiente para detección automática. Modo manual activado para mejor rendimiento.`,
|
|
651
|
+
memoryMB: deviceMemory * 1024
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
// Dispositivos móviles de gama baja
|
|
655
|
+
if (isMobile && isLowEndDevice) {
|
|
656
|
+
return {
|
|
657
|
+
canUseML: false,
|
|
658
|
+
reason: 'Dispositivo móvil optimizado detectado. Modo manual activado para mejor rendimiento y duración de batería.',
|
|
659
|
+
memoryMB: null
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
// Safari tiene limitaciones conocidas con WASM
|
|
663
|
+
if (isSafari) {
|
|
664
|
+
return {
|
|
665
|
+
canUseML: false,
|
|
666
|
+
reason: 'Safari detectado. Modo manual activado por compatibilidad optimizada con este navegador.',
|
|
667
|
+
memoryMB: null
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
canUseML: true,
|
|
672
|
+
reason: 'Dispositivo compatible con detección automática.',
|
|
673
|
+
memoryMB: deviceMemory ? deviceMemory * 1024 : null
|
|
674
|
+
};
|
|
675
|
+
}
|
|
619
676
|
// DETECTION METHODS
|
|
620
677
|
async startDetection() {
|
|
621
678
|
try {
|
|
679
|
+
// Verificar capacidades del dispositivo antes de cargar modelos
|
|
680
|
+
const capabilities = this.checkDeviceCapabilities();
|
|
681
|
+
// Si el dispositivo no puede usar ML o tenemos problemas de memoria, usar modo manual
|
|
682
|
+
if (!capabilities.canUseML && this.useDocumentDetector) {
|
|
683
|
+
this.switchToManualModeWithWarning(capabilities.reason);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
622
686
|
// Paso 1: Verificar y cargar modelos si es necesario
|
|
623
687
|
if (!this.detectionService.isModelLoaded()) {
|
|
624
688
|
const loadStartTime = performance.now();
|
|
@@ -628,16 +692,32 @@ export class JaakStamps {
|
|
|
628
692
|
await this.detectionService.loadModel();
|
|
629
693
|
}
|
|
630
694
|
catch (modelError) {
|
|
631
|
-
|
|
632
|
-
|
|
695
|
+
// Si es un error de memoria, cambiar a modo manual con mensaje específico
|
|
696
|
+
if (modelError.message.includes('Out of memory') || modelError.message.includes('no available backend')) {
|
|
697
|
+
const memoryInfo = navigator.deviceMemory ? `(${navigator.deviceMemory}GB disponible)` : '';
|
|
698
|
+
this.switchToManualModeWithWarning(`Memoria insuficiente para detección automática ${memoryInfo}. Modo manual activado - funciona igual de bien!`);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
this.switchToManualModeWithError('Error al cargar modelo de detección');
|
|
703
|
+
throw modelError;
|
|
704
|
+
}
|
|
633
705
|
}
|
|
634
706
|
this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
|
|
635
707
|
try {
|
|
636
708
|
await this.detectionService.loadClassificationModel();
|
|
637
709
|
}
|
|
638
710
|
catch (classificationError) {
|
|
639
|
-
|
|
640
|
-
|
|
711
|
+
// Si es un error de memoria, cambiar a modo manual con mensaje específico
|
|
712
|
+
if (classificationError.message.includes('Out of memory') || classificationError.message.includes('no available backend')) {
|
|
713
|
+
const memoryInfo = navigator.deviceMemory ? `(${navigator.deviceMemory}GB disponible)` : '';
|
|
714
|
+
this.switchToManualModeWithWarning(`Memoria insuficiente para clasificación automática ${memoryInfo}. Modo manual activado para optimizar rendimiento.`);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
this.switchToManualModeWithError('Error al cargar modelo de clasificación');
|
|
719
|
+
throw classificationError;
|
|
720
|
+
}
|
|
641
721
|
}
|
|
642
722
|
const loadEndTime = performance.now();
|
|
643
723
|
const loadTime = loadEndTime - loadStartTime;
|
|
@@ -648,11 +728,27 @@ export class JaakStamps {
|
|
|
648
728
|
}
|
|
649
729
|
// Paso 2: Detectar y configurar dispositivos
|
|
650
730
|
this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
|
|
651
|
-
|
|
652
|
-
|
|
731
|
+
try {
|
|
732
|
+
await this.cameraService.enumerateDevices();
|
|
733
|
+
}
|
|
734
|
+
catch (enumerateError) {
|
|
735
|
+
throw new Error(`Error al enumerar dispositivos: ${enumerateError.message}`);
|
|
736
|
+
}
|
|
737
|
+
try {
|
|
738
|
+
await this.updateCameraInfoWithAutofocus();
|
|
739
|
+
}
|
|
740
|
+
catch (cameraInfoError) {
|
|
741
|
+
throw new Error(`Error al actualizar información de cámara: ${cameraInfoError.message}`);
|
|
742
|
+
}
|
|
653
743
|
// Paso 3: Configurar cámara seleccionada
|
|
654
744
|
this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
|
|
655
|
-
|
|
745
|
+
let stream;
|
|
746
|
+
try {
|
|
747
|
+
stream = await this.cameraService.setupCamera();
|
|
748
|
+
}
|
|
749
|
+
catch (setupError) {
|
|
750
|
+
throw new Error(`Error al configurar cámara: ${setupError.message}`);
|
|
751
|
+
}
|
|
656
752
|
// Paso 4: Inicializar video y mostrar estado de análisis inicial
|
|
657
753
|
if (this.useDocumentDetector) {
|
|
658
754
|
this.updateStatus('Analizando estabilidad...', 'Evaluando rendimiento del sistema', 'loading');
|
|
@@ -660,7 +756,12 @@ export class JaakStamps {
|
|
|
660
756
|
else {
|
|
661
757
|
this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
|
|
662
758
|
}
|
|
663
|
-
|
|
759
|
+
try {
|
|
760
|
+
await this.initializeVideoStream(stream);
|
|
761
|
+
}
|
|
762
|
+
catch (videoError) {
|
|
763
|
+
throw new Error(`Error al inicializar video: ${videoError.message}`);
|
|
764
|
+
}
|
|
664
765
|
// Paso 5: Calibrar máscara
|
|
665
766
|
if (this.detectionContainer) {
|
|
666
767
|
const container = this.detectionContainer.parentElement;
|
|
@@ -885,7 +986,7 @@ export class JaakStamps {
|
|
|
885
986
|
isCapturing: false
|
|
886
987
|
};
|
|
887
988
|
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
888
|
-
return (h("div", { key: '
|
|
989
|
+
return (h("div", { key: '51cc6d0f873691852f938940b131732252b970c1', class: "detector-container" }, h("div", { key: '009480499c0a2833bf9f6bd28003142ca3dd3750', class: "video-container" }, h("video", { key: '88a99a1a90c8d817e40549a255324b2da342797f', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '7ede15ccd6f67b3081d907777de7b0d1a1b4cfd6', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
|
|
889
990
|
position: 'absolute',
|
|
890
991
|
left: `${box.x}px`,
|
|
891
992
|
top: `${box.y}px`,
|
|
@@ -894,9 +995,9 @@ export class JaakStamps {
|
|
|
894
995
|
border: '2px solid #32406C',
|
|
895
996
|
pointerEvents: 'none',
|
|
896
997
|
boxSizing: 'border-box'
|
|
897
|
-
} })))), this.isMaskReady && (h("div", { key: '
|
|
998
|
+
} })))), this.isMaskReady && (h("div", { key: '63beb4330ab171a5e63237b1d83dcace61422018', class: "overlay-mask" }, h("div", { key: '93cfac6898de6ece6c7dd8f02ba2b79ce6591755', class: "card-outline" }, h("div", { key: '3087d3c51f42ea8d720cea4170dcb68e8bb385e1', class: "side side-top" }), h("div", { key: '992e311b895da161d3b5c49dde346ae378139641', class: "side side-right" }), h("div", { key: '5fb3d7b704f08177b2b9c6e4fbaf685f251a3e53', class: "side side-bottom" }), h("div", { key: '76f6e2cb856632c58809ccdabdf3f7d9a588c9da', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '142dfc187e07100278e16b19018e4b5fb96d964a', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'bba078ce4c9585631b6a8154f878464b1f779c8b', class: "back-capture-section" }, h("div", { key: 'ec94c9ba2cb373a539f0e6cc3eb356294cebf3ea', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (h("button", { key: '30561e1fc88090c6bb4d0cd7909a33a9525b0ac2', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '4b3e3356507c7f654705425e201d0303d727db72', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: '1abfada41312658eab4966d8fcdb711a6c4762e6', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '3cab3ce751cb38174b6d061170a66ac654a90a38', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
898
999
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
899
|
-
: 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '
|
|
1000
|
+
: 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'b773fe579cc039fd90ff5e61976752bcf631d16e', class: "camera-controls" }, h("button", { key: '8535463b13cc02e0030d14c1d57390e560572b4f', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: 'c4b159cf28c24ba2ec0a658bf6f1296c6aba47e3', class: "camera-selector-dropdown" }, h("div", { key: '033273241b77cedc414dee00b70926c3a659910a', class: "camera-selector-header" }, h("span", { key: '83fb4c7ae2387b85134a3ca0c5a156beaf73e481' }, "Seleccionar C\u00E1mara"), h("button", { key: 'f65b6e994603e616af8d69df198c3d8471125695', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'fd90d4c4db1e910c5d05b9ce19a252967c71ad1f', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: '49faa944b3c0c052e18f48e9dda2758cd2c0ae6b', class: "device-info" }, h("small", { key: '15e95207aef60a78b2912a6c00a1b61ab760b72d' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'a67a3795837adcd307fa3666c338376cecae16d0', class: "manual-capture-section" }, h("button", { key: '7f21ccd9b622d97bfce80962038de8be7cd6e33d', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: 'a4b5e865f373cc0e7787098eedd2d4df413d310f', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '0b02e81963136dfda62a8980bc5a1f5662509fb8', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '5eb16e12a7ce16720ca7bdd11aefd902ba772e24', class: "flip-animation" }, h("div", { key: '4c6f3ed33c5080e9e18d17c4594cf394250c3517', class: "id-card-icon" }), h("div", { key: 'd848d7a98540988a8f491bcf8f6f6ad9c5dccfd4', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '94863e6d0c404188d548db428af918274d882652', class: "success-animation" }, h("div", { key: 'a90b777e7b7eb533fabec2c15f79a4bd18778f0b', class: "check-icon" }), h("div", { key: '2152e5a3756f58f996c520b58cb1b6cca1763e94', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: 'ae18e06bf291429a74d1b6c8a0c4d37d473f7b30', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: 'b6b03ed285868d7d3350a2a73e59cb1d65a47401', class: "status-spinner" })), h("div", { key: 'dc85b49dd1cfcddf7300fb3f327d2e1de8ab9bff', class: "status-content" }, h("div", { key: '82461ed0c6d70a0c95ae6b7144ff1d8b8371c6fc', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '24aa77ec92ca5a48d1bed4cf98ae591bdbff74f0', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '3b56a0b305ca07407c09a73624a4040cf6625b56', class: "performance-monitor" }, h("div", { key: '52f975da1502f6b08a0b2c828bfd0be8d10e36f8', class: "performance-expanded" }, h("div", { key: 'ea2f444255add0415c7f88cdea2733e1244b17bd', class: "metrics-row" }, h("div", { key: 'e3c2370f1a900594940cb321d5b1661690018025', class: "metric-compact" }, h("span", { key: '25c2f402c9b69f21736c494420e7a396673e9893', class: "metric-label" }, "FPS"), h("span", { key: '505de593f7927544cd874332127e84c72131da72', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '5cb1ff4bc1ac33988404e59dac3d01d5a55893b9', class: "metric-compact" }, h("span", { key: 'ce701538abbfe797b288520ab4c277bf7a1d6a05', class: "metric-label" }, "MEM"), h("span", { key: 'efc383e993ad72c20cbfc630e20b2bf9a617a02d', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'b6b6b832740c44d4f78cddf99ba1d139dc3d4739', class: "metrics-row" }, h("div", { key: '3a8903a05aed8adb1626f93bee6ba258fbea9a7d', class: "metric-compact" }, h("span", { key: 'aabc56057156217b3be771ab3b1e92590fa886c5', class: "metric-label" }, "INF"), h("span", { key: '1080df34860cc891cda94c5aacc754cbde18a138', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'ac152093d4c0cc25862aac638fc4693dd0c51a69', class: "metric-compact" }, h("span", { key: 'b23712dbc0b9b8e9cb6e2c659e5af3844ec0ebe7', class: "metric-label" }, "FRAME"), h("span", { key: '461f88519b339fbad6444f009312f72efbc6e450', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '8f99afe2c94f37fd3a6feb082cc9aa96f54fc5e1', class: "metrics-row" }, h("div", { key: 'da7ac0fd842f38bbdcac01550764f9fe1bd666a2', class: "metric-compact" }, h("span", { key: 'a5dcd9c8753ba42038f869efa50ae9602cdf6256', class: "metric-label" }, "DET"), h("span", { key: 'e98b91ea98a41637baea1fcfda0bb590cf5d24a0', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '3ac54161a712d305a098eae0a3b5d21a731c6019', class: "metric-compact" }, h("span", { key: '3c8cc465433be3b490887457a6c3967dbbda6a7d', class: "metric-label" }, "RATE"), h("span", { key: 'f2e083118fe107c7dc2a675e3f661466cea6d479', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '10b6c32b0460e194d680c63b30a078c51a26e9c9', class: "watermark" }, h("img", { key: 'f5954a0ba3f90a642e0d5c6c14d5b549cd193018', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
900
1001
|
}
|
|
901
1002
|
// Utility methods
|
|
902
1003
|
updateDetectionBoxes(boxes) {
|
|
@@ -1012,9 +1113,24 @@ export class JaakStamps {
|
|
|
1012
1113
|
}
|
|
1013
1114
|
}
|
|
1014
1115
|
async takeManualScreenshot() {
|
|
1015
|
-
|
|
1116
|
+
console.log('🔵 Botón clicked: Capturar (Frente/Reverso)');
|
|
1117
|
+
// Set processing state immediately
|
|
1118
|
+
const captureState = this.stateManager?.getCaptureState();
|
|
1119
|
+
if (captureState?.step === 'front') {
|
|
1120
|
+
this.processingButton = 'capture-front';
|
|
1121
|
+
}
|
|
1122
|
+
else if (captureState?.step === 'back') {
|
|
1123
|
+
this.processingButton = 'capture-back';
|
|
1124
|
+
}
|
|
1125
|
+
console.log('🔵 processingButton set to:', this.processingButton);
|
|
1126
|
+
// Add small delay to show spinner
|
|
1127
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
1128
|
+
if (!this.videoRef) {
|
|
1129
|
+
this.processingButton = null;
|
|
1016
1130
|
return;
|
|
1131
|
+
}
|
|
1017
1132
|
// When using manual capture, use mask coordinates for cropping
|
|
1133
|
+
// Note: processingButton will be cleared inside takeScreenshotWithMaskCoordinates
|
|
1018
1134
|
await this.takeScreenshotWithMaskCoordinates();
|
|
1019
1135
|
}
|
|
1020
1136
|
async takeScreenshotWithMaskCoordinates() {
|
|
@@ -1090,6 +1206,8 @@ export class JaakStamps {
|
|
|
1090
1206
|
this.completeProcess(true);
|
|
1091
1207
|
this.returnCanvasToPool(captureCanvas);
|
|
1092
1208
|
this.returnCanvasToPool(croppedCanvas);
|
|
1209
|
+
// Clear processing button when passport is detected
|
|
1210
|
+
this.processingButton = null;
|
|
1093
1211
|
return;
|
|
1094
1212
|
}
|
|
1095
1213
|
}
|
|
@@ -1102,12 +1220,16 @@ export class JaakStamps {
|
|
|
1102
1220
|
isDetectionPaused: true,
|
|
1103
1221
|
showFlipAnimation: true
|
|
1104
1222
|
});
|
|
1223
|
+
this.triggerRerender();
|
|
1105
1224
|
setTimeout(() => {
|
|
1106
1225
|
this.stateManager.updateCaptureState({
|
|
1107
1226
|
showFlipAnimation: false,
|
|
1108
1227
|
isDetectionPaused: false
|
|
1109
1228
|
});
|
|
1229
|
+
this.triggerRerender();
|
|
1110
1230
|
this.startBackDocumentTimer();
|
|
1231
|
+
// Clear processing button after animation completes
|
|
1232
|
+
this.processingButton = null;
|
|
1111
1233
|
}, 3000);
|
|
1112
1234
|
}
|
|
1113
1235
|
else if (captureState.step === 'back') {
|
|
@@ -1118,6 +1240,8 @@ export class JaakStamps {
|
|
|
1118
1240
|
}
|
|
1119
1241
|
});
|
|
1120
1242
|
this.completeProcess(false);
|
|
1243
|
+
// Clear processing button after back capture completes
|
|
1244
|
+
this.processingButton = null;
|
|
1121
1245
|
}
|
|
1122
1246
|
// Return canvases to pool after use
|
|
1123
1247
|
this.returnCanvasToPool(captureCanvas);
|
|
@@ -1211,6 +1335,9 @@ export class JaakStamps {
|
|
|
1211
1335
|
this.stopPerformanceMonitoring();
|
|
1212
1336
|
// Liberar recursos de ONNX
|
|
1213
1337
|
this.releaseOnnxResources();
|
|
1338
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1339
|
+
this.resetMaskToWhite();
|
|
1340
|
+
this.detectionBoxes = [];
|
|
1214
1341
|
this.hasAutoSwitchedToManual = true;
|
|
1215
1342
|
this.performanceDegradedMode = true;
|
|
1216
1343
|
this.showManualCaptureButton = true;
|
|
@@ -1221,6 +1348,75 @@ export class JaakStamps {
|
|
|
1221
1348
|
}, 5000);
|
|
1222
1349
|
}
|
|
1223
1350
|
// Método para cambiar a modo manual por errores de ONNX
|
|
1351
|
+
switchToManualModeWithWarning(warningMessage) {
|
|
1352
|
+
// Solo cambiar si el detector está habilitado y no se ha cambiado ya
|
|
1353
|
+
if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
// No necesitamos liberar recursos ONNX porque no se han cargado aún
|
|
1357
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1358
|
+
this.resetMaskToWhite();
|
|
1359
|
+
this.detectionBoxes = [];
|
|
1360
|
+
// Cambiar a modo manual
|
|
1361
|
+
this.hasAutoSwitchedToManual = true;
|
|
1362
|
+
this.performanceDegradedMode = true;
|
|
1363
|
+
this.showManualCaptureButton = true;
|
|
1364
|
+
this.useDocumentDetector = false; // Desactivar detector automático
|
|
1365
|
+
// Actualizar el estado con mensaje informativo (no error)
|
|
1366
|
+
this.updateStatus('Modo manual activado', warningMessage, 'active');
|
|
1367
|
+
// Continuar con la configuración de cámara
|
|
1368
|
+
this.continueWithCameraSetup();
|
|
1369
|
+
}
|
|
1370
|
+
async continueWithCameraSetup() {
|
|
1371
|
+
try {
|
|
1372
|
+
// Paso 2: Detectar y configurar dispositivos
|
|
1373
|
+
this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
|
|
1374
|
+
try {
|
|
1375
|
+
await this.cameraService.enumerateDevices();
|
|
1376
|
+
}
|
|
1377
|
+
catch (enumerateError) {
|
|
1378
|
+
throw new Error(`Error al enumerar dispositivos: ${enumerateError.message}`);
|
|
1379
|
+
}
|
|
1380
|
+
try {
|
|
1381
|
+
await this.updateCameraInfoWithAutofocus();
|
|
1382
|
+
}
|
|
1383
|
+
catch (cameraInfoError) {
|
|
1384
|
+
throw new Error(`Error al actualizar información de cámara: ${cameraInfoError.message}`);
|
|
1385
|
+
}
|
|
1386
|
+
// Paso 3: Configurar cámara seleccionada
|
|
1387
|
+
this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
|
|
1388
|
+
let stream;
|
|
1389
|
+
try {
|
|
1390
|
+
stream = await this.cameraService.setupCamera();
|
|
1391
|
+
}
|
|
1392
|
+
catch (setupError) {
|
|
1393
|
+
throw new Error(`Error al configurar cámara: ${setupError.message}`);
|
|
1394
|
+
}
|
|
1395
|
+
// Paso 4: Inicializar video
|
|
1396
|
+
this.updateStatus('Captura manual', 'Posicione su documento y use el botón para capturar', 'active');
|
|
1397
|
+
try {
|
|
1398
|
+
await this.initializeVideoStream(stream);
|
|
1399
|
+
}
|
|
1400
|
+
catch (videoError) {
|
|
1401
|
+
throw new Error(`Error al inicializar video: ${videoError.message}`);
|
|
1402
|
+
}
|
|
1403
|
+
// Paso 5: Calibrar máscara
|
|
1404
|
+
if (this.detectionContainer) {
|
|
1405
|
+
const container = this.detectionContainer.parentElement;
|
|
1406
|
+
const rect = container.getBoundingClientRect();
|
|
1407
|
+
this.updateMaskDimensions(rect);
|
|
1408
|
+
}
|
|
1409
|
+
// Finalizar
|
|
1410
|
+
this.startTime = Date.now();
|
|
1411
|
+
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1412
|
+
// Mostrar botón manual (ya debe estar habilitado)
|
|
1413
|
+
this.showManualCaptureButton = true;
|
|
1414
|
+
}
|
|
1415
|
+
catch (error) {
|
|
1416
|
+
this.updateStatus('Error de cámara', error.message, 'error');
|
|
1417
|
+
throw error;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1224
1420
|
switchToManualModeWithError(errorMessage) {
|
|
1225
1421
|
// Solo cambiar si el detector está habilitado y no se ha cambiado ya
|
|
1226
1422
|
if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
|
|
@@ -1228,6 +1424,9 @@ export class JaakStamps {
|
|
|
1228
1424
|
}
|
|
1229
1425
|
// Liberar recursos de ONNX inmediatamente
|
|
1230
1426
|
this.releaseOnnxResources();
|
|
1427
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1428
|
+
this.resetMaskToWhite();
|
|
1429
|
+
this.detectionBoxes = [];
|
|
1231
1430
|
// Cambiar a modo manual
|
|
1232
1431
|
this.hasAutoSwitchedToManual = true;
|
|
1233
1432
|
this.performanceDegradedMode = true;
|
|
@@ -1478,6 +1677,24 @@ export class JaakStamps {
|
|
|
1478
1677
|
// Performance is good, use base frame skip
|
|
1479
1678
|
return this.BASE_FRAME_SKIP;
|
|
1480
1679
|
}
|
|
1680
|
+
// Método para resetear la máscara a color blanco
|
|
1681
|
+
resetMaskToWhite() {
|
|
1682
|
+
const cardOutline = this.el.shadowRoot?.querySelector('.card-outline');
|
|
1683
|
+
const corners = this.el.shadowRoot?.querySelectorAll('.corner');
|
|
1684
|
+
const topSide = this.el.shadowRoot?.querySelector('.side-top');
|
|
1685
|
+
const rightSide = this.el.shadowRoot?.querySelector('.side-right');
|
|
1686
|
+
const bottomSide = this.el.shadowRoot?.querySelector('.side-bottom');
|
|
1687
|
+
const leftSide = this.el.shadowRoot?.querySelector('.side-left');
|
|
1688
|
+
// Remover todas las clases de alineación y estado perfecto
|
|
1689
|
+
cardOutline?.classList.remove('perfect-match');
|
|
1690
|
+
corners?.forEach(corner => corner.classList.remove('perfect-match'));
|
|
1691
|
+
topSide?.classList.remove('aligned');
|
|
1692
|
+
rightSide?.classList.remove('aligned');
|
|
1693
|
+
bottomSide?.classList.remove('aligned');
|
|
1694
|
+
leftSide?.classList.remove('aligned');
|
|
1695
|
+
// Resetear estado de alineación
|
|
1696
|
+
this.sideAlignment = { top: false, right: false, bottom: false, left: false };
|
|
1697
|
+
}
|
|
1481
1698
|
// Canvas pool management for screenshots
|
|
1482
1699
|
getPooledCanvas(width, height) {
|
|
1483
1700
|
// Try to find a canvas with matching dimensions
|
|
@@ -1739,7 +1956,9 @@ export class JaakStamps {
|
|
|
1739
1956
|
"backDocumentTimerRemaining": {},
|
|
1740
1957
|
"showManualCaptureButton": {},
|
|
1741
1958
|
"performanceDegradedMode": {},
|
|
1742
|
-
"showPerformanceMessage": {}
|
|
1959
|
+
"showPerformanceMessage": {},
|
|
1960
|
+
"captureStateVersion": {},
|
|
1961
|
+
"processingButton": {}
|
|
1743
1962
|
};
|
|
1744
1963
|
}
|
|
1745
1964
|
static get events() {
|