@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.
- package/README.md +11 -1
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +225 -18
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/my-component/my-component.css +164 -9
- package/dist/collection/components/my-component/my-component.js +230 -16
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/DetectionService.js +16 -1
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/ServiceContainer.js +3 -2
- package/dist/collection/services/ServiceContainer.js.map +1 -1
- package/dist/components/jaak-stamps.js +228 -18
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/jaak-stamps-webcomponent.js +1 -1
- package/dist/esm/jaak-stamps.entry.js +225 -18
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-4b2f9fb3.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-4b2f9fb3.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +8 -0
- package/dist/types/components.d.ts +8 -0
- package/dist/types/services/DetectionService.d.ts +2 -1
- package/dist/types/services/ServiceContainer.d.ts +1 -0
- package/package.json +1 -1
- package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js.map +0 -1
|
@@ -16,7 +16,7 @@ var patchBrowser = () => {
|
|
|
16
16
|
|
|
17
17
|
patchBrowser().then(async (options) => {
|
|
18
18
|
await globalScripts();
|
|
19
|
-
return bootstrapLazy([["jaak-stamps",[[1,"jaak-stamps",{"debug":[4],"alignmentTolerance":[2,"alignment-tolerance"],"maskSize":[2,"mask-size"],"cropMargin":[2,"crop-margin"],"useDocumentClassification":[4,"use-document-classification"],"preferredCamera":[1,"preferred-camera"],"captureDelay":[2,"capture-delay"],"enableBackDocumentTimer":[4,"enable-back-document-timer"],"backDocumentTimerDuration":[2,"back-document-timer-duration"],"detectionBoxes":[32],"sideAlignment":[32],"isMaskReady":[32],"shouldMirrorVideo":[32],"showCameraSelector":[32],"isSwitchingCamera":[32],"hasDocumentDetected":[32],"cameraInfoWithAutofocus":[32],"currentStatus":[32],"performanceData":[32],"backDocumentTimerRemaining":[32],"getCapturedImages":[64],"isProcessCompleted":[64],"startCapture":[64],"stopCapture":[64],"resetCapture":[64],"skipBackCapture":[64],"getStatus":[64],"preloadModel":[64],"getCameraInfo":[64],"setPreferredCamera":[64],"setCaptureDelay":[64],"getCaptureDelay":[64]}]]]], options);
|
|
19
|
+
return bootstrapLazy([["jaak-stamps",[[1,"jaak-stamps",{"debug":[4],"alignmentTolerance":[2,"alignment-tolerance"],"maskSize":[2,"mask-size"],"cropMargin":[2,"crop-margin"],"useDocumentClassification":[4,"use-document-classification"],"useDocumentDetector":[4,"use-document-detector"],"preferredCamera":[1,"preferred-camera"],"captureDelay":[2,"capture-delay"],"enableBackDocumentTimer":[4,"enable-back-document-timer"],"backDocumentTimerDuration":[2,"back-document-timer-duration"],"detectionBoxes":[32],"sideAlignment":[32],"isMaskReady":[32],"shouldMirrorVideo":[32],"showCameraSelector":[32],"isSwitchingCamera":[32],"hasDocumentDetected":[32],"cameraInfoWithAutofocus":[32],"currentStatus":[32],"performanceData":[32],"backDocumentTimerRemaining":[32],"showManualCaptureButton":[32],"captureStateVersion":[32],"getCapturedImages":[64],"isProcessCompleted":[64],"startCapture":[64],"stopCapture":[64],"resetCapture":[64],"skipBackCapture":[64],"getStatus":[64],"preloadModel":[64],"getCameraInfo":[64],"setPreferredCamera":[64],"setCaptureDelay":[64],"getCaptureDelay":[64]}]]]], options);
|
|
20
20
|
});
|
|
21
21
|
//# sourceMappingURL=jaak-stamps-webcomponent.js.map
|
|
22
22
|
|
|
@@ -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 = class {
|
|
1080
1095
|
constructor(hostRef) {
|
|
@@ -1088,6 +1103,7 @@ const JaakStamps = class {
|
|
|
1088
1103
|
maskSize = 90;
|
|
1089
1104
|
cropMargin = 20;
|
|
1090
1105
|
useDocumentClassification = false;
|
|
1106
|
+
useDocumentDetector = true;
|
|
1091
1107
|
preferredCamera = 'auto';
|
|
1092
1108
|
captureDelay = 1500;
|
|
1093
1109
|
enableBackDocumentTimer = false;
|
|
@@ -1128,6 +1144,8 @@ const JaakStamps = class {
|
|
|
1128
1144
|
detectionRate: 0
|
|
1129
1145
|
};
|
|
1130
1146
|
backDocumentTimerRemaining = 0;
|
|
1147
|
+
showManualCaptureButton = false;
|
|
1148
|
+
captureStateVersion = 0; // Triggers re-render when StateManager changes
|
|
1131
1149
|
// Services
|
|
1132
1150
|
serviceContainer;
|
|
1133
1151
|
eventBus;
|
|
@@ -1191,6 +1209,7 @@ const JaakStamps = class {
|
|
|
1191
1209
|
maskSize: this.maskSize,
|
|
1192
1210
|
cropMargin: this.cropMargin,
|
|
1193
1211
|
useDocumentClassification: this.useDocumentClassification,
|
|
1212
|
+
useDocumentDetector: this.useDocumentDetector,
|
|
1194
1213
|
preferredCamera: this.preferredCamera,
|
|
1195
1214
|
captureDelay: this.captureDelay
|
|
1196
1215
|
};
|
|
@@ -1248,27 +1267,39 @@ const JaakStamps = class {
|
|
|
1248
1267
|
document.head.appendChild(script);
|
|
1249
1268
|
await new Promise((resolve) => {
|
|
1250
1269
|
script.onload = () => {
|
|
1251
|
-
setTimeout(() => {
|
|
1252
|
-
this.finalizeInitialization();
|
|
1270
|
+
setTimeout(async () => {
|
|
1271
|
+
await this.finalizeInitialization();
|
|
1253
1272
|
resolve(undefined);
|
|
1254
1273
|
}, 300);
|
|
1255
1274
|
};
|
|
1256
1275
|
});
|
|
1257
1276
|
}
|
|
1258
1277
|
else {
|
|
1259
|
-
setTimeout(() => {
|
|
1260
|
-
this.finalizeInitialization();
|
|
1278
|
+
setTimeout(async () => {
|
|
1279
|
+
await this.finalizeInitialization();
|
|
1261
1280
|
}, 300);
|
|
1262
1281
|
}
|
|
1263
1282
|
}
|
|
1264
|
-
finalizeInitialization() {
|
|
1283
|
+
async finalizeInitialization() {
|
|
1265
1284
|
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1285
|
+
// Check camera permissions before showing "ready" status
|
|
1286
|
+
const hasPermissions = await this.checkCameraPermissions();
|
|
1287
|
+
if (hasPermissions) {
|
|
1288
|
+
this.currentStatus = {
|
|
1289
|
+
message: 'Listo para capturar',
|
|
1290
|
+
description: '',
|
|
1291
|
+
type: 'ready',
|
|
1292
|
+
isInitialized: true
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
else {
|
|
1296
|
+
this.currentStatus = {
|
|
1297
|
+
message: 'Permisos de cámara requeridos',
|
|
1298
|
+
description: 'Por favor, otorgue permisos de cámara para continuar',
|
|
1299
|
+
type: 'error',
|
|
1300
|
+
isInitialized: true
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1272
1303
|
this.emitReadyEvent();
|
|
1273
1304
|
}
|
|
1274
1305
|
updateStatus(message, description, type = 'loading') {
|
|
@@ -1283,6 +1314,26 @@ const JaakStamps = class {
|
|
|
1283
1314
|
const isDocumentReady = !!window.ort && this.detectionService.isModelLoaded();
|
|
1284
1315
|
this.isReady.emit(isDocumentReady);
|
|
1285
1316
|
}
|
|
1317
|
+
async checkCameraPermissions() {
|
|
1318
|
+
try {
|
|
1319
|
+
if (!navigator.permissions) {
|
|
1320
|
+
// Fallback: try to access camera directly
|
|
1321
|
+
try {
|
|
1322
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
1323
|
+
stream.getTracks().forEach(track => track.stop());
|
|
1324
|
+
return true;
|
|
1325
|
+
}
|
|
1326
|
+
catch {
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
const permission = await navigator.permissions.query({ name: 'camera' });
|
|
1331
|
+
return permission.state === 'granted';
|
|
1332
|
+
}
|
|
1333
|
+
catch (error) {
|
|
1334
|
+
return false;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1286
1337
|
handleStateChange(data) {
|
|
1287
1338
|
}
|
|
1288
1339
|
initializeResizeObserver() {
|
|
@@ -1300,6 +1351,32 @@ const JaakStamps = class {
|
|
|
1300
1351
|
this.updateMaskDimensions(rect);
|
|
1301
1352
|
}
|
|
1302
1353
|
}
|
|
1354
|
+
triggerRerender() {
|
|
1355
|
+
this.captureStateVersion = this.captureStateVersion + 1;
|
|
1356
|
+
}
|
|
1357
|
+
getGuideText(step) {
|
|
1358
|
+
if (step === 'completed') {
|
|
1359
|
+
return 'Proceso completado';
|
|
1360
|
+
}
|
|
1361
|
+
if (step === 'front') {
|
|
1362
|
+
// Para el frente, si el detector manual está activo, mostrar instrucciones de captura manual
|
|
1363
|
+
if (this.showManualCaptureButton) {
|
|
1364
|
+
return 'Posicione el frente de su documento en el marco y presione el botón para capturar';
|
|
1365
|
+
}
|
|
1366
|
+
// Instrucción básica de alineación
|
|
1367
|
+
return 'Alinee su identificación con el marco';
|
|
1368
|
+
}
|
|
1369
|
+
else if (step === 'back') {
|
|
1370
|
+
// Para el reverso, mostrar instrucciones específicas según el modo
|
|
1371
|
+
if (this.useDocumentDetector) {
|
|
1372
|
+
return 'Si tu documento no tiene lado trasero, da clic en el botón para continuar con el proceso';
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
return 'Posicione el reverso de su documento en el marco y capture, o salte este paso';
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return 'Alinee su identificación con el marco';
|
|
1379
|
+
}
|
|
1303
1380
|
updateMaskDimensions(containerRect) {
|
|
1304
1381
|
if (!this.videoRef)
|
|
1305
1382
|
return;
|
|
@@ -1647,7 +1724,12 @@ const JaakStamps = class {
|
|
|
1647
1724
|
this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
|
|
1648
1725
|
const stream = await this.cameraService.setupCamera();
|
|
1649
1726
|
// Paso 4: Inicializar video y ocultar estado inmediatamente
|
|
1650
|
-
this.
|
|
1727
|
+
if (this.useDocumentDetector) {
|
|
1728
|
+
this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
|
|
1729
|
+
}
|
|
1730
|
+
else {
|
|
1731
|
+
this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
|
|
1732
|
+
}
|
|
1651
1733
|
await this.initializeVideoStream(stream);
|
|
1652
1734
|
// Paso 5: Calibrar máscara
|
|
1653
1735
|
if (this.detectionContainer) {
|
|
@@ -1658,6 +1740,10 @@ const JaakStamps = class {
|
|
|
1658
1740
|
// Finalizar e iniciar captura
|
|
1659
1741
|
this.startTime = Date.now();
|
|
1660
1742
|
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1743
|
+
// Mostrar botón manual si el detector está deshabilitado
|
|
1744
|
+
if (!this.useDocumentDetector) {
|
|
1745
|
+
this.showManualCaptureButton = true;
|
|
1746
|
+
}
|
|
1661
1747
|
this.detectFrame();
|
|
1662
1748
|
}
|
|
1663
1749
|
catch (err) {
|
|
@@ -1689,6 +1775,18 @@ const JaakStamps = class {
|
|
|
1689
1775
|
const captureState = this.stateManager.getCaptureState();
|
|
1690
1776
|
if (!this.videoRef || !this.detectionContainer || !this.detectionService.isModelLoaded())
|
|
1691
1777
|
return;
|
|
1778
|
+
// Show manual capture button when document detector is disabled
|
|
1779
|
+
if (!this.useDocumentDetector) {
|
|
1780
|
+
this.showManualCaptureButton = true;
|
|
1781
|
+
// Continue the loop for UI updates but skip detection logic
|
|
1782
|
+
if (captureState.step !== 'completed') {
|
|
1783
|
+
this.animationId = requestAnimationFrame(() => this.detectFrame());
|
|
1784
|
+
}
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
else {
|
|
1788
|
+
this.showManualCaptureButton = false;
|
|
1789
|
+
}
|
|
1692
1790
|
if (captureState.isDetectionPaused) {
|
|
1693
1791
|
if (captureState.step !== 'completed') {
|
|
1694
1792
|
this.animationId = requestAnimationFrame(() => this.detectFrame());
|
|
@@ -1816,7 +1914,7 @@ const JaakStamps = class {
|
|
|
1816
1914
|
isCapturing: false
|
|
1817
1915
|
};
|
|
1818
1916
|
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
1819
|
-
return (h("div", { key: '
|
|
1917
|
+
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: {
|
|
1820
1918
|
position: 'absolute',
|
|
1821
1919
|
left: `${box.x}px`,
|
|
1822
1920
|
top: `${box.y}px`,
|
|
@@ -1825,9 +1923,9 @@ const JaakStamps = class {
|
|
|
1825
1923
|
border: '2px solid #32406C',
|
|
1826
1924
|
pointerEvents: 'none',
|
|
1827
1925
|
boxSizing: 'border-box'
|
|
1828
|
-
} })))), this.isMaskReady && (h("div", { key: '
|
|
1926
|
+
} })))), 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
|
|
1829
1927
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
1830
|
-
: 'Saltar reverso'))), captureState.isVideoActive && (h("div", { key: '
|
|
1928
|
+
: '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" })))));
|
|
1831
1929
|
}
|
|
1832
1930
|
// Utility methods
|
|
1833
1931
|
updateDetectionBoxes(boxes) {
|
|
@@ -1942,6 +2040,115 @@ const JaakStamps = class {
|
|
|
1942
2040
|
}
|
|
1943
2041
|
}
|
|
1944
2042
|
}
|
|
2043
|
+
async takeManualScreenshot() {
|
|
2044
|
+
if (!this.videoRef)
|
|
2045
|
+
return;
|
|
2046
|
+
// When using manual capture, use mask coordinates for cropping
|
|
2047
|
+
await this.takeScreenshotWithMaskCoordinates();
|
|
2048
|
+
}
|
|
2049
|
+
async takeScreenshotWithMaskCoordinates() {
|
|
2050
|
+
if (!this.videoRef || !this.detectionContainer)
|
|
2051
|
+
return;
|
|
2052
|
+
this.stateManager.updateCaptureState({ isCapturing: true });
|
|
2053
|
+
this.triggerCaptureAnimation();
|
|
2054
|
+
// Use pooled canvas for optimization
|
|
2055
|
+
const captureCanvas = this.getPooledCanvas(this.videoRef.videoWidth, this.videoRef.videoHeight);
|
|
2056
|
+
const captureCtx = captureCanvas.getContext('2d', { alpha: false });
|
|
2057
|
+
captureCtx.clearRect(0, 0, captureCanvas.width, captureCanvas.height);
|
|
2058
|
+
captureCtx.drawImage(this.videoRef, 0, 0, captureCanvas.width, captureCanvas.height);
|
|
2059
|
+
// Calculate mask coordinates for cropping
|
|
2060
|
+
const container = this.detectionContainer.parentElement;
|
|
2061
|
+
const containerRect = container.getBoundingClientRect();
|
|
2062
|
+
// Get mask dimensions from CSS properties
|
|
2063
|
+
const maskWidthPercent = parseFloat(this.el.style.getPropertyValue('--mask-width').replace('%', '')) || 90;
|
|
2064
|
+
const maskHeightPercent = parseFloat(this.el.style.getPropertyValue('--mask-height').replace('%', '')) || 56.25;
|
|
2065
|
+
const maskCenterXPercent = parseFloat(this.el.style.getPropertyValue('--mask-center-x').replace('%', '')) || 50;
|
|
2066
|
+
const maskCenterYPercent = parseFloat(this.el.style.getPropertyValue('--mask-center-y').replace('%', '')) || 50;
|
|
2067
|
+
// Convert percentages to actual video coordinates
|
|
2068
|
+
const videoWidth = this.videoRef.videoWidth;
|
|
2069
|
+
const videoHeight = this.videoRef.videoHeight;
|
|
2070
|
+
const videoAspectRatio = videoWidth / videoHeight;
|
|
2071
|
+
const containerAspectRatio = containerRect.width / containerRect.height;
|
|
2072
|
+
let displayedVideoWidth, displayedVideoHeight;
|
|
2073
|
+
let videoOffsetX = 0, videoOffsetY = 0;
|
|
2074
|
+
if (videoAspectRatio > containerAspectRatio) {
|
|
2075
|
+
displayedVideoWidth = containerRect.width;
|
|
2076
|
+
displayedVideoHeight = containerRect.width / videoAspectRatio;
|
|
2077
|
+
videoOffsetY = (containerRect.height - displayedVideoHeight) / 2;
|
|
2078
|
+
}
|
|
2079
|
+
else {
|
|
2080
|
+
displayedVideoHeight = containerRect.height;
|
|
2081
|
+
displayedVideoWidth = containerRect.height * videoAspectRatio;
|
|
2082
|
+
videoOffsetX = (containerRect.width - displayedVideoWidth) / 2;
|
|
2083
|
+
}
|
|
2084
|
+
// Calculate mask coordinates in video space
|
|
2085
|
+
const maskWidthInContainer = (maskWidthPercent / 100) * containerRect.width;
|
|
2086
|
+
const maskHeightInContainer = (maskHeightPercent / 100) * containerRect.height;
|
|
2087
|
+
const maskCenterXInContainer = (maskCenterXPercent / 100) * containerRect.width;
|
|
2088
|
+
const maskCenterYInContainer = (maskCenterYPercent / 100) * containerRect.height;
|
|
2089
|
+
// Convert to video coordinates
|
|
2090
|
+
const scaleX = videoWidth / displayedVideoWidth;
|
|
2091
|
+
const scaleY = videoHeight / displayedVideoHeight;
|
|
2092
|
+
const maskCenterXInVideo = (maskCenterXInContainer - videoOffsetX) * scaleX;
|
|
2093
|
+
const maskCenterYInVideo = (maskCenterYInContainer - videoOffsetY) * scaleY;
|
|
2094
|
+
const maskWidthInVideo = maskWidthInContainer * scaleX;
|
|
2095
|
+
const maskHeightInVideo = maskHeightInContainer * scaleY;
|
|
2096
|
+
// Calculate crop coordinates
|
|
2097
|
+
const cropX = Math.max(0, maskCenterXInVideo - (maskWidthInVideo / 2) - this.cropMargin);
|
|
2098
|
+
const cropY = Math.max(0, maskCenterYInVideo - (maskHeightInVideo / 2) - this.cropMargin);
|
|
2099
|
+
const cropWidth = Math.min(maskWidthInVideo + (2 * this.cropMargin), videoWidth - cropX);
|
|
2100
|
+
const cropHeight = Math.min(maskHeightInVideo + (2 * this.cropMargin), videoHeight - cropY);
|
|
2101
|
+
// Use pooled canvas for cropped version
|
|
2102
|
+
const croppedCanvas = this.getPooledCanvas(cropWidth, cropHeight);
|
|
2103
|
+
const croppedCtx = croppedCanvas.getContext('2d', { alpha: false });
|
|
2104
|
+
croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);
|
|
2105
|
+
croppedCtx.drawImage(this.videoRef, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
|
|
2106
|
+
const captureState = this.stateManager.getCaptureState();
|
|
2107
|
+
if (captureState.step === 'front') {
|
|
2108
|
+
this.stateManager.setCapturedImages({
|
|
2109
|
+
front: {
|
|
2110
|
+
fullFrame: captureCanvas.toDataURL('image/png'),
|
|
2111
|
+
cropped: croppedCanvas.toDataURL('image/png')
|
|
2112
|
+
}
|
|
2113
|
+
});
|
|
2114
|
+
// Check if document classification is enabled (independent of detector)
|
|
2115
|
+
if (this.useDocumentClassification) {
|
|
2116
|
+
const classification = await this.detectionService.classifyDocument(croppedCanvas);
|
|
2117
|
+
if (classification && classification.class === 'passport') {
|
|
2118
|
+
this.completeProcess(true);
|
|
2119
|
+
this.returnCanvasToPool(captureCanvas);
|
|
2120
|
+
this.returnCanvasToPool(croppedCanvas);
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
this.stateManager.updateCaptureState({
|
|
2125
|
+
step: 'back',
|
|
2126
|
+
isDetectionPaused: true,
|
|
2127
|
+
showFlipAnimation: true
|
|
2128
|
+
});
|
|
2129
|
+
this.triggerRerender();
|
|
2130
|
+
setTimeout(() => {
|
|
2131
|
+
this.stateManager.updateCaptureState({
|
|
2132
|
+
showFlipAnimation: false,
|
|
2133
|
+
isDetectionPaused: false
|
|
2134
|
+
});
|
|
2135
|
+
this.triggerRerender();
|
|
2136
|
+
this.startBackDocumentTimer();
|
|
2137
|
+
}, 3000);
|
|
2138
|
+
}
|
|
2139
|
+
else if (captureState.step === 'back') {
|
|
2140
|
+
this.stateManager.setCapturedImages({
|
|
2141
|
+
back: {
|
|
2142
|
+
fullFrame: captureCanvas.toDataURL('image/png'),
|
|
2143
|
+
cropped: croppedCanvas.toDataURL('image/png')
|
|
2144
|
+
}
|
|
2145
|
+
});
|
|
2146
|
+
this.completeProcess(false);
|
|
2147
|
+
}
|
|
2148
|
+
// Return canvases to pool after use
|
|
2149
|
+
this.returnCanvasToPool(captureCanvas);
|
|
2150
|
+
this.returnCanvasToPool(croppedCanvas);
|
|
2151
|
+
}
|
|
1945
2152
|
async takeScreenshot() {
|
|
1946
2153
|
if (!this.videoRef || !this.lastDetectedBox)
|
|
1947
2154
|
return;
|