@jaak.ai/stamps 2.1.0-dev.2 → 2.1.0-dev.4

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.
@@ -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],"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],"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;
@@ -621,6 +621,25 @@ video {
621
621
  to { opacity: 1; transform: translateY(0); }
622
622
  }
623
623
 
624
+ /* Performance warning animation for guide-text */
625
+ .guide-text.performance-warning-text {
626
+ animation: gentlePulse 2s ease-in-out infinite;
627
+ background: rgba(255, 107, 107, 0.3);
628
+ border: 1px solid rgba(255, 107, 107, 0.5);
629
+ }
630
+
631
+ @keyframes gentlePulse {
632
+ 0%, 100% {
633
+ transform: translate(-50%, -50%) scale(1);
634
+ background: rgba(255, 107, 107, 0.3);
635
+ }
636
+ 50% {
637
+ transform: translate(-50%, -50%) scale(1.02);
638
+ background: rgba(255, 107, 107, 0.4);
639
+ }
640
+ }
641
+
642
+
624
643
  /* Botón para saltar reverso */
625
644
  .skip-button {
626
645
  pointer-events: auto;
@@ -49,6 +49,8 @@ export class JaakStamps {
49
49
  };
50
50
  backDocumentTimerRemaining = 0;
51
51
  showManualCaptureButton = false;
52
+ performanceDegradedMode = false; // Auto-switched to manual mode due to performance
53
+ showPerformanceMessage = false; // Show performance message temporarily
52
54
  // Services
53
55
  serviceContainer;
54
56
  eventBus;
@@ -72,7 +74,6 @@ export class JaakStamps {
72
74
  fps: 0,
73
75
  inferenceTime: 0,
74
76
  memoryUsage: 0,
75
- cpuUsage: 0,
76
77
  onnxLoadTime: 0,
77
78
  frameProcessingTime: 0,
78
79
  totalDetections: 0,
@@ -91,6 +92,12 @@ export class JaakStamps {
91
92
  MIN_INFERENCE_INTERVAL = 50;
92
93
  performanceHistory = [];
93
94
  PERFORMANCE_HISTORY_SIZE = 10;
95
+ // Sistema simplificado de monitoreo de rendimiento
96
+ PERFORMANCE_THRESHOLD_MS = 2000; // Umbral único para todos los frames
97
+ SLOW_FRAMES_TO_TRIGGER = 2; // Cuántos frames lentos consecutivos antes de cambiar
98
+ slowFrameCount = 0; // Contador de frames consecutivos lentos
99
+ processedFramesCount = 0; // Contador total de frames procesados
100
+ hasAutoSwitchedToManual = false;
94
101
  // Canvas pool for optimized screenshot capture
95
102
  canvasPool = [];
96
103
  MAX_CANVAS_POOL_SIZE = 3;
@@ -123,14 +130,8 @@ export class JaakStamps {
123
130
  this.detectionService = this.serviceContainer.getDetectionService();
124
131
  }
125
132
  async setupEventListeners() {
126
- this.eventBus.on('state-changed', (data) => {
127
- this.handleStateChange(data);
128
- });
129
- this.eventBus.on('camera-changed', () => {
130
- // Camera changed event
131
- });
132
- this.eventBus.on('error', () => {
133
- // Handle service errors
133
+ this.eventBus.on('state-changed', () => {
134
+ this.handleStateChange();
134
135
  });
135
136
  }
136
137
  async initializeComponent() {
@@ -237,13 +238,8 @@ export class JaakStamps {
237
238
  return false;
238
239
  }
239
240
  }
240
- handleStateChange(data) {
241
- // React to state changes from StateManager
242
- // This is where the component updates its internal state based on service state changes
243
- if (data.changes.isLoading !== undefined) {
244
- // Force re-render when loading state changes
245
- // Note: Stencil automatically re-renders when @State changes
246
- }
241
+ handleStateChange() {
242
+ // Handle state changes from StateManager if needed
247
243
  }
248
244
  initializeResizeObserver() {
249
245
  if ('ResizeObserver' in window && this.detectionContainer) {
@@ -264,6 +260,10 @@ export class JaakStamps {
264
260
  if (step === 'completed') {
265
261
  return 'Proceso completado';
266
262
  }
263
+ // Check if we should show performance message
264
+ if (this.showPerformanceMessage && step === 'front') {
265
+ return 'Modo manual activado por rendimiento. Use el botón para capturar.';
266
+ }
267
267
  if (step === 'front') {
268
268
  // Para el frente, si el detector manual está activo, mostrar instrucciones de captura manual
269
269
  if (this.showManualCaptureButton) {
@@ -493,9 +493,23 @@ export class JaakStamps {
493
493
  if (this.stateManager) {
494
494
  this.stateManager.updateCaptureState({ isLoading: true });
495
495
  }
496
- await this.detectionService.loadModel();
496
+ try {
497
+ await this.detectionService.loadModel();
498
+ }
499
+ catch (modelError) {
500
+ console.error('Error cargando modelo de detección:', modelError);
501
+ this.switchToManualModeWithError('Error al cargar modelo de detección');
502
+ throw modelError;
503
+ }
497
504
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
498
- await this.detectionService.loadClassificationModel();
505
+ try {
506
+ await this.detectionService.loadClassificationModel();
507
+ }
508
+ catch (classificationError) {
509
+ console.error('Error cargando modelo de clasificación:', classificationError);
510
+ this.switchToManualModeWithError('Error al cargar modelo de clasificación');
511
+ throw classificationError;
512
+ }
499
513
  const loadEndTime = performance.now();
500
514
  const loadTime = loadEndTime - loadStartTime;
501
515
  // Record ONNX load time
@@ -612,9 +626,23 @@ export class JaakStamps {
612
626
  const loadStartTime = performance.now();
613
627
  this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
614
628
  this.stateManager.updateCaptureState({ isLoading: true });
615
- await this.detectionService.loadModel();
629
+ try {
630
+ await this.detectionService.loadModel();
631
+ }
632
+ catch (modelError) {
633
+ console.error('Error cargando modelo de detección:', modelError);
634
+ this.switchToManualModeWithError('Error al cargar modelo de detección');
635
+ throw modelError;
636
+ }
616
637
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
617
- await this.detectionService.loadClassificationModel();
638
+ try {
639
+ await this.detectionService.loadClassificationModel();
640
+ }
641
+ catch (classificationError) {
642
+ console.error('Error cargando modelo de clasificación:', classificationError);
643
+ this.switchToManualModeWithError('Error al cargar modelo de clasificación');
644
+ throw classificationError;
645
+ }
618
646
  const loadEndTime = performance.now();
619
647
  const loadTime = loadEndTime - loadStartTime;
620
648
  // Record ONNX load time
@@ -629,9 +657,9 @@ export class JaakStamps {
629
657
  // Paso 3: Configurar cámara seleccionada
630
658
  this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
631
659
  const stream = await this.cameraService.setupCamera();
632
- // Paso 4: Inicializar video y ocultar estado inmediatamente
660
+ // Paso 4: Inicializar video y mostrar estado de análisis inicial
633
661
  if (this.useDocumentDetector) {
634
- this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
662
+ this.updateStatus('Analizando estabilidad...', 'Evaluando rendimiento del sistema', 'loading');
635
663
  }
636
664
  else {
637
665
  this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
@@ -681,8 +709,8 @@ export class JaakStamps {
681
709
  const captureState = this.stateManager.getCaptureState();
682
710
  if (!this.videoRef || !this.detectionContainer || !this.detectionService.isModelLoaded())
683
711
  return;
684
- // Show manual capture button when document detector is disabled
685
- if (!this.useDocumentDetector) {
712
+ // Show manual capture button when document detector is disabled or performance is degraded
713
+ if (!this.useDocumentDetector || this.performanceDegradedMode) {
686
714
  this.showManualCaptureButton = true;
687
715
  // Continue the loop for UI updates but skip detection logic
688
716
  if (captureState.step !== 'completed') {
@@ -690,7 +718,7 @@ export class JaakStamps {
690
718
  }
691
719
  return;
692
720
  }
693
- else {
721
+ else if (!this.performanceDegradedMode) {
694
722
  this.showManualCaptureButton = false;
695
723
  }
696
724
  if (captureState.isDetectionPaused) {
@@ -720,10 +748,29 @@ export class JaakStamps {
720
748
  this.lastInferenceTime = currentTime;
721
749
  // Measure preprocessing and inference time
722
750
  const inferenceStartTime = performance.now();
723
- const inputTensor = this.detectionService.preprocess(this.videoRef);
724
- // Standard detection without quality validation
725
- const detections = await this.detectionService.runInference(inputTensor);
726
- const inferenceTime = performance.now() - inferenceStartTime;
751
+ let inputTensor;
752
+ let detections;
753
+ let inferenceTime;
754
+ try {
755
+ inputTensor = this.detectionService.preprocess(this.videoRef);
756
+ }
757
+ catch (preprocessError) {
758
+ console.error('Error en preprocesamiento:', preprocessError);
759
+ this.switchToManualModeWithError('Error al procesar imagen');
760
+ return;
761
+ }
762
+ try {
763
+ // Standard detection without timeout (timeout is handled by frame counting)
764
+ detections = await this.detectionService.runInference(inputTensor);
765
+ inferenceTime = performance.now() - inferenceStartTime;
766
+ }
767
+ catch (inferenceError) {
768
+ console.error('Error en inferencia ONNX:', inferenceError);
769
+ this.switchToManualModeWithError('Error en detección automática');
770
+ return;
771
+ }
772
+ // Increment frame count
773
+ this.processedFramesCount++;
727
774
  // Record ONNX performance metrics
728
775
  if (this.debug) {
729
776
  this.recordOnnxPerformance(0, inferenceTime);
@@ -762,9 +809,30 @@ export class JaakStamps {
762
809
  }
763
810
  this.updateMaskColor(detections);
764
811
  // Record frame processing metrics
812
+ const frameEndTime = performance.now();
813
+ const totalFrameTime = frameEndTime - frameStartTime;
814
+ // Sistema simplificado: Verificar rendimiento de cada frame (solo si no se ha cambiado a manual)
815
+ if (this.useDocumentDetector && !this.hasAutoSwitchedToManual) {
816
+ // Verificar si el frame es lento
817
+ if (totalFrameTime >= this.PERFORMANCE_THRESHOLD_MS) {
818
+ this.slowFrameCount++;
819
+ // Si tenemos suficientes frames lentos consecutivos, cambiar a manual
820
+ if (this.slowFrameCount >= this.SLOW_FRAMES_TO_TRIGGER) {
821
+ this.switchToManualMode();
822
+ }
823
+ }
824
+ else {
825
+ // Frame rápido, reiniciar contador
826
+ if (this.slowFrameCount > 0) {
827
+ this.slowFrameCount = 0;
828
+ }
829
+ }
830
+ // Cambiar mensaje de estado después de analizar los primeros 2 frames
831
+ if (this.processedFramesCount === 2) {
832
+ this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
833
+ }
834
+ }
765
835
  if (this.debug) {
766
- const frameEndTime = performance.now();
767
- const totalFrameTime = frameEndTime - frameStartTime;
768
836
  this.recordFrameProcessing(totalFrameTime, detections.length);
769
837
  }
770
838
  // Continue detection loop
@@ -800,6 +868,8 @@ export class JaakStamps {
800
868
  clearTimeout(this.alignmentTimer);
801
869
  this.alignmentTimer = undefined;
802
870
  }
871
+ // Liberar recursos de ONNX al limpiar el componente
872
+ this.releaseOnnxResources();
803
873
  if (this.performanceUpdateInterval) {
804
874
  clearInterval(this.performanceUpdateInterval);
805
875
  this.performanceUpdateInterval = undefined;
@@ -821,7 +891,7 @@ export class JaakStamps {
821
891
  isCapturing: false
822
892
  };
823
893
  const cameraInfo = this.cameraInfoWithAutofocus;
824
- return (h("div", { key: 'e0b32b1b41b58e3a58b552199384080e8f2c069b', class: "detector-container" }, h("div", { key: '03c77af311ff12d936994d629d58550cbaae190c', class: "video-container" }, h("video", { key: '94f15b34f34367a10d17816a31cd147adb57bc90', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '982be192792dfaf644eb173bfee9b26ebd229288', 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: {
894
+ return (h("div", { key: '79f84b34f4c6641f0afce6825e9a916c5f1ce1fc', class: "detector-container" }, h("div", { key: '1784b74d259071b2c2f879d67bda08cdaf011c76', class: "video-container" }, h("video", { key: '48704208b83a9adfd6511266e1848dab8c583b2c', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: 'b55b96ef64557caee6b0e0c401a96c6b20e3785f', 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: {
825
895
  position: 'absolute',
826
896
  left: `${box.x}px`,
827
897
  top: `${box.y}px`,
@@ -830,9 +900,9 @@ export class JaakStamps {
830
900
  border: '2px solid #32406C',
831
901
  pointerEvents: 'none',
832
902
  boxSizing: 'border-box'
833
- } })))), this.isMaskReady && (h("div", { key: 'f5212ff70db65d2b836a3ac456a1701701630660', class: "overlay-mask" }, h("div", { key: '5080217f4962c2c7152def39cc978ba1c40a3a1c', class: "card-outline" }, h("div", { key: 'd37f1ca8723d79b10d1c6a3afaf54e2f2657ae79', class: "side side-top" }), h("div", { key: 'c5bb4f7d646a991f43598688ffa909c04697bb06', class: "side side-right" }), h("div", { key: '4a5040b4a236367dcdf72f4e72d497b8b6dee6c0', class: "side side-bottom" }), h("div", { key: 'c74ecef56eee8e92cd9491d693fa89a6c01bdbbd', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '2a2932b0c03835193ebf86ddb5f13173b1dba367', class: "guide-text" }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '41fcf2d79106d3ed928fc2842cb6a6725ba4d4f6', class: "back-capture-section" }, h("div", { key: '98dbe1bbe454bad42c3e155247f25a9151aaf1f9', class: "back-capture-buttons" }, !this.useDocumentDetector && (h("button", { key: '01693bc0d6915a4d02f318268020dfbcf3a70b94', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: captureState.isCapturing }, captureState.isCapturing ? 'Capturando...' : 'Capturar Reverso')), h("button", { key: '0a4ba55cbbea867b936a83a16a622a3c36a225f9', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
903
+ } })))), this.isMaskReady && (h("div", { key: 'd398db06f067890474b9ab3fef67fe9712a81948', class: "overlay-mask" }, h("div", { key: 'c328eef8a3da4f01aecc2c163b5c3baadee2ab7a', class: "card-outline" }, h("div", { key: 'e022c6746096b1b35d4e74295d9872092234fa3e', class: "side side-top" }), h("div", { key: '709bd54e7619572b499e8d044ead38e0baab546e', class: "side side-right" }), h("div", { key: '330de7072bbb208fad6f10a160c9794438d0dd76', class: "side side-bottom" }), h("div", { key: 'a12f18f2c2200ee74809f3bed3ee1218ced498ca', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'fb049290c464fa7137c174b2a2fcb5f8e1620f5d', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '7d027c1dfb1ea855b8d49d5b8934c81be2b73d65', class: "back-capture-section" }, h("div", { key: 'cb2f5cbd36d0fa66782a37dc8b00226a35997629', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (h("button", { key: '5cee5c2176f23849eb9494505e66e27ca5bbdf6b', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: captureState.isCapturing }, captureState.isCapturing ? 'Capturando...' : 'Capturar Reverso')), h("button", { key: '24b4171ed4a56fad56a61a9daa4529de618a5f94', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
834
904
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
835
- : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '1db208a53dea37211bdd88254e575142b7abf91f', class: "camera-controls" }, h("button", { key: '90755839358f997c3ce2f4c36a8d17d39ea5f8ad', 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: 'ef7889ab82f9eaabcabec3e3dc3ebb7850818dd4', class: "camera-selector-dropdown" }, h("div", { key: 'e28fb005316a742318be3cf8582f520392b71bba', class: "camera-selector-header" }, h("span", { key: '97338030b1aedb50c5f5a588ea03c0f19bcb0ddd' }, "Seleccionar C\u00E1mara"), h("button", { key: '1537c907dfab17c232a256f9fc9210cbd0125cc6', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '509178119e6eed5fe0a6ea287904a53bd9fbecbc', 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: '485f7e1a97c43ed280a106bde3c6ee910625025c', class: "device-info" }, h("small", { key: 'd76edba943f4e06c14024923496555fa1fe6d845' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'c5184dd93aa41cff61b88ea2046b481efc87c1f2', class: "manual-capture-section" }, h("button", { key: '6e45d9a0fec60469eb926ff353cb1a440d59f5fe', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: captureState.isCapturing }, captureState.isCapturing ? 'Capturando...' : 'Capturar Frente'))))), captureState.isCapturing && (h("div", { key: '9e296a942ba51cc1fd152e76ae406620c84ed721', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '517db50f7273788d94d5e6db6c40f65e00e18784', class: "flip-animation" }, h("div", { key: 'fbf6a26c4cc0539bd16b45899d19984af3449610', class: "id-card-icon" }), h("div", { key: 'fb3a201864d04531a078074458aa6e7f6580d565', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '10c694e233d26523d538ff0d2edbf6dcfb8e37c8', class: "success-animation" }, h("div", { key: '87e545650825ab805e887667281ecdf8bdb34c45', class: "check-icon" }), h("div", { key: '359da71f391d68e9437b749b949ee90685b96b3f', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '7c5533bb875eb3d9cdcc6cfdfc20e4d5bd98d103', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: 'f32df46ab83279387de606eadf53cd4a4009f449', class: "status-spinner" })), h("div", { key: 'dd640de5a3b64f5f99363318e2fc70e32ccbc7db', class: "status-content" }, h("div", { key: '618c0ab9cb0a24b0af3f6946052beab0d660fe6d', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'e52c2a4945c7f5a190a446ac588aa1668c21f26d', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '61cea10bc3438fb74aa176d71c4238b070cb0f09', class: "performance-monitor" }, h("div", { key: '44f6b1accb56d99a236b22c0769f870638d9c768', class: "performance-expanded" }, h("div", { key: '6febc85e319752b0aebc0a4e4050ea6d597375a7', class: "metrics-row" }, h("div", { key: '96b81895ad3cd742c96308ed53a2126af76eb619', class: "metric-compact" }, h("span", { key: '774a11c6fed6a87db7673b018b3cdc378c5252ef', class: "metric-label" }, "FPS"), h("span", { key: 'cb866dcbea661e5f715b82c4dd760f786fd5bb2a', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: 'a935aaadb83ea400bfaa984d1d845bdfd87becc8', class: "metric-compact" }, h("span", { key: 'ffacaa2cb7838403a297145b70586e255cc845c7', class: "metric-label" }, "MEM"), h("span", { key: 'a1d7a7abec97dc0544d50b11b372f699f750b76f', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'bb3d156a46569a8c290bc518c42ec7957bc5b94e', class: "metrics-row" }, h("div", { key: '4cadd8f845e4c12120ed549e4a73e02c6cbd9257', class: "metric-compact" }, h("span", { key: '1fc5661222f495b79fd7eb126e04f52eecab6fb3', class: "metric-label" }, "INF"), h("span", { key: 'eca1f59d1c41ab99158eec81592bc7150bfeabe1', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'f708e2757adcb03e633111d96bd6786fb6b80ea6', class: "metric-compact" }, h("span", { key: 'a13099ded64180996d6f65bf215483ba83daccbc', class: "metric-label" }, "FRAME"), h("span", { key: '2765cd1f7c1a49df9de64639cc1b777c79f89f4c', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '13aaf85a1b4b66294c4204f02d956b64665aa766', class: "metrics-row" }, h("div", { key: '40012f3f646d70c535a606ebbe581a2fe6fd2e50', class: "metric-compact" }, h("span", { key: '03f8b17746fe404485820d06a50173678db2fd25', class: "metric-label" }, "DET"), h("span", { key: 'c4f87430f142355054d9361330d6a5d508974b70', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'cf366a41d7f7cda6046c32cc7c514eb7b46b05d1', class: "metric-compact" }, h("span", { key: '925e7de15b96ba4dbbcdbf8a9f0e3173bae49573', class: "metric-label" }, "RATE"), h("span", { key: '9701e60c7793f2c205f3c9ffd254b6a115b8a68c', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '636aeb9b2b8dd992d8a28a8db44b213e12514096', class: "watermark" }, h("img", { key: '0407b550d962a6113e05ad75e25a1a1bbd76d9ce', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
905
+ : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'f0b8bfed998c950e30de0e43662a1965374c6180', class: "camera-controls" }, h("button", { key: '7c795a9aa31249f2d42bc45e6d77fba633b7400f', 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: '97ffdf5203e6d7ed1139b7d88088761c1f2adb80', class: "camera-selector-dropdown" }, h("div", { key: '0ba533fa51f946abdcc535e07c86f31fd0d1d4bb', class: "camera-selector-header" }, h("span", { key: '244b13d266e69326a81228e9b0679fd8264ea91d' }, "Seleccionar C\u00E1mara"), h("button", { key: '975c809b348f39bccb6887d710909a58a7222a88', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '40df0a7ac0bda961362af72b5a03d448bc4481db', 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: 'ea50280fbc4e96feea026c8ff6dc590ff5e76a59', class: "device-info" }, h("small", { key: '19209e350b073e2cc5d3e713c69da863cf98e92e' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '6a91d0c3d72b9bdbf9f0b6d9d33aff440cb3bcb5', class: "manual-capture-section" }, h("button", { key: '6474971d1a21f45dfeee90b9c39d5fd7e9f142fa', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: captureState.isCapturing }, captureState.isCapturing ? 'Capturando...' : 'Capturar Frente'))))), captureState.isCapturing && (h("div", { key: '0f2cb967caa746f34f442ed17edbeeeea3be1e2d', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'a1a6c67d34b27639d4ca6ab3cf790d184aab64bc', class: "flip-animation" }, h("div", { key: '4b2d5fe6d18bb77d6515312f53fd189ed2af9db6', class: "id-card-icon" }), h("div", { key: '27bd058bd46ecea5ee7bf174a0aa45de519190bf', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'd155e5d2f926c33183f83dabf887bb785d856b14', class: "success-animation" }, h("div", { key: 'e57b7c8d6685146ac2215f52a92ff23e3fbd2d97', class: "check-icon" }), h("div", { key: '0e3de74b19434f49919b4ef78f91dd89c7a80799', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '8401bf78d349be4900de4e0a23a9fddfafcacc4f', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '2b55d22272cd78aa39fd15dd9bdf2317a0737b9f', class: "status-spinner" })), h("div", { key: '10dfb478b8803f70c787c9d9412d517de1bcf852', class: "status-content" }, h("div", { key: '4cf6feb47ad6c2099219bb73eb0d46e3cbfb2bc3', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'e32df69c147070853977ceae6ec45d8c389e1905', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '52a1512ef7b93ecef95cb727c7639661506a7f8d', class: "performance-monitor" }, h("div", { key: 'b7dfdeb4453c507338d4156e99a70c8ed8cb5436', class: "performance-expanded" }, h("div", { key: '79baa5412635f433400b127353c940d4bd1449c4', class: "metrics-row" }, h("div", { key: '12f8d4f530ddea03cbe61bbbf00eb761d55e48b2', class: "metric-compact" }, h("span", { key: '3f8d1d37bbb38424ad8def6c2b7f4aa4dfd35876', class: "metric-label" }, "FPS"), h("span", { key: '3010d0633a058f703c19776d19c88c753085af01', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: 'e5564951b463363ab4965a08f9f8ed37ff202ba2', class: "metric-compact" }, h("span", { key: '2b69e25dd1db6f9239c3de12af10a1ce261158c9', class: "metric-label" }, "MEM"), h("span", { key: '0e0189eb3f91ed5accc197224c55688ea7e93372', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '174c09dbf46186a14d8534a8584c262ba79873fe', class: "metrics-row" }, h("div", { key: '7af195560eeacfd310544d607feee9c3f87327b9', class: "metric-compact" }, h("span", { key: 'fc8a27ca7484635685acab3b7aa0a0fd4fa9021b', class: "metric-label" }, "INF"), h("span", { key: 'bd3ba0e8dd1bf1f21652a986558ccef732572cbc', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '16109503ddd7bd856d6f82a385bece73f1bacc05', class: "metric-compact" }, h("span", { key: '9328438dd8e9c1f0f715f6cfa28baaca27c4244f', class: "metric-label" }, "FRAME"), h("span", { key: '8fa3cf73868bc0cbdaaad3cc6bb48b25a809617c', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: 'dc616be04663364254c38b1f2e96f15ccd52a495', class: "metrics-row" }, h("div", { key: '1fa771776e4dc3b0b86c9a195a6d518aa4e7df5f', class: "metric-compact" }, h("span", { key: '2f5fb1713ab76b0c84677f9df06f53ac83065cee', class: "metric-label" }, "DET"), h("span", { key: 'fd83bda90479a6101593e9b06c7c3fd10da70b67', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '7c7f811a14caa371ac08e7945387f58c30039f9f', class: "metric-compact" }, h("span", { key: '872b9fa154731d48edf6d08b942bdcea4771fd6c', class: "metric-label" }, "RATE"), h("span", { key: '77f9b485a575735159dfbfd846dc2fd2a3dd3413', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: 'b7ab14e0f956ffb7a6658e6d97a26fe49be94a7f', class: "watermark" }, h("img", { key: '3f3807001884966bdf6e1a045d009859f8dcd0f3', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
836
906
  }
837
907
  // Utility methods
838
908
  updateDetectionBoxes(boxes) {
@@ -1020,12 +1090,18 @@ export class JaakStamps {
1020
1090
  });
1021
1091
  // Check if document classification is enabled (independent of detector)
1022
1092
  if (this.useDocumentClassification) {
1023
- const classification = await this.detectionService.classifyDocument(croppedCanvas);
1024
- if (classification && classification.class === 'passport') {
1025
- this.completeProcess(true);
1026
- this.returnCanvasToPool(captureCanvas);
1027
- this.returnCanvasToPool(croppedCanvas);
1028
- return;
1093
+ try {
1094
+ const classification = await this.detectionService.classifyDocument(croppedCanvas);
1095
+ if (classification && classification.class === 'passport') {
1096
+ this.completeProcess(true);
1097
+ this.returnCanvasToPool(captureCanvas);
1098
+ this.returnCanvasToPool(croppedCanvas);
1099
+ return;
1100
+ }
1101
+ }
1102
+ catch (classifyError) {
1103
+ console.error('Error clasificando documento:', classifyError);
1104
+ // No cambiar a manual aquí, solo registrar el error y continuar
1029
1105
  }
1030
1106
  }
1031
1107
  this.stateManager.updateCaptureState({
@@ -1087,10 +1163,16 @@ export class JaakStamps {
1087
1163
  });
1088
1164
  // Check if document classification is enabled
1089
1165
  if (this.useDocumentClassification) {
1090
- const classification = await this.detectionService.classifyDocument(croppedCanvas);
1091
- if (classification && classification.class === 'passport') {
1092
- this.completeProcess(true);
1093
- return;
1166
+ try {
1167
+ const classification = await this.detectionService.classifyDocument(croppedCanvas);
1168
+ if (classification && classification.class === 'passport') {
1169
+ this.completeProcess(true);
1170
+ return;
1171
+ }
1172
+ }
1173
+ catch (classifyError) {
1174
+ console.error('Error clasificando documento:', classifyError);
1175
+ // No cambiar a manual aquí, solo registrar el error y continuar
1094
1176
  }
1095
1177
  }
1096
1178
  this.stateManager.updateCaptureState({
@@ -1127,6 +1209,64 @@ export class JaakStamps {
1127
1209
  cardOutline?.classList.remove('capturing');
1128
1210
  }, 600);
1129
1211
  }
1212
+ // Método simplificado para cambio a modo manual
1213
+ switchToManualMode() {
1214
+ // Only switch if detector is enabled and not already switched
1215
+ if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
1216
+ return;
1217
+ }
1218
+ // Detener todo el monitoreo de performance
1219
+ this.stopPerformanceMonitoring();
1220
+ // Liberar recursos de ONNX
1221
+ this.releaseOnnxResources();
1222
+ this.hasAutoSwitchedToManual = true;
1223
+ this.performanceDegradedMode = true;
1224
+ this.showManualCaptureButton = true;
1225
+ this.showPerformanceMessage = true;
1226
+ // Hide performance message after 5 seconds
1227
+ setTimeout(() => {
1228
+ this.showPerformanceMessage = false;
1229
+ }, 5000);
1230
+ }
1231
+ // Método para cambiar a modo manual por errores de ONNX
1232
+ switchToManualModeWithError(errorMessage) {
1233
+ console.error(`[ONNX Error] ${errorMessage}`);
1234
+ // Solo cambiar si el detector está habilitado y no se ha cambiado ya
1235
+ if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
1236
+ return;
1237
+ }
1238
+ // Liberar recursos de ONNX inmediatamente
1239
+ this.releaseOnnxResources();
1240
+ // Cambiar a modo manual
1241
+ this.hasAutoSwitchedToManual = true;
1242
+ this.performanceDegradedMode = true;
1243
+ this.showManualCaptureButton = true;
1244
+ this.useDocumentDetector = false; // Desactivar detector automático
1245
+ // Actualizar el estado con mensaje de error
1246
+ this.updateStatus('Modo manual activado', errorMessage, 'error');
1247
+ // Ocultar mensaje después de 5 segundos
1248
+ setTimeout(() => {
1249
+ this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
1250
+ }, 5000);
1251
+ }
1252
+ stopPerformanceMonitoring() {
1253
+ // Nota: No necesitamos limpiar más variables porque ya no se usarán
1254
+ // El sistema automáticamente saltará la lógica de performance una vez que hasAutoSwitchedToManual = true
1255
+ }
1256
+ // Método para liberar recursos de ONNX cuando se cambia a modo manual
1257
+ releaseOnnxResources() {
1258
+ console.log('[ONNX] Liberando recursos de modelos ONNX...');
1259
+ try {
1260
+ // Liberar recursos del servicio de detección
1261
+ if (this.detectionService) {
1262
+ this.detectionService.cleanup();
1263
+ console.log('[ONNX] Recursos de ONNX liberados exitosamente');
1264
+ }
1265
+ }
1266
+ catch (error) {
1267
+ console.error('[ONNX] Error al liberar recursos:', error);
1268
+ }
1269
+ }
1130
1270
  completeProcess(skippedBack = false) {
1131
1271
  this.stateManager.updateCaptureState({
1132
1272
  step: 'completed',
@@ -1240,6 +1380,13 @@ export class JaakStamps {
1240
1380
  this.detectionBoxes = [];
1241
1381
  this.alignmentStartTime = undefined;
1242
1382
  this.hasDocumentDetected = false;
1383
+ // Reset sistema simplificado
1384
+ this.processedFramesCount = 0;
1385
+ this.slowFrameCount = 0;
1386
+ // Reset performance degradation state
1387
+ this.hasAutoSwitchedToManual = false;
1388
+ this.performanceDegradedMode = false;
1389
+ this.showPerformanceMessage = false;
1243
1390
  if (this.alignmentTimer) {
1244
1391
  clearTimeout(this.alignmentTimer);
1245
1392
  this.alignmentTimer = undefined;
@@ -1602,7 +1749,9 @@ export class JaakStamps {
1602
1749
  "currentStatus": {},
1603
1750
  "performanceData": {},
1604
1751
  "backDocumentTimerRemaining": {},
1605
- "showManualCaptureButton": {}
1752
+ "showManualCaptureButton": {},
1753
+ "performanceDegradedMode": {},
1754
+ "showPerformanceMessage": {}
1606
1755
  };
1607
1756
  }
1608
1757
  static get events() {