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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -670,7 +670,12 @@ class DetectionService {
670
670
  this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, sessionOptions);
671
671
  }
672
672
  catch (error) {
673
- if (error.message.includes('failed to allocate a buffer')) {
673
+ // Múltiples tipos de errores de memoria que pueden ocurrir
674
+ const isMemoryError = error.message.includes('failed to allocate a buffer') ||
675
+ error.message.includes('Out of memory') ||
676
+ error.message.includes('RangeError: Out of memory') ||
677
+ error.message.includes('no available backend found');
678
+ if (isMemoryError) {
674
679
  const fallbackOptions = {
675
680
  executionProviders: ['wasm'],
676
681
  graphOptimizationLevel: 'disabled',
@@ -680,8 +685,17 @@ class DetectionService {
680
685
  executionMode: 'sequential',
681
686
  interOpNumThreads: 1,
682
687
  intraOpNumThreads: 1,
688
+ // Configuraciones adicionales para dispositivos con poca memoria
689
+ enableProfiling: false,
690
+ sessionLogSeverityLevel: 4,
691
+ sessionLogVerbosityLevel: 0,
683
692
  };
684
- this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, fallbackOptions);
693
+ try {
694
+ this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, fallbackOptions);
695
+ }
696
+ catch (fallbackError) {
697
+ throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
698
+ }
685
699
  }
686
700
  else {
687
701
  throw error;
@@ -721,7 +735,12 @@ class DetectionService {
721
735
  this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, sessionOptions);
722
736
  }
723
737
  catch (error) {
724
- if (error.message.includes('failed to allocate a buffer')) {
738
+ // Múltiples tipos de errores de memoria que pueden ocurrir
739
+ const isMemoryError = error.message.includes('failed to allocate a buffer') ||
740
+ error.message.includes('Out of memory') ||
741
+ error.message.includes('RangeError: Out of memory') ||
742
+ error.message.includes('no available backend found');
743
+ if (isMemoryError) {
725
744
  const fallbackOptions = {
726
745
  executionProviders: ['wasm'],
727
746
  graphOptimizationLevel: 'disabled',
@@ -731,8 +750,17 @@ class DetectionService {
731
750
  executionMode: 'sequential',
732
751
  interOpNumThreads: 1,
733
752
  intraOpNumThreads: 1,
753
+ // Configuraciones adicionales para dispositivos con poca memoria
754
+ enableProfiling: false,
755
+ sessionLogSeverityLevel: 4,
756
+ sessionLogVerbosityLevel: 0,
734
757
  };
735
- this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, fallbackOptions);
758
+ try {
759
+ this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, fallbackOptions);
760
+ }
761
+ catch (fallbackError) {
762
+ throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
763
+ }
736
764
  }
737
765
  else {
738
766
  throw error;
@@ -1089,7 +1117,7 @@ class ServiceContainer {
1089
1117
  }
1090
1118
  }
1091
1119
 
1092
- const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);color:#ffffff;font-size:12px;font-weight:500;text-align:center;white-space:normal;padding:13px;max-width:300px;z-index:20;height:fit-content;width:fit-content;animation:slideInFromTopCentered 0.3s ease-out}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.back-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:320px}.back-capture-buttons{display:flex;gap:12px;align-items:center;justify-content:center;flex-wrap:wrap}.capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.capture-button:hover{background:#f8f9fa}.capture-button:active{background:#e9ecef;transform:translateY(1px)}.capture-button:disabled{background:#e9ecef;color:#6c757d;cursor:not-allowed;transform:none}@media (max-width: 480px){.back-capture-section{bottom:16px;max-width:300px}.back-capture-buttons{flex-direction:column;gap:8px;width:100%}.capture-button,.back-capture-buttons .skip-button{width:100%;max-width:200px;padding:10px 20px;font-size:13px}}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.guide-text.performance-warning-text{animation:gentlePulse 2s ease-in-out infinite;background:rgba(255, 107, 107, 0.3);border:1px solid rgba(255, 107, 107, 0.5)}@keyframes gentlePulse{0%,100%{transform:translate(-50%, -50%) scale(1);background:rgba(255, 107, 107, 0.3)}50%{transform:translate(-50%, -50%) scale(1.02);background:rgba(255, 107, 107, 0.4)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(255, 255, 255, 0.3);border-top:2px solid #ffffff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes slideInFromTopCentered{0%{opacity:0;transform:translate(-50%, -50%) translateY(-8px) scale(0.95)}100%{opacity:1;transform:translate(-50%, -50%) translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}.guide-text{font-size:11px;padding:4px 8px;border-radius:8px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromTop 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}.manual-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.manual-capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.manual-capture-button:hover{background:#f8f9fa}.manual-capture-button:active{background:#e9ecef;transform:translateY(1px)}.manual-capture-button:disabled{background:#e9ecef;color:#6c757d;cursor:not-allowed;transform:none}@media (max-width: 480px){.manual-capture-section{bottom:16px;max-width:280px}.manual-capture-button{padding:10px 20px;font-size:13px}}";
1120
+ const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);color:#ffffff;font-size:12px;font-weight:500;text-align:center;white-space:normal;padding:13px;max-width:300px;z-index:20;height:fit-content;width:fit-content;animation:slideInFromTopCentered 0.3s ease-out}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.back-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:320px}.back-capture-buttons{display:flex;gap:12px;align-items:center;justify-content:center;flex-wrap:wrap}.capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.capture-button:hover{background:#f8f9fa}.capture-button:active{background:#e9ecef;transform:translateY(1px)}.capture-button:disabled,.capture-button.primary-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.back-capture-section{bottom:16px;max-width:300px}.back-capture-buttons{flex-direction:column;gap:8px;width:100%}.capture-button,.back-capture-buttons .skip-button{width:100%;max-width:200px;padding:10px 20px;font-size:13px}.capture-button:disabled,.capture-button.primary-button:disabled,.skip-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.guide-text.performance-warning-text{animation:gentlePulse 2s ease-in-out infinite;background:rgba(255, 107, 107, 0.3);border:1px solid rgba(255, 107, 107, 0.5)}@keyframes gentlePulse{0%,100%{transform:translate(-50%, -50%) scale(1);background:rgba(255, 107, 107, 0.3)}50%{transform:translate(-50%, -50%) scale(1.02);background:rgba(255, 107, 107, 0.4)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.skip-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(0, 0, 0, 0.2);border-top:2px solid #333333;border-radius:50%;animation:spin 1s linear infinite;display:inline-block;margin-right:8px;vertical-align:middle}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes slideInFromTopCentered{0%{opacity:0;transform:translate(-50%, -50%) translateY(-8px) scale(0.95)}100%{opacity:1;transform:translate(-50%, -50%) translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}.guide-text{font-size:11px;padding:4px 8px;border-radius:8px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromTop 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}.manual-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.manual-capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.manual-capture-button:hover{background:#f8f9fa}.manual-capture-button:active{background:#e9ecef;transform:translateY(1px)}.manual-capture-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.manual-capture-section{bottom:16px;max-width:280px}.manual-capture-button{padding:10px 20px;font-size:13px}.manual-capture-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}";
1093
1121
 
1094
1122
  const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H {
1095
1123
  constructor() {
@@ -1149,6 +1177,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1149
1177
  showManualCaptureButton = false;
1150
1178
  performanceDegradedMode = false; // Auto-switched to manual mode due to performance
1151
1179
  showPerformanceMessage = false; // Show performance message temporarily
1180
+ captureStateVersion = 0; // Triggers re-render when StateManager changes
1181
+ processingButton = null; // Track which button is processing
1152
1182
  // Services
1153
1183
  serviceContainer;
1154
1184
  eventBus;
@@ -1200,14 +1230,19 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1200
1230
  canvasPool = [];
1201
1231
  MAX_CANVAS_POOL_SIZE = 3;
1202
1232
  async componentDidLoad() {
1203
- this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
1204
- await this.initializeServices();
1205
- this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
1206
- await this.setupEventListeners();
1207
- this.updateStatus('Configurando cámara...', 'Detectando dispositivos disponibles', 'initializing');
1208
- await this.initializeComponent();
1209
- if (this.debug) {
1210
- this.initializePerformanceMonitor();
1233
+ try {
1234
+ this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
1235
+ await this.initializeServices();
1236
+ this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
1237
+ await this.setupEventListeners();
1238
+ this.updateStatus('Configurando cámara...', 'Detectando dispositivos disponibles', 'initializing');
1239
+ await this.initializeComponent();
1240
+ if (this.debug) {
1241
+ this.initializePerformanceMonitor();
1242
+ }
1243
+ }
1244
+ catch (error) {
1245
+ this.updateStatus('Error de inicialización', error.message, 'error');
1211
1246
  }
1212
1247
  }
1213
1248
  async initializeServices() {
@@ -1354,6 +1389,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1354
1389
  this.updateMaskDimensions(rect);
1355
1390
  }
1356
1391
  }
1392
+ triggerRerender() {
1393
+ this.captureStateVersion = this.captureStateVersion + 1;
1394
+ }
1357
1395
  getGuideText(step) {
1358
1396
  if (step === 'completed') {
1359
1397
  return 'Proceso completado';
@@ -1519,8 +1557,14 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1519
1557
  }
1520
1558
  }
1521
1559
  async skipBackCapture() {
1560
+ console.log('🟡 Botón clicked: Saltar reverso');
1561
+ // Set processing state immediately
1562
+ this.processingButton = 'skip-back';
1563
+ // Add small delay to show spinner
1564
+ await new Promise(resolve => setTimeout(resolve, 100));
1522
1565
  const readyCheck = this.isComponentReady();
1523
1566
  if (!readyCheck.ready) {
1567
+ this.processingButton = null;
1524
1568
  return { success: false, error: readyCheck.message };
1525
1569
  }
1526
1570
  try {
@@ -1537,6 +1581,10 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1537
1581
  catch (error) {
1538
1582
  return { success: false, error: error.message };
1539
1583
  }
1584
+ finally {
1585
+ // Always clear processing state when done
1586
+ this.processingButton = null;
1587
+ }
1540
1588
  }
1541
1589
  async getStatus() {
1542
1590
  const readyCheck = this.isComponentReady();
@@ -1714,9 +1762,53 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1714
1762
  // Always allow getting capture delay, even during initialization
1715
1763
  return this.captureDelay;
1716
1764
  }
1765
+ // Memory and device capability detection
1766
+ checkDeviceCapabilities() {
1767
+ const deviceMemory = navigator.deviceMemory;
1768
+ const hardwareConcurrency = navigator.hardwareConcurrency || 1;
1769
+ const isLowEndDevice = hardwareConcurrency <= 2;
1770
+ const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
1771
+ const isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
1772
+ // Si tenemos info de memoria del dispositivo
1773
+ if (deviceMemory && deviceMemory < 4) {
1774
+ return {
1775
+ canUseML: false,
1776
+ reason: `Memoria del dispositivo (${deviceMemory}GB) insuficiente para detección automática. Modo manual activado para mejor rendimiento.`,
1777
+ memoryMB: deviceMemory * 1024
1778
+ };
1779
+ }
1780
+ // Dispositivos móviles de gama baja
1781
+ if (isMobile && isLowEndDevice) {
1782
+ return {
1783
+ canUseML: false,
1784
+ reason: 'Dispositivo móvil optimizado detectado. Modo manual activado para mejor rendimiento y duración de batería.',
1785
+ memoryMB: null
1786
+ };
1787
+ }
1788
+ // Safari tiene limitaciones conocidas con WASM
1789
+ if (isSafari) {
1790
+ return {
1791
+ canUseML: false,
1792
+ reason: 'Safari detectado. Modo manual activado por compatibilidad optimizada con este navegador.',
1793
+ memoryMB: null
1794
+ };
1795
+ }
1796
+ return {
1797
+ canUseML: true,
1798
+ reason: 'Dispositivo compatible con detección automática.',
1799
+ memoryMB: deviceMemory ? deviceMemory * 1024 : null
1800
+ };
1801
+ }
1717
1802
  // DETECTION METHODS
1718
1803
  async startDetection() {
1719
1804
  try {
1805
+ // Verificar capacidades del dispositivo antes de cargar modelos
1806
+ const capabilities = this.checkDeviceCapabilities();
1807
+ // Si el dispositivo no puede usar ML o tenemos problemas de memoria, usar modo manual
1808
+ if (!capabilities.canUseML && this.useDocumentDetector) {
1809
+ this.switchToManualModeWithWarning(capabilities.reason);
1810
+ return;
1811
+ }
1720
1812
  // Paso 1: Verificar y cargar modelos si es necesario
1721
1813
  if (!this.detectionService.isModelLoaded()) {
1722
1814
  const loadStartTime = performance.now();
@@ -1726,16 +1818,32 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1726
1818
  await this.detectionService.loadModel();
1727
1819
  }
1728
1820
  catch (modelError) {
1729
- this.switchToManualModeWithError('Error al cargar modelo de detección');
1730
- throw modelError;
1821
+ // Si es un error de memoria, cambiar a modo manual con mensaje específico
1822
+ if (modelError.message.includes('Out of memory') || modelError.message.includes('no available backend')) {
1823
+ const memoryInfo = navigator.deviceMemory ? `(${navigator.deviceMemory}GB disponible)` : '';
1824
+ this.switchToManualModeWithWarning(`Memoria insuficiente para detección automática ${memoryInfo}. Modo manual activado - funciona igual de bien!`);
1825
+ return;
1826
+ }
1827
+ else {
1828
+ this.switchToManualModeWithError('Error al cargar modelo de detección');
1829
+ throw modelError;
1830
+ }
1731
1831
  }
1732
1832
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
1733
1833
  try {
1734
1834
  await this.detectionService.loadClassificationModel();
1735
1835
  }
1736
1836
  catch (classificationError) {
1737
- this.switchToManualModeWithError('Error al cargar modelo de clasificación');
1738
- throw classificationError;
1837
+ // Si es un error de memoria, cambiar a modo manual con mensaje específico
1838
+ if (classificationError.message.includes('Out of memory') || classificationError.message.includes('no available backend')) {
1839
+ const memoryInfo = navigator.deviceMemory ? `(${navigator.deviceMemory}GB disponible)` : '';
1840
+ this.switchToManualModeWithWarning(`Memoria insuficiente para clasificación automática ${memoryInfo}. Modo manual activado para optimizar rendimiento.`);
1841
+ return;
1842
+ }
1843
+ else {
1844
+ this.switchToManualModeWithError('Error al cargar modelo de clasificación');
1845
+ throw classificationError;
1846
+ }
1739
1847
  }
1740
1848
  const loadEndTime = performance.now();
1741
1849
  const loadTime = loadEndTime - loadStartTime;
@@ -1746,11 +1854,27 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1746
1854
  }
1747
1855
  // Paso 2: Detectar y configurar dispositivos
1748
1856
  this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
1749
- await this.cameraService.enumerateDevices();
1750
- await this.updateCameraInfoWithAutofocus();
1857
+ try {
1858
+ await this.cameraService.enumerateDevices();
1859
+ }
1860
+ catch (enumerateError) {
1861
+ throw new Error(`Error al enumerar dispositivos: ${enumerateError.message}`);
1862
+ }
1863
+ try {
1864
+ await this.updateCameraInfoWithAutofocus();
1865
+ }
1866
+ catch (cameraInfoError) {
1867
+ throw new Error(`Error al actualizar información de cámara: ${cameraInfoError.message}`);
1868
+ }
1751
1869
  // Paso 3: Configurar cámara seleccionada
1752
1870
  this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
1753
- const stream = await this.cameraService.setupCamera();
1871
+ let stream;
1872
+ try {
1873
+ stream = await this.cameraService.setupCamera();
1874
+ }
1875
+ catch (setupError) {
1876
+ throw new Error(`Error al configurar cámara: ${setupError.message}`);
1877
+ }
1754
1878
  // Paso 4: Inicializar video y mostrar estado de análisis inicial
1755
1879
  if (this.useDocumentDetector) {
1756
1880
  this.updateStatus('Analizando estabilidad...', 'Evaluando rendimiento del sistema', 'loading');
@@ -1758,7 +1882,12 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1758
1882
  else {
1759
1883
  this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
1760
1884
  }
1761
- await this.initializeVideoStream(stream);
1885
+ try {
1886
+ await this.initializeVideoStream(stream);
1887
+ }
1888
+ catch (videoError) {
1889
+ throw new Error(`Error al inicializar video: ${videoError.message}`);
1890
+ }
1762
1891
  // Paso 5: Calibrar máscara
1763
1892
  if (this.detectionContainer) {
1764
1893
  const container = this.detectionContainer.parentElement;
@@ -1982,7 +2111,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1982
2111
  isCapturing: false
1983
2112
  };
1984
2113
  const cameraInfo = this.cameraInfoWithAutofocus;
1985
- 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: {
2114
+ return (h("div", { key: '51cc6d0f873691852f938940b131732252b970c1', class: "detector-container" }, h("div", { key: '009480499c0a2833bf9f6bd28003142ca3dd3750', class: "video-container" }, h("video", { key: '88a99a1a90c8d817e40549a255324b2da342797f', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '7ede15ccd6f67b3081d907777de7b0d1a1b4cfd6', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
1986
2115
  position: 'absolute',
1987
2116
  left: `${box.x}px`,
1988
2117
  top: `${box.y}px`,
@@ -1991,9 +2120,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1991
2120
  border: '2px solid #32406C',
1992
2121
  pointerEvents: 'none',
1993
2122
  boxSizing: 'border-box'
1994
- } })))), 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
2123
+ } })))), this.isMaskReady && (h("div", { key: '63beb4330ab171a5e63237b1d83dcace61422018', class: "overlay-mask" }, h("div", { key: '93cfac6898de6ece6c7dd8f02ba2b79ce6591755', class: "card-outline" }, h("div", { key: '3087d3c51f42ea8d720cea4170dcb68e8bb385e1', class: "side side-top" }), h("div", { key: '992e311b895da161d3b5c49dde346ae378139641', class: "side side-right" }), h("div", { key: '5fb3d7b704f08177b2b9c6e4fbaf685f251a3e53', class: "side side-bottom" }), h("div", { key: '76f6e2cb856632c58809ccdabdf3f7d9a588c9da', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '142dfc187e07100278e16b19018e4b5fb96d964a', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'bba078ce4c9585631b6a8154f878464b1f779c8b', class: "back-capture-section" }, h("div", { key: 'ec94c9ba2cb373a539f0e6cc3eb356294cebf3ea', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (h("button", { key: '30561e1fc88090c6bb4d0cd7909a33a9525b0ac2', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '4b3e3356507c7f654705425e201d0303d727db72', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: '1abfada41312658eab4966d8fcdb711a6c4762e6', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '3cab3ce751cb38174b6d061170a66ac654a90a38', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
1995
2124
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
1996
- : '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" })))));
2125
+ : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'b773fe579cc039fd90ff5e61976752bcf631d16e', class: "camera-controls" }, h("button", { key: '8535463b13cc02e0030d14c1d57390e560572b4f', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: 'c4b159cf28c24ba2ec0a658bf6f1296c6aba47e3', class: "camera-selector-dropdown" }, h("div", { key: '033273241b77cedc414dee00b70926c3a659910a', class: "camera-selector-header" }, h("span", { key: '83fb4c7ae2387b85134a3ca0c5a156beaf73e481' }, "Seleccionar C\u00E1mara"), h("button", { key: 'f65b6e994603e616af8d69df198c3d8471125695', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'fd90d4c4db1e910c5d05b9ce19a252967c71ad1f', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: '49faa944b3c0c052e18f48e9dda2758cd2c0ae6b', class: "device-info" }, h("small", { key: '15e95207aef60a78b2912a6c00a1b61ab760b72d' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'a67a3795837adcd307fa3666c338376cecae16d0', class: "manual-capture-section" }, h("button", { key: '7f21ccd9b622d97bfce80962038de8be7cd6e33d', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: 'a4b5e865f373cc0e7787098eedd2d4df413d310f', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '0b02e81963136dfda62a8980bc5a1f5662509fb8', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '5eb16e12a7ce16720ca7bdd11aefd902ba772e24', class: "flip-animation" }, h("div", { key: '4c6f3ed33c5080e9e18d17c4594cf394250c3517', class: "id-card-icon" }), h("div", { key: 'd848d7a98540988a8f491bcf8f6f6ad9c5dccfd4', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '94863e6d0c404188d548db428af918274d882652', class: "success-animation" }, h("div", { key: 'a90b777e7b7eb533fabec2c15f79a4bd18778f0b', class: "check-icon" }), h("div", { key: '2152e5a3756f58f996c520b58cb1b6cca1763e94', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: 'ae18e06bf291429a74d1b6c8a0c4d37d473f7b30', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: 'b6b03ed285868d7d3350a2a73e59cb1d65a47401', class: "status-spinner" })), h("div", { key: 'dc85b49dd1cfcddf7300fb3f327d2e1de8ab9bff', class: "status-content" }, h("div", { key: '82461ed0c6d70a0c95ae6b7144ff1d8b8371c6fc', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '24aa77ec92ca5a48d1bed4cf98ae591bdbff74f0', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '3b56a0b305ca07407c09a73624a4040cf6625b56', class: "performance-monitor" }, h("div", { key: '52f975da1502f6b08a0b2c828bfd0be8d10e36f8', class: "performance-expanded" }, h("div", { key: 'ea2f444255add0415c7f88cdea2733e1244b17bd', class: "metrics-row" }, h("div", { key: 'e3c2370f1a900594940cb321d5b1661690018025', class: "metric-compact" }, h("span", { key: '25c2f402c9b69f21736c494420e7a396673e9893', class: "metric-label" }, "FPS"), h("span", { key: '505de593f7927544cd874332127e84c72131da72', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '5cb1ff4bc1ac33988404e59dac3d01d5a55893b9', class: "metric-compact" }, h("span", { key: 'ce701538abbfe797b288520ab4c277bf7a1d6a05', class: "metric-label" }, "MEM"), h("span", { key: 'efc383e993ad72c20cbfc630e20b2bf9a617a02d', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'b6b6b832740c44d4f78cddf99ba1d139dc3d4739', class: "metrics-row" }, h("div", { key: '3a8903a05aed8adb1626f93bee6ba258fbea9a7d', class: "metric-compact" }, h("span", { key: 'aabc56057156217b3be771ab3b1e92590fa886c5', class: "metric-label" }, "INF"), h("span", { key: '1080df34860cc891cda94c5aacc754cbde18a138', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'ac152093d4c0cc25862aac638fc4693dd0c51a69', class: "metric-compact" }, h("span", { key: 'b23712dbc0b9b8e9cb6e2c659e5af3844ec0ebe7', class: "metric-label" }, "FRAME"), h("span", { key: '461f88519b339fbad6444f009312f72efbc6e450', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '8f99afe2c94f37fd3a6feb082cc9aa96f54fc5e1', class: "metrics-row" }, h("div", { key: 'da7ac0fd842f38bbdcac01550764f9fe1bd666a2', class: "metric-compact" }, h("span", { key: 'a5dcd9c8753ba42038f869efa50ae9602cdf6256', class: "metric-label" }, "DET"), h("span", { key: 'e98b91ea98a41637baea1fcfda0bb590cf5d24a0', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '3ac54161a712d305a098eae0a3b5d21a731c6019', class: "metric-compact" }, h("span", { key: '3c8cc465433be3b490887457a6c3967dbbda6a7d', class: "metric-label" }, "RATE"), h("span", { key: 'f2e083118fe107c7dc2a675e3f661466cea6d479', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '10b6c32b0460e194d680c63b30a078c51a26e9c9', class: "watermark" }, h("img", { key: 'f5954a0ba3f90a642e0d5c6c14d5b549cd193018', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
1997
2126
  }
1998
2127
  // Utility methods
1999
2128
  updateDetectionBoxes(boxes) {
@@ -2109,9 +2238,24 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2109
2238
  }
2110
2239
  }
2111
2240
  async takeManualScreenshot() {
2112
- if (!this.videoRef)
2241
+ console.log('🔵 Botón clicked: Capturar (Frente/Reverso)');
2242
+ // Set processing state immediately
2243
+ const captureState = this.stateManager?.getCaptureState();
2244
+ if (captureState?.step === 'front') {
2245
+ this.processingButton = 'capture-front';
2246
+ }
2247
+ else if (captureState?.step === 'back') {
2248
+ this.processingButton = 'capture-back';
2249
+ }
2250
+ console.log('🔵 processingButton set to:', this.processingButton);
2251
+ // Add small delay to show spinner
2252
+ await new Promise(resolve => setTimeout(resolve, 100));
2253
+ if (!this.videoRef) {
2254
+ this.processingButton = null;
2113
2255
  return;
2256
+ }
2114
2257
  // When using manual capture, use mask coordinates for cropping
2258
+ // Note: processingButton will be cleared inside takeScreenshotWithMaskCoordinates
2115
2259
  await this.takeScreenshotWithMaskCoordinates();
2116
2260
  }
2117
2261
  async takeScreenshotWithMaskCoordinates() {
@@ -2187,6 +2331,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2187
2331
  this.completeProcess(true);
2188
2332
  this.returnCanvasToPool(captureCanvas);
2189
2333
  this.returnCanvasToPool(croppedCanvas);
2334
+ // Clear processing button when passport is detected
2335
+ this.processingButton = null;
2190
2336
  return;
2191
2337
  }
2192
2338
  }
@@ -2199,12 +2345,16 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2199
2345
  isDetectionPaused: true,
2200
2346
  showFlipAnimation: true
2201
2347
  });
2348
+ this.triggerRerender();
2202
2349
  setTimeout(() => {
2203
2350
  this.stateManager.updateCaptureState({
2204
2351
  showFlipAnimation: false,
2205
2352
  isDetectionPaused: false
2206
2353
  });
2354
+ this.triggerRerender();
2207
2355
  this.startBackDocumentTimer();
2356
+ // Clear processing button after animation completes
2357
+ this.processingButton = null;
2208
2358
  }, 3000);
2209
2359
  }
2210
2360
  else if (captureState.step === 'back') {
@@ -2215,6 +2365,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2215
2365
  }
2216
2366
  });
2217
2367
  this.completeProcess(false);
2368
+ // Clear processing button after back capture completes
2369
+ this.processingButton = null;
2218
2370
  }
2219
2371
  // Return canvases to pool after use
2220
2372
  this.returnCanvasToPool(captureCanvas);
@@ -2308,6 +2460,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2308
2460
  this.stopPerformanceMonitoring();
2309
2461
  // Liberar recursos de ONNX
2310
2462
  this.releaseOnnxResources();
2463
+ // Resetear máscara a blanco y limpiar detecciones
2464
+ this.resetMaskToWhite();
2465
+ this.detectionBoxes = [];
2311
2466
  this.hasAutoSwitchedToManual = true;
2312
2467
  this.performanceDegradedMode = true;
2313
2468
  this.showManualCaptureButton = true;
@@ -2318,6 +2473,75 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2318
2473
  }, 5000);
2319
2474
  }
2320
2475
  // Método para cambiar a modo manual por errores de ONNX
2476
+ switchToManualModeWithWarning(warningMessage) {
2477
+ // Solo cambiar si el detector está habilitado y no se ha cambiado ya
2478
+ if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
2479
+ return;
2480
+ }
2481
+ // No necesitamos liberar recursos ONNX porque no se han cargado aún
2482
+ // Resetear máscara a blanco y limpiar detecciones
2483
+ this.resetMaskToWhite();
2484
+ this.detectionBoxes = [];
2485
+ // Cambiar a modo manual
2486
+ this.hasAutoSwitchedToManual = true;
2487
+ this.performanceDegradedMode = true;
2488
+ this.showManualCaptureButton = true;
2489
+ this.useDocumentDetector = false; // Desactivar detector automático
2490
+ // Actualizar el estado con mensaje informativo (no error)
2491
+ this.updateStatus('Modo manual activado', warningMessage, 'active');
2492
+ // Continuar con la configuración de cámara
2493
+ this.continueWithCameraSetup();
2494
+ }
2495
+ async continueWithCameraSetup() {
2496
+ try {
2497
+ // Paso 2: Detectar y configurar dispositivos
2498
+ this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
2499
+ try {
2500
+ await this.cameraService.enumerateDevices();
2501
+ }
2502
+ catch (enumerateError) {
2503
+ throw new Error(`Error al enumerar dispositivos: ${enumerateError.message}`);
2504
+ }
2505
+ try {
2506
+ await this.updateCameraInfoWithAutofocus();
2507
+ }
2508
+ catch (cameraInfoError) {
2509
+ throw new Error(`Error al actualizar información de cámara: ${cameraInfoError.message}`);
2510
+ }
2511
+ // Paso 3: Configurar cámara seleccionada
2512
+ this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
2513
+ let stream;
2514
+ try {
2515
+ stream = await this.cameraService.setupCamera();
2516
+ }
2517
+ catch (setupError) {
2518
+ throw new Error(`Error al configurar cámara: ${setupError.message}`);
2519
+ }
2520
+ // Paso 4: Inicializar video
2521
+ this.updateStatus('Captura manual', 'Posicione su documento y use el botón para capturar', 'active');
2522
+ try {
2523
+ await this.initializeVideoStream(stream);
2524
+ }
2525
+ catch (videoError) {
2526
+ throw new Error(`Error al inicializar video: ${videoError.message}`);
2527
+ }
2528
+ // Paso 5: Calibrar máscara
2529
+ if (this.detectionContainer) {
2530
+ const container = this.detectionContainer.parentElement;
2531
+ const rect = container.getBoundingClientRect();
2532
+ this.updateMaskDimensions(rect);
2533
+ }
2534
+ // Finalizar
2535
+ this.startTime = Date.now();
2536
+ this.stateManager.updateCaptureState({ isLoading: false });
2537
+ // Mostrar botón manual (ya debe estar habilitado)
2538
+ this.showManualCaptureButton = true;
2539
+ }
2540
+ catch (error) {
2541
+ this.updateStatus('Error de cámara', error.message, 'error');
2542
+ throw error;
2543
+ }
2544
+ }
2321
2545
  switchToManualModeWithError(errorMessage) {
2322
2546
  // Solo cambiar si el detector está habilitado y no se ha cambiado ya
2323
2547
  if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
@@ -2325,6 +2549,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2325
2549
  }
2326
2550
  // Liberar recursos de ONNX inmediatamente
2327
2551
  this.releaseOnnxResources();
2552
+ // Resetear máscara a blanco y limpiar detecciones
2553
+ this.resetMaskToWhite();
2554
+ this.detectionBoxes = [];
2328
2555
  // Cambiar a modo manual
2329
2556
  this.hasAutoSwitchedToManual = true;
2330
2557
  this.performanceDegradedMode = true;
@@ -2575,6 +2802,24 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2575
2802
  // Performance is good, use base frame skip
2576
2803
  return this.BASE_FRAME_SKIP;
2577
2804
  }
2805
+ // Método para resetear la máscara a color blanco
2806
+ resetMaskToWhite() {
2807
+ const cardOutline = this.el.shadowRoot?.querySelector('.card-outline');
2808
+ const corners = this.el.shadowRoot?.querySelectorAll('.corner');
2809
+ const topSide = this.el.shadowRoot?.querySelector('.side-top');
2810
+ const rightSide = this.el.shadowRoot?.querySelector('.side-right');
2811
+ const bottomSide = this.el.shadowRoot?.querySelector('.side-bottom');
2812
+ const leftSide = this.el.shadowRoot?.querySelector('.side-left');
2813
+ // Remover todas las clases de alineación y estado perfecto
2814
+ cardOutline?.classList.remove('perfect-match');
2815
+ corners?.forEach(corner => corner.classList.remove('perfect-match'));
2816
+ topSide?.classList.remove('aligned');
2817
+ rightSide?.classList.remove('aligned');
2818
+ bottomSide?.classList.remove('aligned');
2819
+ leftSide?.classList.remove('aligned');
2820
+ // Resetear estado de alineación
2821
+ this.sideAlignment = { top: false, right: false, bottom: false, left: false };
2822
+ }
2578
2823
  // Canvas pool management for screenshots
2579
2824
  getPooledCanvas(width, height) {
2580
2825
  // Try to find a canvas with matching dimensions
@@ -2631,6 +2876,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2631
2876
  "showManualCaptureButton": [32],
2632
2877
  "performanceDegradedMode": [32],
2633
2878
  "showPerformanceMessage": [32],
2879
+ "captureStateVersion": [32],
2880
+ "processingButton": [32],
2634
2881
  "getCapturedImages": [64],
2635
2882
  "isProcessCompleted": [64],
2636
2883
  "startCapture": [64],