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

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.
Files changed (30) hide show
  1. package/README.md +11 -1
  2. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
  3. package/dist/cjs/jaak-stamps.cjs.entry.js +225 -18
  4. package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
  5. package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
  6. package/dist/cjs/loader.cjs.js +1 -1
  7. package/dist/collection/components/my-component/my-component.css +164 -9
  8. package/dist/collection/components/my-component/my-component.js +230 -16
  9. package/dist/collection/components/my-component/my-component.js.map +1 -1
  10. package/dist/collection/services/DetectionService.js +16 -1
  11. package/dist/collection/services/DetectionService.js.map +1 -1
  12. package/dist/collection/services/ServiceContainer.js +3 -2
  13. package/dist/collection/services/ServiceContainer.js.map +1 -1
  14. package/dist/components/jaak-stamps.js +228 -18
  15. package/dist/components/jaak-stamps.js.map +1 -1
  16. package/dist/esm/jaak-stamps-webcomponent.js +1 -1
  17. package/dist/esm/jaak-stamps.entry.js +225 -18
  18. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  19. package/dist/esm/loader.js +1 -1
  20. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  21. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  22. package/dist/jaak-stamps-webcomponent/p-4b2f9fb3.entry.js +2 -0
  23. package/dist/jaak-stamps-webcomponent/p-4b2f9fb3.entry.js.map +1 -0
  24. package/dist/types/components/my-component/my-component.d.ts +8 -0
  25. package/dist/types/components.d.ts +8 -0
  26. package/dist/types/services/DetectionService.d.ts +2 -1
  27. package/dist/types/services/ServiceContainer.d.ts +1 -0
  28. package/package.json +1 -1
  29. package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js +0 -2
  30. package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js.map +0 -1
@@ -630,6 +630,7 @@ class DeviceStrategyFactory {
630
630
  class DetectionService {
631
631
  debug;
632
632
  useDocumentClassification;
633
+ useDocumentDetector;
633
634
  session;
634
635
  mobileNetSession;
635
636
  mobileNetClassMap;
@@ -647,9 +648,10 @@ class DetectionService {
647
648
  // Static canvas pool for reuse across instances
648
649
  static canvasPool = new Map();
649
650
  static MAX_POOL_SIZE = 5;
650
- constructor(debug = false, useDocumentClassification = false) {
651
+ constructor(debug = false, useDocumentClassification = false, useDocumentDetector = true) {
651
652
  this.debug = debug;
652
653
  this.useDocumentClassification = useDocumentClassification;
654
+ this.useDocumentDetector = useDocumentDetector;
653
655
  this.deviceStrategy = DeviceStrategyFactory.createStrategy();
654
656
  this.initializeCanvasPool();
655
657
  }
@@ -657,6 +659,11 @@ class DetectionService {
657
659
  if (this.modelLoaded || this.session) {
658
660
  return;
659
661
  }
662
+ if (!this.useDocumentDetector) {
663
+ // Skip loading the detection model but mark as "loaded" for compatibility
664
+ this.modelLoaded = true;
665
+ return;
666
+ }
660
667
  try {
661
668
  const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
662
669
  try {
@@ -737,6 +744,10 @@ class DetectionService {
737
744
  }
738
745
  }
739
746
  isModelLoaded() {
747
+ if (!this.useDocumentDetector) {
748
+ // If detector is disabled, consider it "loaded" for compatibility
749
+ return this.modelLoaded;
750
+ }
740
751
  return this.modelLoaded && !!this.session;
741
752
  }
742
753
  preprocess(video) {
@@ -761,6 +772,10 @@ class DetectionService {
761
772
  return new window.ort.Tensor("float16", float16Data, [1, 3, this.INPUT_SIZE, this.INPUT_SIZE]);
762
773
  }
763
774
  async runInference(inputTensor) {
775
+ if (!this.useDocumentDetector) {
776
+ // Return empty array when document detector is disabled
777
+ return [];
778
+ }
764
779
  if (!this.session) {
765
780
  throw new Error('Detection model not loaded');
766
781
  }
@@ -1038,7 +1053,7 @@ class ServiceContainer {
1038
1053
  const eventBus = new EventBusService();
1039
1054
  const stateManager = new StateManagerService(eventBus);
1040
1055
  const cameraService = new CameraService(eventBus, config.preferredCamera);
1041
- const detectionService = new DetectionService(config.debug, config.useDocumentClassification);
1056
+ const detectionService = new DetectionService(config.debug, config.useDocumentClassification, config.useDocumentDetector);
1042
1057
  // Register services
1043
1058
  this.services.set('eventBus', eventBus);
1044
1059
  this.services.set('stateManager', stateManager);
@@ -1074,7 +1089,7 @@ class ServiceContainer {
1074
1089
  }
1075
1090
  }
1076
1091
 
1077
- 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%);color:#fff;font-size:14px;font-weight:600;text-align:center;white-space:normal;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);padding:12px 20px;border-radius:8px;max-width:300px;z-index:20;border:1px solid rgba(255, 255, 255, 0.1)}.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}.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)}}.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}.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)}}.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}}.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:slideInFromLeft 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}}";
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)}}.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}}";
1078
1093
 
1079
1094
  const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H {
1080
1095
  constructor() {
@@ -1090,6 +1105,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1090
1105
  maskSize = 90;
1091
1106
  cropMargin = 20;
1092
1107
  useDocumentClassification = false;
1108
+ useDocumentDetector = true;
1093
1109
  preferredCamera = 'auto';
1094
1110
  captureDelay = 1500;
1095
1111
  enableBackDocumentTimer = false;
@@ -1130,6 +1146,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1130
1146
  detectionRate: 0
1131
1147
  };
1132
1148
  backDocumentTimerRemaining = 0;
1149
+ showManualCaptureButton = false;
1150
+ captureStateVersion = 0; // Triggers re-render when StateManager changes
1133
1151
  // Services
1134
1152
  serviceContainer;
1135
1153
  eventBus;
@@ -1193,6 +1211,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1193
1211
  maskSize: this.maskSize,
1194
1212
  cropMargin: this.cropMargin,
1195
1213
  useDocumentClassification: this.useDocumentClassification,
1214
+ useDocumentDetector: this.useDocumentDetector,
1196
1215
  preferredCamera: this.preferredCamera,
1197
1216
  captureDelay: this.captureDelay
1198
1217
  };
@@ -1250,27 +1269,39 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1250
1269
  document.head.appendChild(script);
1251
1270
  await new Promise((resolve) => {
1252
1271
  script.onload = () => {
1253
- setTimeout(() => {
1254
- this.finalizeInitialization();
1272
+ setTimeout(async () => {
1273
+ await this.finalizeInitialization();
1255
1274
  resolve(undefined);
1256
1275
  }, 300);
1257
1276
  };
1258
1277
  });
1259
1278
  }
1260
1279
  else {
1261
- setTimeout(() => {
1262
- this.finalizeInitialization();
1280
+ setTimeout(async () => {
1281
+ await this.finalizeInitialization();
1263
1282
  }, 300);
1264
1283
  }
1265
1284
  }
1266
- finalizeInitialization() {
1285
+ async finalizeInitialization() {
1267
1286
  this.stateManager.updateCaptureState({ isLoading: false });
1268
- this.currentStatus = {
1269
- message: 'Listo para capturar',
1270
- description: '',
1271
- type: 'ready',
1272
- isInitialized: true
1273
- };
1287
+ // Check camera permissions before showing "ready" status
1288
+ const hasPermissions = await this.checkCameraPermissions();
1289
+ if (hasPermissions) {
1290
+ this.currentStatus = {
1291
+ message: 'Listo para capturar',
1292
+ description: '',
1293
+ type: 'ready',
1294
+ isInitialized: true
1295
+ };
1296
+ }
1297
+ else {
1298
+ this.currentStatus = {
1299
+ message: 'Permisos de cámara requeridos',
1300
+ description: 'Por favor, otorgue permisos de cámara para continuar',
1301
+ type: 'error',
1302
+ isInitialized: true
1303
+ };
1304
+ }
1274
1305
  this.emitReadyEvent();
1275
1306
  }
1276
1307
  updateStatus(message, description, type = 'loading') {
@@ -1285,6 +1316,26 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1285
1316
  const isDocumentReady = !!window.ort && this.detectionService.isModelLoaded();
1286
1317
  this.isReady.emit(isDocumentReady);
1287
1318
  }
1319
+ async checkCameraPermissions() {
1320
+ try {
1321
+ if (!navigator.permissions) {
1322
+ // Fallback: try to access camera directly
1323
+ try {
1324
+ const stream = await navigator.mediaDevices.getUserMedia({ video: true });
1325
+ stream.getTracks().forEach(track => track.stop());
1326
+ return true;
1327
+ }
1328
+ catch {
1329
+ return false;
1330
+ }
1331
+ }
1332
+ const permission = await navigator.permissions.query({ name: 'camera' });
1333
+ return permission.state === 'granted';
1334
+ }
1335
+ catch (error) {
1336
+ return false;
1337
+ }
1338
+ }
1288
1339
  handleStateChange(data) {
1289
1340
  }
1290
1341
  initializeResizeObserver() {
@@ -1302,6 +1353,32 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1302
1353
  this.updateMaskDimensions(rect);
1303
1354
  }
1304
1355
  }
1356
+ triggerRerender() {
1357
+ this.captureStateVersion = this.captureStateVersion + 1;
1358
+ }
1359
+ getGuideText(step) {
1360
+ if (step === 'completed') {
1361
+ return 'Proceso completado';
1362
+ }
1363
+ if (step === 'front') {
1364
+ // Para el frente, si el detector manual está activo, mostrar instrucciones de captura manual
1365
+ if (this.showManualCaptureButton) {
1366
+ return 'Posicione el frente de su documento en el marco y presione el botón para capturar';
1367
+ }
1368
+ // Instrucción básica de alineación
1369
+ return 'Alinee su identificación con el marco';
1370
+ }
1371
+ else if (step === 'back') {
1372
+ // Para el reverso, mostrar instrucciones específicas según el modo
1373
+ if (this.useDocumentDetector) {
1374
+ return 'Si tu documento no tiene lado trasero, da clic en el botón para continuar con el proceso';
1375
+ }
1376
+ else {
1377
+ return 'Posicione el reverso de su documento en el marco y capture, o salte este paso';
1378
+ }
1379
+ }
1380
+ return 'Alinee su identificación con el marco';
1381
+ }
1305
1382
  updateMaskDimensions(containerRect) {
1306
1383
  if (!this.videoRef)
1307
1384
  return;
@@ -1649,7 +1726,12 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1649
1726
  this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
1650
1727
  const stream = await this.cameraService.setupCamera();
1651
1728
  // Paso 4: Inicializar video y ocultar estado inmediatamente
1652
- this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
1729
+ if (this.useDocumentDetector) {
1730
+ this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
1731
+ }
1732
+ else {
1733
+ this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
1734
+ }
1653
1735
  await this.initializeVideoStream(stream);
1654
1736
  // Paso 5: Calibrar máscara
1655
1737
  if (this.detectionContainer) {
@@ -1660,6 +1742,10 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1660
1742
  // Finalizar e iniciar captura
1661
1743
  this.startTime = Date.now();
1662
1744
  this.stateManager.updateCaptureState({ isLoading: false });
1745
+ // Mostrar botón manual si el detector está deshabilitado
1746
+ if (!this.useDocumentDetector) {
1747
+ this.showManualCaptureButton = true;
1748
+ }
1663
1749
  this.detectFrame();
1664
1750
  }
1665
1751
  catch (err) {
@@ -1691,6 +1777,18 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1691
1777
  const captureState = this.stateManager.getCaptureState();
1692
1778
  if (!this.videoRef || !this.detectionContainer || !this.detectionService.isModelLoaded())
1693
1779
  return;
1780
+ // Show manual capture button when document detector is disabled
1781
+ if (!this.useDocumentDetector) {
1782
+ this.showManualCaptureButton = true;
1783
+ // Continue the loop for UI updates but skip detection logic
1784
+ if (captureState.step !== 'completed') {
1785
+ this.animationId = requestAnimationFrame(() => this.detectFrame());
1786
+ }
1787
+ return;
1788
+ }
1789
+ else {
1790
+ this.showManualCaptureButton = false;
1791
+ }
1694
1792
  if (captureState.isDetectionPaused) {
1695
1793
  if (captureState.step !== 'completed') {
1696
1794
  this.animationId = requestAnimationFrame(() => this.detectFrame());
@@ -1818,7 +1916,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1818
1916
  isCapturing: false
1819
1917
  };
1820
1918
  const cameraInfo = this.cameraInfoWithAutofocus;
1821
- return (h("div", { key: '69a8df7969288e19088e8809f1fefcc89b237cda', class: "detector-container" }, h("div", { key: '79e59a315d266be9d4fb5eed5740e5ad825d5890', class: "video-container" }, h("video", { key: 'a3312788c739748b774a25acb30c2dcea7f01695', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: 'ddab107500c2dab24c140049bef0b44aafc35dc7', 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: {
1919
+ 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: {
1822
1920
  position: 'absolute',
1823
1921
  left: `${box.x}px`,
1824
1922
  top: `${box.y}px`,
@@ -1827,9 +1925,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1827
1925
  border: '2px solid #32406C',
1828
1926
  pointerEvents: 'none',
1829
1927
  boxSizing: 'border-box'
1830
- } })))), this.isMaskReady && (h("div", { key: '370337089a3de949b8cb4ae84dc0037e6044b2d5', class: "overlay-mask" }, h("div", { key: 'cb35c2e42bf1729ad02a8fe7e7abaf27cf1a8d7a', class: "card-outline" }, h("div", { key: '637d72ada63805e7f25051cd1ad9093030cb025a', class: "side side-top" }), h("div", { key: 'c7a465aa686afa41f80433d949715b40f6f349dc', class: "side side-right" }), h("div", { key: '384aade0610b5bc7bdd7db350a70d82f9bebd8ab', class: "side side-bottom" }), h("div", { key: '5506cc346f3fd718e3fda4365f69ea780304b6d6', class: "side side-left" }), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'f5b9b62937eabab7c096b7417a13bbf6f822ee8a', class: "guide-text" }, "Alinee su identificaci\u00F3n con el marco"))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '74b122125a42839d2600cf99fb855140042ab761', class: "skip-section" }, h("div", { key: '0b4a39d1599854d6c445e2c35dcc89a0e4970846', class: "skip-explanation" }, "Si tu documento no tiene lado trasero, da clic en el bot\u00F3n para continuar con el proceso"), h("button", { key: 'b52b47df390cc099c8847ca8a676270bb154294d', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
1928
+ } })))), 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
1831
1929
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
1832
- : 'Saltar reverso'))), captureState.isVideoActive && (h("div", { key: 'f04e47749a25197c434b849844c67eadca5c6af3', class: "camera-controls" }, h("button", { key: '82625eef057f59b6dac11c4443ccb315231eac7f', 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: '61cfb89cd9bb549cfff2cb70293db06ee6cdf06b', class: "camera-selector-dropdown" }, h("div", { key: '60f751d8db7b305f2bdfdc089d2110bcff940150', class: "camera-selector-header" }, h("span", { key: '9dae045a34cc06f323ff4850921b495fc845be47' }, "Seleccionar C\u00E1mara"), h("button", { key: '4584d7964d91ddd7f3d980c3e5e90493530d33bd', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '9881f2b4fc6ffbdc76db06a7b49d060cefbe6277', 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: '3dfe9ffc7b4cc9afb199a78026cef6d4b469805e', class: "device-info" }, h("small", { key: 'bddc865a30469bd2724624b2f7c8f641f9d97a07' }, "Dispositivo: ", cameraInfo.deviceType)))))), captureState.isCapturing && (h("div", { key: '2fd963e44f69f80d17c6bbbfa3c9b5e11bc816be', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'ba4872ea5388a0ac3134be61feff5412373465ab', class: "flip-animation" }, h("div", { key: 'e83a92cea79f3e85aefc6964c5d9de7b495d511e', class: "id-card-icon" }), h("div", { key: '1d06b51fd6ad14d9b37aa6bca72bd13c951a386a', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '503c377896d5fdc3ae8476b09f511d571f23ae72', class: "success-animation" }, h("div", { key: 'e1929c245b0d1479a6fb1513b0b37c53070c4291', class: "check-icon" }), h("div", { key: '024c897c70427b205390770fc3dfb999ffcc84d3', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '54e350707628e5c88ae2900e412e6bda2eace943', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '82ea10569ed46285686cf3e63885ca1941bb559c', class: "status-spinner" })), h("div", { key: '1fecf179c10965cccf1be3dc0a54afd377ba38ad', class: "status-content" }, h("div", { key: '1b301c1bbb02e5cac2e6babcb20c190662d45c68', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '5b6c61308d17176daade02c951a436d114a74100', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'b8104db64208dc87a617a072957c007f28055194', class: "performance-monitor" }, h("div", { key: 'e66aec071f10821be2713858dcc251c04f480e63', class: "performance-expanded" }, h("div", { key: '1ae09a91d0704ab21b9eb51097cf452fd5f44359', class: "metrics-row" }, h("div", { key: '340d4d16d048a5217fe13d5de63618c9ea221165', class: "metric-compact" }, h("span", { key: '20cfda14969ef70d232edd68f2e9902bd44b5d9a', class: "metric-label" }, "FPS"), h("span", { key: '3a089c2814a71ed6513bf05b0120a66aa159daee', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '23f700def988c3220fc5df37aeb57a0a20ff56b3', class: "metric-compact" }, h("span", { key: '441e8490a871469813a6d832114e03583d375e91', class: "metric-label" }, "MEM"), h("span", { key: '51f7c49164c9d5e699141c92c88375fc99b312ed', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '6547fee80e2a1db7c9ff889a1c478fc8248159e6', class: "metrics-row" }, h("div", { key: '7b2d1642fd122ee0fbed36387afb23152afe10c2', class: "metric-compact" }, h("span", { key: 'a9137119bf7da366f18feaf8f28807ee40ad7a25', class: "metric-label" }, "INF"), h("span", { key: '5cbc0c040102688f1039cdcd4bb58020248cf700', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'cb4a86f5da46e4081d997c8b8bb6a899f06dfcd2', class: "metric-compact" }, h("span", { key: '88ad5e1d842eb156cf6813ecaa9d2bec76a5999e', class: "metric-label" }, "FRAME"), h("span", { key: 'a9b14cd98b64dcfc058d3b68a3c168a9e092fb3c', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '86e01f3596682cb8ad2ff5464748e6ecb7db374a', class: "metrics-row" }, h("div", { key: 'e73b9d39e9bcbc5d46fa0ba0a737700e3e5566d9', class: "metric-compact" }, h("span", { key: '8ecfe969dfe77c87d9da76d7390706014b4872f3', class: "metric-label" }, "DET"), h("span", { key: 'bd1a6a52e0bd1fb4d377413d7515e4e0f00da10f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '36539ae98787fd60642082db1bf89031003c4bef', class: "metric-compact" }, h("span", { key: '7f3a81629c09053e3beeca97f485da0f60412052', class: "metric-label" }, "RATE"), h("span", { key: '72399e0ac39270c82b265ce64e402f84cda9fb2c', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '4e3a914ba2f5993f85de7b9dd6dc93d76b41c96d', class: "watermark" }, h("img", { key: 'aa634c2ba2090f64ee9b41aa1d75a9114d46fd63', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
1930
+ : '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" })))));
1833
1931
  }
1834
1932
  // Utility methods
1835
1933
  updateDetectionBoxes(boxes) {
@@ -1944,6 +2042,115 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
1944
2042
  }
1945
2043
  }
1946
2044
  }
2045
+ async takeManualScreenshot() {
2046
+ if (!this.videoRef)
2047
+ return;
2048
+ // When using manual capture, use mask coordinates for cropping
2049
+ await this.takeScreenshotWithMaskCoordinates();
2050
+ }
2051
+ async takeScreenshotWithMaskCoordinates() {
2052
+ if (!this.videoRef || !this.detectionContainer)
2053
+ return;
2054
+ this.stateManager.updateCaptureState({ isCapturing: true });
2055
+ this.triggerCaptureAnimation();
2056
+ // Use pooled canvas for optimization
2057
+ const captureCanvas = this.getPooledCanvas(this.videoRef.videoWidth, this.videoRef.videoHeight);
2058
+ const captureCtx = captureCanvas.getContext('2d', { alpha: false });
2059
+ captureCtx.clearRect(0, 0, captureCanvas.width, captureCanvas.height);
2060
+ captureCtx.drawImage(this.videoRef, 0, 0, captureCanvas.width, captureCanvas.height);
2061
+ // Calculate mask coordinates for cropping
2062
+ const container = this.detectionContainer.parentElement;
2063
+ const containerRect = container.getBoundingClientRect();
2064
+ // Get mask dimensions from CSS properties
2065
+ const maskWidthPercent = parseFloat(this.el.style.getPropertyValue('--mask-width').replace('%', '')) || 90;
2066
+ const maskHeightPercent = parseFloat(this.el.style.getPropertyValue('--mask-height').replace('%', '')) || 56.25;
2067
+ const maskCenterXPercent = parseFloat(this.el.style.getPropertyValue('--mask-center-x').replace('%', '')) || 50;
2068
+ const maskCenterYPercent = parseFloat(this.el.style.getPropertyValue('--mask-center-y').replace('%', '')) || 50;
2069
+ // Convert percentages to actual video coordinates
2070
+ const videoWidth = this.videoRef.videoWidth;
2071
+ const videoHeight = this.videoRef.videoHeight;
2072
+ const videoAspectRatio = videoWidth / videoHeight;
2073
+ const containerAspectRatio = containerRect.width / containerRect.height;
2074
+ let displayedVideoWidth, displayedVideoHeight;
2075
+ let videoOffsetX = 0, videoOffsetY = 0;
2076
+ if (videoAspectRatio > containerAspectRatio) {
2077
+ displayedVideoWidth = containerRect.width;
2078
+ displayedVideoHeight = containerRect.width / videoAspectRatio;
2079
+ videoOffsetY = (containerRect.height - displayedVideoHeight) / 2;
2080
+ }
2081
+ else {
2082
+ displayedVideoHeight = containerRect.height;
2083
+ displayedVideoWidth = containerRect.height * videoAspectRatio;
2084
+ videoOffsetX = (containerRect.width - displayedVideoWidth) / 2;
2085
+ }
2086
+ // Calculate mask coordinates in video space
2087
+ const maskWidthInContainer = (maskWidthPercent / 100) * containerRect.width;
2088
+ const maskHeightInContainer = (maskHeightPercent / 100) * containerRect.height;
2089
+ const maskCenterXInContainer = (maskCenterXPercent / 100) * containerRect.width;
2090
+ const maskCenterYInContainer = (maskCenterYPercent / 100) * containerRect.height;
2091
+ // Convert to video coordinates
2092
+ const scaleX = videoWidth / displayedVideoWidth;
2093
+ const scaleY = videoHeight / displayedVideoHeight;
2094
+ const maskCenterXInVideo = (maskCenterXInContainer - videoOffsetX) * scaleX;
2095
+ const maskCenterYInVideo = (maskCenterYInContainer - videoOffsetY) * scaleY;
2096
+ const maskWidthInVideo = maskWidthInContainer * scaleX;
2097
+ const maskHeightInVideo = maskHeightInContainer * scaleY;
2098
+ // Calculate crop coordinates
2099
+ const cropX = Math.max(0, maskCenterXInVideo - (maskWidthInVideo / 2) - this.cropMargin);
2100
+ const cropY = Math.max(0, maskCenterYInVideo - (maskHeightInVideo / 2) - this.cropMargin);
2101
+ const cropWidth = Math.min(maskWidthInVideo + (2 * this.cropMargin), videoWidth - cropX);
2102
+ const cropHeight = Math.min(maskHeightInVideo + (2 * this.cropMargin), videoHeight - cropY);
2103
+ // Use pooled canvas for cropped version
2104
+ const croppedCanvas = this.getPooledCanvas(cropWidth, cropHeight);
2105
+ const croppedCtx = croppedCanvas.getContext('2d', { alpha: false });
2106
+ croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);
2107
+ croppedCtx.drawImage(this.videoRef, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
2108
+ const captureState = this.stateManager.getCaptureState();
2109
+ if (captureState.step === 'front') {
2110
+ this.stateManager.setCapturedImages({
2111
+ front: {
2112
+ fullFrame: captureCanvas.toDataURL('image/png'),
2113
+ cropped: croppedCanvas.toDataURL('image/png')
2114
+ }
2115
+ });
2116
+ // Check if document classification is enabled (independent of detector)
2117
+ if (this.useDocumentClassification) {
2118
+ const classification = await this.detectionService.classifyDocument(croppedCanvas);
2119
+ if (classification && classification.class === 'passport') {
2120
+ this.completeProcess(true);
2121
+ this.returnCanvasToPool(captureCanvas);
2122
+ this.returnCanvasToPool(croppedCanvas);
2123
+ return;
2124
+ }
2125
+ }
2126
+ this.stateManager.updateCaptureState({
2127
+ step: 'back',
2128
+ isDetectionPaused: true,
2129
+ showFlipAnimation: true
2130
+ });
2131
+ this.triggerRerender();
2132
+ setTimeout(() => {
2133
+ this.stateManager.updateCaptureState({
2134
+ showFlipAnimation: false,
2135
+ isDetectionPaused: false
2136
+ });
2137
+ this.triggerRerender();
2138
+ this.startBackDocumentTimer();
2139
+ }, 3000);
2140
+ }
2141
+ else if (captureState.step === 'back') {
2142
+ this.stateManager.setCapturedImages({
2143
+ back: {
2144
+ fullFrame: captureCanvas.toDataURL('image/png'),
2145
+ cropped: croppedCanvas.toDataURL('image/png')
2146
+ }
2147
+ });
2148
+ this.completeProcess(false);
2149
+ }
2150
+ // Return canvases to pool after use
2151
+ this.returnCanvasToPool(captureCanvas);
2152
+ this.returnCanvasToPool(croppedCanvas);
2153
+ }
1947
2154
  async takeScreenshot() {
1948
2155
  if (!this.videoRef || !this.lastDetectedBox)
1949
2156
  return;
@@ -2270,6 +2477,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2270
2477
  "maskSize": [2, "mask-size"],
2271
2478
  "cropMargin": [2, "crop-margin"],
2272
2479
  "useDocumentClassification": [4, "use-document-classification"],
2480
+ "useDocumentDetector": [4, "use-document-detector"],
2273
2481
  "preferredCamera": [1, "preferred-camera"],
2274
2482
  "captureDelay": [2, "capture-delay"],
2275
2483
  "enableBackDocumentTimer": [4, "enable-back-document-timer"],
@@ -2285,6 +2493,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
2285
2493
  "currentStatus": [32],
2286
2494
  "performanceData": [32],
2287
2495
  "backDocumentTimerRemaining": [32],
2496
+ "showManualCaptureButton": [32],
2497
+ "captureStateVersion": [32],
2288
2498
  "getCapturedImages": [64],
2289
2499
  "isProcessCompleted": [64],
2290
2500
  "startCapture": [64],