@jaak.ai/stamps 2.0.0-dev.56 → 2.0.0-dev.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
  2. package/dist/cjs/jaak-stamps.cjs.entry.js +547 -612
  3. package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
  4. package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/collection/components/my-component/my-component.css +14 -2
  7. package/dist/collection/components/my-component/my-component.js +347 -238
  8. package/dist/collection/components/my-component/my-component.js.map +1 -1
  9. package/dist/collection/services/CameraService.js +156 -344
  10. package/dist/collection/services/CameraService.js.map +1 -1
  11. package/dist/collection/services/DetectionService.js +53 -38
  12. package/dist/collection/services/DetectionService.js.map +1 -1
  13. package/dist/collection/services/EventBusService.js +1 -1
  14. package/dist/collection/services/EventBusService.js.map +1 -1
  15. package/dist/collection/services/ServiceContainer.js +2 -12
  16. package/dist/collection/services/ServiceContainer.js.map +1 -1
  17. package/dist/collection/services/interfaces/ICameraService.js.map +1 -1
  18. package/dist/collection/types/component-types.js.map +1 -1
  19. package/dist/components/jaak-stamps.js +549 -615
  20. package/dist/components/jaak-stamps.js.map +1 -1
  21. package/dist/esm/jaak-stamps-webcomponent.js +1 -1
  22. package/dist/esm/jaak-stamps.entry.js +547 -612
  23. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  24. package/dist/esm/loader.js +1 -1
  25. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  26. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  27. package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js +2 -0
  28. package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js.map +1 -0
  29. package/dist/types/components/my-component/my-component.d.ts +62 -17
  30. package/dist/types/components.d.ts +6 -8
  31. package/dist/types/services/CameraService.d.ts +12 -14
  32. package/dist/types/services/DetectionService.d.ts +9 -3
  33. package/dist/types/services/ServiceContainer.d.ts +0 -2
  34. package/dist/types/services/interfaces/ICameraService.d.ts +3 -12
  35. package/dist/types/types/component-types.d.ts +3 -0
  36. package/package.json +2 -2
  37. package/dist/collection/services/LoggerService.js +0 -40
  38. package/dist/collection/services/LoggerService.js.map +0 -1
  39. package/dist/collection/services/interfaces/ILogger.js +0 -2
  40. package/dist/collection/services/interfaces/ILogger.js.map +0 -1
  41. package/dist/jaak-stamps-webcomponent/p-65990bdd.entry.js +0 -2
  42. package/dist/jaak-stamps-webcomponent/p-65990bdd.entry.js.map +0 -1
  43. package/dist/types/services/LoggerService.d.ts +0 -12
  44. package/dist/types/services/interfaces/ILogger.d.ts +0 -8
@@ -23,9 +23,16 @@ export class JaakStamps {
23
23
  showCameraSelector = false;
24
24
  isSwitchingCamera = false;
25
25
  hasDocumentDetected = false;
26
+ cameraInfoWithAutofocus = {
27
+ availableCameras: [],
28
+ selectedCameraId: null,
29
+ deviceType: 'desktop',
30
+ isMultipleCamerasAvailable: false,
31
+ preferredFacing: null
32
+ };
26
33
  currentStatus = {
27
- message: 'Inicializando componente...',
28
- description: 'Configurando servicios y cargando recursos',
34
+ message: 'Preparando capturador...',
35
+ description: 'Configurando herramientas de captura',
29
36
  type: 'initializing',
30
37
  isInitialized: false
31
38
  };
@@ -42,7 +49,6 @@ export class JaakStamps {
42
49
  backDocumentTimerRemaining = 0;
43
50
  // Services
44
51
  serviceContainer;
45
- logger;
46
52
  eventBus;
47
53
  stateManager;
48
54
  cameraService;
@@ -73,19 +79,25 @@ export class JaakStamps {
73
79
  lastUpdateTime: 0
74
80
  };
75
81
  performanceUpdateInterval;
76
- // Performance optimization
82
+ // Performance optimization with adaptive frame skipping
77
83
  frameSkipCounter = 0;
78
- FRAME_SKIP = 2;
84
+ BASE_FRAME_SKIP = 2;
85
+ MAX_FRAME_SKIP = 8;
79
86
  consecutiveFailures = 0;
80
87
  MAX_FAILURES = 30;
81
88
  lastInferenceTime = 0;
82
89
  MIN_INFERENCE_INTERVAL = 50;
90
+ performanceHistory = [];
91
+ PERFORMANCE_HISTORY_SIZE = 10;
92
+ // Canvas pool for optimized screenshot capture
93
+ canvasPool = [];
94
+ MAX_CANVAS_POOL_SIZE = 3;
83
95
  async componentDidLoad() {
84
- this.updateStatus('Iniciando servicios...', 'Configurando módulos internos', 'initializing');
96
+ this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
85
97
  await this.initializeServices();
86
- this.updateStatus('Configurando eventos...', 'Preparando comunicación entre servicios', 'initializing');
98
+ this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
87
99
  await this.setupEventListeners();
88
- this.updateStatus('Inicializando cámara...', 'Detectando dispositivos disponibles', 'initializing');
100
+ this.updateStatus('Configurando cámara...', 'Detectando dispositivos disponibles', 'initializing');
89
101
  await this.initializeComponent();
90
102
  if (this.debug) {
91
103
  this.initializePerformanceMonitor();
@@ -102,33 +114,23 @@ export class JaakStamps {
102
114
  captureDelay: this.captureDelay
103
115
  };
104
116
  this.serviceContainer = new ServiceContainer(config);
105
- this.logger = this.serviceContainer.getLogger();
106
117
  this.eventBus = this.serviceContainer.getEventBus();
107
118
  this.stateManager = this.serviceContainer.getStateManager();
108
119
  this.cameraService = this.serviceContainer.getCameraService();
109
120
  this.detectionService = this.serviceContainer.getDetectionService();
110
- this.logger.state('SERVICIOS_INICIALIZADOS', { timestamp: Date.now() });
111
121
  }
112
122
  async setupEventListeners() {
113
123
  this.eventBus.on('state-changed', (data) => {
114
124
  this.handleStateChange(data);
115
125
  });
116
- this.eventBus.on('camera-changed', (cameraId) => {
117
- this.logger.state('CAMARA_CAMBIADA_EVENT', { cameraId });
126
+ this.eventBus.on('camera-changed', () => {
127
+ // Camera changed event
118
128
  });
119
- this.eventBus.on('error', (error) => {
120
- this.logger.error('Error en servicio:', error);
129
+ this.eventBus.on('error', () => {
130
+ // Handle service errors
121
131
  });
122
132
  }
123
133
  async initializeComponent() {
124
- this.logger.state('COMPONENTE_INICIALIZANDO', {
125
- debug: this.debug,
126
- maskSize: this.maskSize,
127
- cropMargin: this.cropMargin,
128
- useDocumentClassification: this.useDocumentClassification,
129
- preferredCamera: this.preferredCamera,
130
- captureDelay: this.captureDelay
131
- });
132
134
  this.validateProps();
133
135
  if (this.debug) {
134
136
  this.stateManager.updateCaptureState({ isLoading: true });
@@ -137,32 +139,29 @@ export class JaakStamps {
137
139
  this.updateStatus('Detectando cámaras...', 'Buscando dispositivos de captura', 'initializing');
138
140
  await this.cameraService.detectDeviceType();
139
141
  await this.cameraService.enumerateDevices();
140
- this.updateStatus('Cargando modelo IA...', 'Preparando reconocimiento de documentos', 'initializing');
142
+ await this.updateCameraInfoWithAutofocus();
143
+ this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'initializing');
141
144
  await this.loadOnnxRuntime();
142
145
  this.initializeResizeObserver();
143
146
  }
144
147
  validateProps() {
145
148
  if (this.maskSize < 50 || this.maskSize > 100) {
146
- this.logger.warn(`Propiedad maskSize inválida. Valor: ${this.maskSize}, esperado: 50-100. Usando valor por defecto: 90`);
147
149
  this.maskSize = 90;
148
150
  }
149
151
  if (this.cropMargin < 0 || this.cropMargin > 100) {
150
- this.logger.warn(`Propiedad cropMargin inválida. Valor: ${this.cropMargin}, esperado: 0-100. Usando valor por defecto: 0`);
151
152
  this.cropMargin = 0;
152
153
  }
153
154
  const validOptions = ['auto', 'front', 'back'];
154
155
  if (!validOptions.includes(this.preferredCamera)) {
155
- this.logger.warn(`Propiedad preferredCamera inválida. Valor: ${this.preferredCamera}, esperado: ${validOptions.join(', ')}. Usando valor por defecto: 'auto'`);
156
156
  this.preferredCamera = 'auto';
157
157
  }
158
158
  if (this.captureDelay < 0 || this.captureDelay > 10000) {
159
- this.logger.warn(`Propiedad captureDelay inválida. Valor: ${this.captureDelay}, esperado: 0-10000. Usando valor por defecto: 1500`);
160
159
  this.captureDelay = 1500;
161
160
  }
162
161
  }
163
162
  async loadOnnxRuntime() {
164
163
  if (!window.ort) {
165
- this.updateStatus('Descargando librerías...', 'Obteniendo recursos de reconocimiento', 'initializing');
164
+ this.updateStatus('Preparando herramientas...', 'Configurando funciones de captura', 'initializing');
166
165
  const script = document.createElement('script');
167
166
  script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
168
167
  document.head.appendChild(script);
@@ -202,11 +201,6 @@ export class JaakStamps {
202
201
  emitReadyEvent() {
203
202
  const isDocumentReady = !!window.ort && this.detectionService.isModelLoaded();
204
203
  this.isReady.emit(isDocumentReady);
205
- this.logger.state('COMPONENTE_LISTO', {
206
- ortLibraryLoaded: !!window.ort,
207
- modelPreloaded: this.detectionService.isModelLoaded(),
208
- isReady: isDocumentReady
209
- });
210
204
  }
211
205
  handleStateChange(data) {
212
206
  // React to state changes from StateManager
@@ -229,7 +223,6 @@ export class JaakStamps {
229
223
  const container = this.detectionContainer.parentElement;
230
224
  const rect = container.getBoundingClientRect();
231
225
  this.updateMaskDimensions(rect);
232
- this.logger.debug('Container redimensionado:', { width: rect.width, height: rect.height });
233
226
  }
234
227
  }
235
228
  updateMaskDimensions(containerRect) {
@@ -283,16 +276,38 @@ export class JaakStamps {
283
276
  this.el.style.setProperty('--mask-center-x', `${videoCenterXPercent}%`);
284
277
  this.el.style.setProperty('--mask-center-y', `${videoCenterYPercent}%`);
285
278
  this.isMaskReady = true;
286
- this.logger.state('DIMENSIONES_MASCARA_ACTUALIZADAS', {
287
- video: { width: videoWidth, height: videoHeight },
288
- displayed: { width: displayedVideoWidth, height: displayedVideoHeight },
289
- mask: { widthPercent: maskWidthPercent, heightPercent: maskHeightPercent },
290
- center: { x: videoCenterXPercent, y: videoCenterYPercent },
291
- offset: { x: videoOffsetX, y: videoOffsetY }
292
- });
293
279
  }
294
280
  // PUBLIC METHODS
281
+ // Helper method to check if component is ready for operations
282
+ isComponentReady() {
283
+ if (!this.currentStatus.isInitialized) {
284
+ return {
285
+ ready: false,
286
+ message: 'El componente aún se está inicializando',
287
+ status: 'initializing'
288
+ };
289
+ }
290
+ if (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') {
291
+ return {
292
+ ready: false,
293
+ message: 'El componente está cargando recursos',
294
+ status: 'loading'
295
+ };
296
+ }
297
+ if (!this.serviceContainer || !this.stateManager || !this.cameraService || !this.detectionService) {
298
+ return {
299
+ ready: false,
300
+ message: 'Los servicios del componente no están disponibles',
301
+ status: 'error'
302
+ };
303
+ }
304
+ return { ready: true };
305
+ }
295
306
  async getCapturedImages() {
307
+ const readyCheck = this.isComponentReady();
308
+ if (!readyCheck.ready) {
309
+ throw new Error(readyCheck.message);
310
+ }
296
311
  const state = this.stateManager.getCaptureState();
297
312
  if (state.step !== 'completed') {
298
313
  throw new Error('El proceso de captura no ha sido completado');
@@ -300,47 +315,128 @@ export class JaakStamps {
300
315
  return this.stateManager.getCapturedImages();
301
316
  }
302
317
  async isProcessCompleted() {
318
+ const readyCheck = this.isComponentReady();
319
+ if (!readyCheck.ready) {
320
+ return false; // Return false instead of error for this method
321
+ }
303
322
  return this.stateManager.isProcessCompleted();
304
323
  }
305
324
  async startCapture() {
306
- await this.startDetection();
325
+ const readyCheck = this.isComponentReady();
326
+ if (!readyCheck.ready) {
327
+ this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
328
+ return { success: false, error: readyCheck.message };
329
+ }
330
+ try {
331
+ await this.startDetection();
332
+ return { success: true };
333
+ }
334
+ catch (error) {
335
+ this.updateStatus('Error al iniciar captura', error.message, 'error');
336
+ return { success: false, error: error.message };
337
+ }
307
338
  }
308
339
  async stopCapture() {
309
- this.exitSession();
340
+ const readyCheck = this.isComponentReady();
341
+ if (!readyCheck.ready) {
342
+ return { success: false, error: readyCheck.message };
343
+ }
344
+ try {
345
+ this.exitSession();
346
+ return { success: true };
347
+ }
348
+ catch (error) {
349
+ return { success: false, error: error.message };
350
+ }
310
351
  }
311
352
  async resetCapture() {
312
- this.resetDetection();
353
+ const readyCheck = this.isComponentReady();
354
+ if (!readyCheck.ready) {
355
+ return { success: false, error: readyCheck.message };
356
+ }
357
+ try {
358
+ this.resetDetection();
359
+ return { success: true };
360
+ }
361
+ catch (error) {
362
+ return { success: false, error: error.message };
363
+ }
313
364
  }
314
365
  async skipBackCapture() {
315
- const captureState = this.stateManager.getCaptureState();
316
- const capturedImages = this.stateManager.getCapturedImages();
317
- if (captureState.step === 'back' && capturedImages.front.fullFrame) {
318
- this.completeProcess(true);
366
+ const readyCheck = this.isComponentReady();
367
+ if (!readyCheck.ready) {
368
+ return { success: false, error: readyCheck.message };
369
+ }
370
+ try {
371
+ const captureState = this.stateManager.getCaptureState();
372
+ const capturedImages = this.stateManager.getCapturedImages();
373
+ if (captureState.step === 'back' && capturedImages.front.fullFrame) {
374
+ this.completeProcess(true);
375
+ return { success: true };
376
+ }
377
+ else {
378
+ return { success: false, error: 'No se puede saltar el reverso en el estado actual' };
379
+ }
380
+ }
381
+ catch (error) {
382
+ return { success: false, error: error.message };
319
383
  }
320
384
  }
321
385
  async getStatus() {
322
- const captureState = this.stateManager.getCaptureState();
323
- const capturedImages = this.stateManager.getCapturedImages();
324
- return {
325
- isVideoActive: captureState.isVideoActive,
326
- captureStep: captureState.step,
327
- hasImages: !!(capturedImages.front.fullFrame || capturedImages.back.fullFrame),
328
- isProcessCompleted: this.stateManager.isProcessCompleted(),
329
- isModelPreloaded: this.detectionService.isModelLoaded()
330
- };
386
+ const readyCheck = this.isComponentReady();
387
+ if (!readyCheck.ready) {
388
+ return {
389
+ isVideoActive: false,
390
+ captureStep: 'front',
391
+ hasImages: false,
392
+ isProcessCompleted: false,
393
+ isModelPreloaded: false,
394
+ componentStatus: readyCheck.status,
395
+ componentMessage: readyCheck.message
396
+ };
397
+ }
398
+ try {
399
+ const captureState = this.stateManager.getCaptureState();
400
+ const capturedImages = this.stateManager.getCapturedImages();
401
+ return {
402
+ isVideoActive: captureState.isVideoActive,
403
+ captureStep: captureState.step,
404
+ hasImages: !!(capturedImages.front.fullFrame || capturedImages.back.fullFrame),
405
+ isProcessCompleted: this.stateManager.isProcessCompleted(),
406
+ isModelPreloaded: this.detectionService.isModelLoaded(),
407
+ componentStatus: 'ready'
408
+ };
409
+ }
410
+ catch (error) {
411
+ return {
412
+ isVideoActive: false,
413
+ captureStep: 'front',
414
+ hasImages: false,
415
+ isProcessCompleted: false,
416
+ isModelPreloaded: false,
417
+ componentStatus: 'error',
418
+ componentMessage: error.message
419
+ };
420
+ }
331
421
  }
332
422
  async preloadModel() {
423
+ const readyCheck = this.isComponentReady();
424
+ if (!readyCheck.ready) {
425
+ this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
426
+ return { success: false, error: readyCheck.message };
427
+ }
333
428
  if (this.detectionService.isModelLoaded()) {
334
- this.logger.state('MODELO_YA_PRECARGADO');
335
- this.updateStatus('Modelos ya cargados', '', 'ready');
429
+ this.updateStatus('Reconocimiento listo', '', 'ready');
336
430
  return { success: true, message: 'Model already loaded' };
337
431
  }
338
432
  try {
339
433
  const loadStartTime = performance.now();
340
- this.updateStatus('Descargando modelo de detección...', 'Obteniendo red neuronal para reconocimiento de documentos', 'loading');
341
- this.stateManager.updateCaptureState({ isLoading: true });
434
+ this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
435
+ if (this.stateManager) {
436
+ this.stateManager.updateCaptureState({ isLoading: true });
437
+ }
342
438
  await this.detectionService.loadModel();
343
- this.updateStatus('Cargando clasificador...', 'Preparando modelo de clasificación de tipos de documento', 'loading');
439
+ this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
344
440
  await this.detectionService.loadClassificationModel();
345
441
  const loadEndTime = performance.now();
346
442
  const loadTime = loadEndTime - loadStartTime;
@@ -348,80 +444,118 @@ export class JaakStamps {
348
444
  if (this.debug) {
349
445
  this.recordOnnxPerformance(loadTime, 0);
350
446
  }
351
- this.updateStatus('Optimizando modelos...', 'Configurando parámetros de rendimiento', 'loading');
447
+ this.updateStatus('Finalizando configuración...', 'Preparando herramientas para captura', 'loading');
352
448
  await new Promise(resolve => setTimeout(resolve, 300));
353
- this.updateStatus('Modelos precargados', '', 'ready');
354
- this.stateManager.updateCaptureState({ isLoading: false });
449
+ this.updateStatus('Reconocimiento preparado', '', 'ready');
450
+ if (this.stateManager) {
451
+ this.stateManager.updateCaptureState({ isLoading: false });
452
+ }
355
453
  this.emitReadyEvent();
356
- this.logger.state('MODELOS_PRECARGADOS_EXITOSAMENTE', { loadTime: Math.round(loadTime) });
357
454
  return { success: true, message: 'Models preloaded successfully' };
358
455
  }
359
456
  catch (error) {
360
- this.logger.error('Error al precargar modelos:', error);
361
- this.updateStatus('Error al cargar modelos', 'No se pudieron descargar los recursos necesarios', 'error');
362
- this.stateManager.updateCaptureState({ isLoading: false });
457
+ this.updateStatus('Error de configuración', 'No se pudieron preparar las herramientas necesarias', 'error');
458
+ if (this.stateManager) {
459
+ this.stateManager.updateCaptureState({ isLoading: false });
460
+ }
363
461
  return { success: false, error: error.message };
364
462
  }
365
463
  }
366
464
  async getCameraInfo() {
367
- return this.cameraService.getCameraInfo();
465
+ const readyCheck = this.isComponentReady();
466
+ if (!readyCheck.ready) {
467
+ return {
468
+ availableCameras: [],
469
+ selectedCameraId: null,
470
+ deviceType: 'desktop',
471
+ isMultipleCamerasAvailable: false,
472
+ preferredFacing: null,
473
+ error: readyCheck.message
474
+ };
475
+ }
476
+ try {
477
+ return await this.cameraService.getCameraInfo();
478
+ }
479
+ catch (error) {
480
+ return {
481
+ availableCameras: [],
482
+ selectedCameraId: null,
483
+ deviceType: 'desktop',
484
+ isMultipleCamerasAvailable: false,
485
+ preferredFacing: null,
486
+ error: error.message
487
+ };
488
+ }
368
489
  }
369
490
  async setPreferredCamera(camera) {
370
- this.preferredCamera = camera;
371
- this.serviceContainer.updateConfig({ preferredCamera: camera });
372
- await this.cameraService.enumerateDevices();
373
- const captureState = this.stateManager.getCaptureState();
374
- if (captureState.isVideoActive) {
375
- const selectedCameraId = this.cameraService.getSelectedCameraId();
376
- if (selectedCameraId) {
377
- await this.cameraService.switchCamera(selectedCameraId);
491
+ const readyCheck = this.isComponentReady();
492
+ if (!readyCheck.ready) {
493
+ return {
494
+ success: false,
495
+ error: readyCheck.message,
496
+ selectedCamera: null,
497
+ availableCameras: 0
498
+ };
499
+ }
500
+ try {
501
+ this.preferredCamera = camera;
502
+ this.serviceContainer.updateConfig({ preferredCamera: camera });
503
+ await this.cameraService.enumerateDevices();
504
+ await this.updateCameraInfoWithAutofocus();
505
+ const captureState = this.stateManager.getCaptureState();
506
+ if (captureState.isVideoActive) {
507
+ const selectedCameraId = this.cameraService.getSelectedCameraId();
508
+ if (selectedCameraId) {
509
+ await this.cameraService.switchCamera(selectedCameraId);
510
+ }
378
511
  }
512
+ return {
513
+ success: true,
514
+ selectedCamera: this.cameraService.getSelectedCameraId(),
515
+ availableCameras: this.cameraService.getAvailableCameras().length
516
+ };
517
+ }
518
+ catch (error) {
519
+ return {
520
+ success: false,
521
+ error: error.message,
522
+ selectedCamera: null,
523
+ availableCameras: 0
524
+ };
379
525
  }
380
- return {
381
- success: true,
382
- selectedCamera: this.cameraService.getSelectedCameraId(),
383
- availableCameras: this.cameraService.getAvailableCameras().length
384
- };
385
526
  }
386
527
  async setCaptureDelay(delay) {
387
528
  if (delay < 0 || delay > 10000) {
388
- throw new Error('Capture delay must be between 0 and 10000 milliseconds');
529
+ return {
530
+ success: false,
531
+ error: 'Capture delay must be between 0 and 10000 milliseconds',
532
+ captureDelay: this.captureDelay
533
+ };
389
534
  }
535
+ // Allow setting capture delay even during initialization
390
536
  this.captureDelay = delay;
391
- this.serviceContainer.updateConfig({ captureDelay: delay });
537
+ if (this.serviceContainer) {
538
+ this.serviceContainer.updateConfig({ captureDelay: delay });
539
+ }
392
540
  return {
393
541
  success: true,
394
542
  captureDelay: this.captureDelay
395
543
  };
396
544
  }
397
545
  async getCaptureDelay() {
546
+ // Always allow getting capture delay, even during initialization
398
547
  return this.captureDelay;
399
548
  }
400
- async setTorchEnabled(enabled) {
401
- const torchEnabled = await this.cameraService.setTorchEnabled(enabled, this.videoStream);
402
- return {
403
- success: torchEnabled,
404
- enabled: torchEnabled ? enabled : false
405
- };
406
- }
407
- async focusAtPoint(x, y) {
408
- const focused = await this.cameraService.focusAtPoint(x, y, this.videoStream);
409
- return {
410
- success: focused,
411
- coordinates: { x, y }
412
- };
413
- }
414
549
  // DETECTION METHODS
415
550
  async startDetection() {
416
- this.logger.state('INICIANDO_DETECCION');
417
551
  try {
418
552
  // Paso 1: Verificar y cargar modelos si es necesario
419
553
  if (!this.detectionService.isModelLoaded()) {
420
554
  const loadStartTime = performance.now();
421
- this.updateStatus('Cargando modelo de detección...', 'Descargando red neuronal para reconocimiento', 'loading');
555
+ this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
422
556
  this.stateManager.updateCaptureState({ isLoading: true });
423
557
  await this.detectionService.loadModel();
424
- this.updateStatus('Cargando clasificador...', 'Preparando modelo de clasificación de documentos', 'loading');
558
+ this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
425
559
  await this.detectionService.loadClassificationModel();
426
560
  const loadEndTime = performance.now();
427
561
  const loadTime = loadEndTime - loadStartTime;
@@ -429,13 +563,13 @@ export class JaakStamps {
429
563
  if (this.debug) {
430
564
  this.recordOnnxPerformance(loadTime, 0);
431
565
  }
432
- this.logger.state('MODELOS_CARGADOS_EN_DETECCION', { loadTime: Math.round(loadTime) });
433
566
  }
434
567
  // Paso 2: Detectar y configurar dispositivos
435
- this.updateStatus('Detectando cámaras...', 'Buscando dispositivos de captura disponibles', 'loading');
568
+ this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
436
569
  await this.cameraService.enumerateDevices();
570
+ await this.updateCameraInfoWithAutofocus();
437
571
  // Paso 3: Configurar cámara seleccionada
438
- this.updateStatus('Configurando cámara...', 'Estableciendo resolución y parámetros óptimos', 'loading');
572
+ this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
439
573
  const stream = await this.cameraService.setupCamera();
440
574
  // Paso 4: Inicializar video y ocultar estado inmediatamente
441
575
  this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
@@ -452,8 +586,7 @@ export class JaakStamps {
452
586
  this.detectFrame();
453
587
  }
454
588
  catch (err) {
455
- this.logger.error('Error al inicializar detección:', err);
456
- this.updateStatus('Error al iniciar captura', 'No se pudo completar la inicialización', 'error');
589
+ this.updateStatus('Error al iniciar', 'No se pudo preparar la captura', 'error');
457
590
  this.stateManager.updateCaptureState({ isLoading: false });
458
591
  }
459
592
  }
@@ -463,10 +596,6 @@ export class JaakStamps {
463
596
  this.videoStream = stream;
464
597
  const isRear = this.cameraService.isRearCamera(stream);
465
598
  this.shouldMirrorVideo = !isRear;
466
- this.logger.state('CAMARA_CONFIGURADA', {
467
- isRearCamera: isRear,
468
- shouldMirrorVideo: this.shouldMirrorVideo
469
- });
470
599
  return new Promise((resolve) => {
471
600
  this.videoRef.onloadedmetadata = async () => {
472
601
  await this.videoRef.play();
@@ -491,9 +620,10 @@ export class JaakStamps {
491
620
  }
492
621
  return;
493
622
  }
494
- // Frame skipping for performance
623
+ // Adaptive frame skipping for performance
495
624
  this.frameSkipCounter++;
496
- if (this.frameSkipCounter <= this.FRAME_SKIP) {
625
+ const currentFrameSkip = this.getAdaptiveFrameSkip();
626
+ if (this.frameSkipCounter <= currentFrameSkip) {
497
627
  if (captureState.step !== 'completed') {
498
628
  this.animationId = requestAnimationFrame(() => this.detectFrame());
499
629
  }
@@ -569,7 +699,6 @@ export class JaakStamps {
569
699
  }
570
700
  }
571
701
  catch (e) {
572
- this.logger.error('Error en inferencia de modelo:', e);
573
702
  const captureState = this.stateManager.getCaptureState();
574
703
  if (captureState.step !== 'completed') {
575
704
  setTimeout(() => this.detectFrame(), 100);
@@ -596,6 +725,8 @@ export class JaakStamps {
596
725
  clearInterval(this.performanceUpdateInterval);
597
726
  this.performanceUpdateInterval = undefined;
598
727
  }
728
+ // Clear canvas pool
729
+ this.canvasPool = [];
599
730
  this.detectionBoxes = [];
600
731
  this.alignmentStartTime = undefined;
601
732
  this.hasDocumentDetected = false;
@@ -610,14 +741,8 @@ export class JaakStamps {
610
741
  step: 'front',
611
742
  isCapturing: false
612
743
  };
613
- const cameraInfo = this.cameraService?.getCameraInfo() || {
614
- availableCameras: [],
615
- isMultipleCamerasAvailable: false,
616
- selectedCameraId: null,
617
- deviceType: 'desktop',
618
- preferredFacing: null
619
- };
620
- return (h("div", { key: '9d4d77042218ab3d0bc4fb1ce879db09dc3e78b4', class: "detector-container" }, h("div", { key: '51ac8ccb1275637282b038ad8364d19f14ef61b1', class: "video-container" }, h("video", { key: 'a34f0481da20ec5b7b62b21aa275ec5f6dfec5d1', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '43b71dfe3eaf20ffa66a4139e24b1af6a2074631', 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: {
744
+ const cameraInfo = this.cameraInfoWithAutofocus;
745
+ return (h("div", { key: '69a8df7969288e19088e8809f1fefcc89b237cda', class: "detector-container" }, h("div", { key: '79e59a315d266be9d4fb5eed5740e5ad825d5890', class: "video-container" }, h("video", { key: 'a3312788c739748b774a25acb30c2dcea7f01695', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: 'ddab107500c2dab24c140049bef0b44aafc35dc7', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
621
746
  position: 'absolute',
622
747
  left: `${box.x}px`,
623
748
  top: `${box.y}px`,
@@ -626,9 +751,9 @@ export class JaakStamps {
626
751
  border: '2px solid #32406C',
627
752
  pointerEvents: 'none',
628
753
  boxSizing: 'border-box'
629
- } })))), this.isMaskReady && (h("div", { key: 'c2596f8cabb227e3ea3b082495f52b141cb1690f', class: "overlay-mask" }, h("div", { key: 'ba1ea39b5df1cd312a9bd9410b7225a29cfb8a29', class: "card-outline" }, h("div", { key: '6ce4d4d969d5f3fbcf75a4eb285f61d434c5633b', class: "side side-top" }), h("div", { key: '36653ecc4fcda25dfc84bc4d1f4c4c5dfb71ced7', class: "side side-right" }), h("div", { key: '8b108fa8fb370b229a0d1a77cb1ff044319a8f75', class: "side side-bottom" }), h("div", { key: 'a7aa3b45c065a50445aafd5c3ccefb57b0c9c769', class: "side side-left" }), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'b94b92273e12a019238e11ae57f21e140f13c3f1', class: "guide-text" }, "Alinee su identificaci\u00F3n con el marco"))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '144c3741003ec0cc148bb6ce92012d7b3cd110c8', class: "skip-section" }, h("div", { key: 'f0a984a6b75afa374c5d47da9d53e39f98071da0', class: "skip-explanation" }, "Si tu documento no tiene lado trasero, da clic en el bot\u00F3n para continuar con el proceso"), h("button", { key: '57ac30d776e30709aab6e0a621cef889da3cc677', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
754
+ } })))), this.isMaskReady && (h("div", { key: '370337089a3de949b8cb4ae84dc0037e6044b2d5', class: "overlay-mask" }, h("div", { key: 'cb35c2e42bf1729ad02a8fe7e7abaf27cf1a8d7a', class: "card-outline" }, h("div", { key: '637d72ada63805e7f25051cd1ad9093030cb025a', class: "side side-top" }), h("div", { key: 'c7a465aa686afa41f80433d949715b40f6f349dc', class: "side side-right" }), h("div", { key: '384aade0610b5bc7bdd7db350a70d82f9bebd8ab', class: "side side-bottom" }), h("div", { key: '5506cc346f3fd718e3fda4365f69ea780304b6d6', class: "side side-left" }), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'f5b9b62937eabab7c096b7417a13bbf6f822ee8a', class: "guide-text" }, "Alinee su identificaci\u00F3n con el marco"))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '74b122125a42839d2600cf99fb855140042ab761', class: "skip-section" }, h("div", { key: '0b4a39d1599854d6c445e2c35dcc89a0e4970846', class: "skip-explanation" }, "Si tu documento no tiene lado trasero, da clic en el bot\u00F3n para continuar con el proceso"), h("button", { key: 'b52b47df390cc099c8847ca8a676270bb154294d', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
630
755
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
631
- : 'Saltar reverso'))), captureState.isVideoActive && (h("div", { key: 'bd45628707e0169317ed3dd4247e1056d7046104', class: "camera-controls" }, h("button", { key: 'e750fbf4d5d76d9e9596823bb2b86801ef19e485', 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: '40ada780a8ea13aeeaee9cb39bc6496f69f2679d', class: "camera-selector-dropdown" }, h("div", { key: '3a920bb02ba024f0359513f10544bf0528ecee6d', class: "camera-selector-header" }, h("span", { key: 'e5efd478a602b13821ba53cb9a7a59d1b4a71178' }, "Seleccionar C\u00E1mara"), h("button", { key: '2df6cc11e162be74862029e33815927e3fa8f2f8', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '06934642dff134c9e1a6872f935a5892a9c7b835', 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}`), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: '8d5ede287c2efb0ebe418bee8dc7fb310443c991', class: "device-info" }, h("small", { key: 'b7dd10565113694b09f01ab28ffa29262ae1d2bc' }, "Dispositivo: ", cameraInfo.deviceType)))))), captureState.isCapturing && (h("div", { key: '2f9e549e6c0c56d4db5835e58361fb17bba6cf90', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'a6ef0ed127f23e9d764896ed743ca8d8454851a6', class: "flip-animation" }, h("div", { key: '0b04411f88a634bb3243b00ca2d5a45fd6962dea', class: "id-card-icon" }), h("div", { key: '9327be58879b407cea87f455989406e58302e9d1', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'bd268578b5003e99256e3340b4a9a6fb7656be64', class: "success-animation" }, h("div", { key: '064e0a5b9851de14bc6829f028e7ea7161fd1633', class: "check-icon" }), h("div", { key: 'bdcd94a8806399e396e218a359c10b11d05f38d6', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '3d9af617376c16e44e065873919c3d90ff46110a', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: 'be7b56decfd6f12dcca46b2a9456480e3d5ac00e', class: "status-spinner" })), h("div", { key: '5fe9850883cb749cebaa18c3e9c13ca21325e373', class: "status-content" }, h("div", { key: '43fdb50691e4b98e56126af1a129f625061128bf', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'b76cbfa4a0349421fcd535d3fd8bf40c89ca3aef', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'eddefb464af0487ab9ab2a0f76dccbb6a2092e51', class: "performance-monitor" }, h("div", { key: '7b7d93b56cb4cd4fbb1bdde89c6150b282fa805e', class: "performance-expanded" }, h("div", { key: 'f302e8b9d985fb8cad53c4c4e0d37a3bf5a63261', class: "metrics-row" }, h("div", { key: 'e8d8f2420ee8643a7f454d38065c75b5be598f77', class: "metric-compact" }, h("span", { key: '6e52eb5bb06e0df57e61774de38c31349f45e22b', class: "metric-label" }, "FPS"), h("span", { key: '6636f9cea20c49b49c17db86188e2f6b214c45c3', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: 'afa1d9dec05b14256a1b1fcec8638b8c21a56e37', class: "metric-compact" }, h("span", { key: '9c55e30f5d01582648bacc1fb38ca1990485a591', class: "metric-label" }, "MEM"), h("span", { key: 'ca263601e8715027f95ffb2d5413a643dc570cf2', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '03caf19961fd01b7781484d49d8121b1e78c161f', class: "metrics-row" }, h("div", { key: 'bffb08f01f703411f7c8605e4f8d95b1c37b5c0f', class: "metric-compact" }, h("span", { key: '9b92279b3253cc669412733facb3751f4344d69f', class: "metric-label" }, "INF"), h("span", { key: '3234d4150e2e1479e3b552767af8123e69911d6c', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '77082d2669db53f19fb50b3bbfa1ebefb0b86ff4', class: "metric-compact" }, h("span", { key: 'cc6ef96c72c800ba36dab2a388f1de863b2dacd5', class: "metric-label" }, "FRAME"), h("span", { key: 'e2bb4b7351869a76e810f8bbf20d805154495e35', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '5700d7ef839fa77686299d79445203090dc8079f', class: "metrics-row" }, h("div", { key: 'a2999a035fccb7f250c01b084075490678513c87', class: "metric-compact" }, h("span", { key: '3677d58665996fa5969507dbaff9e956b2ea54ee', class: "metric-label" }, "DET"), h("span", { key: 'c584312438b339c82a3bdc33f00fb985ff2a6f55', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '58866219f213bf8607083f30be2d5ca5d0515cea', class: "metric-compact" }, h("span", { key: 'd90b450051331347f219b6c0f197536d0dcee9b7', class: "metric-label" }, "RATE"), h("span", { key: 'f6e6cd7948e06014e5a0416e4e81394fa36ec61d', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: 'c1d0194f181a0082cc1aa5452de382908da61b65', class: "watermark" }, h("img", { key: 'e04d35f3c79a2b4a3922ab5b0f102220171fa7c0', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
756
+ : 'Saltar reverso'))), captureState.isVideoActive && (h("div", { key: 'f04e47749a25197c434b849844c67eadca5c6af3', class: "camera-controls" }, h("button", { key: '82625eef057f59b6dac11c4443ccb315231eac7f', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: '61cfb89cd9bb549cfff2cb70293db06ee6cdf06b', class: "camera-selector-dropdown" }, h("div", { key: '60f751d8db7b305f2bdfdc089d2110bcff940150', class: "camera-selector-header" }, h("span", { key: '9dae045a34cc06f323ff4850921b495fc845be47' }, "Seleccionar C\u00E1mara"), h("button", { key: '4584d7964d91ddd7f3d980c3e5e90493530d33bd', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '9881f2b4fc6ffbdc76db06a7b49d060cefbe6277', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: '3dfe9ffc7b4cc9afb199a78026cef6d4b469805e', class: "device-info" }, h("small", { key: 'bddc865a30469bd2724624b2f7c8f641f9d97a07' }, "Dispositivo: ", cameraInfo.deviceType)))))), captureState.isCapturing && (h("div", { key: '2fd963e44f69f80d17c6bbbfa3c9b5e11bc816be', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'ba4872ea5388a0ac3134be61feff5412373465ab', class: "flip-animation" }, h("div", { key: 'e83a92cea79f3e85aefc6964c5d9de7b495d511e', class: "id-card-icon" }), h("div", { key: '1d06b51fd6ad14d9b37aa6bca72bd13c951a386a', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '503c377896d5fdc3ae8476b09f511d571f23ae72', class: "success-animation" }, h("div", { key: 'e1929c245b0d1479a6fb1513b0b37c53070c4291', class: "check-icon" }), h("div", { key: '024c897c70427b205390770fc3dfb999ffcc84d3', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '54e350707628e5c88ae2900e412e6bda2eace943', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '82ea10569ed46285686cf3e63885ca1941bb559c', class: "status-spinner" })), h("div", { key: '1fecf179c10965cccf1be3dc0a54afd377ba38ad', class: "status-content" }, h("div", { key: '1b301c1bbb02e5cac2e6babcb20c190662d45c68', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '5b6c61308d17176daade02c951a436d114a74100', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'b8104db64208dc87a617a072957c007f28055194', class: "performance-monitor" }, h("div", { key: 'e66aec071f10821be2713858dcc251c04f480e63', class: "performance-expanded" }, h("div", { key: '1ae09a91d0704ab21b9eb51097cf452fd5f44359', class: "metrics-row" }, h("div", { key: '340d4d16d048a5217fe13d5de63618c9ea221165', class: "metric-compact" }, h("span", { key: '20cfda14969ef70d232edd68f2e9902bd44b5d9a', class: "metric-label" }, "FPS"), h("span", { key: '3a089c2814a71ed6513bf05b0120a66aa159daee', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '23f700def988c3220fc5df37aeb57a0a20ff56b3', class: "metric-compact" }, h("span", { key: '441e8490a871469813a6d832114e03583d375e91', class: "metric-label" }, "MEM"), h("span", { key: '51f7c49164c9d5e699141c92c88375fc99b312ed', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '6547fee80e2a1db7c9ff889a1c478fc8248159e6', class: "metrics-row" }, h("div", { key: '7b2d1642fd122ee0fbed36387afb23152afe10c2', class: "metric-compact" }, h("span", { key: 'a9137119bf7da366f18feaf8f28807ee40ad7a25', class: "metric-label" }, "INF"), h("span", { key: '5cbc0c040102688f1039cdcd4bb58020248cf700', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'cb4a86f5da46e4081d997c8b8bb6a899f06dfcd2', class: "metric-compact" }, h("span", { key: '88ad5e1d842eb156cf6813ecaa9d2bec76a5999e', class: "metric-label" }, "FRAME"), h("span", { key: 'a9b14cd98b64dcfc058d3b68a3c168a9e092fb3c', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '86e01f3596682cb8ad2ff5464748e6ecb7db374a', class: "metrics-row" }, h("div", { key: 'e73b9d39e9bcbc5d46fa0ba0a737700e3e5566d9', class: "metric-compact" }, h("span", { key: '8ecfe969dfe77c87d9da76d7390706014b4872f3', class: "metric-label" }, "DET"), h("span", { key: 'bd1a6a52e0bd1fb4d377413d7515e4e0f00da10f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '36539ae98787fd60642082db1bf89031003c4bef', class: "metric-compact" }, h("span", { key: '7f3a81629c09053e3beeca97f485da0f60412052', class: "metric-label" }, "RATE"), h("span", { key: '72399e0ac39270c82b265ce64e402f84cda9fb2c', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '4e3a914ba2f5993f85de7b9dd6dc93d76b41c96d', class: "watermark" }, h("img", { key: 'aa634c2ba2090f64ee9b41aa1d75a9114d46fd63', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
632
757
  }
633
758
  // Utility methods
634
759
  updateDetectionBoxes(boxes) {
@@ -709,35 +834,18 @@ export class JaakStamps {
709
834
  if (allSidesAligned && bestBox) {
710
835
  cardOutline?.classList.add('perfect-match');
711
836
  corners?.forEach(corner => corner.classList.add('perfect-match'));
712
- // Debug logging
713
- this.logger.state('CAPTURE_EVALUATION', {
714
- allSidesAligned: true,
715
- hasScreenshotTaken: this.hasScreenshotTaken,
716
- captureDelay: this.captureDelay,
717
- alignmentStartTime: this.alignmentStartTime
718
- });
719
837
  if (!this.hasScreenshotTaken) {
720
838
  const currentTime = Date.now();
721
839
  // Initialize alignment start time if not set
722
840
  if (!this.alignmentStartTime) {
723
841
  this.alignmentStartTime = currentTime;
724
- this.logger.state('ALIGNMENT_TIMER_STARTED', { startTime: currentTime });
725
842
  }
726
843
  // Check if document has been aligned for the configured delay
727
844
  const alignmentDuration = currentTime - this.alignmentStartTime;
728
- this.logger.state('ALIGNMENT_DURATION_CHECK', {
729
- alignmentDuration,
730
- captureDelay: this.captureDelay,
731
- readyToCapture: alignmentDuration >= this.captureDelay
732
- });
733
845
  if (alignmentDuration >= this.captureDelay) {
734
- this.logger.state('TRIGGERING_CAPTURE', {
735
- alignmentDuration,
736
- captureDelay: this.captureDelay
737
- });
738
846
  this.lastDetectedBox = bestBox;
739
- this.takeScreenshot().catch(error => {
740
- this.logger.error('Error al tomar captura de pantalla:', error);
847
+ this.takeScreenshot().catch(() => {
848
+ // Handle screenshot error silently
741
849
  });
742
850
  this.hasScreenshotTaken = true;
743
851
  this.alignmentStartTime = undefined;
@@ -763,21 +871,12 @@ export class JaakStamps {
763
871
  async takeScreenshot() {
764
872
  if (!this.videoRef || !this.lastDetectedBox)
765
873
  return;
766
- this.logger.state('INICIANDO_CAPTURA', {
767
- captureStep: this.stateManager.getCaptureState().step,
768
- detectedBox: this.lastDetectedBox,
769
- videoResolution: {
770
- width: this.videoRef.videoWidth,
771
- height: this.videoRef.videoHeight
772
- }
773
- });
774
874
  this.stateManager.updateCaptureState({ isCapturing: true });
775
875
  this.triggerCaptureAnimation();
776
- // Create capture canvas
777
- const captureCanvas = document.createElement('canvas');
778
- captureCanvas.width = this.videoRef.videoWidth;
779
- captureCanvas.height = this.videoRef.videoHeight;
876
+ // Use pooled canvas for optimization
877
+ const captureCanvas = this.getPooledCanvas(this.videoRef.videoWidth, this.videoRef.videoHeight);
780
878
  const captureCtx = captureCanvas.getContext('2d', { alpha: false });
879
+ captureCtx.clearRect(0, 0, captureCanvas.width, captureCanvas.height);
781
880
  captureCtx.drawImage(this.videoRef, 0, 0, captureCanvas.width, captureCanvas.height);
782
881
  // Calculate crop coordinates
783
882
  const INPUT_SIZE = 320;
@@ -787,11 +886,10 @@ export class JaakStamps {
787
886
  const cropY = Math.max(0, (this.lastDetectedBox.y * scaleY) - this.cropMargin);
788
887
  const cropWidth = Math.min((this.lastDetectedBox.w * scaleX) + (2 * this.cropMargin), this.videoRef.videoWidth - cropX);
789
888
  const cropHeight = Math.min((this.lastDetectedBox.h * scaleY) + (2 * this.cropMargin), this.videoRef.videoHeight - cropY);
790
- // Create cropped version
791
- const croppedCanvas = document.createElement('canvas');
792
- croppedCanvas.width = cropWidth;
793
- croppedCanvas.height = cropHeight;
889
+ // Use pooled canvas for cropped version
890
+ const croppedCanvas = this.getPooledCanvas(cropWidth, cropHeight);
794
891
  const croppedCtx = croppedCanvas.getContext('2d', { alpha: false });
892
+ croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);
795
893
  croppedCtx.drawImage(this.videoRef, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
796
894
  const captureState = this.stateManager.getCaptureState();
797
895
  if (captureState.step === 'front') {
@@ -805,7 +903,6 @@ export class JaakStamps {
805
903
  if (this.useDocumentClassification) {
806
904
  const classification = await this.detectionService.classifyDocument(croppedCanvas);
807
905
  if (classification && classification.class === 'passport') {
808
- this.logger.state('PASAPORTE_DETECTADO_SALTANDO_REVERSO', { classification: classification?.class });
809
906
  this.completeProcess(true);
810
907
  return;
811
908
  }
@@ -832,6 +929,9 @@ export class JaakStamps {
832
929
  });
833
930
  this.completeProcess(false);
834
931
  }
932
+ // Return canvases to pool after use
933
+ this.returnCanvasToPool(captureCanvas);
934
+ this.returnCanvasToPool(croppedCanvas);
835
935
  }
836
936
  triggerCaptureAnimation() {
837
937
  const cardOutline = this.el.shadowRoot?.querySelector('.card-outline');
@@ -851,7 +951,7 @@ export class JaakStamps {
851
951
  capturedImages.metadata.backCaptureSkipped = skippedBack;
852
952
  this.stateManager.setCapturedImages(capturedImages);
853
953
  this.stopDetection();
854
- this.updateStatus('Proceso completado', `${capturedImages.metadata.totalImages} imágenes capturadas exitosamente`, 'ready');
954
+ this.updateStatus('Proceso completado', `Imágenes capturadas exitosamente`, 'ready');
855
955
  const finalImages = {
856
956
  ...capturedImages,
857
957
  timestamp: new Date().toISOString()
@@ -860,11 +960,6 @@ export class JaakStamps {
860
960
  setTimeout(() => {
861
961
  this.stateManager.updateCaptureState({ showSuccessAnimation: false });
862
962
  }, 3000);
863
- this.logger.state('PROCESO_COMPLETADO', {
864
- skippedBack,
865
- totalImages: capturedImages.metadata.totalImages,
866
- timestamp: new Date().toISOString()
867
- });
868
963
  }
869
964
  stopDetection() {
870
965
  if (this.animationId) {
@@ -873,7 +968,6 @@ export class JaakStamps {
873
968
  }
874
969
  this.clearBackDocumentTimer();
875
970
  this.detectionBoxes = [];
876
- this.logger.state('DETECTOR_DETENIDO', { timestamp: Date.now() });
877
971
  }
878
972
  startBackDocumentTimer() {
879
973
  if (!this.enableBackDocumentTimer)
@@ -902,6 +996,15 @@ export class JaakStamps {
902
996
  return; // Don't toggle if switching camera
903
997
  this.showCameraSelector = !this.showCameraSelector;
904
998
  }
999
+ async updateCameraInfoWithAutofocus() {
1000
+ try {
1001
+ const cameraInfo = await this.cameraService.getCameraInfo();
1002
+ this.cameraInfoWithAutofocus = cameraInfo;
1003
+ }
1004
+ catch (error) {
1005
+ // Keep existing state if update fails
1006
+ }
1007
+ }
905
1008
  async handleCameraSwitch(cameraId) {
906
1009
  if (this.isSwitchingCamera)
907
1010
  return; // Prevent multiple simultaneous switches
@@ -909,10 +1012,6 @@ export class JaakStamps {
909
1012
  // Close the selector immediately when user selects a camera
910
1013
  this.showCameraSelector = false;
911
1014
  this.isSwitchingCamera = true;
912
- this.logger.state('INICIANDO_CAMBIO_CAMARA', {
913
- from: this.cameraService.getSelectedCameraId(),
914
- to: cameraId
915
- });
916
1015
  // Stop current video stream
917
1016
  if (this.videoStream) {
918
1017
  this.videoStream.getTracks().forEach(track => track.stop());
@@ -923,21 +1022,17 @@ export class JaakStamps {
923
1022
  const newStream = await this.cameraService.setupCamera();
924
1023
  // Update video element
925
1024
  await this.initializeVideoStream(newStream);
926
- this.logger.state('CAMBIO_CAMARA_EXITOSO', {
927
- newCameraId: cameraId,
928
- isRearCamera: this.cameraService.isRearCamera(newStream)
929
- });
1025
+ // Update camera info with autofocus data
1026
+ await this.updateCameraInfoWithAutofocus();
930
1027
  }
931
1028
  catch (error) {
932
- this.logger.error('Error al cambiar cámara:', error);
933
1029
  // Try to restore previous camera if switch failed
934
1030
  try {
935
1031
  const fallbackStream = await this.cameraService.setupCamera();
936
1032
  await this.initializeVideoStream(fallbackStream);
937
1033
  }
938
1034
  catch (fallbackError) {
939
- this.logger.error('Error al restaurar cámara anterior:', fallbackError);
940
- this.updateStatus('Error al cambiar cámara', 'No se pudo completar el cambio de dispositivo', 'error');
1035
+ this.updateStatus('Error al cambiar cámara', 'No se pudo cambiar el dispositivo', 'error');
941
1036
  }
942
1037
  }
943
1038
  finally {
@@ -978,7 +1073,7 @@ export class JaakStamps {
978
1073
  this.stateManager.updateCaptureState({ isVideoActive: false, isLoading: false });
979
1074
  }
980
1075
  this.isMaskReady = false;
981
- this.updateStatus('Sesión finalizada', '', 'ready');
1076
+ this.updateStatus('Captura finalizada', '', 'ready');
982
1077
  this.detectionBoxes = [];
983
1078
  this.cleanup();
984
1079
  }
@@ -989,7 +1084,6 @@ export class JaakStamps {
989
1084
  this.performanceUpdateInterval = window.setInterval(() => {
990
1085
  this.updatePerformanceMetrics();
991
1086
  }, 500);
992
- this.logger.debug('Monitor de performance inicializado');
993
1087
  }
994
1088
  updatePerformanceMetrics() {
995
1089
  const currentTime = performance.now();
@@ -1032,6 +1126,66 @@ export class JaakStamps {
1032
1126
  if (detectionsFound > 0) {
1033
1127
  this.performanceMetrics.successfulDetections++;
1034
1128
  }
1129
+ // Update performance history for adaptive frame skipping
1130
+ this.performanceHistory.push(processingTime);
1131
+ if (this.performanceHistory.length > this.PERFORMANCE_HISTORY_SIZE) {
1132
+ this.performanceHistory.shift();
1133
+ }
1134
+ }
1135
+ // Adaptive frame skipping based on performance
1136
+ getAdaptiveFrameSkip() {
1137
+ // Base conditions for high frame skip
1138
+ if (this.consecutiveFailures > 20)
1139
+ return this.MAX_FRAME_SKIP;
1140
+ if (this.performanceMetrics.inferenceTime > 200)
1141
+ return 6;
1142
+ if (this.performanceMetrics.memoryUsage > 200)
1143
+ return 5;
1144
+ // Calculate average processing time
1145
+ if (this.performanceHistory.length > 0) {
1146
+ const avgProcessingTime = this.performanceHistory.reduce((a, b) => a + b, 0) / this.performanceHistory.length;
1147
+ if (avgProcessingTime > 100)
1148
+ return 4;
1149
+ if (avgProcessingTime > 80)
1150
+ return 3;
1151
+ if (avgProcessingTime > 60)
1152
+ return this.BASE_FRAME_SKIP + 1;
1153
+ }
1154
+ // Low memory devices get higher frame skip
1155
+ if (this.performanceMetrics.memoryUsage > 150)
1156
+ return 4;
1157
+ // Performance is good, use base frame skip
1158
+ return this.BASE_FRAME_SKIP;
1159
+ }
1160
+ // Canvas pool management for screenshots
1161
+ getPooledCanvas(width, height) {
1162
+ // Try to find a canvas with matching dimensions
1163
+ const matchingIndex = this.canvasPool.findIndex(canvas => canvas.width === width && canvas.height === height);
1164
+ if (matchingIndex !== -1) {
1165
+ return this.canvasPool.splice(matchingIndex, 1)[0];
1166
+ }
1167
+ // If no matching canvas, try to reuse any canvas and resize
1168
+ if (this.canvasPool.length > 0) {
1169
+ const canvas = this.canvasPool.pop();
1170
+ canvas.width = width;
1171
+ canvas.height = height;
1172
+ return canvas;
1173
+ }
1174
+ // Create new canvas if pool is empty
1175
+ const canvas = document.createElement('canvas');
1176
+ canvas.width = width;
1177
+ canvas.height = height;
1178
+ return canvas;
1179
+ }
1180
+ returnCanvasToPool(canvas) {
1181
+ // Only return to pool if not at max capacity
1182
+ if (this.canvasPool.length < this.MAX_CANVAS_POOL_SIZE) {
1183
+ // Clear the canvas before returning to pool
1184
+ const ctx = canvas.getContext('2d');
1185
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1186
+ this.canvasPool.push(canvas);
1187
+ }
1188
+ // If pool is full, let the canvas be garbage collected
1035
1189
  }
1036
1190
  static get is() { return "jaak-stamps"; }
1037
1191
  static get encapsulation() { return "shadow"; }
@@ -1238,6 +1392,7 @@ export class JaakStamps {
1238
1392
  "showCameraSelector": {},
1239
1393
  "isSwitchingCamera": {},
1240
1394
  "hasDocumentDetected": {},
1395
+ "cameraInfoWithAutofocus": {},
1241
1396
  "currentStatus": {},
1242
1397
  "performanceData": {},
1243
1398
  "backDocumentTimerRemaining": {}
@@ -1319,7 +1474,7 @@ export class JaakStamps {
1319
1474
  },
1320
1475
  "startCapture": {
1321
1476
  "complexType": {
1322
- "signature": "() => Promise<void>",
1477
+ "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
1323
1478
  "parameters": [],
1324
1479
  "references": {
1325
1480
  "Promise": {
@@ -1327,7 +1482,7 @@ export class JaakStamps {
1327
1482
  "id": "global::Promise"
1328
1483
  }
1329
1484
  },
1330
- "return": "Promise<void>"
1485
+ "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
1331
1486
  },
1332
1487
  "docs": {
1333
1488
  "text": "",
@@ -1336,7 +1491,7 @@ export class JaakStamps {
1336
1491
  },
1337
1492
  "stopCapture": {
1338
1493
  "complexType": {
1339
- "signature": "() => Promise<void>",
1494
+ "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
1340
1495
  "parameters": [],
1341
1496
  "references": {
1342
1497
  "Promise": {
@@ -1344,7 +1499,7 @@ export class JaakStamps {
1344
1499
  "id": "global::Promise"
1345
1500
  }
1346
1501
  },
1347
- "return": "Promise<void>"
1502
+ "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
1348
1503
  },
1349
1504
  "docs": {
1350
1505
  "text": "",
@@ -1353,7 +1508,7 @@ export class JaakStamps {
1353
1508
  },
1354
1509
  "resetCapture": {
1355
1510
  "complexType": {
1356
- "signature": "() => Promise<void>",
1511
+ "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
1357
1512
  "parameters": [],
1358
1513
  "references": {
1359
1514
  "Promise": {
@@ -1361,7 +1516,7 @@ export class JaakStamps {
1361
1516
  "id": "global::Promise"
1362
1517
  }
1363
1518
  },
1364
- "return": "Promise<void>"
1519
+ "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
1365
1520
  },
1366
1521
  "docs": {
1367
1522
  "text": "",
@@ -1370,7 +1525,7 @@ export class JaakStamps {
1370
1525
  },
1371
1526
  "skipBackCapture": {
1372
1527
  "complexType": {
1373
- "signature": "() => Promise<void>",
1528
+ "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
1374
1529
  "parameters": [],
1375
1530
  "references": {
1376
1531
  "Promise": {
@@ -1378,7 +1533,7 @@ export class JaakStamps {
1378
1533
  "id": "global::Promise"
1379
1534
  }
1380
1535
  },
1381
- "return": "Promise<void>"
1536
+ "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
1382
1537
  },
1383
1538
  "docs": {
1384
1539
  "text": "",
@@ -1448,7 +1603,7 @@ export class JaakStamps {
1448
1603
  },
1449
1604
  "setPreferredCamera": {
1450
1605
  "complexType": {
1451
- "signature": "(camera: \"auto\" | \"front\" | \"back\") => Promise<{ success: boolean; selectedCamera: string; availableCameras: number; }>",
1606
+ "signature": "(camera: \"auto\" | \"front\" | \"back\") => Promise<{ success: boolean; selectedCamera: string; availableCameras: number; error?: undefined; } | { success: boolean; error: any; selectedCamera: any; availableCameras: number; }>",
1452
1607
  "parameters": [{
1453
1608
  "name": "camera",
1454
1609
  "type": "\"auto\" | \"front\" | \"back\"",
@@ -1460,7 +1615,7 @@ export class JaakStamps {
1460
1615
  "id": "global::Promise"
1461
1616
  }
1462
1617
  },
1463
- "return": "Promise<{ success: boolean; selectedCamera: string; availableCameras: number; }>"
1618
+ "return": "Promise<{ success: boolean; selectedCamera: string; availableCameras: number; error?: undefined; } | { success: boolean; error: any; selectedCamera: any; availableCameras: number; }>"
1464
1619
  },
1465
1620
  "docs": {
1466
1621
  "text": "",
@@ -1469,7 +1624,7 @@ export class JaakStamps {
1469
1624
  },
1470
1625
  "setCaptureDelay": {
1471
1626
  "complexType": {
1472
- "signature": "(delay: number) => Promise<{ success: boolean; captureDelay: number; }>",
1627
+ "signature": "(delay: number) => Promise<{ success: boolean; error: string; captureDelay: number; } | { success: boolean; captureDelay: number; error?: undefined; }>",
1473
1628
  "parameters": [{
1474
1629
  "name": "delay",
1475
1630
  "type": "number",
@@ -1481,7 +1636,7 @@ export class JaakStamps {
1481
1636
  "id": "global::Promise"
1482
1637
  }
1483
1638
  },
1484
- "return": "Promise<{ success: boolean; captureDelay: number; }>"
1639
+ "return": "Promise<{ success: boolean; error: string; captureDelay: number; } | { success: boolean; captureDelay: number; error?: undefined; }>"
1485
1640
  },
1486
1641
  "docs": {
1487
1642
  "text": "",
@@ -1504,52 +1659,6 @@ export class JaakStamps {
1504
1659
  "text": "",
1505
1660
  "tags": []
1506
1661
  }
1507
- },
1508
- "setTorchEnabled": {
1509
- "complexType": {
1510
- "signature": "(enabled: boolean) => Promise<{ success: boolean; enabled: boolean; }>",
1511
- "parameters": [{
1512
- "name": "enabled",
1513
- "type": "boolean",
1514
- "docs": ""
1515
- }],
1516
- "references": {
1517
- "Promise": {
1518
- "location": "global",
1519
- "id": "global::Promise"
1520
- }
1521
- },
1522
- "return": "Promise<{ success: boolean; enabled: boolean; }>"
1523
- },
1524
- "docs": {
1525
- "text": "",
1526
- "tags": []
1527
- }
1528
- },
1529
- "focusAtPoint": {
1530
- "complexType": {
1531
- "signature": "(x: number, y: number) => Promise<{ success: boolean; coordinates: { x: number; y: number; }; }>",
1532
- "parameters": [{
1533
- "name": "x",
1534
- "type": "number",
1535
- "docs": ""
1536
- }, {
1537
- "name": "y",
1538
- "type": "number",
1539
- "docs": ""
1540
- }],
1541
- "references": {
1542
- "Promise": {
1543
- "location": "global",
1544
- "id": "global::Promise"
1545
- }
1546
- },
1547
- "return": "Promise<{ success: boolean; coordinates: { x: number; y: number; }; }>"
1548
- },
1549
- "docs": {
1550
- "text": "",
1551
- "tags": []
1552
- }
1553
1662
  }
1554
1663
  };
1555
1664
  }