@jaak.ai/stamps 2.1.0-dev.9 → 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 -416
- 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 -300
- 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 -148
- 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 -419
- 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 -416
- 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-BP1Q4KOg.js +0 -3
- package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-c115e01a.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-c115e01a.entry.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-Ga0t6BMe.js');
|
|
4
4
|
|
|
5
5
|
class EventBusService {
|
|
6
6
|
events = new Map();
|
|
@@ -206,7 +206,21 @@ class CameraService {
|
|
|
206
206
|
tempStream.getTracks().forEach(track => track.stop());
|
|
207
207
|
}
|
|
208
208
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
209
|
-
|
|
209
|
+
const allCameras = devices.filter(device => device.kind === 'videoinput');
|
|
210
|
+
// Filter out virtual cameras
|
|
211
|
+
this.availableCameras = allCameras.filter(camera => {
|
|
212
|
+
const isVirtual = this.isVirtualCamera(camera);
|
|
213
|
+
if (isVirtual) {
|
|
214
|
+
console.log(`Virtual camera detected and filtered: ${camera.label}`);
|
|
215
|
+
}
|
|
216
|
+
return !isVirtual;
|
|
217
|
+
});
|
|
218
|
+
// Check if there are physical cameras available
|
|
219
|
+
if (this.availableCameras.length === 0) {
|
|
220
|
+
console.log('No physical cameras available');
|
|
221
|
+
this.eventBus.emit('error', new Error('Cámaras virtuales no están permitidas. Use una cámara física.'));
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
210
224
|
// Cache the results for optimization
|
|
211
225
|
CameraService.deviceEnumerationCache = {
|
|
212
226
|
devices: this.availableCameras,
|
|
@@ -256,6 +270,21 @@ class CameraService {
|
|
|
256
270
|
});
|
|
257
271
|
this.lastStreamConstraints = finalConstraints;
|
|
258
272
|
const stream = await this.currentStreamPromise;
|
|
273
|
+
// Validate that active stream is not from a virtual camera
|
|
274
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
275
|
+
if (videoTrack) {
|
|
276
|
+
const settings = videoTrack.getSettings();
|
|
277
|
+
const deviceId = settings.deviceId;
|
|
278
|
+
if (deviceId) {
|
|
279
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
280
|
+
const currentDevice = devices.find(device => device.deviceId === deviceId);
|
|
281
|
+
if (currentDevice && this.isVirtualCamera(currentDevice)) {
|
|
282
|
+
console.log(`Virtual camera detected in active stream: ${currentDevice.label}`);
|
|
283
|
+
stream.getTracks().forEach(track => track.stop());
|
|
284
|
+
throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
259
288
|
return stream;
|
|
260
289
|
}
|
|
261
290
|
catch (error) {
|
|
@@ -273,7 +302,23 @@ class CameraService {
|
|
|
273
302
|
audio: false
|
|
274
303
|
});
|
|
275
304
|
this.lastStreamConstraints = basicConstraints;
|
|
276
|
-
|
|
305
|
+
const stream = await this.currentStreamPromise;
|
|
306
|
+
// Validate fallback stream as well
|
|
307
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
308
|
+
if (videoTrack) {
|
|
309
|
+
const settings = videoTrack.getSettings();
|
|
310
|
+
const deviceId = settings.deviceId;
|
|
311
|
+
if (deviceId) {
|
|
312
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
313
|
+
const currentDevice = devices.find(device => device.deviceId === deviceId);
|
|
314
|
+
if (currentDevice && this.isVirtualCamera(currentDevice)) {
|
|
315
|
+
console.log(`Virtual camera detected in fallback stream: ${currentDevice.label}`);
|
|
316
|
+
stream.getTracks().forEach(track => track.stop());
|
|
317
|
+
throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return stream;
|
|
277
322
|
}
|
|
278
323
|
}
|
|
279
324
|
async switchCamera(cameraId) {
|
|
@@ -282,6 +327,12 @@ class CameraService {
|
|
|
282
327
|
await this.enumerateDevices();
|
|
283
328
|
return;
|
|
284
329
|
}
|
|
330
|
+
// Validate that the target camera is not virtual
|
|
331
|
+
if (this.isVirtualCamera(selectedCamera)) {
|
|
332
|
+
console.log(`Attempted to switch to virtual camera: ${selectedCamera.label}`);
|
|
333
|
+
this.eventBus.emit('error', new Error('No se puede cambiar a cámara virtual'));
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
285
336
|
await this.setSelectedCamera(cameraId);
|
|
286
337
|
}
|
|
287
338
|
isRearCamera(stream) {
|
|
@@ -553,6 +604,37 @@ class CameraService {
|
|
|
553
604
|
invalidateDeviceCache() {
|
|
554
605
|
CameraService.deviceEnumerationCache = null;
|
|
555
606
|
}
|
|
607
|
+
isVirtualCamera(device) {
|
|
608
|
+
const label = device.label.toLowerCase();
|
|
609
|
+
const virtualCameraIndicators = [
|
|
610
|
+
'obs',
|
|
611
|
+
'virtual',
|
|
612
|
+
'snap camera',
|
|
613
|
+
'manycam',
|
|
614
|
+
'xsplit',
|
|
615
|
+
'camtwist',
|
|
616
|
+
'ecamm',
|
|
617
|
+
'nvidia broadcast',
|
|
618
|
+
'droidcam',
|
|
619
|
+
'iriun',
|
|
620
|
+
'elgato',
|
|
621
|
+
'streamlabs',
|
|
622
|
+
'wirecast',
|
|
623
|
+
'zoom virtual',
|
|
624
|
+
'teams virtual',
|
|
625
|
+
'mmhmm',
|
|
626
|
+
'camo',
|
|
627
|
+
'reincubate camo',
|
|
628
|
+
'webcamoid',
|
|
629
|
+
'splitcam',
|
|
630
|
+
'cheese',
|
|
631
|
+
'guvcview',
|
|
632
|
+
'yawcam',
|
|
633
|
+
'webcam 7',
|
|
634
|
+
'altserver'
|
|
635
|
+
];
|
|
636
|
+
return virtualCameraIndicators.some(indicator => label.includes(indicator));
|
|
637
|
+
}
|
|
556
638
|
}
|
|
557
639
|
|
|
558
640
|
class LowMemoryDeviceStrategy {
|
|
@@ -650,10 +732,6 @@ class DetectionService {
|
|
|
650
732
|
// Static canvas pool for reuse across instances
|
|
651
733
|
static canvasPool = new Map();
|
|
652
734
|
static MAX_POOL_SIZE = 5;
|
|
653
|
-
// Resource management for optimization
|
|
654
|
-
modelLoadController;
|
|
655
|
-
classificationLoadController;
|
|
656
|
-
modelBlobUrls = new Set();
|
|
657
735
|
constructor(debug = false, useDocumentClassification = false, useDocumentDetector = true) {
|
|
658
736
|
this.debug = debug;
|
|
659
737
|
this.useDocumentClassification = useDocumentClassification;
|
|
@@ -671,28 +749,11 @@ class DetectionService {
|
|
|
671
749
|
return;
|
|
672
750
|
}
|
|
673
751
|
try {
|
|
674
|
-
if (typeof window.ort === 'undefined') {
|
|
675
|
-
throw new Error('ONNX Runtime not available in window object');
|
|
676
|
-
}
|
|
677
|
-
// Create AbortController for this load operation
|
|
678
|
-
this.modelLoadController = new AbortController();
|
|
679
752
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
680
753
|
try {
|
|
681
|
-
|
|
682
|
-
const response = await fetch(this.MODEL_PATH, {
|
|
683
|
-
signal: this.modelLoadController.signal
|
|
684
|
-
});
|
|
685
|
-
if (!response.ok) {
|
|
686
|
-
throw new Error(`Failed to fetch model: ${response.status}`);
|
|
687
|
-
}
|
|
688
|
-
const modelBuffer = await response.arrayBuffer();
|
|
689
|
-
this.session = await window.ort.InferenceSession.create(modelBuffer, sessionOptions);
|
|
754
|
+
this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, sessionOptions);
|
|
690
755
|
}
|
|
691
756
|
catch (error) {
|
|
692
|
-
// Handle abort signal
|
|
693
|
-
if (error.name === 'AbortError') {
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
757
|
// Múltiples tipos de errores de memoria que pueden ocurrir
|
|
697
758
|
const isMemoryError = error.message.includes('failed to allocate a buffer') ||
|
|
698
759
|
error.message.includes('Out of memory') ||
|
|
@@ -714,15 +775,7 @@ class DetectionService {
|
|
|
714
775
|
sessionLogVerbosityLevel: 0,
|
|
715
776
|
};
|
|
716
777
|
try {
|
|
717
|
-
|
|
718
|
-
const fallbackResponse = await fetch(this.MODEL_PATH, {
|
|
719
|
-
signal: this.modelLoadController.signal
|
|
720
|
-
});
|
|
721
|
-
if (!fallbackResponse.ok) {
|
|
722
|
-
throw new Error(`Failed to fetch model with fallback: ${fallbackResponse.status}`);
|
|
723
|
-
}
|
|
724
|
-
const fallbackBuffer = await fallbackResponse.arrayBuffer();
|
|
725
|
-
this.session = await window.ort.InferenceSession.create(fallbackBuffer, fallbackOptions);
|
|
778
|
+
this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, fallbackOptions);
|
|
726
779
|
}
|
|
727
780
|
catch (fallbackError) {
|
|
728
781
|
throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
|
|
@@ -743,13 +796,9 @@ class DetectionService {
|
|
|
743
796
|
return;
|
|
744
797
|
}
|
|
745
798
|
try {
|
|
746
|
-
// Create AbortController for classification model loading
|
|
747
|
-
this.classificationLoadController = new AbortController();
|
|
748
799
|
// Try to load class map (optional - SqueezeNet may not need it for basic classification)
|
|
749
800
|
try {
|
|
750
|
-
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH
|
|
751
|
-
signal: this.classificationLoadController.signal
|
|
752
|
-
});
|
|
801
|
+
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH);
|
|
753
802
|
if (classResponse.ok) {
|
|
754
803
|
this.mobileNetClassMap = await classResponse.json();
|
|
755
804
|
}
|
|
@@ -767,22 +816,9 @@ class DetectionService {
|
|
|
767
816
|
// Load model
|
|
768
817
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
769
818
|
try {
|
|
770
|
-
|
|
771
|
-
const response = await fetch(this.MOBILENET_MODEL_PATH, {
|
|
772
|
-
signal: this.classificationLoadController.signal
|
|
773
|
-
});
|
|
774
|
-
if (!response.ok) {
|
|
775
|
-
throw new Error(`Failed to fetch classification model: ${response.status}`);
|
|
776
|
-
}
|
|
777
|
-
const modelBuffer = await response.arrayBuffer();
|
|
778
|
-
this.mobileNetSession = await window.ort.InferenceSession.create(modelBuffer, sessionOptions);
|
|
819
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, sessionOptions);
|
|
779
820
|
}
|
|
780
821
|
catch (error) {
|
|
781
|
-
// Handle abort signal
|
|
782
|
-
if (error.name === 'AbortError') {
|
|
783
|
-
console.log('Classification model loading was cancelled');
|
|
784
|
-
return;
|
|
785
|
-
}
|
|
786
822
|
// Múltiples tipos de errores de memoria que pueden ocurrir
|
|
787
823
|
const isMemoryError = error.message.includes('failed to allocate a buffer') ||
|
|
788
824
|
error.message.includes('Out of memory') ||
|
|
@@ -804,15 +840,7 @@ class DetectionService {
|
|
|
804
840
|
sessionLogVerbosityLevel: 0,
|
|
805
841
|
};
|
|
806
842
|
try {
|
|
807
|
-
|
|
808
|
-
const fallbackResponse = await fetch(this.MOBILENET_MODEL_PATH, {
|
|
809
|
-
signal: this.classificationLoadController.signal
|
|
810
|
-
});
|
|
811
|
-
if (!fallbackResponse.ok) {
|
|
812
|
-
throw new Error(`Failed to fetch classification model with fallback: ${fallbackResponse.status}`);
|
|
813
|
-
}
|
|
814
|
-
const fallbackBuffer = await fallbackResponse.arrayBuffer();
|
|
815
|
-
this.mobileNetSession = await window.ort.InferenceSession.create(fallbackBuffer, fallbackOptions);
|
|
843
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, fallbackOptions);
|
|
816
844
|
}
|
|
817
845
|
catch (fallbackError) {
|
|
818
846
|
throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
|
|
@@ -1002,9 +1030,6 @@ class DetectionService {
|
|
|
1002
1030
|
return alignment.top && alignment.right && alignment.bottom && alignment.left;
|
|
1003
1031
|
}
|
|
1004
1032
|
cleanup() {
|
|
1005
|
-
// Cancel any ongoing requests
|
|
1006
|
-
this.abortPendingRequests();
|
|
1007
|
-
// Cleanup ONNX resources
|
|
1008
1033
|
this.cleanupCanvasPool();
|
|
1009
1034
|
if (this.session) {
|
|
1010
1035
|
this.session.release?.();
|
|
@@ -1016,8 +1041,6 @@ class DetectionService {
|
|
|
1016
1041
|
}
|
|
1017
1042
|
this.mobileNetClassMap = undefined;
|
|
1018
1043
|
this.modelLoaded = false;
|
|
1019
|
-
// Cleanup blob URLs
|
|
1020
|
-
this.revokeModelBlobUrls();
|
|
1021
1044
|
}
|
|
1022
1045
|
initializeCanvasPool() {
|
|
1023
1046
|
this.preprocessCanvas = document.createElement('canvas');
|
|
@@ -1115,93 +1138,11 @@ class DetectionService {
|
|
|
1115
1138
|
const pool = DetectionService.canvasPool.get(key);
|
|
1116
1139
|
// Only return to pool if not at max capacity
|
|
1117
1140
|
if (pool.length < DetectionService.MAX_POOL_SIZE) {
|
|
1118
|
-
const ctx = canvas.getContext('2d');
|
|
1119
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1120
1141
|
pool.push(canvas);
|
|
1121
1142
|
}
|
|
1122
|
-
// If pool is full, let the canvas be garbage collected
|
|
1123
|
-
}
|
|
1124
|
-
// Resource management methods for cache and browser optimization
|
|
1125
|
-
async clearModelCache() {
|
|
1126
|
-
try {
|
|
1127
|
-
// Clear cache specific to models
|
|
1128
|
-
const cacheNames = await caches.keys();
|
|
1129
|
-
const modelCaches = cacheNames.filter(name => name.includes('onnx') ||
|
|
1130
|
-
name.includes('jaak-static') ||
|
|
1131
|
-
name.includes('squeezenet') ||
|
|
1132
|
-
name.includes('ddmyp'));
|
|
1133
|
-
await Promise.all(modelCaches.map(cacheName => caches.delete(cacheName)));
|
|
1134
|
-
if (this.debug) {
|
|
1135
|
-
console.log(`🧹 Cleared ${modelCaches.length} model caches`);
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
catch (error) {
|
|
1139
|
-
console.warn('Error clearing model cache:', error);
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
abortPendingRequests() {
|
|
1143
|
-
// Cancel any fetch in progress
|
|
1144
|
-
if (this.modelLoadController) {
|
|
1145
|
-
this.modelLoadController.abort();
|
|
1146
|
-
this.modelLoadController = undefined;
|
|
1147
|
-
}
|
|
1148
|
-
if (this.classificationLoadController) {
|
|
1149
|
-
this.classificationLoadController.abort();
|
|
1150
|
-
this.classificationLoadController = undefined;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
revokeModelBlobUrls() {
|
|
1154
|
-
// Revoke any blob URLs created for models
|
|
1155
|
-
this.modelBlobUrls.forEach(url => URL.revokeObjectURL(url));
|
|
1156
|
-
this.modelBlobUrls.clear();
|
|
1157
|
-
}
|
|
1158
|
-
// Progressive resource release for optimization
|
|
1159
|
-
async releaseMobileNetResources() {
|
|
1160
|
-
if (this.mobileNetSession) {
|
|
1161
|
-
this.mobileNetSession.release?.();
|
|
1162
|
-
this.mobileNetSession = undefined;
|
|
1163
|
-
}
|
|
1164
|
-
this.mobileNetClassMap = undefined;
|
|
1165
|
-
if (this.classificationLoadController) {
|
|
1166
|
-
this.classificationLoadController.abort();
|
|
1167
|
-
this.classificationLoadController = undefined;
|
|
1168
|
-
}
|
|
1169
|
-
if (this.debug) {
|
|
1170
|
-
console.log('🧹 Released MobileNet classification resources');
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
// Optimize canvas pool by reducing to specific size
|
|
1174
|
-
optimizeCanvasPool(maxSize) {
|
|
1175
|
-
DetectionService.canvasPool.forEach((pool, key) => {
|
|
1176
|
-
while (pool.length > maxSize) {
|
|
1177
|
-
const canvas = pool.pop();
|
|
1178
|
-
// Explicitly clear and nullify for garbage collection
|
|
1179
|
-
if (canvas) {
|
|
1180
|
-
const ctx = canvas.getContext('2d');
|
|
1181
|
-
if (ctx) {
|
|
1182
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
if (pool.length === 0) {
|
|
1187
|
-
DetectionService.canvasPool.delete(key);
|
|
1188
|
-
}
|
|
1189
|
-
});
|
|
1190
|
-
if (this.debug) {
|
|
1191
|
-
console.log(`🧹 Optimized canvas pool to max size: ${maxSize}`);
|
|
1192
|
-
}
|
|
1193
1143
|
}
|
|
1194
|
-
//
|
|
1195
|
-
static
|
|
1196
|
-
DetectionService.canvasPool.forEach((pool) => {
|
|
1197
|
-
pool.forEach(canvas => {
|
|
1198
|
-
const ctx = canvas.getContext('2d');
|
|
1199
|
-
if (ctx) {
|
|
1200
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1201
|
-
}
|
|
1202
|
-
});
|
|
1203
|
-
pool.length = 0;
|
|
1204
|
-
});
|
|
1144
|
+
// Static method to clear all canvas pools (useful for memory management)
|
|
1145
|
+
static clearCanvasPools() {
|
|
1205
1146
|
DetectionService.canvasPool.clear();
|
|
1206
1147
|
}
|
|
1207
1148
|
// Method to get pool statistics for debugging
|
|
@@ -1260,7 +1201,7 @@ class ServiceContainer {
|
|
|
1260
1201
|
}
|
|
1261
1202
|
}
|
|
1262
1203
|
|
|
1263
|
-
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}}";
|
|
1204
|
+
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}}";
|
|
1264
1205
|
|
|
1265
1206
|
const JaakStamps = class {
|
|
1266
1207
|
constructor(hostRef) {
|
|
@@ -1338,6 +1279,7 @@ const JaakStamps = class {
|
|
|
1338
1279
|
alignmentStartTime;
|
|
1339
1280
|
alignmentTimer;
|
|
1340
1281
|
backDocumentTimer;
|
|
1282
|
+
orientationTimer;
|
|
1341
1283
|
// Performance monitoring
|
|
1342
1284
|
performanceMetrics = {
|
|
1343
1285
|
fps: 0,
|
|
@@ -1441,7 +1383,7 @@ const JaakStamps = class {
|
|
|
1441
1383
|
if (!window.ort) {
|
|
1442
1384
|
this.updateStatus('Preparando herramientas...', 'Configurando funciones de captura', 'initializing');
|
|
1443
1385
|
const script = document.createElement('script');
|
|
1444
|
-
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
|
|
1386
|
+
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.23.0/dist/ort.min.js';
|
|
1445
1387
|
document.head.appendChild(script);
|
|
1446
1388
|
await new Promise((resolve) => {
|
|
1447
1389
|
script.onload = () => {
|
|
@@ -1522,6 +1464,18 @@ const JaakStamps = class {
|
|
|
1522
1464
|
});
|
|
1523
1465
|
resizeObserver.observe(this.detectionContainer.parentElement);
|
|
1524
1466
|
}
|
|
1467
|
+
// Handle mobile device orientation changes
|
|
1468
|
+
window.addEventListener('orientationchange', () => {
|
|
1469
|
+
// Clear any existing orientation timer
|
|
1470
|
+
if (this.orientationTimer) {
|
|
1471
|
+
clearTimeout(this.orientationTimer);
|
|
1472
|
+
}
|
|
1473
|
+
// Add delay to ensure video dimensions are updated after rotation
|
|
1474
|
+
this.orientationTimer = window.setTimeout(() => {
|
|
1475
|
+
this.handleResize();
|
|
1476
|
+
this.orientationTimer = undefined;
|
|
1477
|
+
}, 300);
|
|
1478
|
+
});
|
|
1525
1479
|
}
|
|
1526
1480
|
handleResize() {
|
|
1527
1481
|
if (this.detectionContainer) {
|
|
@@ -1678,13 +1632,7 @@ const JaakStamps = class {
|
|
|
1678
1632
|
}
|
|
1679
1633
|
try {
|
|
1680
1634
|
this.exitSession();
|
|
1681
|
-
|
|
1682
|
-
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1683
|
-
return {
|
|
1684
|
-
success: true,
|
|
1685
|
-
resourcesFreed: cleanupResult.freed,
|
|
1686
|
-
cleanupErrors: cleanupResult.errors
|
|
1687
|
-
};
|
|
1635
|
+
return { success: true };
|
|
1688
1636
|
}
|
|
1689
1637
|
catch (error) {
|
|
1690
1638
|
return { success: false, error: error.message };
|
|
@@ -1909,65 +1857,11 @@ const JaakStamps = class {
|
|
|
1909
1857
|
// Always allow getting capture delay, even during initialization
|
|
1910
1858
|
return this.captureDelay;
|
|
1911
1859
|
}
|
|
1912
|
-
async getResourceReport() {
|
|
1913
|
-
// Public method to get resource usage report
|
|
1914
|
-
return this.getResourceReportInternal();
|
|
1915
|
-
}
|
|
1916
|
-
async forceResourceCleanup() {
|
|
1917
|
-
// Public method to force aggressive resource cleanup
|
|
1918
|
-
const readyCheck = this.isComponentReady();
|
|
1919
|
-
if (!readyCheck.ready) {
|
|
1920
|
-
return { success: false, error: readyCheck.message };
|
|
1921
|
-
}
|
|
1922
|
-
try {
|
|
1923
|
-
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
1924
|
-
return {
|
|
1925
|
-
success: true,
|
|
1926
|
-
resourcesFreed: cleanupResult.freed,
|
|
1927
|
-
errors: cleanupResult.errors,
|
|
1928
|
-
timestamp: new Date().toISOString()
|
|
1929
|
-
};
|
|
1930
|
-
}
|
|
1931
|
-
catch (error) {
|
|
1932
|
-
return { success: false, error: error.message };
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
1860
|
// Memory and device capability detection
|
|
1936
1861
|
checkDeviceCapabilities() {
|
|
1937
1862
|
const deviceMemory = navigator.deviceMemory;
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
1941
|
-
const isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
|
|
1942
|
-
// Si tenemos info de memoria del dispositivo
|
|
1943
|
-
if (deviceMemory && deviceMemory < 4) {
|
|
1944
|
-
return {
|
|
1945
|
-
canUseML: false,
|
|
1946
|
-
reason: `Memoria del dispositivo (${deviceMemory}GB) insuficiente para detección automática. Modo manual activado para mejor rendimiento.`,
|
|
1947
|
-
memoryMB: deviceMemory * 1024
|
|
1948
|
-
};
|
|
1949
|
-
}
|
|
1950
|
-
// Dispositivos móviles de gama baja
|
|
1951
|
-
if (isMobile && isLowEndDevice) {
|
|
1952
|
-
return {
|
|
1953
|
-
canUseML: false,
|
|
1954
|
-
reason: 'Dispositivo móvil optimizado detectado. Modo manual activado para mejor rendimiento y duración de batería.',
|
|
1955
|
-
memoryMB: null
|
|
1956
|
-
};
|
|
1957
|
-
}
|
|
1958
|
-
// Safari moderno soporta WASM, solo bloquearlo en versiones muy antiguas
|
|
1959
|
-
if (isSafari) {
|
|
1960
|
-
const safariMatch = navigator.userAgent.match(/Version\/(\d+)/);
|
|
1961
|
-
const safariVersion = safariMatch ? parseInt(safariMatch[1]) : 0;
|
|
1962
|
-
// Solo bloquear Safari muy antiguo (< version 14)
|
|
1963
|
-
if (safariVersion > 0 && safariVersion < 14) {
|
|
1964
|
-
return {
|
|
1965
|
-
canUseML: false,
|
|
1966
|
-
reason: 'Safari antiguo detectado. Modo manual activado por compatibilidad.',
|
|
1967
|
-
memoryMB: null
|
|
1968
|
-
};
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1863
|
+
// Siempre intentar usar ML, independientemente del dispositivo
|
|
1864
|
+
// Las restricciones anteriores no eran confiables
|
|
1971
1865
|
return {
|
|
1972
1866
|
canUseML: true,
|
|
1973
1867
|
reason: 'Dispositivo compatible con detección automática.',
|
|
@@ -2093,6 +1987,12 @@ const JaakStamps = class {
|
|
|
2093
1987
|
this.videoRef.onloadedmetadata = async () => {
|
|
2094
1988
|
await this.videoRef.play();
|
|
2095
1989
|
this.stateManager.updateCaptureState({ isVideoActive: true });
|
|
1990
|
+
// Recalculate mask dimensions when new camera stream loads
|
|
1991
|
+
if (this.detectionContainer) {
|
|
1992
|
+
const container = this.detectionContainer.parentElement;
|
|
1993
|
+
const rect = container.getBoundingClientRect();
|
|
1994
|
+
this.updateMaskDimensions(rect);
|
|
1995
|
+
}
|
|
2096
1996
|
resolve();
|
|
2097
1997
|
};
|
|
2098
1998
|
});
|
|
@@ -2264,6 +2164,10 @@ const JaakStamps = class {
|
|
|
2264
2164
|
clearTimeout(this.alignmentTimer);
|
|
2265
2165
|
this.alignmentTimer = undefined;
|
|
2266
2166
|
}
|
|
2167
|
+
if (this.orientationTimer) {
|
|
2168
|
+
clearTimeout(this.orientationTimer);
|
|
2169
|
+
this.orientationTimer = undefined;
|
|
2170
|
+
}
|
|
2267
2171
|
// Liberar recursos de ONNX al limpiar el componente
|
|
2268
2172
|
this.releaseOnnxResources();
|
|
2269
2173
|
if (this.performanceUpdateInterval) {
|
|
@@ -2272,182 +2176,11 @@ const JaakStamps = class {
|
|
|
2272
2176
|
}
|
|
2273
2177
|
// Clear canvas pool
|
|
2274
2178
|
this.canvasPool = [];
|
|
2275
|
-
// Clean DOM references and memory leaks
|
|
2276
|
-
this.cleanupDOMReferences();
|
|
2277
2179
|
this.detectionBoxes = [];
|
|
2278
2180
|
this.alignmentStartTime = undefined;
|
|
2279
2181
|
this.hasDocumentDetected = false;
|
|
2280
2182
|
this.serviceContainer?.cleanup();
|
|
2281
2183
|
}
|
|
2282
|
-
// AGGRESSIVE RESOURCE CLEANUP METHODS
|
|
2283
|
-
// Clean DOM references and memory leaks
|
|
2284
|
-
cleanupDOMReferences() {
|
|
2285
|
-
// Remove event listeners that could maintain references
|
|
2286
|
-
if (this.videoRef) {
|
|
2287
|
-
// Remove common event listeners (if any were added)
|
|
2288
|
-
this.videoRef.srcObject = null;
|
|
2289
|
-
this.videoRef = undefined;
|
|
2290
|
-
}
|
|
2291
|
-
// Clean detection container
|
|
2292
|
-
if (this.detectionContainer) {
|
|
2293
|
-
this.detectionContainer = undefined;
|
|
2294
|
-
}
|
|
2295
|
-
// Clean references to images in the DOM and revoke blob URLs
|
|
2296
|
-
const images = this.el.shadowRoot?.querySelectorAll('img');
|
|
2297
|
-
images?.forEach(img => {
|
|
2298
|
-
if (img.src.startsWith('blob:')) {
|
|
2299
|
-
URL.revokeObjectURL(img.src);
|
|
2300
|
-
}
|
|
2301
|
-
img.src = '';
|
|
2302
|
-
});
|
|
2303
|
-
}
|
|
2304
|
-
// Clear browser cache and storage
|
|
2305
|
-
async clearBrowserCache() {
|
|
2306
|
-
try {
|
|
2307
|
-
// 1. Clear Service Worker cache if exists
|
|
2308
|
-
if ('serviceWorker' in navigator) {
|
|
2309
|
-
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
2310
|
-
await Promise.all(registrations.map(registration => registration.unregister()));
|
|
2311
|
-
}
|
|
2312
|
-
// 2. Clear Cache API specific to the component
|
|
2313
|
-
const cacheNames = await caches.keys();
|
|
2314
|
-
const componentCaches = cacheNames.filter(name => name.includes('jaak') || name.includes('stamps'));
|
|
2315
|
-
await Promise.all(componentCaches.map(cacheName => caches.delete(cacheName)));
|
|
2316
|
-
// 3. Force garbage collection if available (only in dev)
|
|
2317
|
-
if (this.debug && window.gc) {
|
|
2318
|
-
window.gc();
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
catch (error) {
|
|
2322
|
-
console.warn('Error clearing browser cache:', error);
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
// Clear local storage specific to the component
|
|
2326
|
-
async clearLocalStorage() {
|
|
2327
|
-
try {
|
|
2328
|
-
// Clear localStorage specific to the component
|
|
2329
|
-
const keysToRemove = [];
|
|
2330
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
2331
|
-
const key = localStorage.key(i);
|
|
2332
|
-
if (key && (key.includes('jaak') || key.includes('onnx') || key.includes('stamps'))) {
|
|
2333
|
-
keysToRemove.push(key);
|
|
2334
|
-
}
|
|
2335
|
-
}
|
|
2336
|
-
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
2337
|
-
// Clear sessionStorage
|
|
2338
|
-
const sessionKeysToRemove = [];
|
|
2339
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
2340
|
-
const key = sessionStorage.key(i);
|
|
2341
|
-
if (key && (key.includes('jaak') || key.includes('onnx'))) {
|
|
2342
|
-
sessionKeysToRemove.push(key);
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
|
|
2346
|
-
}
|
|
2347
|
-
catch (error) {
|
|
2348
|
-
console.warn('Error clearing local storage:', error);
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
// Aggressive resource cleanup combining all strategies
|
|
2352
|
-
async aggressiveResourceCleanup() {
|
|
2353
|
-
const freed = [];
|
|
2354
|
-
const errors = [];
|
|
2355
|
-
try {
|
|
2356
|
-
// 1. Release ONNX resources
|
|
2357
|
-
this.releaseOnnxResources();
|
|
2358
|
-
freed.push('ONNX models');
|
|
2359
|
-
// 2. Clear model cache in detection service
|
|
2360
|
-
if (this.detectionService) {
|
|
2361
|
-
await this.detectionService.clearModelCache();
|
|
2362
|
-
freed.push('Model cache');
|
|
2363
|
-
}
|
|
2364
|
-
// 3. Clear browser cache
|
|
2365
|
-
await this.clearBrowserCache();
|
|
2366
|
-
freed.push('Browser cache');
|
|
2367
|
-
// 4. Clear local storage
|
|
2368
|
-
await this.clearLocalStorage();
|
|
2369
|
-
freed.push('Local storage');
|
|
2370
|
-
// 5. Clean DOM references
|
|
2371
|
-
this.cleanupDOMReferences();
|
|
2372
|
-
freed.push('DOM references');
|
|
2373
|
-
// 6. Optimize canvas pools
|
|
2374
|
-
if (this.detectionService) {
|
|
2375
|
-
this.detectionService.optimizeCanvasPool(0);
|
|
2376
|
-
freed.push('Canvas pools');
|
|
2377
|
-
}
|
|
2378
|
-
// 7. Clear component canvas pool
|
|
2379
|
-
this.canvasPool = [];
|
|
2380
|
-
freed.push('Component canvas pool');
|
|
2381
|
-
// 8. Force garbage collection (if available)
|
|
2382
|
-
if (this.debug && window.gc) {
|
|
2383
|
-
window.gc();
|
|
2384
|
-
freed.push('Garbage collection');
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
catch (error) {
|
|
2388
|
-
errors.push(error.message);
|
|
2389
|
-
}
|
|
2390
|
-
return { freed, errors };
|
|
2391
|
-
}
|
|
2392
|
-
// Progressive resource release after front capture
|
|
2393
|
-
async onFrontCaptureComplete() {
|
|
2394
|
-
try {
|
|
2395
|
-
// Release classification model if not needed for back
|
|
2396
|
-
if (this.detectionService && !this.useDocumentClassification) {
|
|
2397
|
-
await this.detectionService.releaseMobileNetResources();
|
|
2398
|
-
}
|
|
2399
|
-
// Optimize canvas pool to minimum needed for back capture
|
|
2400
|
-
if (this.detectionService) {
|
|
2401
|
-
this.detectionService.optimizeCanvasPool(1);
|
|
2402
|
-
}
|
|
2403
|
-
// Reduce component canvas pool
|
|
2404
|
-
while (this.canvasPool.length > 1) {
|
|
2405
|
-
const canvas = this.canvasPool.pop();
|
|
2406
|
-
if (canvas) {
|
|
2407
|
-
const ctx = canvas.getContext('2d');
|
|
2408
|
-
if (ctx) {
|
|
2409
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
if (this.debug) {
|
|
2414
|
-
console.log('🧹 Progressive cleanup after front capture completed');
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
catch (error) {
|
|
2418
|
-
console.warn('Error in progressive cleanup:', error);
|
|
2419
|
-
}
|
|
2420
|
-
}
|
|
2421
|
-
// Resource reporting for debugging
|
|
2422
|
-
getResourceReportInternal() {
|
|
2423
|
-
const report = {
|
|
2424
|
-
timestamp: new Date().toISOString(),
|
|
2425
|
-
component: {
|
|
2426
|
-
hasVideoStream: !!this.videoStream,
|
|
2427
|
-
hasAnimationFrame: !!this.animationId,
|
|
2428
|
-
canvasPoolSize: this.canvasPool.length,
|
|
2429
|
-
detectionBoxesCount: this.detectionBoxes.length,
|
|
2430
|
-
performanceMonitorActive: !!this.performanceUpdateInterval
|
|
2431
|
-
}
|
|
2432
|
-
};
|
|
2433
|
-
// Add detection service stats if available
|
|
2434
|
-
if (this.detectionService) {
|
|
2435
|
-
report.detectionService = {
|
|
2436
|
-
isModelLoaded: this.detectionService.isModelLoaded(),
|
|
2437
|
-
canvasPoolStats: this.detectionService.getPoolStats()
|
|
2438
|
-
};
|
|
2439
|
-
}
|
|
2440
|
-
// Add memory info if available
|
|
2441
|
-
if ('memory' in performance) {
|
|
2442
|
-
const memInfo = performance.memory;
|
|
2443
|
-
report.memory = {
|
|
2444
|
-
usedJSHeapSize: Math.round(memInfo.usedJSHeapSize / 1048576), // MB
|
|
2445
|
-
totalJSHeapSize: Math.round(memInfo.totalJSHeapSize / 1048576), // MB
|
|
2446
|
-
jsHeapSizeLimit: Math.round(memInfo.jsHeapSizeLimit / 1048576) // MB
|
|
2447
|
-
};
|
|
2448
|
-
}
|
|
2449
|
-
return report;
|
|
2450
|
-
}
|
|
2451
2184
|
render() {
|
|
2452
2185
|
const captureState = this.stateManager?.getCaptureState() || {
|
|
2453
2186
|
isVideoActive: false,
|
|
@@ -2457,7 +2190,7 @@ const JaakStamps = class {
|
|
|
2457
2190
|
isCapturing: false
|
|
2458
2191
|
};
|
|
2459
2192
|
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
2460
|
-
return (index.h("div", { key: '
|
|
2193
|
+
return (index.h("div", { key: 'f9cbca2fd6b8548a29ace7fcb972b53f402f61af', class: "detector-container" }, index.h("div", { key: '4f3cac734eb6bfd44a8de644eadca17e7893e11a', class: "video-container" }, index.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' } }), index.h("div", { key: '38c8ad8b691db60e9b5a2a8b641fe6febbeacfca', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index$1) => (index.h("div", { key: index$1, class: "detection-box", style: {
|
|
2461
2194
|
position: 'absolute',
|
|
2462
2195
|
left: `${box.x}px`,
|
|
2463
2196
|
top: `${box.y}px`,
|
|
@@ -2466,9 +2199,9 @@ const JaakStamps = class {
|
|
|
2466
2199
|
border: '2px solid #32406C',
|
|
2467
2200
|
pointerEvents: 'none',
|
|
2468
2201
|
boxSizing: 'border-box'
|
|
2469
|
-
} })))), this.isMaskReady && (index.h("div", { key: '
|
|
2202
|
+
} })))), this.isMaskReady && (index.h("div", { key: 'c56fde56b7a86a4e8a4cf558745291ea5cde12fd', class: "overlay-mask" }, index.h("div", { key: '04ed3f01f271dc84eed594cb4b60c2453645bd41', class: "card-outline" }, index.h("div", { key: '648bd423a5e886d4768115237de5896c7223b30b', class: "side side-top" }), index.h("div", { key: 'b8878059493ca61961b9b3c82ecc18b50b79ac85', class: "side side-right" }), index.h("div", { key: '09e5ad64ff33dbd5c112bf52b71e14409ab6aa65', class: "side side-bottom" }), index.h("div", { key: 'b8ba3d83c9bc1b4a0da859d7ed4b9a15eaddcc2a', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '3279701a240f77ac96e68a34ad95c099340c9a6a', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '2adf96d0a57e482372fd9586229dac2301b64fd4', class: "back-capture-section" }, index.h("div", { key: '305a147c9cfee854c60ac946a48a1d21b2792725', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (index.h("button", { key: '43270a0647f39307229c540dd5130fb58b1ff4fb', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (index.h("span", { key: 'eb4666603774564eef95a120ac13faeb9e10c8b4', class: "button-spinner" })), "Capturar Reverso")), index.h("button", { key: 'cac667d9548ae2314ad5a677c5ed90f25184f728', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (index.h("span", { key: '0af7aa4106ce189ae2d79bec78ede01abe25b1e7', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
2470
2203
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
2471
|
-
: 'Saltar reverso')))), captureState.isVideoActive && (index.h("div", { key: '
|
|
2204
|
+
: 'Saltar reverso')))), captureState.isVideoActive && (index.h("div", { key: 'de6e5c4fad4d4d996b894457c9af52bc71e533fb', class: "camera-controls" }, index.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 ? (index.h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (index.h("div", { key: '5fb73f958b9a5a6f9242032c4530b0bdab84f6c5', class: "camera-selector-dropdown" }, index.h("div", { key: '8ee2d26b1001e571e7b6c326e927926fa08287fd', class: "camera-selector-header" }, index.h("span", { key: '78350b0fb1bd933f7c8c59d8dab0b7122ffbe363' }, "Seleccionar C\u00E1mara"), index.h("button", { key: 'bb750e0ddd5a1ee5ff96bd57805a7f90b8821e9f', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: 'd0f6343cf1b7367f3da33ec8cf64891109d923b8', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (index.h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (index.h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: 'c9642fbfb0acea766d3f0542f4cc9f17f4af4d7b', class: "device-info" }, index.h("small", { key: 'b6e9507ff7addf011f339f6c22a88a96b40db68a' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '04ae9262409284782850b90945eb7038673e2097', class: "manual-capture-section" }, index.h("button", { key: '27c315e503ccba468b89ebd58d6b70f40ddd06ba', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (index.h("span", { key: '8f2dcd2df9a218b37eb0b8204a8724362cfeaa53', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (index.h("div", { key: '6fd6fe1d0213b01c8119a67fb8108f73227feee1', class: "capture-animation" })), captureState.showFlipAnimation && (index.h("div", { key: 'fba3816b48b99ca64510ecbefc0d41431569c9ae', class: "flip-animation" }, index.h("div", { key: 'beea1c8245b5d81f1008efc6d1b079a61c821e7f', class: "id-card-icon" }), index.h("div", { key: 'f0fbc34f619a653e2c0ade764ca99e7aa582d157', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (index.h("div", { key: 'e4a0e6d31112e3a50024d1d0430b4ad997049fe7', class: "success-animation" }, index.h("div", { key: '5dd3f781f55630bc904c81207548ae35bc79b452', class: "check-icon" }), index.h("div", { key: 'a10ebc557582325091efb0abd12d62b27a43b98c', class: "success-text" }, "\u00A1Proceso completado!"))), index.h("div", { key: '975865955dee1803b5215ecf049701c98b324855', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (index.h("div", { key: '84f6f67202e26ce80f7fc5d5f321e23b5bc55c57', class: "status-spinner" })), index.h("div", { key: 'a1ae56ed156e8feb279a1bf160f749c5d0b5ce45', class: "status-content" }, index.h("div", { key: 'd62932554cea257ce19763df26e0ba702e50eeca', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (index.h("div", { key: 'c5506fa12dd0d37d26dc26c9f2381c843330451f', class: "status-description" }, this.currentStatus.description)))), this.debug && (index.h("div", { key: '2097eda63185f336f7319014fae2ef6db1e41761', class: "performance-monitor" }, index.h("div", { key: 'd611c494c6b1a5930ef64aebdcba41e5d94c9ee6', class: "performance-expanded" }, index.h("div", { key: '800d46831e7b9bec260d9c5e67ea1a3eb9c2933e', class: "metrics-row" }, index.h("div", { key: 'dd0db1faa9050d98e71da4b5cd26a4a484c37c64', class: "metric-compact" }, index.h("span", { key: 'f0e6ea61a35ddb2ae5c2a6aff8c255a02c5c1219', class: "metric-label" }, "FPS"), index.h("span", { key: 'cbf4df4d142f6f01e45f566a8ede8fbbf29ad673', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), index.h("div", { key: '83861ad31fa46c8199a5583e4b3f8eefd44bacd6', class: "metric-compact" }, index.h("span", { key: '41b8de4cda47959dfc9df393e95fdadde534dd21', class: "metric-label" }, "MEM"), index.h("span", { key: 'b268f13cc78066e240ed158f1443113a94827aff', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), index.h("div", { key: '9f9a9d0690906b186492af23b900c5fa0f5cff89', class: "metrics-row" }, index.h("div", { key: '84fa6e453dc36e4a74e8b573447b17230766229e', class: "metric-compact" }, index.h("span", { key: 'f17d52899090c89f532d74c552dd8c5338fc2d83', class: "metric-label" }, "INF"), index.h("span", { key: '2beca0d7ce55ed574f6b01efc086c5846983e024', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), index.h("div", { key: '981e300cf076e81bfe946974f6cffd63619eacde', class: "metric-compact" }, index.h("span", { key: '3dfb0c996a69eb87b0f711322675cad69c471146', class: "metric-label" }, "FRAME"), index.h("span", { key: '3ae2ac6e90cda77f23e559f3197c3b761f9bdc40', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), index.h("div", { key: '2c51216838d87728d9d7764e5ba8a50fa02eb493', class: "metrics-row" }, index.h("div", { key: 'eda810ffb80ec38ab84583f59b26706f90a50fc9', class: "metric-compact" }, index.h("span", { key: '978ba9c0c67d6da6b29b3535af33a314bab9276a', class: "metric-label" }, "DET"), index.h("span", { key: '3e5e169fd4a3906283d8f918b5bbc7c5df6ccd0f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), index.h("div", { key: 'b4b915c745e35c09470a4dc5edfc112d32cfb8b6', class: "metric-compact" }, index.h("span", { key: '3e2a7b88a4446813c955efb0dfa9e2958e8e1448', class: "metric-label" }, "RATE"), index.h("span", { key: 'f4f073f7ee622167a241c6a1c535ef580c13d9ab', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), index.h("div", { key: '76c556e92f6d7980539f1a30efe8751f665164fb', class: "watermark" }, index.h("img", { key: 'af23e115d6b4e92a72e55d6d98fde0e4b1d09e9b', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
2472
2205
|
}
|
|
2473
2206
|
// Utility methods
|
|
2474
2207
|
updateDetectionBoxes(boxes) {
|
|
@@ -2665,8 +2398,8 @@ const JaakStamps = class {
|
|
|
2665
2398
|
if (captureState.step === 'front') {
|
|
2666
2399
|
this.stateManager.setCapturedImages({
|
|
2667
2400
|
front: {
|
|
2668
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
2669
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
2401
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
2402
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
2670
2403
|
}
|
|
2671
2404
|
});
|
|
2672
2405
|
// Check if document classification is enabled (independent of detector)
|
|
@@ -2692,8 +2425,6 @@ const JaakStamps = class {
|
|
|
2692
2425
|
showFlipAnimation: true
|
|
2693
2426
|
});
|
|
2694
2427
|
this.triggerRerender();
|
|
2695
|
-
// Progressive resource cleanup after front capture
|
|
2696
|
-
await this.onFrontCaptureComplete();
|
|
2697
2428
|
setTimeout(() => {
|
|
2698
2429
|
this.stateManager.updateCaptureState({
|
|
2699
2430
|
showFlipAnimation: false,
|
|
@@ -2708,8 +2439,8 @@ const JaakStamps = class {
|
|
|
2708
2439
|
else if (captureState.step === 'back') {
|
|
2709
2440
|
this.stateManager.setCapturedImages({
|
|
2710
2441
|
back: {
|
|
2711
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
2712
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
2442
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
2443
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
2713
2444
|
}
|
|
2714
2445
|
});
|
|
2715
2446
|
this.completeProcess(false);
|
|
@@ -2747,8 +2478,8 @@ const JaakStamps = class {
|
|
|
2747
2478
|
if (captureState.step === 'front') {
|
|
2748
2479
|
this.stateManager.setCapturedImages({
|
|
2749
2480
|
front: {
|
|
2750
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
2751
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
2481
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
2482
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
2752
2483
|
}
|
|
2753
2484
|
});
|
|
2754
2485
|
// Check if document classification is enabled
|
|
@@ -2769,8 +2500,6 @@ const JaakStamps = class {
|
|
|
2769
2500
|
isDetectionPaused: true,
|
|
2770
2501
|
showFlipAnimation: true
|
|
2771
2502
|
});
|
|
2772
|
-
// Progressive resource cleanup after front capture
|
|
2773
|
-
await this.onFrontCaptureComplete();
|
|
2774
2503
|
setTimeout(() => {
|
|
2775
2504
|
this.stateManager.updateCaptureState({
|
|
2776
2505
|
showFlipAnimation: false,
|
|
@@ -2782,8 +2511,8 @@ const JaakStamps = class {
|
|
|
2782
2511
|
else if (captureState.step === 'back') {
|
|
2783
2512
|
this.stateManager.setCapturedImages({
|
|
2784
2513
|
back: {
|
|
2785
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
2786
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
2514
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
2515
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
2787
2516
|
}
|
|
2788
2517
|
});
|
|
2789
2518
|
this.completeProcess(false);
|
|
@@ -2909,10 +2638,13 @@ const JaakStamps = class {
|
|
|
2909
2638
|
this.useDocumentDetector = false; // Desactivar detector automático
|
|
2910
2639
|
// Actualizar el estado con mensaje de error
|
|
2911
2640
|
this.updateStatus('Modo manual activado', errorMessage, 'error');
|
|
2912
|
-
//
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2641
|
+
// Continuar con la configuración de cámara para mostrar el video
|
|
2642
|
+
this.continueWithCameraSetup().catch(() => {
|
|
2643
|
+
// Si falla la configuración de cámara, mantener el mensaje de error pero cambiar el estado
|
|
2644
|
+
setTimeout(() => {
|
|
2645
|
+
this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
|
|
2646
|
+
}, 5000);
|
|
2647
|
+
});
|
|
2916
2648
|
}
|
|
2917
2649
|
stopPerformanceMonitoring() {
|
|
2918
2650
|
// Nota: No necesitamos limpiar más variables porque ya no se usarán
|
|
@@ -2948,16 +2680,6 @@ const JaakStamps = class {
|
|
|
2948
2680
|
setTimeout(() => {
|
|
2949
2681
|
this.stateManager.updateCaptureState({ showSuccessAnimation: false });
|
|
2950
2682
|
}, 3000);
|
|
2951
|
-
// Aggressive cleanup after process completion with delay
|
|
2952
|
-
setTimeout(async () => {
|
|
2953
|
-
const cleanupResult = await this.aggressiveResourceCleanup();
|
|
2954
|
-
if (this.debug) {
|
|
2955
|
-
console.log('🧹 Post-completion cleanup:', cleanupResult.freed);
|
|
2956
|
-
if (cleanupResult.errors.length > 0) {
|
|
2957
|
-
console.warn('⚠️ Cleanup errors:', cleanupResult.errors);
|
|
2958
|
-
}
|
|
2959
|
-
}
|
|
2960
|
-
}, 1000); // Wait 1 second after completion
|
|
2961
2683
|
}
|
|
2962
2684
|
stopDetection() {
|
|
2963
2685
|
if (this.animationId) {
|
|
@@ -3009,6 +2731,12 @@ const JaakStamps = class {
|
|
|
3009
2731
|
try {
|
|
3010
2732
|
// Close the selector immediately when user selects a camera
|
|
3011
2733
|
this.showCameraSelector = false;
|
|
2734
|
+
// Check if user selected the same camera that's already active
|
|
2735
|
+
const currentCameraId = this.cameraService.getSelectedCameraId();
|
|
2736
|
+
if (currentCameraId === cameraId) {
|
|
2737
|
+
// Same camera selected, just close the selector without switching
|
|
2738
|
+
return;
|
|
2739
|
+
}
|
|
3012
2740
|
this.isSwitchingCamera = true;
|
|
3013
2741
|
// Stop current video stream
|
|
3014
2742
|
if (this.videoStream) {
|