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

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 +611 -546
  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 +2 -14
  7. package/dist/collection/components/my-component/my-component.js +237 -346
  8. package/dist/collection/components/my-component/my-component.js.map +1 -1
  9. package/dist/collection/services/CameraService.js +344 -156
  10. package/dist/collection/services/CameraService.js.map +1 -1
  11. package/dist/collection/services/DetectionService.js +38 -53
  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/LoggerService.js +40 -0
  16. package/dist/collection/services/LoggerService.js.map +1 -0
  17. package/dist/collection/services/ServiceContainer.js +12 -2
  18. package/dist/collection/services/ServiceContainer.js.map +1 -1
  19. package/dist/collection/services/interfaces/ICameraService.js.map +1 -1
  20. package/dist/collection/services/interfaces/ILogger.js +2 -0
  21. package/dist/collection/services/interfaces/ILogger.js.map +1 -0
  22. package/dist/collection/types/component-types.js.map +1 -1
  23. package/dist/components/jaak-stamps.js +614 -548
  24. package/dist/components/jaak-stamps.js.map +1 -1
  25. package/dist/esm/jaak-stamps-webcomponent.js +1 -1
  26. package/dist/esm/jaak-stamps.entry.js +611 -546
  27. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  28. package/dist/esm/loader.js +1 -1
  29. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  30. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  31. package/dist/jaak-stamps-webcomponent/p-2264b5b4.entry.js +2 -0
  32. package/dist/jaak-stamps-webcomponent/p-2264b5b4.entry.js.map +1 -0
  33. package/dist/types/components/my-component/my-component.d.ts +17 -62
  34. package/dist/types/components.d.ts +8 -6
  35. package/dist/types/services/CameraService.d.ts +14 -12
  36. package/dist/types/services/DetectionService.d.ts +3 -9
  37. package/dist/types/services/LoggerService.d.ts +12 -0
  38. package/dist/types/services/ServiceContainer.d.ts +2 -0
  39. package/dist/types/services/interfaces/ICameraService.d.ts +12 -3
  40. package/dist/types/services/interfaces/ILogger.d.ts +8 -0
  41. package/dist/types/types/component-types.d.ts +0 -3
  42. package/package.json +4 -4
  43. package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js +0 -2
  44. package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js.map +0 -1
@@ -23,16 +23,9 @@ 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
- };
33
26
  currentStatus = {
34
- message: 'Preparando capturador...',
35
- description: 'Configurando herramientas de captura',
27
+ message: 'Inicializando componente...',
28
+ description: 'Configurando servicios y cargando recursos',
36
29
  type: 'initializing',
37
30
  isInitialized: false
38
31
  };
@@ -49,6 +42,7 @@ export class JaakStamps {
49
42
  backDocumentTimerRemaining = 0;
50
43
  // Services
51
44
  serviceContainer;
45
+ logger;
52
46
  eventBus;
53
47
  stateManager;
54
48
  cameraService;
@@ -79,25 +73,19 @@ export class JaakStamps {
79
73
  lastUpdateTime: 0
80
74
  };
81
75
  performanceUpdateInterval;
82
- // Performance optimization with adaptive frame skipping
76
+ // Performance optimization
83
77
  frameSkipCounter = 0;
84
- BASE_FRAME_SKIP = 2;
85
- MAX_FRAME_SKIP = 8;
78
+ FRAME_SKIP = 2;
86
79
  consecutiveFailures = 0;
87
80
  MAX_FAILURES = 30;
88
81
  lastInferenceTime = 0;
89
82
  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;
95
83
  async componentDidLoad() {
96
- this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
84
+ this.updateStatus('Iniciando servicios...', 'Configurando módulos internos', 'initializing');
97
85
  await this.initializeServices();
98
- this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
86
+ this.updateStatus('Configurando eventos...', 'Preparando comunicación entre servicios', 'initializing');
99
87
  await this.setupEventListeners();
100
- this.updateStatus('Configurando cámara...', 'Detectando dispositivos disponibles', 'initializing');
88
+ this.updateStatus('Inicializando cámara...', 'Detectando dispositivos disponibles', 'initializing');
101
89
  await this.initializeComponent();
102
90
  if (this.debug) {
103
91
  this.initializePerformanceMonitor();
@@ -114,23 +102,33 @@ export class JaakStamps {
114
102
  captureDelay: this.captureDelay
115
103
  };
116
104
  this.serviceContainer = new ServiceContainer(config);
105
+ this.logger = this.serviceContainer.getLogger();
117
106
  this.eventBus = this.serviceContainer.getEventBus();
118
107
  this.stateManager = this.serviceContainer.getStateManager();
119
108
  this.cameraService = this.serviceContainer.getCameraService();
120
109
  this.detectionService = this.serviceContainer.getDetectionService();
110
+ this.logger.state('SERVICIOS_INICIALIZADOS', { timestamp: Date.now() });
121
111
  }
122
112
  async setupEventListeners() {
123
113
  this.eventBus.on('state-changed', (data) => {
124
114
  this.handleStateChange(data);
125
115
  });
126
- this.eventBus.on('camera-changed', () => {
127
- // Camera changed event
116
+ this.eventBus.on('camera-changed', (cameraId) => {
117
+ this.logger.state('CAMARA_CAMBIADA_EVENT', { cameraId });
128
118
  });
129
- this.eventBus.on('error', () => {
130
- // Handle service errors
119
+ this.eventBus.on('error', (error) => {
120
+ this.logger.error('Error en servicio:', error);
131
121
  });
132
122
  }
133
123
  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
+ });
134
132
  this.validateProps();
135
133
  if (this.debug) {
136
134
  this.stateManager.updateCaptureState({ isLoading: true });
@@ -139,29 +137,32 @@ export class JaakStamps {
139
137
  this.updateStatus('Detectando cámaras...', 'Buscando dispositivos de captura', 'initializing');
140
138
  await this.cameraService.detectDeviceType();
141
139
  await this.cameraService.enumerateDevices();
142
- await this.updateCameraInfoWithAutofocus();
143
- this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'initializing');
140
+ this.updateStatus('Cargando modelo IA...', 'Preparando reconocimiento de documentos', 'initializing');
144
141
  await this.loadOnnxRuntime();
145
142
  this.initializeResizeObserver();
146
143
  }
147
144
  validateProps() {
148
145
  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`);
149
147
  this.maskSize = 90;
150
148
  }
151
149
  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`);
152
151
  this.cropMargin = 0;
153
152
  }
154
153
  const validOptions = ['auto', 'front', 'back'];
155
154
  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`);
159
160
  this.captureDelay = 1500;
160
161
  }
161
162
  }
162
163
  async loadOnnxRuntime() {
163
164
  if (!window.ort) {
164
- this.updateStatus('Preparando herramientas...', 'Configurando funciones de captura', 'initializing');
165
+ this.updateStatus('Descargando librerías...', 'Obteniendo recursos de reconocimiento', 'initializing');
165
166
  const script = document.createElement('script');
166
167
  script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
167
168
  document.head.appendChild(script);
@@ -201,6 +202,11 @@ export class JaakStamps {
201
202
  emitReadyEvent() {
202
203
  const isDocumentReady = !!window.ort && this.detectionService.isModelLoaded();
203
204
  this.isReady.emit(isDocumentReady);
205
+ this.logger.state('COMPONENTE_LISTO', {
206
+ ortLibraryLoaded: !!window.ort,
207
+ modelPreloaded: this.detectionService.isModelLoaded(),
208
+ isReady: isDocumentReady
209
+ });
204
210
  }
205
211
  handleStateChange(data) {
206
212
  // React to state changes from StateManager
@@ -223,6 +229,7 @@ export class JaakStamps {
223
229
  const container = this.detectionContainer.parentElement;
224
230
  const rect = container.getBoundingClientRect();
225
231
  this.updateMaskDimensions(rect);
232
+ this.logger.debug('Container redimensionado:', { width: rect.width, height: rect.height });
226
233
  }
227
234
  }
228
235
  updateMaskDimensions(containerRect) {
@@ -276,38 +283,16 @@ export class JaakStamps {
276
283
  this.el.style.setProperty('--mask-center-x', `${videoCenterXPercent}%`);
277
284
  this.el.style.setProperty('--mask-center-y', `${videoCenterYPercent}%`);
278
285
  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
+ });
279
293
  }
280
294
  // 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
- }
306
295
  async getCapturedImages() {
307
- const readyCheck = this.isComponentReady();
308
- if (!readyCheck.ready) {
309
- throw new Error(readyCheck.message);
310
- }
311
296
  const state = this.stateManager.getCaptureState();
312
297
  if (state.step !== 'completed') {
313
298
  throw new Error('El proceso de captura no ha sido completado');
@@ -315,128 +300,47 @@ export class JaakStamps {
315
300
  return this.stateManager.getCapturedImages();
316
301
  }
317
302
  async isProcessCompleted() {
318
- const readyCheck = this.isComponentReady();
319
- if (!readyCheck.ready) {
320
- return false; // Return false instead of error for this method
321
- }
322
303
  return this.stateManager.isProcessCompleted();
323
304
  }
324
305
  async startCapture() {
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
- }
306
+ await this.startDetection();
338
307
  }
339
308
  async stopCapture() {
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
- }
309
+ this.exitSession();
351
310
  }
352
311
  async resetCapture() {
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
- }
312
+ this.resetDetection();
364
313
  }
365
314
  async skipBackCapture() {
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 };
315
+ const captureState = this.stateManager.getCaptureState();
316
+ const capturedImages = this.stateManager.getCapturedImages();
317
+ if (captureState.step === 'back' && capturedImages.front.fullFrame) {
318
+ this.completeProcess(true);
383
319
  }
384
320
  }
385
321
  async getStatus() {
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
- }
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
+ };
421
331
  }
422
332
  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
- }
428
333
  if (this.detectionService.isModelLoaded()) {
429
- this.updateStatus('Reconocimiento listo', '', 'ready');
334
+ this.logger.state('MODELO_YA_PRECARGADO');
335
+ this.updateStatus('Modelos ya cargados', '', 'ready');
430
336
  return { success: true, message: 'Model already loaded' };
431
337
  }
432
338
  try {
433
339
  const loadStartTime = performance.now();
434
- this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
435
- if (this.stateManager) {
436
- this.stateManager.updateCaptureState({ isLoading: true });
437
- }
340
+ this.updateStatus('Descargando modelo de detección...', 'Obteniendo red neuronal para reconocimiento de documentos', 'loading');
341
+ this.stateManager.updateCaptureState({ isLoading: true });
438
342
  await this.detectionService.loadModel();
439
- this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
343
+ this.updateStatus('Cargando clasificador...', 'Preparando modelo de clasificación de tipos de documento', 'loading');
440
344
  await this.detectionService.loadClassificationModel();
441
345
  const loadEndTime = performance.now();
442
346
  const loadTime = loadEndTime - loadStartTime;
@@ -444,118 +348,80 @@ export class JaakStamps {
444
348
  if (this.debug) {
445
349
  this.recordOnnxPerformance(loadTime, 0);
446
350
  }
447
- this.updateStatus('Finalizando configuración...', 'Preparando herramientas para captura', 'loading');
351
+ this.updateStatus('Optimizando modelos...', 'Configurando parámetros de rendimiento', 'loading');
448
352
  await new Promise(resolve => setTimeout(resolve, 300));
449
- this.updateStatus('Reconocimiento preparado', '', 'ready');
450
- if (this.stateManager) {
451
- this.stateManager.updateCaptureState({ isLoading: false });
452
- }
353
+ this.updateStatus('Modelos precargados', '', 'ready');
354
+ this.stateManager.updateCaptureState({ isLoading: false });
453
355
  this.emitReadyEvent();
356
+ this.logger.state('MODELOS_PRECARGADOS_EXITOSAMENTE', { loadTime: Math.round(loadTime) });
454
357
  return { success: true, message: 'Models preloaded successfully' };
455
358
  }
456
359
  catch (error) {
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
- }
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 });
461
363
  return { success: false, error: error.message };
462
364
  }
463
365
  }
464
366
  async 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
- }
367
+ return this.cameraService.getCameraInfo();
489
368
  }
490
369
  async setPreferredCamera(camera) {
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
- }
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);
511
378
  }
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
- };
525
379
  }
380
+ return {
381
+ success: true,
382
+ selectedCamera: this.cameraService.getSelectedCameraId(),
383
+ availableCameras: this.cameraService.getAvailableCameras().length
384
+ };
526
385
  }
527
386
  async setCaptureDelay(delay) {
528
387
  if (delay < 0 || delay > 10000) {
529
- return {
530
- success: false,
531
- error: 'Capture delay must be between 0 and 10000 milliseconds',
532
- captureDelay: this.captureDelay
533
- };
388
+ throw new Error('Capture delay must be between 0 and 10000 milliseconds');
534
389
  }
535
- // Allow setting capture delay even during initialization
536
390
  this.captureDelay = delay;
537
- if (this.serviceContainer) {
538
- this.serviceContainer.updateConfig({ captureDelay: delay });
539
- }
391
+ this.serviceContainer.updateConfig({ captureDelay: delay });
540
392
  return {
541
393
  success: true,
542
394
  captureDelay: this.captureDelay
543
395
  };
544
396
  }
545
397
  async getCaptureDelay() {
546
- // Always allow getting capture delay, even during initialization
547
398
  return this.captureDelay;
548
399
  }
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
+ }
549
414
  // DETECTION METHODS
550
415
  async startDetection() {
416
+ this.logger.state('INICIANDO_DETECCION');
551
417
  try {
552
418
  // Paso 1: Verificar y cargar modelos si es necesario
553
419
  if (!this.detectionService.isModelLoaded()) {
554
420
  const loadStartTime = performance.now();
555
- this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
421
+ this.updateStatus('Cargando modelo de detección...', 'Descargando red neuronal para reconocimiento', 'loading');
556
422
  this.stateManager.updateCaptureState({ isLoading: true });
557
423
  await this.detectionService.loadModel();
558
- this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
424
+ this.updateStatus('Cargando clasificador...', 'Preparando modelo de clasificación de documentos', 'loading');
559
425
  await this.detectionService.loadClassificationModel();
560
426
  const loadEndTime = performance.now();
561
427
  const loadTime = loadEndTime - loadStartTime;
@@ -563,13 +429,13 @@ export class JaakStamps {
563
429
  if (this.debug) {
564
430
  this.recordOnnxPerformance(loadTime, 0);
565
431
  }
432
+ this.logger.state('MODELOS_CARGADOS_EN_DETECCION', { loadTime: Math.round(loadTime) });
566
433
  }
567
434
  // Paso 2: Detectar y configurar dispositivos
568
- this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
435
+ this.updateStatus('Detectando cámaras...', 'Buscando dispositivos de captura disponibles', 'loading');
569
436
  await this.cameraService.enumerateDevices();
570
- await this.updateCameraInfoWithAutofocus();
571
437
  // Paso 3: Configurar cámara seleccionada
572
- this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
438
+ this.updateStatus('Configurando cámara...', 'Estableciendo resolución y parámetros óptimos', 'loading');
573
439
  const stream = await this.cameraService.setupCamera();
574
440
  // Paso 4: Inicializar video y ocultar estado inmediatamente
575
441
  this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
@@ -586,7 +452,8 @@ export class JaakStamps {
586
452
  this.detectFrame();
587
453
  }
588
454
  catch (err) {
589
- this.updateStatus('Error al iniciar', 'No se pudo preparar la captura', 'error');
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');
590
457
  this.stateManager.updateCaptureState({ isLoading: false });
591
458
  }
592
459
  }
@@ -596,6 +463,10 @@ export class JaakStamps {
596
463
  this.videoStream = stream;
597
464
  const isRear = this.cameraService.isRearCamera(stream);
598
465
  this.shouldMirrorVideo = !isRear;
466
+ this.logger.state('CAMARA_CONFIGURADA', {
467
+ isRearCamera: isRear,
468
+ shouldMirrorVideo: this.shouldMirrorVideo
469
+ });
599
470
  return new Promise((resolve) => {
600
471
  this.videoRef.onloadedmetadata = async () => {
601
472
  await this.videoRef.play();
@@ -620,10 +491,9 @@ export class JaakStamps {
620
491
  }
621
492
  return;
622
493
  }
623
- // Adaptive frame skipping for performance
494
+ // Frame skipping for performance
624
495
  this.frameSkipCounter++;
625
- const currentFrameSkip = this.getAdaptiveFrameSkip();
626
- if (this.frameSkipCounter <= currentFrameSkip) {
496
+ if (this.frameSkipCounter <= this.FRAME_SKIP) {
627
497
  if (captureState.step !== 'completed') {
628
498
  this.animationId = requestAnimationFrame(() => this.detectFrame());
629
499
  }
@@ -699,6 +569,7 @@ export class JaakStamps {
699
569
  }
700
570
  }
701
571
  catch (e) {
572
+ this.logger.error('Error en inferencia de modelo:', e);
702
573
  const captureState = this.stateManager.getCaptureState();
703
574
  if (captureState.step !== 'completed') {
704
575
  setTimeout(() => this.detectFrame(), 100);
@@ -725,8 +596,6 @@ export class JaakStamps {
725
596
  clearInterval(this.performanceUpdateInterval);
726
597
  this.performanceUpdateInterval = undefined;
727
598
  }
728
- // Clear canvas pool
729
- this.canvasPool = [];
730
599
  this.detectionBoxes = [];
731
600
  this.alignmentStartTime = undefined;
732
601
  this.hasDocumentDetected = false;
@@ -741,8 +610,14 @@ export class JaakStamps {
741
610
  step: 'front',
742
611
  isCapturing: false
743
612
  };
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: {
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: {
746
621
  position: 'absolute',
747
622
  left: `${box.x}px`,
748
623
  top: `${box.y}px`,
@@ -751,9 +626,9 @@ export class JaakStamps {
751
626
  border: '2px solid #32406C',
752
627
  pointerEvents: 'none',
753
628
  boxSizing: 'border-box'
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
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
755
630
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
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" })))));
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" })))));
757
632
  }
758
633
  // Utility methods
759
634
  updateDetectionBoxes(boxes) {
@@ -834,18 +709,35 @@ export class JaakStamps {
834
709
  if (allSidesAligned && bestBox) {
835
710
  cardOutline?.classList.add('perfect-match');
836
711
  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
+ });
837
719
  if (!this.hasScreenshotTaken) {
838
720
  const currentTime = Date.now();
839
721
  // Initialize alignment start time if not set
840
722
  if (!this.alignmentStartTime) {
841
723
  this.alignmentStartTime = currentTime;
724
+ this.logger.state('ALIGNMENT_TIMER_STARTED', { startTime: currentTime });
842
725
  }
843
726
  // Check if document has been aligned for the configured delay
844
727
  const alignmentDuration = currentTime - this.alignmentStartTime;
728
+ this.logger.state('ALIGNMENT_DURATION_CHECK', {
729
+ alignmentDuration,
730
+ captureDelay: this.captureDelay,
731
+ readyToCapture: alignmentDuration >= this.captureDelay
732
+ });
845
733
  if (alignmentDuration >= this.captureDelay) {
734
+ this.logger.state('TRIGGERING_CAPTURE', {
735
+ alignmentDuration,
736
+ captureDelay: this.captureDelay
737
+ });
846
738
  this.lastDetectedBox = bestBox;
847
- this.takeScreenshot().catch(() => {
848
- // Handle screenshot error silently
739
+ this.takeScreenshot().catch(error => {
740
+ this.logger.error('Error al tomar captura de pantalla:', error);
849
741
  });
850
742
  this.hasScreenshotTaken = true;
851
743
  this.alignmentStartTime = undefined;
@@ -871,12 +763,21 @@ export class JaakStamps {
871
763
  async takeScreenshot() {
872
764
  if (!this.videoRef || !this.lastDetectedBox)
873
765
  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
+ });
874
774
  this.stateManager.updateCaptureState({ isCapturing: true });
875
775
  this.triggerCaptureAnimation();
876
- // Use pooled canvas for optimization
877
- const captureCanvas = this.getPooledCanvas(this.videoRef.videoWidth, this.videoRef.videoHeight);
776
+ // Create capture canvas
777
+ const captureCanvas = document.createElement('canvas');
778
+ captureCanvas.width = this.videoRef.videoWidth;
779
+ captureCanvas.height = this.videoRef.videoHeight;
878
780
  const captureCtx = captureCanvas.getContext('2d', { alpha: false });
879
- captureCtx.clearRect(0, 0, captureCanvas.width, captureCanvas.height);
880
781
  captureCtx.drawImage(this.videoRef, 0, 0, captureCanvas.width, captureCanvas.height);
881
782
  // Calculate crop coordinates
882
783
  const INPUT_SIZE = 320;
@@ -886,10 +787,11 @@ export class JaakStamps {
886
787
  const cropY = Math.max(0, (this.lastDetectedBox.y * scaleY) - this.cropMargin);
887
788
  const cropWidth = Math.min((this.lastDetectedBox.w * scaleX) + (2 * this.cropMargin), this.videoRef.videoWidth - cropX);
888
789
  const cropHeight = Math.min((this.lastDetectedBox.h * scaleY) + (2 * this.cropMargin), this.videoRef.videoHeight - cropY);
889
- // Use pooled canvas for cropped version
890
- const croppedCanvas = this.getPooledCanvas(cropWidth, cropHeight);
790
+ // Create cropped version
791
+ const croppedCanvas = document.createElement('canvas');
792
+ croppedCanvas.width = cropWidth;
793
+ croppedCanvas.height = cropHeight;
891
794
  const croppedCtx = croppedCanvas.getContext('2d', { alpha: false });
892
- croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);
893
795
  croppedCtx.drawImage(this.videoRef, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
894
796
  const captureState = this.stateManager.getCaptureState();
895
797
  if (captureState.step === 'front') {
@@ -903,6 +805,7 @@ export class JaakStamps {
903
805
  if (this.useDocumentClassification) {
904
806
  const classification = await this.detectionService.classifyDocument(croppedCanvas);
905
807
  if (classification && classification.class === 'passport') {
808
+ this.logger.state('PASAPORTE_DETECTADO_SALTANDO_REVERSO', { classification: classification?.class });
906
809
  this.completeProcess(true);
907
810
  return;
908
811
  }
@@ -929,9 +832,6 @@ export class JaakStamps {
929
832
  });
930
833
  this.completeProcess(false);
931
834
  }
932
- // Return canvases to pool after use
933
- this.returnCanvasToPool(captureCanvas);
934
- this.returnCanvasToPool(croppedCanvas);
935
835
  }
936
836
  triggerCaptureAnimation() {
937
837
  const cardOutline = this.el.shadowRoot?.querySelector('.card-outline');
@@ -960,6 +860,11 @@ export class JaakStamps {
960
860
  setTimeout(() => {
961
861
  this.stateManager.updateCaptureState({ showSuccessAnimation: false });
962
862
  }, 3000);
863
+ this.logger.state('PROCESO_COMPLETADO', {
864
+ skippedBack,
865
+ totalImages: capturedImages.metadata.totalImages,
866
+ timestamp: new Date().toISOString()
867
+ });
963
868
  }
964
869
  stopDetection() {
965
870
  if (this.animationId) {
@@ -968,6 +873,7 @@ export class JaakStamps {
968
873
  }
969
874
  this.clearBackDocumentTimer();
970
875
  this.detectionBoxes = [];
876
+ this.logger.state('DETECTOR_DETENIDO', { timestamp: Date.now() });
971
877
  }
972
878
  startBackDocumentTimer() {
973
879
  if (!this.enableBackDocumentTimer)
@@ -996,15 +902,6 @@ export class JaakStamps {
996
902
  return; // Don't toggle if switching camera
997
903
  this.showCameraSelector = !this.showCameraSelector;
998
904
  }
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
- }
1008
905
  async handleCameraSwitch(cameraId) {
1009
906
  if (this.isSwitchingCamera)
1010
907
  return; // Prevent multiple simultaneous switches
@@ -1012,6 +909,10 @@ export class JaakStamps {
1012
909
  // Close the selector immediately when user selects a camera
1013
910
  this.showCameraSelector = false;
1014
911
  this.isSwitchingCamera = true;
912
+ this.logger.state('INICIANDO_CAMBIO_CAMARA', {
913
+ from: this.cameraService.getSelectedCameraId(),
914
+ to: cameraId
915
+ });
1015
916
  // Stop current video stream
1016
917
  if (this.videoStream) {
1017
918
  this.videoStream.getTracks().forEach(track => track.stop());
@@ -1022,17 +923,21 @@ export class JaakStamps {
1022
923
  const newStream = await this.cameraService.setupCamera();
1023
924
  // Update video element
1024
925
  await this.initializeVideoStream(newStream);
1025
- // Update camera info with autofocus data
1026
- await this.updateCameraInfoWithAutofocus();
926
+ this.logger.state('CAMBIO_CAMARA_EXITOSO', {
927
+ newCameraId: cameraId,
928
+ isRearCamera: this.cameraService.isRearCamera(newStream)
929
+ });
1027
930
  }
1028
931
  catch (error) {
932
+ this.logger.error('Error al cambiar cámara:', error);
1029
933
  // Try to restore previous camera if switch failed
1030
934
  try {
1031
935
  const fallbackStream = await this.cameraService.setupCamera();
1032
936
  await this.initializeVideoStream(fallbackStream);
1033
937
  }
1034
938
  catch (fallbackError) {
1035
- this.updateStatus('Error al cambiar cámara', 'No se pudo cambiar el dispositivo', 'error');
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');
1036
941
  }
1037
942
  }
1038
943
  finally {
@@ -1073,7 +978,7 @@ export class JaakStamps {
1073
978
  this.stateManager.updateCaptureState({ isVideoActive: false, isLoading: false });
1074
979
  }
1075
980
  this.isMaskReady = false;
1076
- this.updateStatus('Captura finalizada', '', 'ready');
981
+ this.updateStatus('Sesión finalizada', '', 'ready');
1077
982
  this.detectionBoxes = [];
1078
983
  this.cleanup();
1079
984
  }
@@ -1084,6 +989,7 @@ export class JaakStamps {
1084
989
  this.performanceUpdateInterval = window.setInterval(() => {
1085
990
  this.updatePerformanceMetrics();
1086
991
  }, 500);
992
+ this.logger.debug('Monitor de performance inicializado');
1087
993
  }
1088
994
  updatePerformanceMetrics() {
1089
995
  const currentTime = performance.now();
@@ -1126,66 +1032,6 @@ export class JaakStamps {
1126
1032
  if (detectionsFound > 0) {
1127
1033
  this.performanceMetrics.successfulDetections++;
1128
1034
  }
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
1189
1035
  }
1190
1036
  static get is() { return "jaak-stamps"; }
1191
1037
  static get encapsulation() { return "shadow"; }
@@ -1392,7 +1238,6 @@ export class JaakStamps {
1392
1238
  "showCameraSelector": {},
1393
1239
  "isSwitchingCamera": {},
1394
1240
  "hasDocumentDetected": {},
1395
- "cameraInfoWithAutofocus": {},
1396
1241
  "currentStatus": {},
1397
1242
  "performanceData": {},
1398
1243
  "backDocumentTimerRemaining": {}
@@ -1474,7 +1319,7 @@ export class JaakStamps {
1474
1319
  },
1475
1320
  "startCapture": {
1476
1321
  "complexType": {
1477
- "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
1322
+ "signature": "() => Promise<void>",
1478
1323
  "parameters": [],
1479
1324
  "references": {
1480
1325
  "Promise": {
@@ -1482,7 +1327,7 @@ export class JaakStamps {
1482
1327
  "id": "global::Promise"
1483
1328
  }
1484
1329
  },
1485
- "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
1330
+ "return": "Promise<void>"
1486
1331
  },
1487
1332
  "docs": {
1488
1333
  "text": "",
@@ -1491,7 +1336,7 @@ export class JaakStamps {
1491
1336
  },
1492
1337
  "stopCapture": {
1493
1338
  "complexType": {
1494
- "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
1339
+ "signature": "() => Promise<void>",
1495
1340
  "parameters": [],
1496
1341
  "references": {
1497
1342
  "Promise": {
@@ -1499,7 +1344,7 @@ export class JaakStamps {
1499
1344
  "id": "global::Promise"
1500
1345
  }
1501
1346
  },
1502
- "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
1347
+ "return": "Promise<void>"
1503
1348
  },
1504
1349
  "docs": {
1505
1350
  "text": "",
@@ -1508,7 +1353,7 @@ export class JaakStamps {
1508
1353
  },
1509
1354
  "resetCapture": {
1510
1355
  "complexType": {
1511
- "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
1356
+ "signature": "() => Promise<void>",
1512
1357
  "parameters": [],
1513
1358
  "references": {
1514
1359
  "Promise": {
@@ -1516,7 +1361,7 @@ export class JaakStamps {
1516
1361
  "id": "global::Promise"
1517
1362
  }
1518
1363
  },
1519
- "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
1364
+ "return": "Promise<void>"
1520
1365
  },
1521
1366
  "docs": {
1522
1367
  "text": "",
@@ -1525,7 +1370,7 @@ export class JaakStamps {
1525
1370
  },
1526
1371
  "skipBackCapture": {
1527
1372
  "complexType": {
1528
- "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
1373
+ "signature": "() => Promise<void>",
1529
1374
  "parameters": [],
1530
1375
  "references": {
1531
1376
  "Promise": {
@@ -1533,7 +1378,7 @@ export class JaakStamps {
1533
1378
  "id": "global::Promise"
1534
1379
  }
1535
1380
  },
1536
- "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
1381
+ "return": "Promise<void>"
1537
1382
  },
1538
1383
  "docs": {
1539
1384
  "text": "",
@@ -1603,7 +1448,7 @@ export class JaakStamps {
1603
1448
  },
1604
1449
  "setPreferredCamera": {
1605
1450
  "complexType": {
1606
- "signature": "(camera: \"auto\" | \"front\" | \"back\") => Promise<{ success: boolean; selectedCamera: string; availableCameras: number; error?: undefined; } | { success: boolean; error: any; selectedCamera: any; availableCameras: number; }>",
1451
+ "signature": "(camera: \"auto\" | \"front\" | \"back\") => Promise<{ success: boolean; selectedCamera: string; availableCameras: number; }>",
1607
1452
  "parameters": [{
1608
1453
  "name": "camera",
1609
1454
  "type": "\"auto\" | \"front\" | \"back\"",
@@ -1615,7 +1460,7 @@ export class JaakStamps {
1615
1460
  "id": "global::Promise"
1616
1461
  }
1617
1462
  },
1618
- "return": "Promise<{ success: boolean; selectedCamera: string; availableCameras: number; error?: undefined; } | { success: boolean; error: any; selectedCamera: any; availableCameras: number; }>"
1463
+ "return": "Promise<{ success: boolean; selectedCamera: string; availableCameras: number; }>"
1619
1464
  },
1620
1465
  "docs": {
1621
1466
  "text": "",
@@ -1624,7 +1469,7 @@ export class JaakStamps {
1624
1469
  },
1625
1470
  "setCaptureDelay": {
1626
1471
  "complexType": {
1627
- "signature": "(delay: number) => Promise<{ success: boolean; error: string; captureDelay: number; } | { success: boolean; captureDelay: number; error?: undefined; }>",
1472
+ "signature": "(delay: number) => Promise<{ success: boolean; captureDelay: number; }>",
1628
1473
  "parameters": [{
1629
1474
  "name": "delay",
1630
1475
  "type": "number",
@@ -1636,7 +1481,7 @@ export class JaakStamps {
1636
1481
  "id": "global::Promise"
1637
1482
  }
1638
1483
  },
1639
- "return": "Promise<{ success: boolean; error: string; captureDelay: number; } | { success: boolean; captureDelay: number; error?: undefined; }>"
1484
+ "return": "Promise<{ success: boolean; captureDelay: number; }>"
1640
1485
  },
1641
1486
  "docs": {
1642
1487
  "text": "",
@@ -1659,6 +1504,52 @@ export class JaakStamps {
1659
1504
  "text": "",
1660
1505
  "tags": []
1661
1506
  }
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
+ }
1662
1553
  }
1663
1554
  };
1664
1555
  }