@jaak.ai/stamps 2.0.0-dev.25 → 2.0.0-dev.27
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 +3 -0
- package/dist/cjs/{index-DGM9-FNg.js → index-BfhtOB0D.js} +5 -2
- package/dist/cjs/index-BfhtOB0D.js.map +1 -0
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +2 -2
- package/dist/cjs/jaak-stamps.cjs.entry.js +431 -23
- 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 +2 -2
- package/dist/collection/components/my-component/my-component.css +252 -0
- package/dist/collection/components/my-component/my-component.js +493 -22
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/components/index.js +3 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/jaak-stamps.js +438 -23
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/{index-DqoVMnc7.js → index-BP1Q4KOg.js} +5 -2
- package/dist/esm/index-BP1Q4KOg.js.map +1 -0
- package/dist/esm/jaak-stamps-webcomponent.js +3 -3
- package/dist/esm/jaak-stamps.entry.js +431 -23
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +3 -3
- 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-BP1Q4KOg.js +3 -0
- package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js.map +1 -0
- package/dist/jaak-stamps-webcomponent/p-eb318f43.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-eb318f43.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +36 -0
- package/dist/types/components.d.ts +10 -0
- package/package.json +1 -1
- package/dist/cjs/index-DGM9-FNg.js.map +0 -1
- package/dist/esm/index-DqoVMnc7.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-DqoVMnc7.js +0 -3
- package/dist/jaak-stamps-webcomponent/p-DqoVMnc7.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-f1172b06.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-f1172b06.entry.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as registerInstance, c as createEvent, a as getElement, h } from './index-
|
|
1
|
+
import { r as registerInstance, c as createEvent, a as getElement, h } from './index-BP1Q4KOg.js';
|
|
2
2
|
|
|
3
|
-
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,canvas{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,canvas.mirror{transform:rotateY(180deg)}canvas{z-index:1}video{z-index:0}.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;box-shadow:0 0 0 9999px rgba(0, 0, 0, 0.7);opacity:0.8}.card-outline.perfect-match{box-shadow:0 0 0 9999px rgba(0, 0, 0, 0.7)}.side{position:absolute;background:#999;transition:background-color 0.3s ease}.side.aligned{background:#28a745}.side-top{top:0;left:0;width:100%;height:3px}.side-right{top:0;right:0;width:3px;height:100%}.side-bottom{bottom:0;left:0;width:100%;height:3px}.side-left{top:0;left:0;width:3px;height:100%}.corner{position:absolute;width:20px;height:20px;border:2px solid #999;z-index:12}.corner-tl{top:-10px;left:-10px;border-right:none;border-bottom:none}.corner-tr{top:-10px;right:-10px;border-left:none;border-bottom:none}.corner-bl{bottom:-10px;left:-10px;border-right:none;border-top:none}.corner-br{bottom:-10px;right:-10px;border-left:none;border-top:none}.corner.perfect-match{border-color:#28a745}.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(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;z-index:20}.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 9999px rgba(0, 0, 0, 0.7), 0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 9999px rgba(0, 0, 0, 0.7), 0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 9999px rgba(0, 0, 0, 0.7), 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-button{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;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:translateX(-50%) translateY(0)}.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))}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.8);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:20px}.loading-spinner{width:60px;height:60px;border:4px solid rgba(255, 255, 255, 0.2);border-top:4px solid #007bff;border-radius:50%;animation:spin 1s linear infinite;margin-bottom:20px}.loading-text{color:white;font-size:16px;font-weight:500;text-align:center;opacity:0.9}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}";
|
|
3
|
+
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,canvas{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,canvas.mirror{transform:rotateY(180deg)}canvas{z-index:1}video{z-index:0}.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;box-shadow:0 0 0 9999px rgba(0, 0, 0, 0.7);opacity:0.8}.card-outline.perfect-match{box-shadow:0 0 0 9999px rgba(0, 0, 0, 0.7)}.side{position:absolute;background:#999;transition:background-color 0.3s ease}.side.aligned{background:#28a745}.side-top{top:0;left:0;width:100%;height:3px}.side-right{top:0;right:0;width:3px;height:100%}.side-bottom{bottom:0;left:0;width:100%;height:3px}.side-left{top:0;left:0;width:3px;height:100%}.corner{position:absolute;width:20px;height:20px;border:2px solid #999;z-index:12}.corner-tl{top:-10px;left:-10px;border-right:none;border-bottom:none}.corner-tr{top:-10px;right:-10px;border-left:none;border-bottom:none}.corner-bl{bottom:-10px;left:-10px;border-right:none;border-top:none}.corner-br{bottom:-10px;right:-10px;border-left:none;border-top:none}.corner.perfect-match{border-color:#28a745}.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(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;z-index:20}.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 9999px rgba(0, 0, 0, 0.7), 0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 9999px rgba(0, 0, 0, 0.7), 0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 9999px rgba(0, 0, 0, 0.7), 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-button{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;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:translateX(-50%) translateY(0)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto}.flip-camera-button{height:40px;padding:0 16px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.6);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{height:40px;padding:0 16px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.6);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}.flip-camera-button:hover,.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.flip-camera-button:active,.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.85);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:12px 16px;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:10px 16px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:space-between;align-items:center;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:8px;font-weight:400}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9}.device-info{padding:8px 16px;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}.flip-camera-button{height:36px;padding:0 12px;font-size:11px;border-radius: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:10px 14px}.camera-option{padding:8px 14px}.device-info{padding:6px 14px}}.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))}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.8);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:20px}.loading-spinner{width:60px;height:60px;border:4px solid rgba(255, 255, 255, 0.2);border-top:4px solid #007bff;border-radius:50%;animation:spin 1s linear infinite;margin-bottom:20px}.loading-text{color:white;font-size:16px;font-weight:500;text-align:center;opacity:0.9}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.status-bar{position:absolute;bottom:10px;left:50%;transform:translateX(-50%);background:rgba(0, 0, 0, 0.7);border-radius:15px;padding:8px 16px;z-index:40;backdrop-filter:blur(5px);border:1px solid rgba(255, 255, 255, 0.1)}.status-message{font-size:12px;font-weight:500;text-align:center;color:#fff;white-space:nowrap;max-width:280px;overflow:hidden;text-overflow:ellipsis}";
|
|
4
4
|
|
|
5
5
|
const JaakStamps = class {
|
|
6
6
|
constructor(hostRef) {
|
|
@@ -14,6 +14,7 @@ const JaakStamps = class {
|
|
|
14
14
|
maskSize = 90; // Porcentaje del video que ocupará la máscara (default 90%)
|
|
15
15
|
cropMargin = 0; // Margen en píxeles para el recorte del documento (default 0)
|
|
16
16
|
useDocumentClassification = false; // Habilita la clasificación automática de documentos para determinar si solicitar reverso
|
|
17
|
+
preferredCamera = 'auto'; // Define qué cámara usar: 'auto' (automático según dispositivo), 'front' (frontal), 'back' (trasera)
|
|
17
18
|
captureCompleted;
|
|
18
19
|
isReady;
|
|
19
20
|
isVideoActive = false;
|
|
@@ -39,6 +40,10 @@ const JaakStamps = class {
|
|
|
39
40
|
isLoading = false;
|
|
40
41
|
isModelPreloaded = false;
|
|
41
42
|
isMaskReady = false;
|
|
43
|
+
availableCameras = [];
|
|
44
|
+
selectedCameraId = null;
|
|
45
|
+
showCameraSelector = false;
|
|
46
|
+
isMultipleCamerasAvailable = false;
|
|
42
47
|
videoRef;
|
|
43
48
|
canvasRef;
|
|
44
49
|
session;
|
|
@@ -49,6 +54,9 @@ const JaakStamps = class {
|
|
|
49
54
|
lastDetectedBox;
|
|
50
55
|
mobileNetSession;
|
|
51
56
|
mobileNetClassMap;
|
|
57
|
+
// Camera management properties
|
|
58
|
+
deviceType = 'desktop';
|
|
59
|
+
preferredCameraFacing = null;
|
|
52
60
|
// Performance optimization properties
|
|
53
61
|
frameSkipCounter = 0;
|
|
54
62
|
FRAME_SKIP = 2; // Process every 3rd frame (reduces CPU by ~66%)
|
|
@@ -85,6 +93,13 @@ const JaakStamps = class {
|
|
|
85
93
|
this.cropMargin = 0;
|
|
86
94
|
}
|
|
87
95
|
}
|
|
96
|
+
validatePreferredCamera() {
|
|
97
|
+
const validOptions = ['auto', 'front', 'back'];
|
|
98
|
+
if (!validOptions.includes(this.preferredCamera)) {
|
|
99
|
+
console.warn(`preferredCamera debe ser uno de: ${validOptions.join(', ')}. Valor actual: ${this.preferredCamera}. Usando valor por defecto: 'auto'`);
|
|
100
|
+
this.preferredCamera = 'auto';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
88
103
|
emitReadyEvent() {
|
|
89
104
|
const isDocumentReady = !!window.ort && this.isModelPreloaded;
|
|
90
105
|
this.isReady.emit(isDocumentReady);
|
|
@@ -97,21 +112,318 @@ const JaakStamps = class {
|
|
|
97
112
|
const settings = videoTrack.getSettings();
|
|
98
113
|
return settings.facingMode === 'environment';
|
|
99
114
|
}
|
|
115
|
+
async detectDeviceTypeAndCameras() {
|
|
116
|
+
// Detect device type
|
|
117
|
+
const userAgent = navigator.userAgent;
|
|
118
|
+
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
|
|
119
|
+
const isTablet = /iPad|Android/i.test(userAgent) && window.innerWidth >= 768;
|
|
120
|
+
if (isTablet) {
|
|
121
|
+
this.deviceType = 'tablet';
|
|
122
|
+
}
|
|
123
|
+
else if (isMobile) {
|
|
124
|
+
this.deviceType = 'mobile';
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.deviceType = 'desktop';
|
|
128
|
+
}
|
|
129
|
+
this.debugLog('📱 Device type detected:', this.deviceType);
|
|
130
|
+
// Enumerate available cameras
|
|
131
|
+
await this.enumerateAndDetectCameras();
|
|
132
|
+
// Load user preference
|
|
133
|
+
this.loadCameraPreference();
|
|
134
|
+
}
|
|
135
|
+
async enumerateAndDetectCameras() {
|
|
136
|
+
try {
|
|
137
|
+
// First, check if we have permission to enumerate devices
|
|
138
|
+
const permissionStatus = await this.checkCameraPermission();
|
|
139
|
+
if (permissionStatus === 'denied') {
|
|
140
|
+
this.debugLog('❌ Camera permission denied');
|
|
141
|
+
this.statusMessage = "Permiso de cámara denegado";
|
|
142
|
+
this.statusColor = "#ff6b6b";
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Request minimal permission to get device labels
|
|
146
|
+
if (permissionStatus === 'prompt') {
|
|
147
|
+
const tempStream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
148
|
+
tempStream.getTracks().forEach(track => track.stop());
|
|
149
|
+
}
|
|
150
|
+
// Now enumerate devices with labels
|
|
151
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
152
|
+
this.availableCameras = devices.filter(device => device.kind === 'videoinput');
|
|
153
|
+
this.isMultipleCamerasAvailable = this.availableCameras.length > 1;
|
|
154
|
+
this.debugLog('📹 Available cameras:', {
|
|
155
|
+
count: this.availableCameras.length,
|
|
156
|
+
isMultipleCamerasAvailable: this.isMultipleCamerasAvailable,
|
|
157
|
+
cameras: this.availableCameras.map(cam => ({
|
|
158
|
+
id: cam.deviceId,
|
|
159
|
+
label: cam.label || 'Unknown Camera'
|
|
160
|
+
}))
|
|
161
|
+
});
|
|
162
|
+
// Set initial camera preference based on device type
|
|
163
|
+
this.setInitialCameraPreference();
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
this.debugLog('❌ Error enumerating cameras:', error);
|
|
167
|
+
this.handleCameraPermissionError(error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async checkCameraPermission() {
|
|
171
|
+
try {
|
|
172
|
+
if (!navigator.permissions) {
|
|
173
|
+
return 'prompt'; // Assume we need to prompt on older browsers
|
|
174
|
+
}
|
|
175
|
+
const permission = await navigator.permissions.query({ name: 'camera' });
|
|
176
|
+
return permission.state;
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
this.debugLog('⚠️ Could not check camera permission:', error);
|
|
180
|
+
return 'prompt';
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
handleCameraPermissionError(error) {
|
|
184
|
+
if (error.name === 'NotAllowedError') {
|
|
185
|
+
this.statusMessage = "Permiso de cámara denegado. Active el permiso en configuración.";
|
|
186
|
+
this.statusColor = "#ff6b6b";
|
|
187
|
+
this.availableCameras = [];
|
|
188
|
+
this.isMultipleCamerasAvailable = false;
|
|
189
|
+
}
|
|
190
|
+
else if (error.name === 'NotFoundError') {
|
|
191
|
+
this.statusMessage = "No se encontraron cámaras disponibles.";
|
|
192
|
+
this.statusColor = "#ff6b6b";
|
|
193
|
+
this.availableCameras = [];
|
|
194
|
+
this.isMultipleCamerasAvailable = false;
|
|
195
|
+
}
|
|
196
|
+
else if (error.name === 'NotReadableError') {
|
|
197
|
+
this.statusMessage = "Cámara en uso por otra aplicación.";
|
|
198
|
+
this.statusColor = "#ff6b6b";
|
|
199
|
+
this.availableCameras = [];
|
|
200
|
+
this.isMultipleCamerasAvailable = false;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
this.statusMessage = "Error al acceder a las cámaras.";
|
|
204
|
+
this.statusColor = "#ff6b6b";
|
|
205
|
+
this.availableCameras = [];
|
|
206
|
+
this.isMultipleCamerasAvailable = false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
setInitialCameraPreference() {
|
|
210
|
+
if (this.availableCameras.length === 0)
|
|
211
|
+
return;
|
|
212
|
+
// Apply user preference for camera selection
|
|
213
|
+
if (this.preferredCamera === 'front') {
|
|
214
|
+
// User explicitly wants front camera
|
|
215
|
+
this.preferredCameraFacing = 'user';
|
|
216
|
+
const frontCamera = this.availableCameras.find(camera => camera.label.toLowerCase().includes('front') ||
|
|
217
|
+
camera.label.toLowerCase().includes('user') ||
|
|
218
|
+
camera.label.toLowerCase().includes('selfie') ||
|
|
219
|
+
!camera.label.toLowerCase().includes('back') && !camera.label.toLowerCase().includes('rear'));
|
|
220
|
+
if (frontCamera) {
|
|
221
|
+
this.selectedCameraId = frontCamera.deviceId;
|
|
222
|
+
this.debugLog('👤 User selected front camera:', frontCamera.label);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
this.selectedCameraId = this.availableCameras[0].deviceId;
|
|
226
|
+
this.debugLog('⚠️ Front camera not found, using first available:', this.availableCameras[0].label);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else if (this.preferredCamera === 'back') {
|
|
230
|
+
// User explicitly wants back camera
|
|
231
|
+
this.preferredCameraFacing = 'environment';
|
|
232
|
+
const backCamera = this.availableCameras.find(camera => camera.label.toLowerCase().includes('back') ||
|
|
233
|
+
camera.label.toLowerCase().includes('rear') ||
|
|
234
|
+
camera.label.toLowerCase().includes('environment'));
|
|
235
|
+
if (backCamera) {
|
|
236
|
+
this.selectedCameraId = backCamera.deviceId;
|
|
237
|
+
this.debugLog('📷 User selected back camera:', backCamera.label);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
this.selectedCameraId = this.availableCameras[0].deviceId;
|
|
241
|
+
this.debugLog('⚠️ Back camera not found, using first available:', this.availableCameras[0].label);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Auto mode - use device type to determine best camera
|
|
246
|
+
if (this.deviceType === 'mobile' || this.deviceType === 'tablet') {
|
|
247
|
+
// For mobile/tablet, prefer rear camera for document scanning
|
|
248
|
+
this.preferredCameraFacing = 'environment';
|
|
249
|
+
const rearCamera = this.availableCameras.find(camera => camera.label.toLowerCase().includes('back') ||
|
|
250
|
+
camera.label.toLowerCase().includes('rear') ||
|
|
251
|
+
camera.label.toLowerCase().includes('environment'));
|
|
252
|
+
if (rearCamera) {
|
|
253
|
+
this.selectedCameraId = rearCamera.deviceId;
|
|
254
|
+
this.debugLog('📱 Auto-selected rear camera for mobile:', rearCamera.label);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
this.selectedCameraId = this.availableCameras[0].deviceId;
|
|
258
|
+
this.debugLog('📱 Rear camera not found, using first available:', this.availableCameras[0].label);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// For desktop, use first available camera (usually the only one)
|
|
263
|
+
this.selectedCameraId = this.availableCameras[0].deviceId;
|
|
264
|
+
this.debugLog('💻 Auto-selected desktop camera:', this.availableCameras[0].label);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
loadCameraPreference() {
|
|
269
|
+
try {
|
|
270
|
+
const saved = localStorage.getItem('jaak-stamps-camera-preference');
|
|
271
|
+
if (saved) {
|
|
272
|
+
const preference = JSON.parse(saved);
|
|
273
|
+
// Validate that the saved camera is still available
|
|
274
|
+
const isStillAvailable = this.availableCameras.some(camera => camera.deviceId === preference.cameraId);
|
|
275
|
+
if (isStillAvailable) {
|
|
276
|
+
this.selectedCameraId = preference.cameraId;
|
|
277
|
+
this.preferredCameraFacing = preference.facing;
|
|
278
|
+
this.debugLog('💾 Loaded camera preference:', preference);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
this.debugLog('⚠️ Error loading camera preference:', error);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
saveCameraPreference() {
|
|
287
|
+
try {
|
|
288
|
+
const preference = {
|
|
289
|
+
cameraId: this.selectedCameraId,
|
|
290
|
+
facing: this.preferredCameraFacing,
|
|
291
|
+
timestamp: Date.now()
|
|
292
|
+
};
|
|
293
|
+
localStorage.setItem('jaak-stamps-camera-preference', JSON.stringify(preference));
|
|
294
|
+
this.debugLog('💾 Saved camera preference:', preference);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
this.debugLog('⚠️ Error saving camera preference:', error);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async switchCamera(cameraId) {
|
|
301
|
+
if (!this.isVideoActive || this.selectedCameraId === cameraId)
|
|
302
|
+
return;
|
|
303
|
+
try {
|
|
304
|
+
// Check if the selected camera is still available
|
|
305
|
+
const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);
|
|
306
|
+
if (!selectedCamera) {
|
|
307
|
+
this.debugLog('❌ Selected camera not found, re-enumerating...');
|
|
308
|
+
await this.enumerateAndDetectCameras();
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
// Show loading state
|
|
312
|
+
if (this.debug) {
|
|
313
|
+
this.statusMessage = "Cambiando cámara...";
|
|
314
|
+
this.statusColor = "#007bff";
|
|
315
|
+
}
|
|
316
|
+
// Stop current stream
|
|
317
|
+
if (this.videoStream) {
|
|
318
|
+
this.videoStream.getTracks().forEach(track => track.stop());
|
|
319
|
+
}
|
|
320
|
+
// Update selected camera
|
|
321
|
+
this.selectedCameraId = cameraId;
|
|
322
|
+
// Update facing mode preference
|
|
323
|
+
const isRearCamera = selectedCamera.label.toLowerCase().includes('back') ||
|
|
324
|
+
selectedCamera.label.toLowerCase().includes('rear') ||
|
|
325
|
+
selectedCamera.label.toLowerCase().includes('environment');
|
|
326
|
+
this.preferredCameraFacing = isRearCamera ? 'environment' : 'user';
|
|
327
|
+
// Save preference
|
|
328
|
+
this.saveCameraPreference();
|
|
329
|
+
// Setup new camera with error handling
|
|
330
|
+
await this.setupCameraWithRetry();
|
|
331
|
+
this.debugLog('🔄 Switched to camera:', selectedCamera.label);
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
this.debugLog('❌ Error switching camera:', error);
|
|
335
|
+
this.handleCameraPermissionError(error);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async setupCameraWithRetry(maxRetries = 3) {
|
|
339
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
340
|
+
try {
|
|
341
|
+
await this.setupCamera();
|
|
342
|
+
if (this.debug) {
|
|
343
|
+
this.statusMessage = "Cámara configurada correctamente";
|
|
344
|
+
this.statusColor = "#28a745";
|
|
345
|
+
}
|
|
346
|
+
return; // Success
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
this.debugLog(`❌ Camera setup attempt ${attempt} failed:`, error);
|
|
350
|
+
if (attempt === maxRetries) {
|
|
351
|
+
// Last attempt failed, handle the error
|
|
352
|
+
this.statusMessage = "Error al configurar la cámara";
|
|
353
|
+
this.statusColor = "#ff6b6b";
|
|
354
|
+
this.handleCameraPermissionError(error);
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
357
|
+
// Update status for retry attempt
|
|
358
|
+
if (this.debug) {
|
|
359
|
+
this.statusMessage = `Reintentando configuración de cámara... (${attempt}/${maxRetries})`;
|
|
360
|
+
this.statusColor = "#ffb366";
|
|
361
|
+
}
|
|
362
|
+
// Wait before retrying
|
|
363
|
+
await new Promise(resolve => setTimeout(resolve, 500 * attempt));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
toggleCameraSelector() {
|
|
368
|
+
this.showCameraSelector = !this.showCameraSelector;
|
|
369
|
+
this.debugLog('📹 Camera selector toggled:', {
|
|
370
|
+
showCameraSelector: this.showCameraSelector,
|
|
371
|
+
isMultipleCamerasAvailable: this.isMultipleCamerasAvailable,
|
|
372
|
+
availableCameras: this.availableCameras.length,
|
|
373
|
+
isVideoActive: this.isVideoActive
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
async flipCamera() {
|
|
377
|
+
if (!this.isMultipleCamerasAvailable)
|
|
378
|
+
return;
|
|
379
|
+
const currentIndex = this.availableCameras.findIndex(camera => camera.deviceId === this.selectedCameraId);
|
|
380
|
+
const nextIndex = (currentIndex + 1) % this.availableCameras.length;
|
|
381
|
+
const nextCamera = this.availableCameras[nextIndex];
|
|
382
|
+
await this.switchCamera(nextCamera.deviceId);
|
|
383
|
+
}
|
|
100
384
|
async componentDidLoad() {
|
|
385
|
+
if (this.debug) {
|
|
386
|
+
// Show detailed initialization loading state only in debug mode
|
|
387
|
+
this.isLoading = true;
|
|
388
|
+
this.statusMessage = 'Inicializando componente...';
|
|
389
|
+
this.statusColor = '#007bff';
|
|
390
|
+
// Small delay to ensure initialization message is visible
|
|
391
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
392
|
+
}
|
|
101
393
|
this.validateMaskSize();
|
|
102
394
|
this.validateCropMargin();
|
|
395
|
+
this.validatePreferredCamera();
|
|
396
|
+
if (this.debug) {
|
|
397
|
+
// Update status for camera detection
|
|
398
|
+
this.statusMessage = 'Detectando cámaras disponibles...';
|
|
399
|
+
}
|
|
400
|
+
await this.detectDeviceTypeAndCameras();
|
|
103
401
|
if (!window.ort) {
|
|
402
|
+
if (this.debug) {
|
|
403
|
+
// Update status for ONNX runtime loading
|
|
404
|
+
this.statusMessage = 'Cargando librerías de IA...';
|
|
405
|
+
}
|
|
104
406
|
const script = document.createElement('script');
|
|
105
407
|
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
|
|
106
408
|
document.head.appendChild(script);
|
|
107
409
|
// Wait for ONNX runtime to load before emitting ready event
|
|
108
410
|
script.onload = () => {
|
|
109
|
-
|
|
411
|
+
setTimeout(() => {
|
|
412
|
+
this.statusMessage = 'Presione el botón para activar la cámara';
|
|
413
|
+
this.statusColor = '#aaa';
|
|
414
|
+
this.isLoading = false;
|
|
415
|
+
this.emitReadyEvent();
|
|
416
|
+
}, 300);
|
|
110
417
|
};
|
|
111
418
|
}
|
|
112
419
|
else {
|
|
113
420
|
// ONNX runtime already loaded
|
|
114
|
-
|
|
421
|
+
setTimeout(() => {
|
|
422
|
+
this.statusMessage = 'Presione el botón para activar la cámara';
|
|
423
|
+
this.statusColor = '#aaa';
|
|
424
|
+
this.isLoading = false;
|
|
425
|
+
this.emitReadyEvent();
|
|
426
|
+
}, 300);
|
|
115
427
|
}
|
|
116
428
|
// Initialize canvas pool for better performance
|
|
117
429
|
this.initializeCanvasPool();
|
|
@@ -301,6 +613,35 @@ const JaakStamps = class {
|
|
|
301
613
|
this.completeProcess(true);
|
|
302
614
|
}
|
|
303
615
|
}
|
|
616
|
+
async getCameraInfo() {
|
|
617
|
+
return {
|
|
618
|
+
availableCameras: this.availableCameras.map(camera => ({
|
|
619
|
+
id: camera.deviceId,
|
|
620
|
+
label: camera.label || 'Unknown Camera',
|
|
621
|
+
selected: camera.deviceId === this.selectedCameraId
|
|
622
|
+
})),
|
|
623
|
+
selectedCameraId: this.selectedCameraId,
|
|
624
|
+
deviceType: this.deviceType,
|
|
625
|
+
isMultipleCamerasAvailable: this.isMultipleCamerasAvailable,
|
|
626
|
+
preferredFacing: this.preferredCameraFacing,
|
|
627
|
+
userPreferredCamera: this.preferredCamera
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
async setPreferredCamera(camera) {
|
|
631
|
+
this.preferredCamera = camera;
|
|
632
|
+
this.debugLog('🎯 Camera preference changed to:', camera);
|
|
633
|
+
// Re-detect and apply new camera preference
|
|
634
|
+
await this.enumerateAndDetectCameras();
|
|
635
|
+
// If video is active, switch to the new preferred camera
|
|
636
|
+
if (this.isVideoActive && this.selectedCameraId) {
|
|
637
|
+
await this.switchCamera(this.selectedCameraId);
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
success: true,
|
|
641
|
+
selectedCamera: this.selectedCameraId,
|
|
642
|
+
availableCameras: this.availableCameras.length
|
|
643
|
+
};
|
|
644
|
+
}
|
|
304
645
|
async loadMobileNetModel() {
|
|
305
646
|
try {
|
|
306
647
|
this.debugLog('🤖 Loading MobileNet model...');
|
|
@@ -417,18 +758,37 @@ const JaakStamps = class {
|
|
|
417
758
|
}
|
|
418
759
|
async getMaxResolution() {
|
|
419
760
|
try {
|
|
420
|
-
//
|
|
761
|
+
// Build constraints with selected camera if available
|
|
762
|
+
const videoConstraints = {};
|
|
763
|
+
if (this.selectedCameraId) {
|
|
764
|
+
videoConstraints.deviceId = { exact: this.selectedCameraId };
|
|
765
|
+
}
|
|
766
|
+
else if (this.preferredCameraFacing) {
|
|
767
|
+
videoConstraints.facingMode = this.preferredCameraFacing;
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
videoConstraints.facingMode = "environment";
|
|
771
|
+
}
|
|
772
|
+
// Get temporary stream to access capabilities
|
|
421
773
|
const tempStream = await navigator.mediaDevices.getUserMedia({
|
|
422
|
-
video:
|
|
774
|
+
video: videoConstraints
|
|
423
775
|
});
|
|
424
776
|
const videoTrack = tempStream.getVideoTracks()[0];
|
|
425
777
|
const capabilities = videoTrack.getCapabilities();
|
|
426
778
|
// Detener el stream temporal
|
|
427
779
|
tempStream.getTracks().forEach(track => track.stop());
|
|
428
|
-
//
|
|
429
|
-
const constraints = {
|
|
430
|
-
|
|
431
|
-
|
|
780
|
+
// Build constraints with resolution optimized for tablets
|
|
781
|
+
const constraints = {};
|
|
782
|
+
// Set camera selection constraints
|
|
783
|
+
if (this.selectedCameraId) {
|
|
784
|
+
constraints.deviceId = { exact: this.selectedCameraId };
|
|
785
|
+
}
|
|
786
|
+
else if (this.preferredCameraFacing) {
|
|
787
|
+
constraints.facingMode = this.preferredCameraFacing;
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
constraints.facingMode = "environment";
|
|
791
|
+
}
|
|
432
792
|
if (capabilities.width && capabilities.height) {
|
|
433
793
|
// Limitar resolución máxima para tablets para evitar problemas de rendimiento
|
|
434
794
|
const maxWidth = Math.min(capabilities.width.max, 1920);
|
|
@@ -455,13 +815,23 @@ const JaakStamps = class {
|
|
|
455
815
|
}
|
|
456
816
|
catch (err) {
|
|
457
817
|
this.debugLog('⚠️ Could not get capabilities, using fallback');
|
|
458
|
-
//
|
|
818
|
+
// Optimized fallback for tablets
|
|
459
819
|
const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
|
|
460
|
-
|
|
461
|
-
facingMode: "environment",
|
|
820
|
+
const fallbackConstraints = {
|
|
462
821
|
width: { ideal: isTablet ? 1280 : 1920 },
|
|
463
822
|
height: { ideal: isTablet ? 720 : 1080 }
|
|
464
823
|
};
|
|
824
|
+
// Add camera selection to fallback
|
|
825
|
+
if (this.selectedCameraId) {
|
|
826
|
+
fallbackConstraints.deviceId = { exact: this.selectedCameraId };
|
|
827
|
+
}
|
|
828
|
+
else if (this.preferredCameraFacing) {
|
|
829
|
+
fallbackConstraints.facingMode = this.preferredCameraFacing;
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
fallbackConstraints.facingMode = "environment";
|
|
833
|
+
}
|
|
834
|
+
return fallbackConstraints;
|
|
465
835
|
}
|
|
466
836
|
}
|
|
467
837
|
async setupCamera() {
|
|
@@ -496,8 +866,7 @@ const JaakStamps = class {
|
|
|
496
866
|
}
|
|
497
867
|
catch (err) {
|
|
498
868
|
this.debugLog("❌ No se pudo acceder a la cámara:", err);
|
|
499
|
-
this.
|
|
500
|
-
this.statusColor = "#ff6b6b";
|
|
869
|
+
this.handleCameraPermissionError(err);
|
|
501
870
|
}
|
|
502
871
|
}
|
|
503
872
|
preprocess(video) {
|
|
@@ -542,28 +911,48 @@ const JaakStamps = class {
|
|
|
542
911
|
// Check if model is already preloaded
|
|
543
912
|
if (!this.session) {
|
|
544
913
|
this.isLoading = true;
|
|
545
|
-
this.
|
|
546
|
-
|
|
914
|
+
if (this.debug) {
|
|
915
|
+
this.statusMessage = "Cargando modelo de detección...";
|
|
916
|
+
this.statusColor = "#007bff";
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
this.statusMessage = "Cargando modelos...";
|
|
920
|
+
this.statusColor = "#007bff";
|
|
921
|
+
}
|
|
547
922
|
const modelPath = this.MODEL_PATH;
|
|
548
923
|
this.debugLog('🤖 Loading detection model:', modelPath);
|
|
549
924
|
const sessionOptions = this.getSessionOptions();
|
|
550
925
|
this.session = await window.ort.InferenceSession.create(modelPath, sessionOptions);
|
|
551
926
|
// Load MobileNet model if classification is enabled and not already loaded
|
|
552
927
|
if (this.useDocumentClassification && !this.mobileNetSession) {
|
|
928
|
+
if (this.debug) {
|
|
929
|
+
this.statusMessage = "Cargando modelo de clasificación...";
|
|
930
|
+
}
|
|
553
931
|
await this.loadMobileNetModel();
|
|
554
932
|
}
|
|
555
933
|
this.isModelPreloaded = true;
|
|
934
|
+
if (this.debug) {
|
|
935
|
+
this.statusMessage = "Modelos cargados exitosamente";
|
|
936
|
+
}
|
|
556
937
|
this.emitReadyEvent();
|
|
557
938
|
}
|
|
558
939
|
else {
|
|
559
940
|
this.debugLog('🚀 Using preloaded models');
|
|
560
|
-
this.
|
|
561
|
-
|
|
941
|
+
if (this.debug) {
|
|
942
|
+
this.statusMessage = "Usando modelos precargados...";
|
|
943
|
+
this.statusColor = "#007bff";
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (this.debug) {
|
|
947
|
+
this.statusMessage = "Configurando cámara...";
|
|
562
948
|
}
|
|
563
|
-
this.statusMessage = "Configurando cámara...";
|
|
564
949
|
await this.setupCamera();
|
|
565
950
|
this.startTime = Date.now();
|
|
566
951
|
this.isLoading = false;
|
|
952
|
+
if (this.debug) {
|
|
953
|
+
this.statusMessage = "Detección activa - Busque su identificación";
|
|
954
|
+
this.statusColor = "#28a745";
|
|
955
|
+
}
|
|
567
956
|
this.detectFrame();
|
|
568
957
|
}
|
|
569
958
|
catch (err) {
|
|
@@ -1035,8 +1424,14 @@ const JaakStamps = class {
|
|
|
1035
1424
|
this.videoStream.getTracks().forEach(track => track.stop());
|
|
1036
1425
|
this.videoStream = undefined;
|
|
1037
1426
|
this.isVideoActive = false;
|
|
1038
|
-
this.
|
|
1039
|
-
|
|
1427
|
+
if (this.debug) {
|
|
1428
|
+
this.statusMessage = "Cámara desactivada - Listo para nueva sesión";
|
|
1429
|
+
this.statusColor = "#6c757d";
|
|
1430
|
+
}
|
|
1431
|
+
else {
|
|
1432
|
+
this.statusMessage = "Presione el botón para activar la cámara";
|
|
1433
|
+
this.statusColor = "#aaa";
|
|
1434
|
+
}
|
|
1040
1435
|
}
|
|
1041
1436
|
this.isLoading = false;
|
|
1042
1437
|
// Limpiar canvas al finalizar sesión
|
|
@@ -1047,7 +1442,20 @@ const JaakStamps = class {
|
|
|
1047
1442
|
this.cleanup();
|
|
1048
1443
|
}
|
|
1049
1444
|
render() {
|
|
1050
|
-
return (h("div", { key: '
|
|
1445
|
+
return (h("div", { key: '86ff6966e69804bea4faef5a9201480bd7ef5b9a', class: "detector-container" }, h("div", { key: 'aaed4a078d4eff98674f2163a56a1596782612bd', class: "video-container" }, h("video", { key: '6ae6a7c626c9b2b6e6915c826517d1365205b3a0', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: this.isVideoActive ? 'block' : 'none' } }), h("canvas", { key: 'c8666e4b4814e633155be738e23ada098fb3be93', ref: el => this.canvasRef = el, class: this.shouldMirrorVideo ? 'mirror' : '' }), this.isMaskReady && (h("div", { key: '968bcd21315afcf054f881647723299e7e57c29b', class: "overlay-mask" }, h("div", { key: '77f8a331f3d9e19b35c11b6c23b06420cbab9fd5', class: "card-outline" }, h("div", { key: '9d0164e7b2dcf6392471151c236127cd57507512', class: "side side-top" }), h("div", { key: '05a003eb49d25c72644aea2511b6519c5241ffb4', class: "side side-right" }), h("div", { key: 'a561ae9a94eb78aacfc24bac717164b3e22f41e5', class: "side side-bottom" }), h("div", { key: '81d64e5b58032c14062fc8efdee417db7f8a9f12', class: "side side-left" }), h("div", { key: '03c02e2c4c73c525965e34c7c47bb74f769d577f', class: "corner corner-tl" }), h("div", { key: '84780aec021ec4e0c7d38f6a659be765c4604ed5', class: "corner corner-tr" }), h("div", { key: '1ea67789c5a38cbc790f911f8f06b55c866d3927', class: "corner corner-bl" }), h("div", { key: 'e0e1999b0c5c37451850203eed4dbc2ff5834261', class: "corner corner-br" }), !this.showFlipAnimation && !this.showSuccessAnimation && (h("div", { key: '4909eb28d62b1674436c148ef46cd05a35ff8ec8', class: "guide-text" }, this.statusMessage))), this.captureStep === 'back' && !this.showFlipAnimation && !this.showSuccessAnimation && (h("button", { key: '5a971c2121cf9962719ad89fc45530b8acccb550', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, "Saltar reverso")), this.isVideoActive && (h("div", { key: '109e5885c49212f89d5678d9c9716ebd48614126', class: "camera-controls" }, this.isMultipleCamerasAvailable && (h("button", { key: '7c8ce2cd775c0646c80378cd0ad1f70243443ddf', class: "flip-camera-button", onClick: () => this.flipCamera(), type: "button", title: "Cambiar c\u00E1mara" }, "Girar c\u00E1mara")), h("button", { key: '5d3d066ba7e317fdc33878eeb9f2f7d28c6f65dc', class: "camera-selector-button", onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara" }, "C\u00E1maras"), this.debug && (h("div", { key: '4cbc284f2efbcb90ef91ea2e064bf0aa1e7980af', style: {
|
|
1446
|
+
position: 'absolute',
|
|
1447
|
+
top: '50px',
|
|
1448
|
+
right: '0',
|
|
1449
|
+
background: 'rgba(0,0,0,0.8)',
|
|
1450
|
+
color: 'white',
|
|
1451
|
+
padding: '8px',
|
|
1452
|
+
fontSize: '10px',
|
|
1453
|
+
borderRadius: '4px',
|
|
1454
|
+
whiteSpace: 'nowrap'
|
|
1455
|
+
} }, "C\u00E1maras: ", this.availableCameras.length, h("br", { key: 'df214515289add2cc73f989bfacb59069334a0e6' }), "M\u00FAltiples: ", this.isMultipleCamerasAvailable ? 'Sí' : 'No', h("br", { key: 'aa2b1784781b5b45d24139e2b6cc4865d9f3a00c' }), "Selector: ", this.showCameraSelector ? 'Visible' : 'Oculto', h("br", { key: 'fb95b2b58c9fb89f632d85a9271b7788d45a66c0' }), "Video: ", this.isVideoActive ? 'Activo' : 'Inactivo')))), this.showCameraSelector && this.availableCameras.length > 0 && (h("div", { key: 'ec1c90c5a6c84e3ef75724e7e600a60714545608', class: "camera-selector-dropdown" }, h("div", { key: 'ea118817b6563c78b7fbf5ee66af2e38a441a8b7', class: "camera-selector-header" }, h("span", { key: '52603259d50ffb7aa6ea5753762f0f0d018b39ff' }, "Seleccionar C\u00E1mara"), h("button", { key: '422ae0643f5d1451cf539b3a77becbf44904a484', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'd5acef7ba0b1645c52fb3ea7b3c64e70fa187a5f', class: "camera-list" }, this.availableCameras.map((camera) => (h("button", { key: camera.deviceId, class: `camera-option ${this.selectedCameraId === camera.deviceId ? 'selected' : ''}`, onClick: () => {
|
|
1456
|
+
this.switchCamera(camera.deviceId);
|
|
1457
|
+
this.toggleCameraSelector();
|
|
1458
|
+
}, type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${this.availableCameras.indexOf(camera) + 1}`), this.selectedCameraId === camera.deviceId && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: 'b670e7bf441559bf156874dbe3bbf83dbff2fa0d', class: "device-info" }, h("small", { key: '4d333925c1ff4197b6009414ed694b0a0eb06159' }, "Dispositivo: ", this.deviceType)))))), this.isCapturing && (h("div", { key: '21f7ea0e50575226daba8607ab4bccc5029aa179', class: "capture-animation" })), this.showFlipAnimation && (h("div", { key: 'c2350cc43d3a119391f62f2cf771ed5afcdb3558', class: "flip-animation" }, h("div", { key: 'a91de9e8662a4750555bce2a879b89f9216b8156', class: "id-card-icon" }), h("div", { key: 'def1c2534e1aae27e262deb6fd26a73ff0c8cb9b', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), this.showSuccessAnimation && (h("div", { key: 'acbbfb6d07e4f6bf343ff18af2d55979e76db3d4', class: "success-animation" }, h("div", { key: 'f5acd84325226e6b1ede1180335d7495a6b2d95e', class: "check-icon" }), h("div", { key: '393fd8300ce48e59bd3ef94f0e895abc3a938c1f', class: "success-text" }, "\u00A1Proceso completado!"))), this.isLoading && (h("div", { key: 'ce0d1baebab8bf0e18531a26b549f38e349a0d72', class: "loading-overlay" }, h("div", { key: '14c76fa00ddd814f37bd9c6dc57f761119886566', class: "loading-spinner" }), h("div", { key: '87ccb288f927e61ec126d83f44061699810c9e06', class: "loading-text" }, this.statusMessage))), this.debug && (h("div", { key: '943dff6e2772f2595a95116e8943866f97cada6e', class: "status-bar" }, h("div", { key: '00fe53b71ef94d51636259f106e4518df910848d', class: "status-message", style: { 'color': this.statusColor } }, this.statusMessage))), h("div", { key: '4c3bd150006473d9540fb1418f11f45fb96d1e56', class: "watermark" }, h("img", { key: 'c634701f52a4422c7be5b4007bf34ba4aeb4ebcc', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
1051
1459
|
}
|
|
1052
1460
|
};
|
|
1053
1461
|
JaakStamps.style = myComponentCss;
|