@jaak.ai/stamps 2.1.0-dev.3 → 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],"captureStateVersion":[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,7 +49,8 @@ export class JaakStamps {
49
49
  };
50
50
  backDocumentTimerRemaining = 0;
51
51
  showManualCaptureButton = false;
52
- captureStateVersion = 0; // Triggers re-render when StateManager changes
52
+ performanceDegradedMode = false; // Auto-switched to manual mode due to performance
53
+ showPerformanceMessage = false; // Show performance message temporarily
53
54
  // Services
54
55
  serviceContainer;
55
56
  eventBus;
@@ -73,7 +74,6 @@ export class JaakStamps {
73
74
  fps: 0,
74
75
  inferenceTime: 0,
75
76
  memoryUsage: 0,
76
- cpuUsage: 0,
77
77
  onnxLoadTime: 0,
78
78
  frameProcessingTime: 0,
79
79
  totalDetections: 0,
@@ -92,6 +92,12 @@ export class JaakStamps {
92
92
  MIN_INFERENCE_INTERVAL = 50;
93
93
  performanceHistory = [];
94
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;
95
101
  // Canvas pool for optimized screenshot capture
96
102
  canvasPool = [];
97
103
  MAX_CANVAS_POOL_SIZE = 3;
@@ -124,14 +130,8 @@ export class JaakStamps {
124
130
  this.detectionService = this.serviceContainer.getDetectionService();
125
131
  }
126
132
  async setupEventListeners() {
127
- this.eventBus.on('state-changed', (data) => {
128
- this.handleStateChange(data);
129
- });
130
- this.eventBus.on('camera-changed', () => {
131
- // Camera changed event
132
- });
133
- this.eventBus.on('error', () => {
134
- // Handle service errors
133
+ this.eventBus.on('state-changed', () => {
134
+ this.handleStateChange();
135
135
  });
136
136
  }
137
137
  async initializeComponent() {
@@ -238,13 +238,8 @@ export class JaakStamps {
238
238
  return false;
239
239
  }
240
240
  }
241
- handleStateChange(data) {
242
- // React to state changes from StateManager
243
- // This is where the component updates its internal state based on service state changes
244
- if (data.changes.isLoading !== undefined) {
245
- // Force re-render when loading state changes
246
- // Note: Stencil automatically re-renders when @State changes
247
- }
241
+ handleStateChange() {
242
+ // Handle state changes from StateManager if needed
248
243
  }
249
244
  initializeResizeObserver() {
250
245
  if ('ResizeObserver' in window && this.detectionContainer) {
@@ -261,13 +256,14 @@ export class JaakStamps {
261
256
  this.updateMaskDimensions(rect);
262
257
  }
263
258
  }
264
- triggerRerender() {
265
- this.captureStateVersion = this.captureStateVersion + 1;
266
- }
267
259
  getGuideText(step) {
268
260
  if (step === 'completed') {
269
261
  return 'Proceso completado';
270
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
+ }
271
267
  if (step === 'front') {
272
268
  // Para el frente, si el detector manual está activo, mostrar instrucciones de captura manual
273
269
  if (this.showManualCaptureButton) {
@@ -497,9 +493,23 @@ export class JaakStamps {
497
493
  if (this.stateManager) {
498
494
  this.stateManager.updateCaptureState({ isLoading: true });
499
495
  }
500
- 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
+ }
501
504
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
502
- 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
+ }
503
513
  const loadEndTime = performance.now();
504
514
  const loadTime = loadEndTime - loadStartTime;
505
515
  // Record ONNX load time
@@ -616,9 +626,23 @@ export class JaakStamps {
616
626
  const loadStartTime = performance.now();
617
627
  this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
618
628
  this.stateManager.updateCaptureState({ isLoading: true });
619
- 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
+ }
620
637
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
621
- 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
+ }
622
646
  const loadEndTime = performance.now();
623
647
  const loadTime = loadEndTime - loadStartTime;
624
648
  // Record ONNX load time
@@ -633,9 +657,9 @@ export class JaakStamps {
633
657
  // Paso 3: Configurar cámara seleccionada
634
658
  this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
635
659
  const stream = await this.cameraService.setupCamera();
636
- // Paso 4: Inicializar video y ocultar estado inmediatamente
660
+ // Paso 4: Inicializar video y mostrar estado de análisis inicial
637
661
  if (this.useDocumentDetector) {
638
- this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
662
+ this.updateStatus('Analizando estabilidad...', 'Evaluando rendimiento del sistema', 'loading');
639
663
  }
640
664
  else {
641
665
  this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
@@ -685,8 +709,8 @@ export class JaakStamps {
685
709
  const captureState = this.stateManager.getCaptureState();
686
710
  if (!this.videoRef || !this.detectionContainer || !this.detectionService.isModelLoaded())
687
711
  return;
688
- // Show manual capture button when document detector is disabled
689
- if (!this.useDocumentDetector) {
712
+ // Show manual capture button when document detector is disabled or performance is degraded
713
+ if (!this.useDocumentDetector || this.performanceDegradedMode) {
690
714
  this.showManualCaptureButton = true;
691
715
  // Continue the loop for UI updates but skip detection logic
692
716
  if (captureState.step !== 'completed') {
@@ -694,7 +718,7 @@ export class JaakStamps {
694
718
  }
695
719
  return;
696
720
  }
697
- else {
721
+ else if (!this.performanceDegradedMode) {
698
722
  this.showManualCaptureButton = false;
699
723
  }
700
724
  if (captureState.isDetectionPaused) {
@@ -724,10 +748,29 @@ export class JaakStamps {
724
748
  this.lastInferenceTime = currentTime;
725
749
  // Measure preprocessing and inference time
726
750
  const inferenceStartTime = performance.now();
727
- const inputTensor = this.detectionService.preprocess(this.videoRef);
728
- // Standard detection without quality validation
729
- const detections = await this.detectionService.runInference(inputTensor);
730
- 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++;
731
774
  // Record ONNX performance metrics
732
775
  if (this.debug) {
733
776
  this.recordOnnxPerformance(0, inferenceTime);
@@ -766,9 +809,30 @@ export class JaakStamps {
766
809
  }
767
810
  this.updateMaskColor(detections);
768
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
+ }
769
835
  if (this.debug) {
770
- const frameEndTime = performance.now();
771
- const totalFrameTime = frameEndTime - frameStartTime;
772
836
  this.recordFrameProcessing(totalFrameTime, detections.length);
773
837
  }
774
838
  // Continue detection loop
@@ -804,6 +868,8 @@ export class JaakStamps {
804
868
  clearTimeout(this.alignmentTimer);
805
869
  this.alignmentTimer = undefined;
806
870
  }
871
+ // Liberar recursos de ONNX al limpiar el componente
872
+ this.releaseOnnxResources();
807
873
  if (this.performanceUpdateInterval) {
808
874
  clearInterval(this.performanceUpdateInterval);
809
875
  this.performanceUpdateInterval = undefined;
@@ -825,7 +891,7 @@ export class JaakStamps {
825
891
  isCapturing: false
826
892
  };
827
893
  const cameraInfo = this.cameraInfoWithAutofocus;
828
- return (h("div", { key: '3026fb421368ee82a1d2cefd8f9355ebf3194146', class: "detector-container" }, h("div", { key: '62b1230c3268412f4ff301d480d666065037d9b3', class: "video-container" }, h("video", { key: '170a0e7235522dec33b914b6d2841b36503c781f', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '915af3649a7a551a46f1cb97af0a45916666cc12', 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: {
829
895
  position: 'absolute',
830
896
  left: `${box.x}px`,
831
897
  top: `${box.y}px`,
@@ -834,9 +900,9 @@ export class JaakStamps {
834
900
  border: '2px solid #32406C',
835
901
  pointerEvents: 'none',
836
902
  boxSizing: 'border-box'
837
- } })))), this.isMaskReady && (h("div", { key: 'b40cf136e5b9a120aa7243c12005383196428b34', class: "overlay-mask" }, h("div", { key: 'e82324a37682c94f03576e89d880c3b17520b5e5', class: "card-outline" }, h("div", { key: 'b1fd7df2a36300f2e6eb96474b449be74abd1ed4', class: "side side-top" }), h("div", { key: 'a6b3eb63fb35b22d0d53c8fd5aa88b2e299ae8c4', class: "side side-right" }), h("div", { key: '2d43e9281ad4fe05e314627aa249f56a7b0de9f1', class: "side side-bottom" }), h("div", { key: '231440a2dccc0f8ede35971ef092523d7be444b9', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'a923e81e11ead323b9bcf76bd6b0fea421bc1517', class: "guide-text" }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '4b68212634cbc4916cd16c4e00ca4e91b924de6e', class: "back-capture-section" }, h("div", { key: 'f2ec22b132defe1e4026ad38977c52974c5cdb21', class: "back-capture-buttons" }, !this.useDocumentDetector && (h("button", { key: 'ae8c0e452fbf1bb69b6683ae775f1c1a4b7d22d9', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: captureState.isCapturing }, captureState.isCapturing ? 'Capturando...' : 'Capturar Reverso')), h("button", { key: 'f1ec52715a584da161885bcbae0db7a38632905c', 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
838
904
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
839
- : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'a831afce8ac493fe9716fc5608c1be5373cac9f4', class: "camera-controls" }, h("button", { key: '9a56a494d6357eac86211af857b74db4509aad64', 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: 'b08699bf97018b3318771aa9c19a5848fae6e09f', class: "camera-selector-dropdown" }, h("div", { key: '24d010fde00991a80f92803511ae31ab23d2d4ee', class: "camera-selector-header" }, h("span", { key: 'ea0a21c5989cc28dff59bab870cfbb39b401b466' }, "Seleccionar C\u00E1mara"), h("button", { key: '2cbd997565bfaeceb799a95fcb8e26681bab22ef', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'c00abb13cb7299ff325b85d3fcd44dcad7839e53', 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: 'e369c9ae07f0638b490d67bb01ad2a3a7f9b56a1', class: "device-info" }, h("small", { key: '04d4a8c24a1a4487939f87e8af240962705fd685' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'f955e8e5b6917d01477fd171e3c16a44a3d0a916', class: "manual-capture-section" }, h("button", { key: 'cb122ec84fec7afae8185e561f4da197a210b0dd', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: captureState.isCapturing }, captureState.isCapturing ? 'Capturando...' : 'Capturar Frente'))))), captureState.isCapturing && (h("div", { key: '98d15bcd0afab9daa3bc64096535db74f795723b', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '86132c96fc3ff3296c04c32c03ef624511d175fd', class: "flip-animation" }, h("div", { key: '05efba20dc5a4890a6de5650e67b5144853fa9d6', class: "id-card-icon" }), h("div", { key: 'a2e307b126a1c65681733e68bb737ed21e9cb119', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '38e1e4f5c3c8a5002fc45c8490e3134e31b4652a', class: "success-animation" }, h("div", { key: '2f6faebde51e90059106e64ef50af51d9395c0cf', class: "check-icon" }), h("div", { key: '63385f75ab3933966f58bb954c13f4cacb4d5c21', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '0853a33e794b26c727336fb13c9430d99b992d7f', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: 'e0d258b6897ebaa281473ee6633244a89d9b3cc2', class: "status-spinner" })), h("div", { key: 'e54b82a5a895734bdaeb95fc9bfb631fd4680c87', class: "status-content" }, h("div", { key: '57e712d858574138a9ffdefa298adc99765788f3', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'b2e1240eb71ed506157353fe5e24d62c3a0da8ab', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '3f3acb1ffc8e2c1d64cc8dd0e76bf64772a92def', class: "performance-monitor" }, h("div", { key: '83c1640404aad9552ad082a78327acf9facd79ad', class: "performance-expanded" }, h("div", { key: '18ee837b0fa64bae25285183cbf79733f7654153', class: "metrics-row" }, h("div", { key: 'b7d6b1c15b7e57e67d886c1bbc279573e795a3a3', class: "metric-compact" }, h("span", { key: '2b02e2323aadc064231ab8735a5810acc7bdd871', class: "metric-label" }, "FPS"), h("span", { key: 'd0cbe3e212f216834127456574eef6e61b4f3d6c', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '974538f93bbe0abb25eadba4a73d9a28f9c9ae0a', class: "metric-compact" }, h("span", { key: 'e04bc2180ede406b787f63f92eb6086d4e372c77', class: "metric-label" }, "MEM"), h("span", { key: 'c7911542d7a39a334b0a2f0f258256d11b297fa4', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '92bbce4d85240a84d53afce554792ae51597e16f', class: "metrics-row" }, h("div", { key: '7b78afc87a85a229b582d870e85d3015ca54b941', class: "metric-compact" }, h("span", { key: '7579b2b804c8a869894c0ed5a89c71f19d493c69', class: "metric-label" }, "INF"), h("span", { key: '029733bca2504d1d3c1fef77519e0df90b28d5d1', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'f7fe505877d8e9214c2c819e714e3039c95c0ec7', class: "metric-compact" }, h("span", { key: 'ecef2280e78aecb7b811f9fa14c7fdbda8c4d065', class: "metric-label" }, "FRAME"), h("span", { key: 'c6cadf43725c125f371d3d849e8b09953a3747fc', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '832e8ee75377791a17279a3c7eee12fc45f21820', class: "metrics-row" }, h("div", { key: 'abb2dc9ce8a52374bc04e4b36560885427303fb6', class: "metric-compact" }, h("span", { key: '8f8c9cfab162a01807d07f44278f4f5889a1b206', class: "metric-label" }, "DET"), h("span", { key: '7359af4c236091e99b1f653ba20d8e3bd3acaf1b', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '7dd17d6d47289a11087a33c6104269899b72e46e', class: "metric-compact" }, h("span", { key: '0b1317becd3af6804e1fb1965593882886c51b15', class: "metric-label" }, "RATE"), h("span", { key: '9e94ba4257aa707e6c751852db48a17fb7ec8bae', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '77beb6f47153c43fddbc643fe00d1777ecfab295', class: "watermark" }, h("img", { key: 'e6aec0055f8ffb058dc2249fa1182dae06638a31', 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" })))));
840
906
  }
841
907
  // Utility methods
842
908
  updateDetectionBoxes(boxes) {
@@ -1024,12 +1090,18 @@ export class JaakStamps {
1024
1090
  });
1025
1091
  // Check if document classification is enabled (independent of detector)
1026
1092
  if (this.useDocumentClassification) {
1027
- const classification = await this.detectionService.classifyDocument(croppedCanvas);
1028
- if (classification && classification.class === 'passport') {
1029
- this.completeProcess(true);
1030
- this.returnCanvasToPool(captureCanvas);
1031
- this.returnCanvasToPool(croppedCanvas);
1032
- 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
1033
1105
  }
1034
1106
  }
1035
1107
  this.stateManager.updateCaptureState({
@@ -1037,13 +1109,11 @@ export class JaakStamps {
1037
1109
  isDetectionPaused: true,
1038
1110
  showFlipAnimation: true
1039
1111
  });
1040
- this.triggerRerender();
1041
1112
  setTimeout(() => {
1042
1113
  this.stateManager.updateCaptureState({
1043
1114
  showFlipAnimation: false,
1044
1115
  isDetectionPaused: false
1045
1116
  });
1046
- this.triggerRerender();
1047
1117
  this.startBackDocumentTimer();
1048
1118
  }, 3000);
1049
1119
  }
@@ -1093,10 +1163,16 @@ export class JaakStamps {
1093
1163
  });
1094
1164
  // Check if document classification is enabled
1095
1165
  if (this.useDocumentClassification) {
1096
- const classification = await this.detectionService.classifyDocument(croppedCanvas);
1097
- if (classification && classification.class === 'passport') {
1098
- this.completeProcess(true);
1099
- 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
1100
1176
  }
1101
1177
  }
1102
1178
  this.stateManager.updateCaptureState({
@@ -1133,6 +1209,64 @@ export class JaakStamps {
1133
1209
  cardOutline?.classList.remove('capturing');
1134
1210
  }, 600);
1135
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
+ }
1136
1270
  completeProcess(skippedBack = false) {
1137
1271
  this.stateManager.updateCaptureState({
1138
1272
  step: 'completed',
@@ -1246,6 +1380,13 @@ export class JaakStamps {
1246
1380
  this.detectionBoxes = [];
1247
1381
  this.alignmentStartTime = undefined;
1248
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;
1249
1390
  if (this.alignmentTimer) {
1250
1391
  clearTimeout(this.alignmentTimer);
1251
1392
  this.alignmentTimer = undefined;
@@ -1609,7 +1750,8 @@ export class JaakStamps {
1609
1750
  "performanceData": {},
1610
1751
  "backDocumentTimerRemaining": {},
1611
1752
  "showManualCaptureButton": {},
1612
- "captureStateVersion": {}
1753
+ "performanceDegradedMode": {},
1754
+ "showPerformanceMessage": {}
1613
1755
  };
1614
1756
  }
1615
1757
  static get events() {