@jaak.ai/stamps 2.1.0-dev.8 → 2.1.0
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/dist/cjs/app-globals-V2Kpy_OQ.js +8 -0
- package/dist/cjs/app-globals-V2Kpy_OQ.js.map +1 -0
- package/dist/cjs/{index-BfhtOB0D.js → index-Ga0t6BMe.js} +176 -119
- package/dist/cjs/index-Ga0t6BMe.js.map +1 -0
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +5 -4
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js.map +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +144 -409
- 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 +4 -3
- package/dist/cjs/loader.cjs.js.map +1 -1
- package/dist/collection/collection-manifest.json +1 -1
- package/dist/collection/components/my-component/my-component.css +1 -1
- package/dist/collection/components/my-component/my-component.js +53 -295
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/CameraService.js +84 -2
- package/dist/collection/services/CameraService.js.map +1 -1
- package/dist/collection/services/DetectionService.js +7 -146
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
- package/dist/components/index.js +165 -113
- package/dist/components/index.js.map +1 -1
- package/dist/components/jaak-stamps.js +145 -412
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/app-globals-DQuL1Twl.js +6 -0
- package/dist/esm/app-globals-DQuL1Twl.js.map +1 -0
- package/dist/esm/{index-BP1Q4KOg.js → index-Drbzcuq-.js} +177 -119
- package/dist/esm/index-Drbzcuq-.js.map +1 -0
- package/dist/esm/jaak-stamps-webcomponent.js +5 -4
- package/dist/esm/jaak-stamps-webcomponent.js.map +1 -1
- package/dist/esm/jaak-stamps.entry.js +144 -409
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +4 -3
- package/dist/esm/loader.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/loader.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-DQuL1Twl.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-DQuL1Twl.js.map +1 -0
- package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js +3 -0
- package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js.map +1 -0
- package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +1 -27
- package/dist/types/components.d.ts +1 -3
- package/dist/types/services/CameraService.d.ts +1 -0
- package/dist/types/services/DetectionService.d.ts +1 -9
- package/dist/types/services/interfaces/IDetectionService.d.ts +0 -6
- package/dist/types/stencil-public-runtime.d.ts +12 -5
- package/package.json +3 -1
- package/dist/cjs/index-BfhtOB0D.js.map +0 -1
- package/dist/esm/index-BP1Q4KOg.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-8e25497e.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-8e25497e.entry.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js +0 -3
- package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as registerInstance, c as createEvent,
|
|
1
|
+
import { r as registerInstance, c as createEvent, g as getElement, h } from './index-Drbzcuq-.js';
|
|
2
2
|
|
|
3
3
|
class EventBusService {
|
|
4
4
|
events = new Map();
|
|
@@ -204,7 +204,21 @@ class CameraService {
|
|
|
204
204
|
tempStream.getTracks().forEach(track => track.stop());
|
|
205
205
|
}
|
|
206
206
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
207
|
-
|
|
207
|
+
const allCameras = devices.filter(device => device.kind === 'videoinput');
|
|
208
|
+
// Filter out virtual cameras
|
|
209
|
+
this.availableCameras = allCameras.filter(camera => {
|
|
210
|
+
const isVirtual = this.isVirtualCamera(camera);
|
|
211
|
+
if (isVirtual) {
|
|
212
|
+
console.log(`Virtual camera detected and filtered: ${camera.label}`);
|
|
213
|
+
}
|
|
214
|
+
return !isVirtual;
|
|
215
|
+
});
|
|
216
|
+
// Check if there are physical cameras available
|
|
217
|
+
if (this.availableCameras.length === 0) {
|
|
218
|
+
console.log('No physical cameras available');
|
|
219
|
+
this.eventBus.emit('error', new Error('Cámaras virtuales no están permitidas. Use una cámara física.'));
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
208
222
|
// Cache the results for optimization
|
|
209
223
|
CameraService.deviceEnumerationCache = {
|
|
210
224
|
devices: this.availableCameras,
|
|
@@ -254,6 +268,21 @@ class CameraService {
|
|
|
254
268
|
});
|
|
255
269
|
this.lastStreamConstraints = finalConstraints;
|
|
256
270
|
const stream = await this.currentStreamPromise;
|
|
271
|
+
// Validate that active stream is not from a virtual camera
|
|
272
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
273
|
+
if (videoTrack) {
|
|
274
|
+
const settings = videoTrack.getSettings();
|
|
275
|
+
const deviceId = settings.deviceId;
|
|
276
|
+
if (deviceId) {
|
|
277
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
278
|
+
const currentDevice = devices.find(device => device.deviceId === deviceId);
|
|
279
|
+
if (currentDevice && this.isVirtualCamera(currentDevice)) {
|
|
280
|
+
console.log(`Virtual camera detected in active stream: ${currentDevice.label}`);
|
|
281
|
+
stream.getTracks().forEach(track => track.stop());
|
|
282
|
+
throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
257
286
|
return stream;
|
|
258
287
|
}
|
|
259
288
|
catch (error) {
|
|
@@ -271,7 +300,23 @@ class CameraService {
|
|
|
271
300
|
audio: false
|
|
272
301
|
});
|
|
273
302
|
this.lastStreamConstraints = basicConstraints;
|
|
274
|
-
|
|
303
|
+
const stream = await this.currentStreamPromise;
|
|
304
|
+
// Validate fallback stream as well
|
|
305
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
306
|
+
if (videoTrack) {
|
|
307
|
+
const settings = videoTrack.getSettings();
|
|
308
|
+
const deviceId = settings.deviceId;
|
|
309
|
+
if (deviceId) {
|
|
310
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
311
|
+
const currentDevice = devices.find(device => device.deviceId === deviceId);
|
|
312
|
+
if (currentDevice && this.isVirtualCamera(currentDevice)) {
|
|
313
|
+
console.log(`Virtual camera detected in fallback stream: ${currentDevice.label}`);
|
|
314
|
+
stream.getTracks().forEach(track => track.stop());
|
|
315
|
+
throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return stream;
|
|
275
320
|
}
|
|
276
321
|
}
|
|
277
322
|
async switchCamera(cameraId) {
|
|
@@ -280,6 +325,12 @@ class CameraService {
|
|
|
280
325
|
await this.enumerateDevices();
|
|
281
326
|
return;
|
|
282
327
|
}
|
|
328
|
+
// Validate that the target camera is not virtual
|
|
329
|
+
if (this.isVirtualCamera(selectedCamera)) {
|
|
330
|
+
console.log(`Attempted to switch to virtual camera: ${selectedCamera.label}`);
|
|
331
|
+
this.eventBus.emit('error', new Error('No se puede cambiar a cámara virtual'));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
283
334
|
await this.setSelectedCamera(cameraId);
|
|
284
335
|
}
|
|
285
336
|
isRearCamera(stream) {
|
|
@@ -551,6 +602,37 @@ class CameraService {
|
|
|
551
602
|
invalidateDeviceCache() {
|
|
552
603
|
CameraService.deviceEnumerationCache = null;
|
|
553
604
|
}
|
|
605
|
+
isVirtualCamera(device) {
|
|
606
|
+
const label = device.label.toLowerCase();
|
|
607
|
+
const virtualCameraIndicators = [
|
|
608
|
+
'obs',
|
|
609
|
+
'virtual',
|
|
610
|
+
'snap camera',
|
|
611
|
+
'manycam',
|
|
612
|
+
'xsplit',
|
|
613
|
+
'camtwist',
|
|
614
|
+
'ecamm',
|
|
615
|
+
'nvidia broadcast',
|
|
616
|
+
'droidcam',
|
|
617
|
+
'iriun',
|
|
618
|
+
'elgato',
|
|
619
|
+
'streamlabs',
|
|
620
|
+
'wirecast',
|
|
621
|
+
'zoom virtual',
|
|
622
|
+
'teams virtual',
|
|
623
|
+
'mmhmm',
|
|
624
|
+
'camo',
|
|
625
|
+
'reincubate camo',
|
|
626
|
+
'webcamoid',
|
|
627
|
+
'splitcam',
|
|
628
|
+
'cheese',
|
|
629
|
+
'guvcview',
|
|
630
|
+
'yawcam',
|
|
631
|
+
'webcam 7',
|
|
632
|
+
'altserver'
|
|
633
|
+
];
|
|
634
|
+
return virtualCameraIndicators.some(indicator => label.includes(indicator));
|
|
635
|
+
}
|
|
554
636
|
}
|
|
555
637
|
|
|
556
638
|
class LowMemoryDeviceStrategy {
|
|
@@ -648,10 +730,6 @@ class DetectionService {
|
|
|
648
730
|
// Static canvas pool for reuse across instances
|
|
649
731
|
static canvasPool = new Map();
|
|
650
732
|
static MAX_POOL_SIZE = 5;
|
|
651
|
-
// Resource management for optimization
|
|
652
|
-
modelLoadController;
|
|
653
|
-
classificationLoadController;
|
|
654
|
-
modelBlobUrls = new Set();
|
|
655
733
|
constructor(debug = false, useDocumentClassification = false, useDocumentDetector = true) {
|
|
656
734
|
this.debug = debug;
|
|
657
735
|
this.useDocumentClassification = useDocumentClassification;
|
|
@@ -669,26 +747,11 @@ class DetectionService {
|
|
|
669
747
|
return;
|
|
670
748
|
}
|
|
671
749
|
try {
|
|
672
|
-
// Create AbortController for this load operation
|
|
673
|
-
this.modelLoadController = new AbortController();
|
|
674
750
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
675
751
|
try {
|
|
676
|
-
|
|
677
|
-
const response = await fetch(this.MODEL_PATH, {
|
|
678
|
-
signal: this.modelLoadController.signal
|
|
679
|
-
});
|
|
680
|
-
if (!response.ok) {
|
|
681
|
-
throw new Error(`Failed to fetch model: ${response.status}`);
|
|
682
|
-
}
|
|
683
|
-
const modelBuffer = await response.arrayBuffer();
|
|
684
|
-
this.session = await window.ort.InferenceSession.create(modelBuffer, sessionOptions);
|
|
752
|
+
this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, sessionOptions);
|
|
685
753
|
}
|
|
686
754
|
catch (error) {
|
|
687
|
-
// Handle abort signal
|
|
688
|
-
if (error.name === 'AbortError') {
|
|
689
|
-
console.log('Model loading was cancelled');
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
755
|
// Múltiples tipos de errores de memoria que pueden ocurrir
|
|
693
756
|
const isMemoryError = error.message.includes('failed to allocate a buffer') ||
|
|
694
757
|
error.message.includes('Out of memory') ||
|
|
@@ -710,15 +773,7 @@ class DetectionService {
|
|
|
710
773
|
sessionLogVerbosityLevel: 0,
|
|
711
774
|
};
|
|
712
775
|
try {
|
|
713
|
-
|
|
714
|
-
const fallbackResponse = await fetch(this.MODEL_PATH, {
|
|
715
|
-
signal: this.modelLoadController.signal
|
|
716
|
-
});
|
|
717
|
-
if (!fallbackResponse.ok) {
|
|
718
|
-
throw new Error(`Failed to fetch model with fallback: ${fallbackResponse.status}`);
|
|
719
|
-
}
|
|
720
|
-
const fallbackBuffer = await fallbackResponse.arrayBuffer();
|
|
721
|
-
this.session = await window.ort.InferenceSession.create(fallbackBuffer, fallbackOptions);
|
|
776
|
+
this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, fallbackOptions);
|
|
722
777
|
}
|
|
723
778
|
catch (fallbackError) {
|
|
724
779
|
throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
|
|
@@ -739,13 +794,9 @@ class DetectionService {
|
|
|
739
794
|
return;
|
|
740
795
|
}
|
|
741
796
|
try {
|
|
742
|
-
// Create AbortController for classification model loading
|
|
743
|
-
this.classificationLoadController = new AbortController();
|
|
744
797
|
// Try to load class map (optional - SqueezeNet may not need it for basic classification)
|
|
745
798
|
try {
|
|
746
|
-
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH
|
|
747
|
-
signal: this.classificationLoadController.signal
|
|
748
|
-
});
|
|
799
|
+
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH);
|
|
749
800
|
if (classResponse.ok) {
|
|
750
801
|
this.mobileNetClassMap = await classResponse.json();
|
|
751
802
|
}
|
|
@@ -763,22 +814,9 @@ class DetectionService {
|
|
|
763
814
|
// Load model
|
|
764
815
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
765
816
|
try {
|
|
766
|
-
|
|
767
|
-
const response = await fetch(this.MOBILENET_MODEL_PATH, {
|
|
768
|
-
signal: this.classificationLoadController.signal
|
|
769
|
-
});
|
|
770
|
-
if (!response.ok) {
|
|
771
|
-
throw new Error(`Failed to fetch classification model: ${response.status}`);
|
|
772
|
-
}
|
|
773
|
-
const modelBuffer = await response.arrayBuffer();
|
|
774
|
-
this.mobileNetSession = await window.ort.InferenceSession.create(modelBuffer, sessionOptions);
|
|
817
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, sessionOptions);
|
|
775
818
|
}
|
|
776
819
|
catch (error) {
|
|
777
|
-
// Handle abort signal
|
|
778
|
-
if (error.name === 'AbortError') {
|
|
779
|
-
console.log('Classification model loading was cancelled');
|
|
780
|
-
return;
|
|
781
|
-
}
|
|
782
820
|
// Múltiples tipos de errores de memoria que pueden ocurrir
|
|
783
821
|
const isMemoryError = error.message.includes('failed to allocate a buffer') ||
|
|
784
822
|
error.message.includes('Out of memory') ||
|
|
@@ -800,15 +838,7 @@ class DetectionService {
|
|
|
800
838
|
sessionLogVerbosityLevel: 0,
|
|
801
839
|
};
|
|
802
840
|
try {
|
|
803
|
-
|
|
804
|
-
const fallbackResponse = await fetch(this.MOBILENET_MODEL_PATH, {
|
|
805
|
-
signal: this.classificationLoadController.signal
|
|
806
|
-
});
|
|
807
|
-
if (!fallbackResponse.ok) {
|
|
808
|
-
throw new Error(`Failed to fetch classification model with fallback: ${fallbackResponse.status}`);
|
|
809
|
-
}
|
|
810
|
-
const fallbackBuffer = await fallbackResponse.arrayBuffer();
|
|
811
|
-
this.mobileNetSession = await window.ort.InferenceSession.create(fallbackBuffer, fallbackOptions);
|
|
841
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, fallbackOptions);
|
|
812
842
|
}
|
|
813
843
|
catch (fallbackError) {
|
|
814
844
|
throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
|
|
@@ -998,9 +1028,6 @@ class DetectionService {
|
|
|
998
1028
|
return alignment.top && alignment.right && alignment.bottom && alignment.left;
|
|
999
1029
|
}
|
|
1000
1030
|
cleanup() {
|
|
1001
|
-
// Cancel any ongoing requests
|
|
1002
|
-
this.abortPendingRequests();
|
|
1003
|
-
// Cleanup ONNX resources
|
|
1004
1031
|
this.cleanupCanvasPool();
|
|
1005
1032
|
if (this.session) {
|
|
1006
1033
|
this.session.release?.();
|
|
@@ -1012,8 +1039,6 @@ class DetectionService {
|
|
|
1012
1039
|
}
|
|
1013
1040
|
this.mobileNetClassMap = undefined;
|
|
1014
1041
|
this.modelLoaded = false;
|
|
1015
|
-
// Cleanup blob URLs
|
|
1016
|
-
this.revokeModelBlobUrls();
|
|
1017
1042
|
}
|
|
1018
1043
|
initializeCanvasPool() {
|
|
1019
1044
|
this.preprocessCanvas = document.createElement('canvas');
|
|
@@ -1111,93 +1136,11 @@ class DetectionService {
|
|
|
1111
1136
|
const pool = DetectionService.canvasPool.get(key);
|
|
1112
1137
|
// Only return to pool if not at max capacity
|
|
1113
1138
|
if (pool.length < DetectionService.MAX_POOL_SIZE) {
|
|
1114
|
-
const ctx = canvas.getContext('2d');
|
|
1115
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1116
1139
|
pool.push(canvas);
|
|
1117
1140
|
}
|
|
1118
|
-
// If pool is full, let the canvas be garbage collected
|
|
1119
1141
|
}
|
|
1120
|
-
//
|
|
1121
|
-
|
|
1122
|
-
try {
|
|
1123
|
-
// Clear cache specific to models
|
|
1124
|
-
const cacheNames = await caches.keys();
|
|
1125
|
-
const modelCaches = cacheNames.filter(name => name.includes('onnx') ||
|
|
1126
|
-
name.includes('jaak-static') ||
|
|
1127
|
-
name.includes('squeezenet') ||
|
|
1128
|
-
name.includes('ddmyp'));
|
|
1129
|
-
await Promise.all(modelCaches.map(cacheName => caches.delete(cacheName)));
|
|
1130
|
-
if (this.debug) {
|
|
1131
|
-
console.log(`🧹 Cleared ${modelCaches.length} model caches`);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
catch (error) {
|
|
1135
|
-
console.warn('Error clearing model cache:', error);
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
abortPendingRequests() {
|
|
1139
|
-
// Cancel any fetch in progress
|
|
1140
|
-
if (this.modelLoadController) {
|
|
1141
|
-
this.modelLoadController.abort();
|
|
1142
|
-
this.modelLoadController = undefined;
|
|
1143
|
-
}
|
|
1144
|
-
if (this.classificationLoadController) {
|
|
1145
|
-
this.classificationLoadController.abort();
|
|
1146
|
-
this.classificationLoadController = undefined;
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
revokeModelBlobUrls() {
|
|
1150
|
-
// Revoke any blob URLs created for models
|
|
1151
|
-
this.modelBlobUrls.forEach(url => URL.revokeObjectURL(url));
|
|
1152
|
-
this.modelBlobUrls.clear();
|
|
1153
|
-
}
|
|
1154
|
-
// Progressive resource release for optimization
|
|
1155
|
-
async releaseMobileNetResources() {
|
|
1156
|
-
if (this.mobileNetSession) {
|
|
1157
|
-
this.mobileNetSession.release?.();
|
|
1158
|
-
this.mobileNetSession = undefined;
|
|
1159
|
-
}
|
|
1160
|
-
this.mobileNetClassMap = undefined;
|
|
1161
|
-
if (this.classificationLoadController) {
|
|
1162
|
-
this.classificationLoadController.abort();
|
|
1163
|
-
this.classificationLoadController = undefined;
|
|
1164
|
-
}
|
|
1165
|
-
if (this.debug) {
|
|
1166
|
-
console.log('🧹 Released MobileNet classification resources');
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
// Optimize canvas pool by reducing to specific size
|
|
1170
|
-
optimizeCanvasPool(maxSize) {
|
|
1171
|
-
DetectionService.canvasPool.forEach((pool, key) => {
|
|
1172
|
-
while (pool.length > maxSize) {
|
|
1173
|
-
const canvas = pool.pop();
|
|
1174
|
-
// Explicitly clear and nullify for garbage collection
|
|
1175
|
-
if (canvas) {
|
|
1176
|
-
const ctx = canvas.getContext('2d');
|
|
1177
|
-
if (ctx) {
|
|
1178
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
if (pool.length === 0) {
|
|
1183
|
-
DetectionService.canvasPool.delete(key);
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
if (this.debug) {
|
|
1187
|
-
console.log(`🧹 Optimized canvas pool to max size: ${maxSize}`);
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
// Clear all static canvas pools
|
|
1191
|
-
static clearAllCanvasPools() {
|
|
1192
|
-
DetectionService.canvasPool.forEach((pool) => {
|
|
1193
|
-
pool.forEach(canvas => {
|
|
1194
|
-
const ctx = canvas.getContext('2d');
|
|
1195
|
-
if (ctx) {
|
|
1196
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1197
|
-
}
|
|
1198
|
-
});
|
|
1199
|
-
pool.length = 0;
|
|
1200
|
-
});
|
|
1142
|
+
// Static method to clear all canvas pools (useful for memory management)
|
|
1143
|
+
static clearCanvasPools() {
|
|
1201
1144
|
DetectionService.canvasPool.clear();
|
|
1202
1145
|
}
|
|
1203
1146
|
// Method to get pool statistics for debugging
|
|
@@ -1256,7 +1199,7 @@ class ServiceContainer {
|
|
|
1256
1199
|
}
|
|
1257
1200
|
}
|
|
1258
1201
|
|
|
1259
|
-
const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);color:#ffffff;font-size:12px;font-weight:500;text-align:center;white-space:normal;padding:13px;max-width:300px;z-index:20;height:fit-content;width:fit-content;animation:slideInFromTopCentered 0.3s ease-out}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.back-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:320px}.back-capture-buttons{display:flex;gap:12px;align-items:center;justify-content:center;flex-wrap:wrap}.capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.capture-button:hover{background:#f8f9fa}.capture-button:active{background:#e9ecef;transform:translateY(1px)}.capture-button:disabled,.capture-button.primary-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.back-capture-section{bottom:16px;max-width:300px}.back-capture-buttons{flex-direction:column;gap:8px;width:100%}.capture-button,.back-capture-buttons .skip-button{width:100%;max-width:200px;padding:10px 20px;font-size:13px}.capture-button:disabled,.capture-button.primary-button:disabled,.skip-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.guide-text.performance-warning-text{animation:gentlePulse 2s ease-in-out infinite;background:rgba(255, 107, 107, 0.3);border:1px solid rgba(255, 107, 107, 0.5)}@keyframes gentlePulse{0%,100%{transform:translate(-50%, -50%) scale(1);background:rgba(255, 107, 107, 0.3)}50%{transform:translate(-50%, -50%) scale(1.02);background:rgba(255, 107, 107, 0.4)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.skip-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(0, 0, 0, 0.2);border-top:2px solid #333333;border-radius:50%;animation:spin 1s linear infinite;display:inline-block;margin-right:8px;vertical-align:middle}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes slideInFromTopCentered{0%{opacity:0;transform:translate(-50%, -50%) translateY(-8px) scale(0.95)}100%{opacity:1;transform:translate(-50%, -50%) translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}.guide-text{font-size:11px;padding:4px 8px;border-radius:8px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromTop 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}.manual-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.manual-capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.manual-capture-button:hover{background:#f8f9fa}.manual-capture-button:active{background:#e9ecef;transform:translateY(1px)}.manual-capture-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.manual-capture-section{bottom:16px;max-width:280px}.manual-capture-button{padding:10px 20px;font-size:13px}.manual-capture-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}";
|
|
1202
|
+
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);}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.back-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:320px}.back-capture-buttons{display:flex;gap:12px;align-items:center;justify-content:center;flex-wrap:wrap}.capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.capture-button:hover{background:#f8f9fa}.capture-button:active{background:#e9ecef;transform:translateY(1px)}.capture-button:disabled,.capture-button.primary-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.back-capture-section{bottom:16px;max-width:300px}.back-capture-buttons{flex-direction:column;gap:8px;width:100%}.capture-button,.back-capture-buttons .skip-button{width:100%;max-width:200px;padding:10px 20px;font-size:13px}.capture-button:disabled,.capture-button.primary-button:disabled,.skip-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.guide-text.performance-warning-text{animation:gentlePulse 2s ease-in-out infinite;background:rgba(255, 107, 107, 0.3);border:1px solid rgba(255, 107, 107, 0.5)}@keyframes gentlePulse{0%,100%{transform:translate(-50%, -50%) scale(1);background:rgba(255, 107, 107, 0.3)}50%{transform:translate(-50%, -50%) scale(1.02);background:rgba(255, 107, 107, 0.4)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.skip-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(0, 0, 0, 0.2);border-top:2px solid #333333;border-radius:50%;animation:spin 1s linear infinite;display:inline-block;margin-right:8px;vertical-align:middle}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes slideInFromTopCentered{0%{opacity:0;transform:translate(-50%, -50%) translateY(-8px) scale(0.95)}100%{opacity:1;transform:translate(-50%, -50%) translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}.guide-text{font-size:11px;padding:4px 8px;border-radius:8px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromTop 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}.manual-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.manual-capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.manual-capture-button:hover{background:#f8f9fa}.manual-capture-button:active{background:#e9ecef;transform:translateY(1px)}.manual-capture-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.manual-capture-section{bottom:16px;max-width:280px}.manual-capture-button{padding:10px 20px;font-size:13px}.manual-capture-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}";
|
|
1260
1203
|
|
|
1261
1204
|
const JaakStamps = class {
|
|
1262
1205
|
constructor(hostRef) {
|
|
@@ -1334,6 +1277,7 @@ const JaakStamps = class {
|
|
|
1334
1277
|
alignmentStartTime;
|
|
1335
1278
|
alignmentTimer;
|
|
1336
1279
|
backDocumentTimer;
|
|
1280
|
+
orientationTimer;
|
|
1337
1281
|
// Performance monitoring
|
|
1338
1282
|
performanceMetrics = {
|
|
1339
1283
|
fps: 0,
|
|
@@ -1437,7 +1381,7 @@ const JaakStamps = class {
|
|
|
1437
1381
|
if (!window.ort) {
|
|
1438
1382
|
this.updateStatus('Preparando herramientas...', 'Configurando funciones de captura', 'initializing');
|
|
1439
1383
|
const script = document.createElement('script');
|
|
1440
|
-
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
|
|
1384
|
+
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.23.0/dist/ort.min.js';
|
|
1441
1385
|
document.head.appendChild(script);
|
|
1442
1386
|
await new Promise((resolve) => {
|
|
1443
1387
|
script.onload = () => {
|
|
@@ -1518,6 +1462,18 @@ const JaakStamps = class {
|
|
|
1518
1462
|
});
|
|
1519
1463
|
resizeObserver.observe(this.detectionContainer.parentElement);
|
|
1520
1464
|
}
|
|
1465
|
+
// Handle mobile device orientation changes
|
|
1466
|
+
window.addEventListener('orientationchange', () => {
|
|
1467
|
+
// Clear any existing orientation timer
|
|
1468
|
+
if (this.orientationTimer) {
|
|
1469
|
+
clearTimeout(this.orientationTimer);
|
|
1470
|
+
}
|
|
1471
|
+
// Add delay to ensure video dimensions are updated after rotation
|
|
1472
|
+
this.orientationTimer = window.setTimeout(() => {
|
|
1473
|
+
this.handleResize();
|
|
1474
|
+
this.orientationTimer = undefined;
|
|
1475
|
+
}, 300);
|
|
1476
|
+
});
|
|
1521
1477
|
}
|
|
1522
1478
|
handleResize() {
|
|
1523
1479
|
if (this.detectionContainer) {
|
|
@@ -1674,13 +1630,7 @@ const JaakStamps = class {
|
|
|
1674
1630
|
}
|
|
1675
1631
|
try {
|
|
1676
1632
|
this.exitSession();
|
|
1677
|
-
|
|
1678
|
-
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1679
|
-
return {
|
|
1680
|
-
success: true,
|
|
1681
|
-
resourcesFreed: cleanupResult.freed,
|
|
1682
|
-
cleanupErrors: cleanupResult.errors
|
|
1683
|
-
};
|
|
1633
|
+
return { success: true };
|
|
1684
1634
|
}
|
|
1685
1635
|
catch (error) {
|
|
1686
1636
|
return { success: false, error: error.message };
|
|
@@ -1905,60 +1855,11 @@ const JaakStamps = class {
|
|
|
1905
1855
|
// Always allow getting capture delay, even during initialization
|
|
1906
1856
|
return this.captureDelay;
|
|
1907
1857
|
}
|
|
1908
|
-
async getResourceReport() {
|
|
1909
|
-
// Public method to get resource usage report
|
|
1910
|
-
return this.getResourceReportInternal();
|
|
1911
|
-
}
|
|
1912
|
-
async forceResourceCleanup() {
|
|
1913
|
-
// Public method to force aggressive resource cleanup
|
|
1914
|
-
const readyCheck = this.isComponentReady();
|
|
1915
|
-
if (!readyCheck.ready) {
|
|
1916
|
-
return { success: false, error: readyCheck.message };
|
|
1917
|
-
}
|
|
1918
|
-
try {
|
|
1919
|
-
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1920
|
-
return {
|
|
1921
|
-
success: true,
|
|
1922
|
-
resourcesFreed: cleanupResult.freed,
|
|
1923
|
-
errors: cleanupResult.errors,
|
|
1924
|
-
timestamp: new Date().toISOString()
|
|
1925
|
-
};
|
|
1926
|
-
}
|
|
1927
|
-
catch (error) {
|
|
1928
|
-
return { success: false, error: error.message };
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
1858
|
// Memory and device capability detection
|
|
1932
1859
|
checkDeviceCapabilities() {
|
|
1933
1860
|
const deviceMemory = navigator.deviceMemory;
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
1937
|
-
const isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
|
|
1938
|
-
// Si tenemos info de memoria del dispositivo
|
|
1939
|
-
if (deviceMemory && deviceMemory < 4) {
|
|
1940
|
-
return {
|
|
1941
|
-
canUseML: false,
|
|
1942
|
-
reason: `Memoria del dispositivo (${deviceMemory}GB) insuficiente para detección automática. Modo manual activado para mejor rendimiento.`,
|
|
1943
|
-
memoryMB: deviceMemory * 1024
|
|
1944
|
-
};
|
|
1945
|
-
}
|
|
1946
|
-
// Dispositivos móviles de gama baja
|
|
1947
|
-
if (isMobile && isLowEndDevice) {
|
|
1948
|
-
return {
|
|
1949
|
-
canUseML: false,
|
|
1950
|
-
reason: 'Dispositivo móvil optimizado detectado. Modo manual activado para mejor rendimiento y duración de batería.',
|
|
1951
|
-
memoryMB: null
|
|
1952
|
-
};
|
|
1953
|
-
}
|
|
1954
|
-
// Safari tiene limitaciones conocidas con WASM
|
|
1955
|
-
if (isSafari) {
|
|
1956
|
-
return {
|
|
1957
|
-
canUseML: false,
|
|
1958
|
-
reason: 'Safari detectado. Modo manual activado por compatibilidad optimizada con este navegador.',
|
|
1959
|
-
memoryMB: null
|
|
1960
|
-
};
|
|
1961
|
-
}
|
|
1861
|
+
// Siempre intentar usar ML, independientemente del dispositivo
|
|
1862
|
+
// Las restricciones anteriores no eran confiables
|
|
1962
1863
|
return {
|
|
1963
1864
|
canUseML: true,
|
|
1964
1865
|
reason: 'Dispositivo compatible con detección automática.',
|
|
@@ -2084,6 +1985,12 @@ const JaakStamps = class {
|
|
|
2084
1985
|
this.videoRef.onloadedmetadata = async () => {
|
|
2085
1986
|
await this.videoRef.play();
|
|
2086
1987
|
this.stateManager.updateCaptureState({ isVideoActive: true });
|
|
1988
|
+
// Recalculate mask dimensions when new camera stream loads
|
|
1989
|
+
if (this.detectionContainer) {
|
|
1990
|
+
const container = this.detectionContainer.parentElement;
|
|
1991
|
+
const rect = container.getBoundingClientRect();
|
|
1992
|
+
this.updateMaskDimensions(rect);
|
|
1993
|
+
}
|
|
2087
1994
|
resolve();
|
|
2088
1995
|
};
|
|
2089
1996
|
});
|
|
@@ -2255,6 +2162,10 @@ const JaakStamps = class {
|
|
|
2255
2162
|
clearTimeout(this.alignmentTimer);
|
|
2256
2163
|
this.alignmentTimer = undefined;
|
|
2257
2164
|
}
|
|
2165
|
+
if (this.orientationTimer) {
|
|
2166
|
+
clearTimeout(this.orientationTimer);
|
|
2167
|
+
this.orientationTimer = undefined;
|
|
2168
|
+
}
|
|
2258
2169
|
// Liberar recursos de ONNX al limpiar el componente
|
|
2259
2170
|
this.releaseOnnxResources();
|
|
2260
2171
|
if (this.performanceUpdateInterval) {
|
|
@@ -2263,182 +2174,11 @@ const JaakStamps = class {
|
|
|
2263
2174
|
}
|
|
2264
2175
|
// Clear canvas pool
|
|
2265
2176
|
this.canvasPool = [];
|
|
2266
|
-
// Clean DOM references and memory leaks
|
|
2267
|
-
this.cleanupDOMReferences();
|
|
2268
2177
|
this.detectionBoxes = [];
|
|
2269
2178
|
this.alignmentStartTime = undefined;
|
|
2270
2179
|
this.hasDocumentDetected = false;
|
|
2271
2180
|
this.serviceContainer?.cleanup();
|
|
2272
2181
|
}
|
|
2273
|
-
// AGGRESSIVE RESOURCE CLEANUP METHODS
|
|
2274
|
-
// Clean DOM references and memory leaks
|
|
2275
|
-
cleanupDOMReferences() {
|
|
2276
|
-
// Remove event listeners that could maintain references
|
|
2277
|
-
if (this.videoRef) {
|
|
2278
|
-
// Remove common event listeners (if any were added)
|
|
2279
|
-
this.videoRef.srcObject = null;
|
|
2280
|
-
this.videoRef = undefined;
|
|
2281
|
-
}
|
|
2282
|
-
// Clean detection container
|
|
2283
|
-
if (this.detectionContainer) {
|
|
2284
|
-
this.detectionContainer = undefined;
|
|
2285
|
-
}
|
|
2286
|
-
// Clean references to images in the DOM and revoke blob URLs
|
|
2287
|
-
const images = this.el.shadowRoot?.querySelectorAll('img');
|
|
2288
|
-
images?.forEach(img => {
|
|
2289
|
-
if (img.src.startsWith('blob:')) {
|
|
2290
|
-
URL.revokeObjectURL(img.src);
|
|
2291
|
-
}
|
|
2292
|
-
img.src = '';
|
|
2293
|
-
});
|
|
2294
|
-
}
|
|
2295
|
-
// Clear browser cache and storage
|
|
2296
|
-
async clearBrowserCache() {
|
|
2297
|
-
try {
|
|
2298
|
-
// 1. Clear Service Worker cache if exists
|
|
2299
|
-
if ('serviceWorker' in navigator) {
|
|
2300
|
-
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
2301
|
-
await Promise.all(registrations.map(registration => registration.unregister()));
|
|
2302
|
-
}
|
|
2303
|
-
// 2. Clear Cache API specific to the component
|
|
2304
|
-
const cacheNames = await caches.keys();
|
|
2305
|
-
const componentCaches = cacheNames.filter(name => name.includes('jaak') || name.includes('stamps'));
|
|
2306
|
-
await Promise.all(componentCaches.map(cacheName => caches.delete(cacheName)));
|
|
2307
|
-
// 3. Force garbage collection if available (only in dev)
|
|
2308
|
-
if (this.debug && window.gc) {
|
|
2309
|
-
window.gc();
|
|
2310
|
-
}
|
|
2311
|
-
}
|
|
2312
|
-
catch (error) {
|
|
2313
|
-
console.warn('Error clearing browser cache:', error);
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
// Clear local storage specific to the component
|
|
2317
|
-
async clearLocalStorage() {
|
|
2318
|
-
try {
|
|
2319
|
-
// Clear localStorage specific to the component
|
|
2320
|
-
const keysToRemove = [];
|
|
2321
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
2322
|
-
const key = localStorage.key(i);
|
|
2323
|
-
if (key && (key.includes('jaak') || key.includes('onnx') || key.includes('stamps'))) {
|
|
2324
|
-
keysToRemove.push(key);
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
2328
|
-
// Clear sessionStorage
|
|
2329
|
-
const sessionKeysToRemove = [];
|
|
2330
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
2331
|
-
const key = sessionStorage.key(i);
|
|
2332
|
-
if (key && (key.includes('jaak') || key.includes('onnx'))) {
|
|
2333
|
-
sessionKeysToRemove.push(key);
|
|
2334
|
-
}
|
|
2335
|
-
}
|
|
2336
|
-
sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
|
|
2337
|
-
}
|
|
2338
|
-
catch (error) {
|
|
2339
|
-
console.warn('Error clearing local storage:', error);
|
|
2340
|
-
}
|
|
2341
|
-
}
|
|
2342
|
-
// Aggressive resource cleanup combining all strategies
|
|
2343
|
-
async aggressiveResourceCleanup() {
|
|
2344
|
-
const freed = [];
|
|
2345
|
-
const errors = [];
|
|
2346
|
-
try {
|
|
2347
|
-
// 1. Release ONNX resources
|
|
2348
|
-
this.releaseOnnxResources();
|
|
2349
|
-
freed.push('ONNX models');
|
|
2350
|
-
// 2. Clear model cache in detection service
|
|
2351
|
-
if (this.detectionService) {
|
|
2352
|
-
await this.detectionService.clearModelCache();
|
|
2353
|
-
freed.push('Model cache');
|
|
2354
|
-
}
|
|
2355
|
-
// 3. Clear browser cache
|
|
2356
|
-
await this.clearBrowserCache();
|
|
2357
|
-
freed.push('Browser cache');
|
|
2358
|
-
// 4. Clear local storage
|
|
2359
|
-
await this.clearLocalStorage();
|
|
2360
|
-
freed.push('Local storage');
|
|
2361
|
-
// 5. Clean DOM references
|
|
2362
|
-
this.cleanupDOMReferences();
|
|
2363
|
-
freed.push('DOM references');
|
|
2364
|
-
// 6. Optimize canvas pools
|
|
2365
|
-
if (this.detectionService) {
|
|
2366
|
-
this.detectionService.optimizeCanvasPool(0);
|
|
2367
|
-
freed.push('Canvas pools');
|
|
2368
|
-
}
|
|
2369
|
-
// 7. Clear component canvas pool
|
|
2370
|
-
this.canvasPool = [];
|
|
2371
|
-
freed.push('Component canvas pool');
|
|
2372
|
-
// 8. Force garbage collection (if available)
|
|
2373
|
-
if (this.debug && window.gc) {
|
|
2374
|
-
window.gc();
|
|
2375
|
-
freed.push('Garbage collection');
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
catch (error) {
|
|
2379
|
-
errors.push(error.message);
|
|
2380
|
-
}
|
|
2381
|
-
return { freed, errors };
|
|
2382
|
-
}
|
|
2383
|
-
// Progressive resource release after front capture
|
|
2384
|
-
async onFrontCaptureComplete() {
|
|
2385
|
-
try {
|
|
2386
|
-
// Release classification model if not needed for back
|
|
2387
|
-
if (this.detectionService && !this.useDocumentClassification) {
|
|
2388
|
-
await this.detectionService.releaseMobileNetResources();
|
|
2389
|
-
}
|
|
2390
|
-
// Optimize canvas pool to minimum needed for back capture
|
|
2391
|
-
if (this.detectionService) {
|
|
2392
|
-
this.detectionService.optimizeCanvasPool(1);
|
|
2393
|
-
}
|
|
2394
|
-
// Reduce component canvas pool
|
|
2395
|
-
while (this.canvasPool.length > 1) {
|
|
2396
|
-
const canvas = this.canvasPool.pop();
|
|
2397
|
-
if (canvas) {
|
|
2398
|
-
const ctx = canvas.getContext('2d');
|
|
2399
|
-
if (ctx) {
|
|
2400
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
if (this.debug) {
|
|
2405
|
-
console.log('🧹 Progressive cleanup after front capture completed');
|
|
2406
|
-
}
|
|
2407
|
-
}
|
|
2408
|
-
catch (error) {
|
|
2409
|
-
console.warn('Error in progressive cleanup:', error);
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
// Resource reporting for debugging
|
|
2413
|
-
getResourceReportInternal() {
|
|
2414
|
-
const report = {
|
|
2415
|
-
timestamp: new Date().toISOString(),
|
|
2416
|
-
component: {
|
|
2417
|
-
hasVideoStream: !!this.videoStream,
|
|
2418
|
-
hasAnimationFrame: !!this.animationId,
|
|
2419
|
-
canvasPoolSize: this.canvasPool.length,
|
|
2420
|
-
detectionBoxesCount: this.detectionBoxes.length,
|
|
2421
|
-
performanceMonitorActive: !!this.performanceUpdateInterval
|
|
2422
|
-
}
|
|
2423
|
-
};
|
|
2424
|
-
// Add detection service stats if available
|
|
2425
|
-
if (this.detectionService) {
|
|
2426
|
-
report.detectionService = {
|
|
2427
|
-
isModelLoaded: this.detectionService.isModelLoaded(),
|
|
2428
|
-
canvasPoolStats: this.detectionService.getPoolStats()
|
|
2429
|
-
};
|
|
2430
|
-
}
|
|
2431
|
-
// Add memory info if available
|
|
2432
|
-
if ('memory' in performance) {
|
|
2433
|
-
const memInfo = performance.memory;
|
|
2434
|
-
report.memory = {
|
|
2435
|
-
usedJSHeapSize: Math.round(memInfo.usedJSHeapSize / 1048576), // MB
|
|
2436
|
-
totalJSHeapSize: Math.round(memInfo.totalJSHeapSize / 1048576), // MB
|
|
2437
|
-
jsHeapSizeLimit: Math.round(memInfo.jsHeapSizeLimit / 1048576) // MB
|
|
2438
|
-
};
|
|
2439
|
-
}
|
|
2440
|
-
return report;
|
|
2441
|
-
}
|
|
2442
2182
|
render() {
|
|
2443
2183
|
const captureState = this.stateManager?.getCaptureState() || {
|
|
2444
2184
|
isVideoActive: false,
|
|
@@ -2448,7 +2188,7 @@ const JaakStamps = class {
|
|
|
2448
2188
|
isCapturing: false
|
|
2449
2189
|
};
|
|
2450
2190
|
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
2451
|
-
return (h("div", { key: '
|
|
2191
|
+
return (h("div", { key: 'f9cbca2fd6b8548a29ace7fcb972b53f402f61af', class: "detector-container" }, h("div", { key: '4f3cac734eb6bfd44a8de644eadca17e7893e11a', class: "video-container" }, h("video", { key: '5e15a38c989a72d5274281272d41ba064447ca6f', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '38c8ad8b691db60e9b5a2a8b641fe6febbeacfca', 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: {
|
|
2452
2192
|
position: 'absolute',
|
|
2453
2193
|
left: `${box.x}px`,
|
|
2454
2194
|
top: `${box.y}px`,
|
|
@@ -2457,9 +2197,9 @@ const JaakStamps = class {
|
|
|
2457
2197
|
border: '2px solid #32406C',
|
|
2458
2198
|
pointerEvents: 'none',
|
|
2459
2199
|
boxSizing: 'border-box'
|
|
2460
|
-
} })))), this.isMaskReady && (h("div", { key: '
|
|
2200
|
+
} })))), this.isMaskReady && (h("div", { key: 'c56fde56b7a86a4e8a4cf558745291ea5cde12fd', class: "overlay-mask" }, h("div", { key: '04ed3f01f271dc84eed594cb4b60c2453645bd41', class: "card-outline" }, h("div", { key: '648bd423a5e886d4768115237de5896c7223b30b', class: "side side-top" }), h("div", { key: 'b8878059493ca61961b9b3c82ecc18b50b79ac85', class: "side side-right" }), h("div", { key: '09e5ad64ff33dbd5c112bf52b71e14409ab6aa65', class: "side side-bottom" }), h("div", { key: 'b8ba3d83c9bc1b4a0da859d7ed4b9a15eaddcc2a', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '3279701a240f77ac96e68a34ad95c099340c9a6a', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '2adf96d0a57e482372fd9586229dac2301b64fd4', class: "back-capture-section" }, h("div", { key: '305a147c9cfee854c60ac946a48a1d21b2792725', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (h("button", { key: '43270a0647f39307229c540dd5130fb58b1ff4fb', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: 'eb4666603774564eef95a120ac13faeb9e10c8b4', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: 'cac667d9548ae2314ad5a677c5ed90f25184f728', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '0af7aa4106ce189ae2d79bec78ede01abe25b1e7', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
2461
2201
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
2462
|
-
: 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '
|
|
2202
|
+
: 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'de6e5c4fad4d4d996b894457c9af52bc71e533fb', class: "camera-controls" }, h("button", { key: 'cfddcf2420d5f5923a6b02ca446bbe1b8e9fc5e4', 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: '5fb73f958b9a5a6f9242032c4530b0bdab84f6c5', class: "camera-selector-dropdown" }, h("div", { key: '8ee2d26b1001e571e7b6c326e927926fa08287fd', class: "camera-selector-header" }, h("span", { key: '78350b0fb1bd933f7c8c59d8dab0b7122ffbe363' }, "Seleccionar C\u00E1mara"), h("button", { key: 'bb750e0ddd5a1ee5ff96bd57805a7f90b8821e9f', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'd0f6343cf1b7367f3da33ec8cf64891109d923b8', 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: 'c9642fbfb0acea766d3f0542f4cc9f17f4af4d7b', class: "device-info" }, h("small", { key: 'b6e9507ff7addf011f339f6c22a88a96b40db68a' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '04ae9262409284782850b90945eb7038673e2097', class: "manual-capture-section" }, h("button", { key: '27c315e503ccba468b89ebd58d6b70f40ddd06ba', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '8f2dcd2df9a218b37eb0b8204a8724362cfeaa53', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '6fd6fe1d0213b01c8119a67fb8108f73227feee1', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'fba3816b48b99ca64510ecbefc0d41431569c9ae', class: "flip-animation" }, h("div", { key: 'beea1c8245b5d81f1008efc6d1b079a61c821e7f', class: "id-card-icon" }), h("div", { key: 'f0fbc34f619a653e2c0ade764ca99e7aa582d157', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'e4a0e6d31112e3a50024d1d0430b4ad997049fe7', class: "success-animation" }, h("div", { key: '5dd3f781f55630bc904c81207548ae35bc79b452', class: "check-icon" }), h("div", { key: 'a10ebc557582325091efb0abd12d62b27a43b98c', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '975865955dee1803b5215ecf049701c98b324855', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '84f6f67202e26ce80f7fc5d5f321e23b5bc55c57', class: "status-spinner" })), h("div", { key: 'a1ae56ed156e8feb279a1bf160f749c5d0b5ce45', class: "status-content" }, h("div", { key: 'd62932554cea257ce19763df26e0ba702e50eeca', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'c5506fa12dd0d37d26dc26c9f2381c843330451f', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '2097eda63185f336f7319014fae2ef6db1e41761', class: "performance-monitor" }, h("div", { key: 'd611c494c6b1a5930ef64aebdcba41e5d94c9ee6', class: "performance-expanded" }, h("div", { key: '800d46831e7b9bec260d9c5e67ea1a3eb9c2933e', class: "metrics-row" }, h("div", { key: 'dd0db1faa9050d98e71da4b5cd26a4a484c37c64', class: "metric-compact" }, h("span", { key: 'f0e6ea61a35ddb2ae5c2a6aff8c255a02c5c1219', class: "metric-label" }, "FPS"), h("span", { key: 'cbf4df4d142f6f01e45f566a8ede8fbbf29ad673', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '83861ad31fa46c8199a5583e4b3f8eefd44bacd6', class: "metric-compact" }, h("span", { key: '41b8de4cda47959dfc9df393e95fdadde534dd21', class: "metric-label" }, "MEM"), h("span", { key: 'b268f13cc78066e240ed158f1443113a94827aff', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '9f9a9d0690906b186492af23b900c5fa0f5cff89', class: "metrics-row" }, h("div", { key: '84fa6e453dc36e4a74e8b573447b17230766229e', class: "metric-compact" }, h("span", { key: 'f17d52899090c89f532d74c552dd8c5338fc2d83', class: "metric-label" }, "INF"), h("span", { key: '2beca0d7ce55ed574f6b01efc086c5846983e024', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '981e300cf076e81bfe946974f6cffd63619eacde', class: "metric-compact" }, h("span", { key: '3dfb0c996a69eb87b0f711322675cad69c471146', class: "metric-label" }, "FRAME"), h("span", { key: '3ae2ac6e90cda77f23e559f3197c3b761f9bdc40', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '2c51216838d87728d9d7764e5ba8a50fa02eb493', class: "metrics-row" }, h("div", { key: 'eda810ffb80ec38ab84583f59b26706f90a50fc9', class: "metric-compact" }, h("span", { key: '978ba9c0c67d6da6b29b3535af33a314bab9276a', class: "metric-label" }, "DET"), h("span", { key: '3e5e169fd4a3906283d8f918b5bbc7c5df6ccd0f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'b4b915c745e35c09470a4dc5edfc112d32cfb8b6', class: "metric-compact" }, h("span", { key: '3e2a7b88a4446813c955efb0dfa9e2958e8e1448', class: "metric-label" }, "RATE"), h("span", { key: 'f4f073f7ee622167a241c6a1c535ef580c13d9ab', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '76c556e92f6d7980539f1a30efe8751f665164fb', class: "watermark" }, h("img", { key: 'af23e115d6b4e92a72e55d6d98fde0e4b1d09e9b', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
2463
2203
|
}
|
|
2464
2204
|
// Utility methods
|
|
2465
2205
|
updateDetectionBoxes(boxes) {
|
|
@@ -2656,8 +2396,8 @@ const JaakStamps = class {
|
|
|
2656
2396
|
if (captureState.step === 'front') {
|
|
2657
2397
|
this.stateManager.setCapturedImages({
|
|
2658
2398
|
front: {
|
|
2659
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
2660
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
2399
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
2400
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
2661
2401
|
}
|
|
2662
2402
|
});
|
|
2663
2403
|
// Check if document classification is enabled (independent of detector)
|
|
@@ -2683,8 +2423,6 @@ const JaakStamps = class {
|
|
|
2683
2423
|
showFlipAnimation: true
|
|
2684
2424
|
});
|
|
2685
2425
|
this.triggerRerender();
|
|
2686
|
-
// Progressive resource cleanup after front capture
|
|
2687
|
-
await this.onFrontCaptureComplete();
|
|
2688
2426
|
setTimeout(() => {
|
|
2689
2427
|
this.stateManager.updateCaptureState({
|
|
2690
2428
|
showFlipAnimation: false,
|
|
@@ -2699,8 +2437,8 @@ const JaakStamps = class {
|
|
|
2699
2437
|
else if (captureState.step === 'back') {
|
|
2700
2438
|
this.stateManager.setCapturedImages({
|
|
2701
2439
|
back: {
|
|
2702
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
2703
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
2440
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
2441
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
2704
2442
|
}
|
|
2705
2443
|
});
|
|
2706
2444
|
this.completeProcess(false);
|
|
@@ -2738,8 +2476,8 @@ const JaakStamps = class {
|
|
|
2738
2476
|
if (captureState.step === 'front') {
|
|
2739
2477
|
this.stateManager.setCapturedImages({
|
|
2740
2478
|
front: {
|
|
2741
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
2742
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
2479
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
2480
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
2743
2481
|
}
|
|
2744
2482
|
});
|
|
2745
2483
|
// Check if document classification is enabled
|
|
@@ -2760,8 +2498,6 @@ const JaakStamps = class {
|
|
|
2760
2498
|
isDetectionPaused: true,
|
|
2761
2499
|
showFlipAnimation: true
|
|
2762
2500
|
});
|
|
2763
|
-
// Progressive resource cleanup after front capture
|
|
2764
|
-
await this.onFrontCaptureComplete();
|
|
2765
2501
|
setTimeout(() => {
|
|
2766
2502
|
this.stateManager.updateCaptureState({
|
|
2767
2503
|
showFlipAnimation: false,
|
|
@@ -2773,8 +2509,8 @@ const JaakStamps = class {
|
|
|
2773
2509
|
else if (captureState.step === 'back') {
|
|
2774
2510
|
this.stateManager.setCapturedImages({
|
|
2775
2511
|
back: {
|
|
2776
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
2777
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
2512
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
2513
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
2778
2514
|
}
|
|
2779
2515
|
});
|
|
2780
2516
|
this.completeProcess(false);
|
|
@@ -2900,10 +2636,13 @@ const JaakStamps = class {
|
|
|
2900
2636
|
this.useDocumentDetector = false; // Desactivar detector automático
|
|
2901
2637
|
// Actualizar el estado con mensaje de error
|
|
2902
2638
|
this.updateStatus('Modo manual activado', errorMessage, 'error');
|
|
2903
|
-
//
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2639
|
+
// Continuar con la configuración de cámara para mostrar el video
|
|
2640
|
+
this.continueWithCameraSetup().catch(() => {
|
|
2641
|
+
// Si falla la configuración de cámara, mantener el mensaje de error pero cambiar el estado
|
|
2642
|
+
setTimeout(() => {
|
|
2643
|
+
this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
|
|
2644
|
+
}, 5000);
|
|
2645
|
+
});
|
|
2907
2646
|
}
|
|
2908
2647
|
stopPerformanceMonitoring() {
|
|
2909
2648
|
// Nota: No necesitamos limpiar más variables porque ya no se usarán
|
|
@@ -2939,16 +2678,6 @@ const JaakStamps = class {
|
|
|
2939
2678
|
setTimeout(() => {
|
|
2940
2679
|
this.stateManager.updateCaptureState({ showSuccessAnimation: false });
|
|
2941
2680
|
}, 3000);
|
|
2942
|
-
// Aggressive cleanup after process completion with delay
|
|
2943
|
-
setTimeout(async () => {
|
|
2944
|
-
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
2945
|
-
if (this.debug) {
|
|
2946
|
-
console.log('🧹 Post-completion cleanup:', cleanupResult.freed);
|
|
2947
|
-
if (cleanupResult.errors.length > 0) {
|
|
2948
|
-
console.warn('⚠️ Cleanup errors:', cleanupResult.errors);
|
|
2949
|
-
}
|
|
2950
|
-
}
|
|
2951
|
-
}, 1000); // Wait 1 second after completion
|
|
2952
2681
|
}
|
|
2953
2682
|
stopDetection() {
|
|
2954
2683
|
if (this.animationId) {
|
|
@@ -3000,6 +2729,12 @@ const JaakStamps = class {
|
|
|
3000
2729
|
try {
|
|
3001
2730
|
// Close the selector immediately when user selects a camera
|
|
3002
2731
|
this.showCameraSelector = false;
|
|
2732
|
+
// Check if user selected the same camera that's already active
|
|
2733
|
+
const currentCameraId = this.cameraService.getSelectedCameraId();
|
|
2734
|
+
if (currentCameraId === cameraId) {
|
|
2735
|
+
// Same camera selected, just close the selector without switching
|
|
2736
|
+
return;
|
|
2737
|
+
}
|
|
3003
2738
|
this.isSwitchingCamera = true;
|
|
3004
2739
|
// Stop current video stream
|
|
3005
2740
|
if (this.videoStream) {
|