@jaak.ai/stamps 2.1.0-dev.6 → 2.1.0-dev.8
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 +623 -31
- 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 +484 -24
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/DetectionService.js +176 -9
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
- package/dist/components/jaak-stamps.js +627 -32
- 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 +623 -31
- 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-8e25497e.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-8e25497e.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +32 -0
- package/dist/types/components.d.ts +3 -1
- package/dist/types/services/DetectionService.d.ts +9 -1
- package/dist/types/services/interfaces/IDetectionService.d.ts +6 -0
- package/package.json +1 -1
- package/dist/jaak-stamps-webcomponent/p-d05dc382.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-d05dc382.entry.js.map +0 -1
|
@@ -52,6 +52,7 @@ export class JaakStamps {
|
|
|
52
52
|
performanceDegradedMode = false; // Auto-switched to manual mode due to performance
|
|
53
53
|
showPerformanceMessage = false; // Show performance message temporarily
|
|
54
54
|
captureStateVersion = 0; // Triggers re-render when StateManager changes
|
|
55
|
+
processingButton = null; // Track which button is processing
|
|
55
56
|
// Services
|
|
56
57
|
serviceContainer;
|
|
57
58
|
eventBus;
|
|
@@ -103,14 +104,19 @@ export class JaakStamps {
|
|
|
103
104
|
canvasPool = [];
|
|
104
105
|
MAX_CANVAS_POOL_SIZE = 3;
|
|
105
106
|
async componentDidLoad() {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
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');
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
async initializeServices() {
|
|
@@ -405,7 +411,13 @@ export class JaakStamps {
|
|
|
405
411
|
}
|
|
406
412
|
try {
|
|
407
413
|
this.exitSession();
|
|
408
|
-
|
|
414
|
+
// Aggressive cleanup when stopping capture
|
|
415
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
416
|
+
return {
|
|
417
|
+
success: true,
|
|
418
|
+
resourcesFreed: cleanupResult.freed,
|
|
419
|
+
cleanupErrors: cleanupResult.errors
|
|
420
|
+
};
|
|
409
421
|
}
|
|
410
422
|
catch (error) {
|
|
411
423
|
return { success: false, error: error.message };
|
|
@@ -425,8 +437,14 @@ export class JaakStamps {
|
|
|
425
437
|
}
|
|
426
438
|
}
|
|
427
439
|
async skipBackCapture() {
|
|
440
|
+
console.log('🟡 Botón clicked: Saltar reverso');
|
|
441
|
+
// Set processing state immediately
|
|
442
|
+
this.processingButton = 'skip-back';
|
|
443
|
+
// Add small delay to show spinner
|
|
444
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
428
445
|
const readyCheck = this.isComponentReady();
|
|
429
446
|
if (!readyCheck.ready) {
|
|
447
|
+
this.processingButton = null;
|
|
430
448
|
return { success: false, error: readyCheck.message };
|
|
431
449
|
}
|
|
432
450
|
try {
|
|
@@ -443,6 +461,10 @@ export class JaakStamps {
|
|
|
443
461
|
catch (error) {
|
|
444
462
|
return { success: false, error: error.message };
|
|
445
463
|
}
|
|
464
|
+
finally {
|
|
465
|
+
// Always clear processing state when done
|
|
466
|
+
this.processingButton = null;
|
|
467
|
+
}
|
|
446
468
|
}
|
|
447
469
|
async getStatus() {
|
|
448
470
|
const readyCheck = this.isComponentReady();
|
|
@@ -620,9 +642,76 @@ export class JaakStamps {
|
|
|
620
642
|
// Always allow getting capture delay, even during initialization
|
|
621
643
|
return this.captureDelay;
|
|
622
644
|
}
|
|
645
|
+
async getResourceReport() {
|
|
646
|
+
// Public method to get resource usage report
|
|
647
|
+
return this.getResourceReportInternal();
|
|
648
|
+
}
|
|
649
|
+
async forceResourceCleanup() {
|
|
650
|
+
// Public method to force aggressive resource cleanup
|
|
651
|
+
const readyCheck = this.isComponentReady();
|
|
652
|
+
if (!readyCheck.ready) {
|
|
653
|
+
return { success: false, error: readyCheck.message };
|
|
654
|
+
}
|
|
655
|
+
try {
|
|
656
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
657
|
+
return {
|
|
658
|
+
success: true,
|
|
659
|
+
resourcesFreed: cleanupResult.freed,
|
|
660
|
+
errors: cleanupResult.errors,
|
|
661
|
+
timestamp: new Date().toISOString()
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
catch (error) {
|
|
665
|
+
return { success: false, error: error.message };
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// Memory and device capability detection
|
|
669
|
+
checkDeviceCapabilities() {
|
|
670
|
+
const deviceMemory = navigator.deviceMemory;
|
|
671
|
+
const hardwareConcurrency = navigator.hardwareConcurrency || 1;
|
|
672
|
+
const isLowEndDevice = hardwareConcurrency <= 2;
|
|
673
|
+
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
674
|
+
const isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
|
|
675
|
+
// Si tenemos info de memoria del dispositivo
|
|
676
|
+
if (deviceMemory && deviceMemory < 4) {
|
|
677
|
+
return {
|
|
678
|
+
canUseML: false,
|
|
679
|
+
reason: `Memoria del dispositivo (${deviceMemory}GB) insuficiente para detección automática. Modo manual activado para mejor rendimiento.`,
|
|
680
|
+
memoryMB: deviceMemory * 1024
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
// Dispositivos móviles de gama baja
|
|
684
|
+
if (isMobile && isLowEndDevice) {
|
|
685
|
+
return {
|
|
686
|
+
canUseML: false,
|
|
687
|
+
reason: 'Dispositivo móvil optimizado detectado. Modo manual activado para mejor rendimiento y duración de batería.',
|
|
688
|
+
memoryMB: null
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
// Safari tiene limitaciones conocidas con WASM
|
|
692
|
+
if (isSafari) {
|
|
693
|
+
return {
|
|
694
|
+
canUseML: false,
|
|
695
|
+
reason: 'Safari detectado. Modo manual activado por compatibilidad optimizada con este navegador.',
|
|
696
|
+
memoryMB: null
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
return {
|
|
700
|
+
canUseML: true,
|
|
701
|
+
reason: 'Dispositivo compatible con detección automática.',
|
|
702
|
+
memoryMB: deviceMemory ? deviceMemory * 1024 : null
|
|
703
|
+
};
|
|
704
|
+
}
|
|
623
705
|
// DETECTION METHODS
|
|
624
706
|
async startDetection() {
|
|
625
707
|
try {
|
|
708
|
+
// Verificar capacidades del dispositivo antes de cargar modelos
|
|
709
|
+
const capabilities = this.checkDeviceCapabilities();
|
|
710
|
+
// Si el dispositivo no puede usar ML o tenemos problemas de memoria, usar modo manual
|
|
711
|
+
if (!capabilities.canUseML && this.useDocumentDetector) {
|
|
712
|
+
this.switchToManualModeWithWarning(capabilities.reason);
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
626
715
|
// Paso 1: Verificar y cargar modelos si es necesario
|
|
627
716
|
if (!this.detectionService.isModelLoaded()) {
|
|
628
717
|
const loadStartTime = performance.now();
|
|
@@ -632,16 +721,32 @@ export class JaakStamps {
|
|
|
632
721
|
await this.detectionService.loadModel();
|
|
633
722
|
}
|
|
634
723
|
catch (modelError) {
|
|
635
|
-
|
|
636
|
-
|
|
724
|
+
// Si es un error de memoria, cambiar a modo manual con mensaje específico
|
|
725
|
+
if (modelError.message.includes('Out of memory') || modelError.message.includes('no available backend')) {
|
|
726
|
+
const memoryInfo = navigator.deviceMemory ? `(${navigator.deviceMemory}GB disponible)` : '';
|
|
727
|
+
this.switchToManualModeWithWarning(`Memoria insuficiente para detección automática ${memoryInfo}. Modo manual activado - funciona igual de bien!`);
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
this.switchToManualModeWithError('Error al cargar modelo de detección');
|
|
732
|
+
throw modelError;
|
|
733
|
+
}
|
|
637
734
|
}
|
|
638
735
|
this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
|
|
639
736
|
try {
|
|
640
737
|
await this.detectionService.loadClassificationModel();
|
|
641
738
|
}
|
|
642
739
|
catch (classificationError) {
|
|
643
|
-
|
|
644
|
-
|
|
740
|
+
// Si es un error de memoria, cambiar a modo manual con mensaje específico
|
|
741
|
+
if (classificationError.message.includes('Out of memory') || classificationError.message.includes('no available backend')) {
|
|
742
|
+
const memoryInfo = navigator.deviceMemory ? `(${navigator.deviceMemory}GB disponible)` : '';
|
|
743
|
+
this.switchToManualModeWithWarning(`Memoria insuficiente para clasificación automática ${memoryInfo}. Modo manual activado para optimizar rendimiento.`);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
this.switchToManualModeWithError('Error al cargar modelo de clasificación');
|
|
748
|
+
throw classificationError;
|
|
749
|
+
}
|
|
645
750
|
}
|
|
646
751
|
const loadEndTime = performance.now();
|
|
647
752
|
const loadTime = loadEndTime - loadStartTime;
|
|
@@ -652,11 +757,27 @@ export class JaakStamps {
|
|
|
652
757
|
}
|
|
653
758
|
// Paso 2: Detectar y configurar dispositivos
|
|
654
759
|
this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
|
|
655
|
-
|
|
656
|
-
|
|
760
|
+
try {
|
|
761
|
+
await this.cameraService.enumerateDevices();
|
|
762
|
+
}
|
|
763
|
+
catch (enumerateError) {
|
|
764
|
+
throw new Error(`Error al enumerar dispositivos: ${enumerateError.message}`);
|
|
765
|
+
}
|
|
766
|
+
try {
|
|
767
|
+
await this.updateCameraInfoWithAutofocus();
|
|
768
|
+
}
|
|
769
|
+
catch (cameraInfoError) {
|
|
770
|
+
throw new Error(`Error al actualizar información de cámara: ${cameraInfoError.message}`);
|
|
771
|
+
}
|
|
657
772
|
// Paso 3: Configurar cámara seleccionada
|
|
658
773
|
this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
|
|
659
|
-
|
|
774
|
+
let stream;
|
|
775
|
+
try {
|
|
776
|
+
stream = await this.cameraService.setupCamera();
|
|
777
|
+
}
|
|
778
|
+
catch (setupError) {
|
|
779
|
+
throw new Error(`Error al configurar cámara: ${setupError.message}`);
|
|
780
|
+
}
|
|
660
781
|
// Paso 4: Inicializar video y mostrar estado de análisis inicial
|
|
661
782
|
if (this.useDocumentDetector) {
|
|
662
783
|
this.updateStatus('Analizando estabilidad...', 'Evaluando rendimiento del sistema', 'loading');
|
|
@@ -664,7 +785,12 @@ export class JaakStamps {
|
|
|
664
785
|
else {
|
|
665
786
|
this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
|
|
666
787
|
}
|
|
667
|
-
|
|
788
|
+
try {
|
|
789
|
+
await this.initializeVideoStream(stream);
|
|
790
|
+
}
|
|
791
|
+
catch (videoError) {
|
|
792
|
+
throw new Error(`Error al inicializar video: ${videoError.message}`);
|
|
793
|
+
}
|
|
668
794
|
// Paso 5: Calibrar máscara
|
|
669
795
|
if (this.detectionContainer) {
|
|
670
796
|
const container = this.detectionContainer.parentElement;
|
|
@@ -874,11 +1000,182 @@ export class JaakStamps {
|
|
|
874
1000
|
}
|
|
875
1001
|
// Clear canvas pool
|
|
876
1002
|
this.canvasPool = [];
|
|
1003
|
+
// Clean DOM references and memory leaks
|
|
1004
|
+
this.cleanupDOMReferences();
|
|
877
1005
|
this.detectionBoxes = [];
|
|
878
1006
|
this.alignmentStartTime = undefined;
|
|
879
1007
|
this.hasDocumentDetected = false;
|
|
880
1008
|
this.serviceContainer?.cleanup();
|
|
881
1009
|
}
|
|
1010
|
+
// AGGRESSIVE RESOURCE CLEANUP METHODS
|
|
1011
|
+
// Clean DOM references and memory leaks
|
|
1012
|
+
cleanupDOMReferences() {
|
|
1013
|
+
// Remove event listeners that could maintain references
|
|
1014
|
+
if (this.videoRef) {
|
|
1015
|
+
// Remove common event listeners (if any were added)
|
|
1016
|
+
this.videoRef.srcObject = null;
|
|
1017
|
+
this.videoRef = undefined;
|
|
1018
|
+
}
|
|
1019
|
+
// Clean detection container
|
|
1020
|
+
if (this.detectionContainer) {
|
|
1021
|
+
this.detectionContainer = undefined;
|
|
1022
|
+
}
|
|
1023
|
+
// Clean references to images in the DOM and revoke blob URLs
|
|
1024
|
+
const images = this.el.shadowRoot?.querySelectorAll('img');
|
|
1025
|
+
images?.forEach(img => {
|
|
1026
|
+
if (img.src.startsWith('blob:')) {
|
|
1027
|
+
URL.revokeObjectURL(img.src);
|
|
1028
|
+
}
|
|
1029
|
+
img.src = '';
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
// Clear browser cache and storage
|
|
1033
|
+
async clearBrowserCache() {
|
|
1034
|
+
try {
|
|
1035
|
+
// 1. Clear Service Worker cache if exists
|
|
1036
|
+
if ('serviceWorker' in navigator) {
|
|
1037
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
1038
|
+
await Promise.all(registrations.map(registration => registration.unregister()));
|
|
1039
|
+
}
|
|
1040
|
+
// 2. Clear Cache API specific to the component
|
|
1041
|
+
const cacheNames = await caches.keys();
|
|
1042
|
+
const componentCaches = cacheNames.filter(name => name.includes('jaak') || name.includes('stamps'));
|
|
1043
|
+
await Promise.all(componentCaches.map(cacheName => caches.delete(cacheName)));
|
|
1044
|
+
// 3. Force garbage collection if available (only in dev)
|
|
1045
|
+
if (this.debug && window.gc) {
|
|
1046
|
+
window.gc();
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
catch (error) {
|
|
1050
|
+
console.warn('Error clearing browser cache:', error);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
// Clear local storage specific to the component
|
|
1054
|
+
async clearLocalStorage() {
|
|
1055
|
+
try {
|
|
1056
|
+
// Clear localStorage specific to the component
|
|
1057
|
+
const keysToRemove = [];
|
|
1058
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
1059
|
+
const key = localStorage.key(i);
|
|
1060
|
+
if (key && (key.includes('jaak') || key.includes('onnx') || key.includes('stamps'))) {
|
|
1061
|
+
keysToRemove.push(key);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
1065
|
+
// Clear sessionStorage
|
|
1066
|
+
const sessionKeysToRemove = [];
|
|
1067
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
1068
|
+
const key = sessionStorage.key(i);
|
|
1069
|
+
if (key && (key.includes('jaak') || key.includes('onnx'))) {
|
|
1070
|
+
sessionKeysToRemove.push(key);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
|
|
1074
|
+
}
|
|
1075
|
+
catch (error) {
|
|
1076
|
+
console.warn('Error clearing local storage:', error);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
// Aggressive resource cleanup combining all strategies
|
|
1080
|
+
async aggressiveResourceCleanup() {
|
|
1081
|
+
const freed = [];
|
|
1082
|
+
const errors = [];
|
|
1083
|
+
try {
|
|
1084
|
+
// 1. Release ONNX resources
|
|
1085
|
+
this.releaseOnnxResources();
|
|
1086
|
+
freed.push('ONNX models');
|
|
1087
|
+
// 2. Clear model cache in detection service
|
|
1088
|
+
if (this.detectionService) {
|
|
1089
|
+
await this.detectionService.clearModelCache();
|
|
1090
|
+
freed.push('Model cache');
|
|
1091
|
+
}
|
|
1092
|
+
// 3. Clear browser cache
|
|
1093
|
+
await this.clearBrowserCache();
|
|
1094
|
+
freed.push('Browser cache');
|
|
1095
|
+
// 4. Clear local storage
|
|
1096
|
+
await this.clearLocalStorage();
|
|
1097
|
+
freed.push('Local storage');
|
|
1098
|
+
// 5. Clean DOM references
|
|
1099
|
+
this.cleanupDOMReferences();
|
|
1100
|
+
freed.push('DOM references');
|
|
1101
|
+
// 6. Optimize canvas pools
|
|
1102
|
+
if (this.detectionService) {
|
|
1103
|
+
this.detectionService.optimizeCanvasPool(0);
|
|
1104
|
+
freed.push('Canvas pools');
|
|
1105
|
+
}
|
|
1106
|
+
// 7. Clear component canvas pool
|
|
1107
|
+
this.canvasPool = [];
|
|
1108
|
+
freed.push('Component canvas pool');
|
|
1109
|
+
// 8. Force garbage collection (if available)
|
|
1110
|
+
if (this.debug && window.gc) {
|
|
1111
|
+
window.gc();
|
|
1112
|
+
freed.push('Garbage collection');
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
catch (error) {
|
|
1116
|
+
errors.push(error.message);
|
|
1117
|
+
}
|
|
1118
|
+
return { freed, errors };
|
|
1119
|
+
}
|
|
1120
|
+
// Progressive resource release after front capture
|
|
1121
|
+
async onFrontCaptureComplete() {
|
|
1122
|
+
try {
|
|
1123
|
+
// Release classification model if not needed for back
|
|
1124
|
+
if (this.detectionService && !this.useDocumentClassification) {
|
|
1125
|
+
await this.detectionService.releaseMobileNetResources();
|
|
1126
|
+
}
|
|
1127
|
+
// Optimize canvas pool to minimum needed for back capture
|
|
1128
|
+
if (this.detectionService) {
|
|
1129
|
+
this.detectionService.optimizeCanvasPool(1);
|
|
1130
|
+
}
|
|
1131
|
+
// Reduce component canvas pool
|
|
1132
|
+
while (this.canvasPool.length > 1) {
|
|
1133
|
+
const canvas = this.canvasPool.pop();
|
|
1134
|
+
if (canvas) {
|
|
1135
|
+
const ctx = canvas.getContext('2d');
|
|
1136
|
+
if (ctx) {
|
|
1137
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
if (this.debug) {
|
|
1142
|
+
console.log('🧹 Progressive cleanup after front capture completed');
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
catch (error) {
|
|
1146
|
+
console.warn('Error in progressive cleanup:', error);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
// Resource reporting for debugging
|
|
1150
|
+
getResourceReportInternal() {
|
|
1151
|
+
const report = {
|
|
1152
|
+
timestamp: new Date().toISOString(),
|
|
1153
|
+
component: {
|
|
1154
|
+
hasVideoStream: !!this.videoStream,
|
|
1155
|
+
hasAnimationFrame: !!this.animationId,
|
|
1156
|
+
canvasPoolSize: this.canvasPool.length,
|
|
1157
|
+
detectionBoxesCount: this.detectionBoxes.length,
|
|
1158
|
+
performanceMonitorActive: !!this.performanceUpdateInterval
|
|
1159
|
+
}
|
|
1160
|
+
};
|
|
1161
|
+
// Add detection service stats if available
|
|
1162
|
+
if (this.detectionService) {
|
|
1163
|
+
report.detectionService = {
|
|
1164
|
+
isModelLoaded: this.detectionService.isModelLoaded(),
|
|
1165
|
+
canvasPoolStats: this.detectionService.getPoolStats()
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
// Add memory info if available
|
|
1169
|
+
if ('memory' in performance) {
|
|
1170
|
+
const memInfo = performance.memory;
|
|
1171
|
+
report.memory = {
|
|
1172
|
+
usedJSHeapSize: Math.round(memInfo.usedJSHeapSize / 1048576), // MB
|
|
1173
|
+
totalJSHeapSize: Math.round(memInfo.totalJSHeapSize / 1048576), // MB
|
|
1174
|
+
jsHeapSizeLimit: Math.round(memInfo.jsHeapSizeLimit / 1048576) // MB
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
return report;
|
|
1178
|
+
}
|
|
882
1179
|
render() {
|
|
883
1180
|
const captureState = this.stateManager?.getCaptureState() || {
|
|
884
1181
|
isVideoActive: false,
|
|
@@ -889,7 +1186,7 @@ export class JaakStamps {
|
|
|
889
1186
|
isCapturing: false
|
|
890
1187
|
};
|
|
891
1188
|
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
892
|
-
return (h("div", { key: '
|
|
1189
|
+
return (h("div", { key: 'ca44b9eacbde9f688f2c41aa21ad35a2b882002b', class: "detector-container" }, h("div", { key: '86c7b5d31ed69276b62fa43e01c92b0c0b9f298d', class: "video-container" }, h("video", { key: 'bb310544c17ae734d8b42ed2617748c2f69743ff', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '078a79a0c6386ca6c040e629acbf4f52f12d8e29', 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: {
|
|
893
1190
|
position: 'absolute',
|
|
894
1191
|
left: `${box.x}px`,
|
|
895
1192
|
top: `${box.y}px`,
|
|
@@ -898,9 +1195,9 @@ export class JaakStamps {
|
|
|
898
1195
|
border: '2px solid #32406C',
|
|
899
1196
|
pointerEvents: 'none',
|
|
900
1197
|
boxSizing: 'border-box'
|
|
901
|
-
} })))), this.isMaskReady && (h("div", { key: '
|
|
1198
|
+
} })))), this.isMaskReady && (h("div", { key: '990015ff5b2ded89405dd2b891c027aad8788f3e', class: "overlay-mask" }, h("div", { key: 'dd9145140987addb1b6e4cc2119b8e9f89003726', class: "card-outline" }, h("div", { key: 'e8f4ec692a8b80c7a98075e4f708b79a272db67b', class: "side side-top" }), h("div", { key: '2577253b40425ecdcb3dd51b2f4a43c15c83ca79', class: "side side-right" }), h("div", { key: '30f4454020c00cf4fb92308af5ecf1843ec24005', class: "side side-bottom" }), h("div", { key: '3459f634987b8396106fff23fa1c99b295ee7d0a', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '84f4d70947e211a9c6b296531d53420223d2ca34', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '7c28e86e8de6c33bf7d7baab3083c5f6a0ab060f', class: "back-capture-section" }, h("div", { key: '27bdcf9cfe9790c4bbad484423f6f4c353ccf0b3', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (h("button", { key: 'ed0320b1df412bf53cbe4e4119f6d769926eb2c5', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '0bba706c8ebda59c33faca8e97b7536146462d21', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: 'b072c46b25d16a04c5e0a8f3975d8aa8d24576d5', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: 'efd0827d0f19c9072d49d1b3c0046ef6c95a842c', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
902
1199
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
903
|
-
: 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '
|
|
1200
|
+
: 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'bd0303e8ce85211e4c68fc3cd79545db7c74288b', class: "camera-controls" }, h("button", { key: 'e4327d260b988b67e0de3654c72f24455cd3bbbd', 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: '35da16baf4c62466c2a66650209a1daf842c0db2', class: "camera-selector-dropdown" }, h("div", { key: '7035dd0e22f614cf239a015c6d59531b68fa74a9', class: "camera-selector-header" }, h("span", { key: 'ae026f31931f52ea0daf72391a9de4ff9e4b88f1' }, "Seleccionar C\u00E1mara"), h("button", { key: '40f2ba0770bafea71fedbf849938010e743be776', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '6f26927094a724a35ebb07ef701caf14f296a837', 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: '082236981c369f8fd487f036439b16e6560514be', class: "device-info" }, h("small", { key: 'ccbbfa0bca96395f574bda71103338ae9226f709' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '6baa487409503777e90453b39fd30649941cbbe4', class: "manual-capture-section" }, h("button", { key: '893f4715ca3315613f779603f178459955f686f0', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '7c30b680fe84f4c8227beba45d8416f1edb8348f', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: 'd8ef052cf67d7bda4b3534febf96efa2cdcc6bf7', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '3dae7634e770dda2655b1042ec8fb997b3cb2220', class: "flip-animation" }, h("div", { key: 'a977fc5e889369121bacc06cf5213dfee39d858d', class: "id-card-icon" }), h("div", { key: '4f052cdc8c7b493cddce90e108e976325c9bab8e', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '5117cd6e63e1e0763c1503f6f60f3c7ef60dd665', class: "success-animation" }, h("div", { key: 'd11128c45c7118915cc0dd9c25845bb55b4b3eb2', class: "check-icon" }), h("div", { key: '693e197bf8a4c91ac7ddedb457d15aa3aa46a5d5', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '89caaa9d78eb522e0fadfd1c0e844e05d29b8286', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '3f41d40b05b71e13e7e1d0c0e980b48308cba9d6', class: "status-spinner" })), h("div", { key: '8674480928f4f4a77099c0600ed84f0e27903379', class: "status-content" }, h("div", { key: 'e88f4044ab8115a6a5cbb78cc4030975eef9149a', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'ad581e42c30c304b837711549148c11b801dee5e', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '1b371c1b47f3ba699885db00fef52eb448f9e245', class: "performance-monitor" }, h("div", { key: '8b9ac60449a15e9fb966c329b2e6c9285c0228c0', class: "performance-expanded" }, h("div", { key: '760c11a3ed28e248a144d53a92fd8adee6822ff2', class: "metrics-row" }, h("div", { key: '858ab8b106f1d12210e4e15c423ab3bab11bdb60', class: "metric-compact" }, h("span", { key: '65fe2479c3822d99452e7e5faa93c25a80765024', class: "metric-label" }, "FPS"), h("span", { key: '9faf7763d1284a6f3c1e15fb0f3c99f1d45bb4dc', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: 'c95ed8fa8101f42bfba5cc8ca7debbad028caf42', class: "metric-compact" }, h("span", { key: '3555b3778cb71e959d132d6aa563f58f757915d8', class: "metric-label" }, "MEM"), h("span", { key: 'd72b0491147a6f80dcf33f5d72dd8dae5898f44a', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'cd4dc81f28f9e59a36a0295dde7bfee89f50d3e2', class: "metrics-row" }, h("div", { key: 'c43d8624c5da256ce3e97f67b022bdd98fb04e07', class: "metric-compact" }, h("span", { key: 'adb402d83809a90580b81544f31b5331bdff59dd', class: "metric-label" }, "INF"), h("span", { key: 'e48c9814c08acbe321460a29e42cb49b0eab38ff', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '71517f9279e9a6aa0d5b02e2169db868ca3fb676', class: "metric-compact" }, h("span", { key: '3ddbcc56b137aea702a40f900178e8e16efd146e', class: "metric-label" }, "FRAME"), h("span", { key: 'd3a70afb511d3f72b485b3a1cb922876faa62b11', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: 'a3b62cdbe830b1024ab96f7fb726825df24fecdc', class: "metrics-row" }, h("div", { key: '95471e5cdde748d1ce6bdee3540740dd3aa66865', class: "metric-compact" }, h("span", { key: 'c93e01c0bdc67621c3a666357d2477b7a5275d52', class: "metric-label" }, "DET"), h("span", { key: 'ef33c86a83101b0989b14c7f1ab0ce0b331b397f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'e14d56128bf96337e4c3859f8e4003e001e8e953', class: "metric-compact" }, h("span", { key: '8aa828632f0a977a27ed377f103bb8d5e7e0ed1a', class: "metric-label" }, "RATE"), h("span", { key: '59035c473b5a747a27f774e2554322a084b6d19f', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '4d0c2de00954416faac30ab622fb21f36c932aca', class: "watermark" }, h("img", { key: 'd8f775428ebb3ba86a943ca73b074db2180d0c29', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
904
1201
|
}
|
|
905
1202
|
// Utility methods
|
|
906
1203
|
updateDetectionBoxes(boxes) {
|
|
@@ -1016,9 +1313,24 @@ export class JaakStamps {
|
|
|
1016
1313
|
}
|
|
1017
1314
|
}
|
|
1018
1315
|
async takeManualScreenshot() {
|
|
1019
|
-
|
|
1316
|
+
console.log('🔵 Botón clicked: Capturar (Frente/Reverso)');
|
|
1317
|
+
// Set processing state immediately
|
|
1318
|
+
const captureState = this.stateManager?.getCaptureState();
|
|
1319
|
+
if (captureState?.step === 'front') {
|
|
1320
|
+
this.processingButton = 'capture-front';
|
|
1321
|
+
}
|
|
1322
|
+
else if (captureState?.step === 'back') {
|
|
1323
|
+
this.processingButton = 'capture-back';
|
|
1324
|
+
}
|
|
1325
|
+
console.log('🔵 processingButton set to:', this.processingButton);
|
|
1326
|
+
// Add small delay to show spinner
|
|
1327
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
1328
|
+
if (!this.videoRef) {
|
|
1329
|
+
this.processingButton = null;
|
|
1020
1330
|
return;
|
|
1331
|
+
}
|
|
1021
1332
|
// When using manual capture, use mask coordinates for cropping
|
|
1333
|
+
// Note: processingButton will be cleared inside takeScreenshotWithMaskCoordinates
|
|
1022
1334
|
await this.takeScreenshotWithMaskCoordinates();
|
|
1023
1335
|
}
|
|
1024
1336
|
async takeScreenshotWithMaskCoordinates() {
|
|
@@ -1094,6 +1406,8 @@ export class JaakStamps {
|
|
|
1094
1406
|
this.completeProcess(true);
|
|
1095
1407
|
this.returnCanvasToPool(captureCanvas);
|
|
1096
1408
|
this.returnCanvasToPool(croppedCanvas);
|
|
1409
|
+
// Clear processing button when passport is detected
|
|
1410
|
+
this.processingButton = null;
|
|
1097
1411
|
return;
|
|
1098
1412
|
}
|
|
1099
1413
|
}
|
|
@@ -1107,6 +1421,8 @@ export class JaakStamps {
|
|
|
1107
1421
|
showFlipAnimation: true
|
|
1108
1422
|
});
|
|
1109
1423
|
this.triggerRerender();
|
|
1424
|
+
// Progressive resource cleanup after front capture
|
|
1425
|
+
await this.onFrontCaptureComplete();
|
|
1110
1426
|
setTimeout(() => {
|
|
1111
1427
|
this.stateManager.updateCaptureState({
|
|
1112
1428
|
showFlipAnimation: false,
|
|
@@ -1114,6 +1430,8 @@ export class JaakStamps {
|
|
|
1114
1430
|
});
|
|
1115
1431
|
this.triggerRerender();
|
|
1116
1432
|
this.startBackDocumentTimer();
|
|
1433
|
+
// Clear processing button after animation completes
|
|
1434
|
+
this.processingButton = null;
|
|
1117
1435
|
}, 3000);
|
|
1118
1436
|
}
|
|
1119
1437
|
else if (captureState.step === 'back') {
|
|
@@ -1124,6 +1442,8 @@ export class JaakStamps {
|
|
|
1124
1442
|
}
|
|
1125
1443
|
});
|
|
1126
1444
|
this.completeProcess(false);
|
|
1445
|
+
// Clear processing button after back capture completes
|
|
1446
|
+
this.processingButton = null;
|
|
1127
1447
|
}
|
|
1128
1448
|
// Return canvases to pool after use
|
|
1129
1449
|
this.returnCanvasToPool(captureCanvas);
|
|
@@ -1178,6 +1498,8 @@ export class JaakStamps {
|
|
|
1178
1498
|
isDetectionPaused: true,
|
|
1179
1499
|
showFlipAnimation: true
|
|
1180
1500
|
});
|
|
1501
|
+
// Progressive resource cleanup after front capture
|
|
1502
|
+
await this.onFrontCaptureComplete();
|
|
1181
1503
|
setTimeout(() => {
|
|
1182
1504
|
this.stateManager.updateCaptureState({
|
|
1183
1505
|
showFlipAnimation: false,
|
|
@@ -1217,6 +1539,9 @@ export class JaakStamps {
|
|
|
1217
1539
|
this.stopPerformanceMonitoring();
|
|
1218
1540
|
// Liberar recursos de ONNX
|
|
1219
1541
|
this.releaseOnnxResources();
|
|
1542
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1543
|
+
this.resetMaskToWhite();
|
|
1544
|
+
this.detectionBoxes = [];
|
|
1220
1545
|
this.hasAutoSwitchedToManual = true;
|
|
1221
1546
|
this.performanceDegradedMode = true;
|
|
1222
1547
|
this.showManualCaptureButton = true;
|
|
@@ -1227,6 +1552,75 @@ export class JaakStamps {
|
|
|
1227
1552
|
}, 5000);
|
|
1228
1553
|
}
|
|
1229
1554
|
// Método para cambiar a modo manual por errores de ONNX
|
|
1555
|
+
switchToManualModeWithWarning(warningMessage) {
|
|
1556
|
+
// Solo cambiar si el detector está habilitado y no se ha cambiado ya
|
|
1557
|
+
if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
// No necesitamos liberar recursos ONNX porque no se han cargado aún
|
|
1561
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1562
|
+
this.resetMaskToWhite();
|
|
1563
|
+
this.detectionBoxes = [];
|
|
1564
|
+
// Cambiar a modo manual
|
|
1565
|
+
this.hasAutoSwitchedToManual = true;
|
|
1566
|
+
this.performanceDegradedMode = true;
|
|
1567
|
+
this.showManualCaptureButton = true;
|
|
1568
|
+
this.useDocumentDetector = false; // Desactivar detector automático
|
|
1569
|
+
// Actualizar el estado con mensaje informativo (no error)
|
|
1570
|
+
this.updateStatus('Modo manual activado', warningMessage, 'active');
|
|
1571
|
+
// Continuar con la configuración de cámara
|
|
1572
|
+
this.continueWithCameraSetup();
|
|
1573
|
+
}
|
|
1574
|
+
async continueWithCameraSetup() {
|
|
1575
|
+
try {
|
|
1576
|
+
// Paso 2: Detectar y configurar dispositivos
|
|
1577
|
+
this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
|
|
1578
|
+
try {
|
|
1579
|
+
await this.cameraService.enumerateDevices();
|
|
1580
|
+
}
|
|
1581
|
+
catch (enumerateError) {
|
|
1582
|
+
throw new Error(`Error al enumerar dispositivos: ${enumerateError.message}`);
|
|
1583
|
+
}
|
|
1584
|
+
try {
|
|
1585
|
+
await this.updateCameraInfoWithAutofocus();
|
|
1586
|
+
}
|
|
1587
|
+
catch (cameraInfoError) {
|
|
1588
|
+
throw new Error(`Error al actualizar información de cámara: ${cameraInfoError.message}`);
|
|
1589
|
+
}
|
|
1590
|
+
// Paso 3: Configurar cámara seleccionada
|
|
1591
|
+
this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
|
|
1592
|
+
let stream;
|
|
1593
|
+
try {
|
|
1594
|
+
stream = await this.cameraService.setupCamera();
|
|
1595
|
+
}
|
|
1596
|
+
catch (setupError) {
|
|
1597
|
+
throw new Error(`Error al configurar cámara: ${setupError.message}`);
|
|
1598
|
+
}
|
|
1599
|
+
// Paso 4: Inicializar video
|
|
1600
|
+
this.updateStatus('Captura manual', 'Posicione su documento y use el botón para capturar', 'active');
|
|
1601
|
+
try {
|
|
1602
|
+
await this.initializeVideoStream(stream);
|
|
1603
|
+
}
|
|
1604
|
+
catch (videoError) {
|
|
1605
|
+
throw new Error(`Error al inicializar video: ${videoError.message}`);
|
|
1606
|
+
}
|
|
1607
|
+
// Paso 5: Calibrar máscara
|
|
1608
|
+
if (this.detectionContainer) {
|
|
1609
|
+
const container = this.detectionContainer.parentElement;
|
|
1610
|
+
const rect = container.getBoundingClientRect();
|
|
1611
|
+
this.updateMaskDimensions(rect);
|
|
1612
|
+
}
|
|
1613
|
+
// Finalizar
|
|
1614
|
+
this.startTime = Date.now();
|
|
1615
|
+
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1616
|
+
// Mostrar botón manual (ya debe estar habilitado)
|
|
1617
|
+
this.showManualCaptureButton = true;
|
|
1618
|
+
}
|
|
1619
|
+
catch (error) {
|
|
1620
|
+
this.updateStatus('Error de cámara', error.message, 'error');
|
|
1621
|
+
throw error;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1230
1624
|
switchToManualModeWithError(errorMessage) {
|
|
1231
1625
|
// Solo cambiar si el detector está habilitado y no se ha cambiado ya
|
|
1232
1626
|
if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
|
|
@@ -1234,6 +1628,9 @@ export class JaakStamps {
|
|
|
1234
1628
|
}
|
|
1235
1629
|
// Liberar recursos de ONNX inmediatamente
|
|
1236
1630
|
this.releaseOnnxResources();
|
|
1631
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1632
|
+
this.resetMaskToWhite();
|
|
1633
|
+
this.detectionBoxes = [];
|
|
1237
1634
|
// Cambiar a modo manual
|
|
1238
1635
|
this.hasAutoSwitchedToManual = true;
|
|
1239
1636
|
this.performanceDegradedMode = true;
|
|
@@ -1280,6 +1677,16 @@ export class JaakStamps {
|
|
|
1280
1677
|
setTimeout(() => {
|
|
1281
1678
|
this.stateManager.updateCaptureState({ showSuccessAnimation: false });
|
|
1282
1679
|
}, 3000);
|
|
1680
|
+
// Aggressive cleanup after process completion with delay
|
|
1681
|
+
setTimeout(async () => {
|
|
1682
|
+
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1683
|
+
if (this.debug) {
|
|
1684
|
+
console.log('🧹 Post-completion cleanup:', cleanupResult.freed);
|
|
1685
|
+
if (cleanupResult.errors.length > 0) {
|
|
1686
|
+
console.warn('⚠️ Cleanup errors:', cleanupResult.errors);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}, 1000); // Wait 1 second after completion
|
|
1283
1690
|
}
|
|
1284
1691
|
stopDetection() {
|
|
1285
1692
|
if (this.animationId) {
|
|
@@ -1484,6 +1891,24 @@ export class JaakStamps {
|
|
|
1484
1891
|
// Performance is good, use base frame skip
|
|
1485
1892
|
return this.BASE_FRAME_SKIP;
|
|
1486
1893
|
}
|
|
1894
|
+
// Método para resetear la máscara a color blanco
|
|
1895
|
+
resetMaskToWhite() {
|
|
1896
|
+
const cardOutline = this.el.shadowRoot?.querySelector('.card-outline');
|
|
1897
|
+
const corners = this.el.shadowRoot?.querySelectorAll('.corner');
|
|
1898
|
+
const topSide = this.el.shadowRoot?.querySelector('.side-top');
|
|
1899
|
+
const rightSide = this.el.shadowRoot?.querySelector('.side-right');
|
|
1900
|
+
const bottomSide = this.el.shadowRoot?.querySelector('.side-bottom');
|
|
1901
|
+
const leftSide = this.el.shadowRoot?.querySelector('.side-left');
|
|
1902
|
+
// Remover todas las clases de alineación y estado perfecto
|
|
1903
|
+
cardOutline?.classList.remove('perfect-match');
|
|
1904
|
+
corners?.forEach(corner => corner.classList.remove('perfect-match'));
|
|
1905
|
+
topSide?.classList.remove('aligned');
|
|
1906
|
+
rightSide?.classList.remove('aligned');
|
|
1907
|
+
bottomSide?.classList.remove('aligned');
|
|
1908
|
+
leftSide?.classList.remove('aligned');
|
|
1909
|
+
// Resetear estado de alineación
|
|
1910
|
+
this.sideAlignment = { top: false, right: false, bottom: false, left: false };
|
|
1911
|
+
}
|
|
1487
1912
|
// Canvas pool management for screenshots
|
|
1488
1913
|
getPooledCanvas(width, height) {
|
|
1489
1914
|
// Try to find a canvas with matching dimensions
|
|
@@ -1746,7 +2171,8 @@ export class JaakStamps {
|
|
|
1746
2171
|
"showManualCaptureButton": {},
|
|
1747
2172
|
"performanceDegradedMode": {},
|
|
1748
2173
|
"showPerformanceMessage": {},
|
|
1749
|
-
"captureStateVersion": {}
|
|
2174
|
+
"captureStateVersion": {},
|
|
2175
|
+
"processingButton": {}
|
|
1750
2176
|
};
|
|
1751
2177
|
}
|
|
1752
2178
|
static get events() {
|
|
@@ -1842,7 +2268,7 @@ export class JaakStamps {
|
|
|
1842
2268
|
},
|
|
1843
2269
|
"stopCapture": {
|
|
1844
2270
|
"complexType": {
|
|
1845
|
-
"signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
|
|
2271
|
+
"signature": "() => Promise<{ success: boolean; resourcesFreed: string[]; cleanupErrors: string[]; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; cleanupErrors?: undefined; }>",
|
|
1846
2272
|
"parameters": [],
|
|
1847
2273
|
"references": {
|
|
1848
2274
|
"Promise": {
|
|
@@ -1850,7 +2276,7 @@ export class JaakStamps {
|
|
|
1850
2276
|
"id": "global::Promise"
|
|
1851
2277
|
}
|
|
1852
2278
|
},
|
|
1853
|
-
"return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
|
|
2279
|
+
"return": "Promise<{ success: boolean; resourcesFreed: string[]; cleanupErrors: string[]; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; cleanupErrors?: undefined; }>"
|
|
1854
2280
|
},
|
|
1855
2281
|
"docs": {
|
|
1856
2282
|
"text": "",
|
|
@@ -2010,6 +2436,40 @@ export class JaakStamps {
|
|
|
2010
2436
|
"text": "",
|
|
2011
2437
|
"tags": []
|
|
2012
2438
|
}
|
|
2439
|
+
},
|
|
2440
|
+
"getResourceReport": {
|
|
2441
|
+
"complexType": {
|
|
2442
|
+
"signature": "() => Promise<any>",
|
|
2443
|
+
"parameters": [],
|
|
2444
|
+
"references": {
|
|
2445
|
+
"Promise": {
|
|
2446
|
+
"location": "global",
|
|
2447
|
+
"id": "global::Promise"
|
|
2448
|
+
}
|
|
2449
|
+
},
|
|
2450
|
+
"return": "Promise<any>"
|
|
2451
|
+
},
|
|
2452
|
+
"docs": {
|
|
2453
|
+
"text": "",
|
|
2454
|
+
"tags": []
|
|
2455
|
+
}
|
|
2456
|
+
},
|
|
2457
|
+
"forceResourceCleanup": {
|
|
2458
|
+
"complexType": {
|
|
2459
|
+
"signature": "() => Promise<{ success: boolean; resourcesFreed: string[]; errors: string[]; timestamp: string; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; errors?: undefined; timestamp?: undefined; }>",
|
|
2460
|
+
"parameters": [],
|
|
2461
|
+
"references": {
|
|
2462
|
+
"Promise": {
|
|
2463
|
+
"location": "global",
|
|
2464
|
+
"id": "global::Promise"
|
|
2465
|
+
}
|
|
2466
|
+
},
|
|
2467
|
+
"return": "Promise<{ success: boolean; resourcesFreed: string[]; errors: string[]; timestamp: string; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; errors?: undefined; timestamp?: undefined; }>"
|
|
2468
|
+
},
|
|
2469
|
+
"docs": {
|
|
2470
|
+
"text": "",
|
|
2471
|
+
"tags": []
|
|
2472
|
+
}
|
|
2013
2473
|
}
|
|
2014
2474
|
};
|
|
2015
2475
|
}
|