@jaak.ai/stamps 2.1.0-dev.3 → 2.1.0-dev.5

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,21 @@ 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
+ this.switchToManualModeWithError('Error al cargar modelo de detección');
501
+ throw modelError;
502
+ }
501
503
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
502
- await this.detectionService.loadClassificationModel();
504
+ try {
505
+ await this.detectionService.loadClassificationModel();
506
+ }
507
+ catch (classificationError) {
508
+ this.switchToManualModeWithError('Error al cargar modelo de clasificación');
509
+ throw classificationError;
510
+ }
503
511
  const loadEndTime = performance.now();
504
512
  const loadTime = loadEndTime - loadStartTime;
505
513
  // Record ONNX load time
@@ -616,9 +624,21 @@ export class JaakStamps {
616
624
  const loadStartTime = performance.now();
617
625
  this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
618
626
  this.stateManager.updateCaptureState({ isLoading: true });
619
- await this.detectionService.loadModel();
627
+ try {
628
+ await this.detectionService.loadModel();
629
+ }
630
+ catch (modelError) {
631
+ this.switchToManualModeWithError('Error al cargar modelo de detección');
632
+ throw modelError;
633
+ }
620
634
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
621
- await this.detectionService.loadClassificationModel();
635
+ try {
636
+ await this.detectionService.loadClassificationModel();
637
+ }
638
+ catch (classificationError) {
639
+ this.switchToManualModeWithError('Error al cargar modelo de clasificación');
640
+ throw classificationError;
641
+ }
622
642
  const loadEndTime = performance.now();
623
643
  const loadTime = loadEndTime - loadStartTime;
624
644
  // Record ONNX load time
@@ -633,9 +653,9 @@ export class JaakStamps {
633
653
  // Paso 3: Configurar cámara seleccionada
634
654
  this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
635
655
  const stream = await this.cameraService.setupCamera();
636
- // Paso 4: Inicializar video y ocultar estado inmediatamente
656
+ // Paso 4: Inicializar video y mostrar estado de análisis inicial
637
657
  if (this.useDocumentDetector) {
638
- this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
658
+ this.updateStatus('Analizando estabilidad...', 'Evaluando rendimiento del sistema', 'loading');
639
659
  }
640
660
  else {
641
661
  this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
@@ -685,8 +705,8 @@ export class JaakStamps {
685
705
  const captureState = this.stateManager.getCaptureState();
686
706
  if (!this.videoRef || !this.detectionContainer || !this.detectionService.isModelLoaded())
687
707
  return;
688
- // Show manual capture button when document detector is disabled
689
- if (!this.useDocumentDetector) {
708
+ // Show manual capture button when document detector is disabled or performance is degraded
709
+ if (!this.useDocumentDetector || this.performanceDegradedMode) {
690
710
  this.showManualCaptureButton = true;
691
711
  // Continue the loop for UI updates but skip detection logic
692
712
  if (captureState.step !== 'completed') {
@@ -694,7 +714,7 @@ export class JaakStamps {
694
714
  }
695
715
  return;
696
716
  }
697
- else {
717
+ else if (!this.performanceDegradedMode) {
698
718
  this.showManualCaptureButton = false;
699
719
  }
700
720
  if (captureState.isDetectionPaused) {
@@ -724,10 +744,27 @@ export class JaakStamps {
724
744
  this.lastInferenceTime = currentTime;
725
745
  // Measure preprocessing and inference time
726
746
  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;
747
+ let inputTensor;
748
+ let detections;
749
+ let inferenceTime;
750
+ try {
751
+ inputTensor = this.detectionService.preprocess(this.videoRef);
752
+ }
753
+ catch (preprocessError) {
754
+ this.switchToManualModeWithError('Error al procesar imagen');
755
+ return;
756
+ }
757
+ try {
758
+ // Standard detection without timeout (timeout is handled by frame counting)
759
+ detections = await this.detectionService.runInference(inputTensor);
760
+ inferenceTime = performance.now() - inferenceStartTime;
761
+ }
762
+ catch (inferenceError) {
763
+ this.switchToManualModeWithError('Error en detección automática');
764
+ return;
765
+ }
766
+ // Increment frame count
767
+ this.processedFramesCount++;
731
768
  // Record ONNX performance metrics
732
769
  if (this.debug) {
733
770
  this.recordOnnxPerformance(0, inferenceTime);
@@ -766,9 +803,30 @@ export class JaakStamps {
766
803
  }
767
804
  this.updateMaskColor(detections);
768
805
  // Record frame processing metrics
806
+ const frameEndTime = performance.now();
807
+ const totalFrameTime = frameEndTime - frameStartTime;
808
+ // Sistema simplificado: Verificar rendimiento de cada frame (solo si no se ha cambiado a manual)
809
+ if (this.useDocumentDetector && !this.hasAutoSwitchedToManual) {
810
+ // Verificar si el frame es lento
811
+ if (totalFrameTime >= this.PERFORMANCE_THRESHOLD_MS) {
812
+ this.slowFrameCount++;
813
+ // Si tenemos suficientes frames lentos consecutivos, cambiar a manual
814
+ if (this.slowFrameCount >= this.SLOW_FRAMES_TO_TRIGGER) {
815
+ this.switchToManualMode();
816
+ }
817
+ }
818
+ else {
819
+ // Frame rápido, reiniciar contador
820
+ if (this.slowFrameCount > 0) {
821
+ this.slowFrameCount = 0;
822
+ }
823
+ }
824
+ // Cambiar mensaje de estado después de analizar los primeros 2 frames
825
+ if (this.processedFramesCount === 2) {
826
+ this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
827
+ }
828
+ }
769
829
  if (this.debug) {
770
- const frameEndTime = performance.now();
771
- const totalFrameTime = frameEndTime - frameStartTime;
772
830
  this.recordFrameProcessing(totalFrameTime, detections.length);
773
831
  }
774
832
  // Continue detection loop
@@ -804,6 +862,8 @@ export class JaakStamps {
804
862
  clearTimeout(this.alignmentTimer);
805
863
  this.alignmentTimer = undefined;
806
864
  }
865
+ // Liberar recursos de ONNX al limpiar el componente
866
+ this.releaseOnnxResources();
807
867
  if (this.performanceUpdateInterval) {
808
868
  clearInterval(this.performanceUpdateInterval);
809
869
  this.performanceUpdateInterval = undefined;
@@ -825,7 +885,7 @@ export class JaakStamps {
825
885
  isCapturing: false
826
886
  };
827
887
  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: {
888
+ return (h("div", { key: '687a01ebb87f3c9e4424f4bcd355cd68c21366f0', class: "detector-container" }, h("div", { key: 'd4c9f1fbf653ea7764dcc7265f5f7180c99fe5e7', class: "video-container" }, h("video", { key: '37a159daaaa4682d8e14a1dcdef32225706e1f20', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '74fe5c8612be7a8d904510c226d68a6860a0f67d', 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
889
  position: 'absolute',
830
890
  left: `${box.x}px`,
831
891
  top: `${box.y}px`,
@@ -834,9 +894,9 @@ export class JaakStamps {
834
894
  border: '2px solid #32406C',
835
895
  pointerEvents: 'none',
836
896
  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
897
+ } })))), this.isMaskReady && (h("div", { key: '4f2e282a8eb99947b1e67690ef236019e87ad87e', class: "overlay-mask" }, h("div", { key: '1d102684d59e5f7f5d6243f95e39ee0e1812032e', class: "card-outline" }, h("div", { key: '4ba0df1528de02cc037022948e425ffb13c49822', class: "side side-top" }), h("div", { key: '36bc3f2d39f61d39af2522ffe13b6df16c8bf00b', class: "side side-right" }), h("div", { key: '88d2402044de0309055c31fd98e596b88b1d51ce', class: "side side-bottom" }), h("div", { key: '72262621ee2c2b4824f3b313f27a5793fa4c123f', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'df9320c0f5518c04b1504d355223730e6d686779', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'e86f6eec1ea7712fb8c94d1455dd9c0c7c01db21', class: "back-capture-section" }, h("div", { key: 'ad965847b25eeae92eb22c3d89b09e953fabb2d2', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (h("button", { key: '3e57f0bf7600bc650e44f40e3c1fd13747885781', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: captureState.isCapturing }, captureState.isCapturing ? 'Capturando...' : 'Capturar Reverso')), h("button", { key: 'd598cd7778869b46b5c7ceafcb43c469b4b5c7e7', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
838
898
  ? `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" })))));
899
+ : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'b1508382d2b75584a19dade3f8e685a34ff2c3a3', class: "camera-controls" }, h("button", { key: '9c1f945b051f8e000f87fb3674ecfd386bdc2208', 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: '4adab3b896be86322be168db377cbacbcb3a7422', class: "camera-selector-dropdown" }, h("div", { key: 'a60e3f46cfe5997cf9b82d685a1574b7002c0071', class: "camera-selector-header" }, h("span", { key: '71e012a8a4953f584aeffc985040e434fadfb00b' }, "Seleccionar C\u00E1mara"), h("button", { key: '046ee4f6d81b34349ecad4e9de98f4d8653dad6b', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '539ad135914d9ceb3a6b9a2de2d9ae4ea7a996b3', 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: '082d1cc259af3c1fc95b53da1e754382e874b837', class: "device-info" }, h("small", { key: 'dad398582ac7bfd31ee43704d47fd3036fc59d06' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '231ff3eb4672b4ac310419cccc8cd6d1caab9e14', class: "manual-capture-section" }, h("button", { key: 'fa2fcc4bfe63bf2ccdba2c2ad3c8c4b4a5aabbb5', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: captureState.isCapturing }, captureState.isCapturing ? 'Capturando...' : 'Capturar Frente'))))), captureState.isCapturing && (h("div", { key: '39426754973f1a9e07b56399f344add21bf9b848', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '307a7eb06b41a8baebe37ad26b1f5e4cf0a43ec1', class: "flip-animation" }, h("div", { key: '7070ad263e3894dc1488bb8a17f5bf23b7b757f0', class: "id-card-icon" }), h("div", { key: '81a0d8b6377b397bbeebe2e31800fe88f88593a0', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '741e5043bd57a3e13e39fd897a64cd20a75dad2e', class: "success-animation" }, h("div", { key: '6269a51c0651e5f76490906e50eb111f6a88f267', class: "check-icon" }), h("div", { key: '1c90a9cf6c90bb10af90660c714b7ff1ed6acfa0', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: 'f6746c1fb15cc238846f047743a9d45b1d4a41f8', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '00d198abdc90683e5f05a9fd9b54d44e55c16630', class: "status-spinner" })), h("div", { key: 'b0254dea36bae65692bfbd200dfb75eb5a3a50dc', class: "status-content" }, h("div", { key: 'e3e48ea04f40ddd7c66aa8eda3d341f24a9c3dc4', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'd3f40ca5b72a661f9631145880fc6d1bba878961', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'eabe40809e744eb62b2ac76733952060988b675c', class: "performance-monitor" }, h("div", { key: '44e51257d4ba2c2153749cf14f3aebbf22ca02ec', class: "performance-expanded" }, h("div", { key: '493fc680db2d1e82068eadda3786fad57e8594cc', class: "metrics-row" }, h("div", { key: 'aaa734d066246c9364d51c04d41a0a84d0b7e216', class: "metric-compact" }, h("span", { key: 'f0b59f76e8aa1723a429289b7898d35f22ff86d4', class: "metric-label" }, "FPS"), h("span", { key: 'a562359df92cabe40f54b355ffa264f246e7ec8e', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '9adca77e934ee3cd07fb54a056132e305637848c', class: "metric-compact" }, h("span", { key: '65ec83dca0e74b42065962d2a2e4bae7515b9fc1', class: "metric-label" }, "MEM"), h("span", { key: '427fc6b5b538742d0987b0240f526616b910ae69', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'fc551d8ef39308980437b72b3fb4c0bdb2bfbf67', class: "metrics-row" }, h("div", { key: 'b88785c108e5586fecc8702f3e4da883ace00f15', class: "metric-compact" }, h("span", { key: '31db440861e6fd9c2ee36ccd242f1b7bb3d47f45', class: "metric-label" }, "INF"), h("span", { key: 'af45bbb41110a04ed3d22a9e112b8644dd7ec355', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'deabe5ce0661557fabe4ccdde1af48beed564ad9', class: "metric-compact" }, h("span", { key: '072f9159e95b3f3226f07b879b377a3d34940825', class: "metric-label" }, "FRAME"), h("span", { key: '841de1ae8df76919c2b644c6ff813dd927b52013', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '68c6c777db1430987f20536849a3ed9169ec794a', class: "metrics-row" }, h("div", { key: '736e361beaa88d268041ec55fd73db0ee405e1ae', class: "metric-compact" }, h("span", { key: 'bfa6c3aa54a96e5eab32dc4fa0227ca836142561', class: "metric-label" }, "DET"), h("span", { key: 'f9a2479c470779403bf1cbfd4f433f80e5fa34d8', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'bfc1d7f8cbc7449a96e751b005a02bb112e63a86', class: "metric-compact" }, h("span", { key: '45a7171a72ac6ae9c29a4de3c7a260ae78681dc1', class: "metric-label" }, "RATE"), h("span", { key: '7419ace73d0302d662b3a6308f313be271049b5a', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '786544dff913bb4b1003e62d9ab35b3f29f4b497', class: "watermark" }, h("img", { key: 'e08c893afa8bad94d04b01ea4732044b1c9a2d6c', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
840
900
  }
841
901
  // Utility methods
842
902
  updateDetectionBoxes(boxes) {
@@ -1024,12 +1084,17 @@ export class JaakStamps {
1024
1084
  });
1025
1085
  // Check if document classification is enabled (independent of detector)
1026
1086
  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;
1087
+ try {
1088
+ const classification = await this.detectionService.classifyDocument(croppedCanvas);
1089
+ if (classification && classification.class === 'passport') {
1090
+ this.completeProcess(true);
1091
+ this.returnCanvasToPool(captureCanvas);
1092
+ this.returnCanvasToPool(croppedCanvas);
1093
+ return;
1094
+ }
1095
+ }
1096
+ catch (classifyError) {
1097
+ // No cambiar a manual aquí, solo registrar el error y continuar
1033
1098
  }
1034
1099
  }
1035
1100
  this.stateManager.updateCaptureState({
@@ -1037,13 +1102,11 @@ export class JaakStamps {
1037
1102
  isDetectionPaused: true,
1038
1103
  showFlipAnimation: true
1039
1104
  });
1040
- this.triggerRerender();
1041
1105
  setTimeout(() => {
1042
1106
  this.stateManager.updateCaptureState({
1043
1107
  showFlipAnimation: false,
1044
1108
  isDetectionPaused: false
1045
1109
  });
1046
- this.triggerRerender();
1047
1110
  this.startBackDocumentTimer();
1048
1111
  }, 3000);
1049
1112
  }
@@ -1093,10 +1156,15 @@ export class JaakStamps {
1093
1156
  });
1094
1157
  // Check if document classification is enabled
1095
1158
  if (this.useDocumentClassification) {
1096
- const classification = await this.detectionService.classifyDocument(croppedCanvas);
1097
- if (classification && classification.class === 'passport') {
1098
- this.completeProcess(true);
1099
- return;
1159
+ try {
1160
+ const classification = await this.detectionService.classifyDocument(croppedCanvas);
1161
+ if (classification && classification.class === 'passport') {
1162
+ this.completeProcess(true);
1163
+ return;
1164
+ }
1165
+ }
1166
+ catch (classifyError) {
1167
+ // No cambiar a manual aquí, solo registrar el error y continuar
1100
1168
  }
1101
1169
  }
1102
1170
  this.stateManager.updateCaptureState({
@@ -1133,6 +1201,60 @@ export class JaakStamps {
1133
1201
  cardOutline?.classList.remove('capturing');
1134
1202
  }, 600);
1135
1203
  }
1204
+ // Método simplificado para cambio a modo manual
1205
+ switchToManualMode() {
1206
+ // Only switch if detector is enabled and not already switched
1207
+ if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
1208
+ return;
1209
+ }
1210
+ // Detener todo el monitoreo de performance
1211
+ this.stopPerformanceMonitoring();
1212
+ // Liberar recursos de ONNX
1213
+ this.releaseOnnxResources();
1214
+ this.hasAutoSwitchedToManual = true;
1215
+ this.performanceDegradedMode = true;
1216
+ this.showManualCaptureButton = true;
1217
+ this.showPerformanceMessage = true;
1218
+ // Hide performance message after 5 seconds
1219
+ setTimeout(() => {
1220
+ this.showPerformanceMessage = false;
1221
+ }, 5000);
1222
+ }
1223
+ // Método para cambiar a modo manual por errores de ONNX
1224
+ switchToManualModeWithError(errorMessage) {
1225
+ // Solo cambiar si el detector está habilitado y no se ha cambiado ya
1226
+ if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
1227
+ return;
1228
+ }
1229
+ // Liberar recursos de ONNX inmediatamente
1230
+ this.releaseOnnxResources();
1231
+ // Cambiar a modo manual
1232
+ this.hasAutoSwitchedToManual = true;
1233
+ this.performanceDegradedMode = true;
1234
+ this.showManualCaptureButton = true;
1235
+ this.useDocumentDetector = false; // Desactivar detector automático
1236
+ // Actualizar el estado con mensaje de error
1237
+ this.updateStatus('Modo manual activado', errorMessage, 'error');
1238
+ // Ocultar mensaje después de 5 segundos
1239
+ setTimeout(() => {
1240
+ this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
1241
+ }, 5000);
1242
+ }
1243
+ stopPerformanceMonitoring() {
1244
+ // Nota: No necesitamos limpiar más variables porque ya no se usarán
1245
+ // El sistema automáticamente saltará la lógica de performance una vez que hasAutoSwitchedToManual = true
1246
+ }
1247
+ // Método para liberar recursos de ONNX cuando se cambia a modo manual
1248
+ releaseOnnxResources() {
1249
+ try {
1250
+ // Liberar recursos del servicio de detección
1251
+ if (this.detectionService) {
1252
+ this.detectionService.cleanup();
1253
+ }
1254
+ }
1255
+ catch (error) {
1256
+ }
1257
+ }
1136
1258
  completeProcess(skippedBack = false) {
1137
1259
  this.stateManager.updateCaptureState({
1138
1260
  step: 'completed',
@@ -1246,6 +1368,13 @@ export class JaakStamps {
1246
1368
  this.detectionBoxes = [];
1247
1369
  this.alignmentStartTime = undefined;
1248
1370
  this.hasDocumentDetected = false;
1371
+ // Reset sistema simplificado
1372
+ this.processedFramesCount = 0;
1373
+ this.slowFrameCount = 0;
1374
+ // Reset performance degradation state
1375
+ this.hasAutoSwitchedToManual = false;
1376
+ this.performanceDegradedMode = false;
1377
+ this.showPerformanceMessage = false;
1249
1378
  if (this.alignmentTimer) {
1250
1379
  clearTimeout(this.alignmentTimer);
1251
1380
  this.alignmentTimer = undefined;
@@ -1609,7 +1738,8 @@ export class JaakStamps {
1609
1738
  "performanceData": {},
1610
1739
  "backDocumentTimerRemaining": {},
1611
1740
  "showManualCaptureButton": {},
1612
- "captureStateVersion": {}
1741
+ "performanceDegradedMode": {},
1742
+ "showPerformanceMessage": {}
1613
1743
  };
1614
1744
  }
1615
1745
  static get events() {