@jaak.ai/stamps 2.1.0-dev.9 → 2.2.0-beta.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 (79) hide show
  1. package/README.md +227 -14
  2. package/dist/cjs/app-globals-V2Kpy_OQ.js +8 -0
  3. package/dist/cjs/app-globals-V2Kpy_OQ.js.map +1 -0
  4. package/dist/cjs/{index-BfhtOB0D.js → index-D6NBn_qu.js} +203 -121
  5. package/dist/cjs/index-D6NBn_qu.js.map +1 -0
  6. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +5 -4
  7. package/dist/cjs/jaak-stamps-webcomponent.cjs.js.map +1 -1
  8. package/dist/cjs/jaak-stamps.cjs.entry.js +17780 -442
  9. package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
  10. package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
  11. package/dist/cjs/loader.cjs.js +4 -3
  12. package/dist/cjs/loader.cjs.js.map +1 -1
  13. package/dist/collection/collection-manifest.json +1 -1
  14. package/dist/collection/components/my-component/my-component.css +106 -5
  15. package/dist/collection/components/my-component/my-component.js +558 -313
  16. package/dist/collection/components/my-component/my-component.js.map +1 -1
  17. package/dist/collection/services/CameraService.js +125 -11
  18. package/dist/collection/services/CameraService.js.map +1 -1
  19. package/dist/collection/services/DetectionService.js +7 -148
  20. package/dist/collection/services/DetectionService.js.map +1 -1
  21. package/dist/collection/services/LicenseValidationService.js +111 -0
  22. package/dist/collection/services/LicenseValidationService.js.map +1 -0
  23. package/dist/collection/services/MetricsService.js +350 -0
  24. package/dist/collection/services/MetricsService.js.map +1 -0
  25. package/dist/collection/services/ServiceContainer.js +66 -1
  26. package/dist/collection/services/ServiceContainer.js.map +1 -1
  27. package/dist/collection/services/TracingService.js +494 -0
  28. package/dist/collection/services/TracingService.js.map +1 -0
  29. package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
  30. package/dist/collection/services/interfaces/ILicenseValidationService.js +2 -0
  31. package/dist/collection/services/interfaces/ILicenseValidationService.js.map +1 -0
  32. package/dist/collection/services/interfaces/IMetricsService.js +2 -0
  33. package/dist/collection/services/interfaces/IMetricsService.js.map +1 -0
  34. package/dist/collection/services/interfaces/ITracingService.js +2 -0
  35. package/dist/collection/services/interfaces/ITracingService.js.map +1 -0
  36. package/dist/components/index.js +192 -115
  37. package/dist/components/index.js.map +1 -1
  38. package/dist/components/jaak-stamps.js +17796 -445
  39. package/dist/components/jaak-stamps.js.map +1 -1
  40. package/dist/esm/app-globals-DQuL1Twl.js +6 -0
  41. package/dist/esm/app-globals-DQuL1Twl.js.map +1 -0
  42. package/dist/esm/{index-BP1Q4KOg.js → index-BCfAsrzL.js} +204 -121
  43. package/dist/esm/index-BCfAsrzL.js.map +1 -0
  44. package/dist/esm/jaak-stamps-webcomponent.js +5 -4
  45. package/dist/esm/jaak-stamps-webcomponent.js.map +1 -1
  46. package/dist/esm/jaak-stamps.entry.js +17780 -442
  47. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  48. package/dist/esm/loader.js +4 -3
  49. package/dist/esm/loader.js.map +1 -1
  50. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  51. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js.map +1 -1
  52. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  53. package/dist/jaak-stamps-webcomponent/loader.esm.js.map +1 -1
  54. package/dist/jaak-stamps-webcomponent/p-34bcebb1.entry.js +7 -0
  55. package/dist/jaak-stamps-webcomponent/p-34bcebb1.entry.js.map +1 -0
  56. package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js +3 -0
  57. package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js.map +1 -0
  58. package/dist/jaak-stamps-webcomponent/p-DQuL1Twl.js +2 -0
  59. package/dist/jaak-stamps-webcomponent/p-DQuL1Twl.js.map +1 -0
  60. package/dist/types/components/my-component/my-component.d.ts +25 -27
  61. package/dist/types/components.d.ts +77 -3
  62. package/dist/types/services/CameraService.d.ts +1 -0
  63. package/dist/types/services/DetectionService.d.ts +1 -9
  64. package/dist/types/services/LicenseValidationService.d.ts +59 -0
  65. package/dist/types/services/MetricsService.d.ts +74 -0
  66. package/dist/types/services/ServiceContainer.d.ts +14 -1
  67. package/dist/types/services/TracingService.d.ts +72 -0
  68. package/dist/types/services/interfaces/IDetectionService.d.ts +0 -6
  69. package/dist/types/services/interfaces/ILicenseValidationService.d.ts +4 -0
  70. package/dist/types/services/interfaces/IMetricsService.d.ts +70 -0
  71. package/dist/types/services/interfaces/ITracingService.d.ts +64 -0
  72. package/dist/types/stencil-public-runtime.d.ts +12 -5
  73. package/package.json +20 -2
  74. package/dist/cjs/index-BfhtOB0D.js.map +0 -1
  75. package/dist/esm/index-BP1Q4KOg.js.map +0 -1
  76. package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js +0 -3
  77. package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js.map +0 -1
  78. package/dist/jaak-stamps-webcomponent/p-c115e01a.entry.js +0 -2
  79. package/dist/jaak-stamps-webcomponent/p-c115e01a.entry.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { h } from "@stencil/core";
2
2
  import { ServiceContainer } from "../../services/ServiceContainer";
3
+ import { LicenseValidationService } from "../../services/LicenseValidationService";
3
4
  export class JaakStamps {
4
5
  el;
5
6
  debug = false;
@@ -12,8 +13,22 @@ export class JaakStamps {
12
13
  captureDelay = 1500;
13
14
  enableBackDocumentTimer = false;
14
15
  backDocumentTimerDuration = 20;
16
+ telemetryCollectorUrl = 'https://collector.jaak.ai/v1/traces';
17
+ metricsCollectorUrl = 'https://collector.jaak.ai/v1/metrics';
18
+ enableTelemetry = true;
19
+ enableMetrics = true;
20
+ customerId;
21
+ tenantId;
22
+ environment = 'production';
23
+ propagateTraceHeaderCorsUrls; // Comma-separated URLs or regex patterns
24
+ metricsExportIntervalMillis = 60000; // Export metrics every 60 seconds
25
+ license; // License key for validation
26
+ licenseEnvironment = 'prod'; // Environment for license validation
27
+ traceId; // Optional trace ID for tracking
28
+ appId = 'jaak-stamps-web'; // Application ID for license validation
15
29
  captureCompleted;
16
30
  isReady;
31
+ traceIdGenerated;
17
32
  // State derived from services
18
33
  detectionBoxes = [];
19
34
  sideAlignment = {
@@ -53,12 +68,17 @@ export class JaakStamps {
53
68
  showPerformanceMessage = false; // Show performance message temporarily
54
69
  captureStateVersion = 0; // Triggers re-render when StateManager changes
55
70
  processingButton = null; // Track which button is processing
71
+ licenseValid = true; // License validation status
72
+ licenseError = null; // License validation error message
56
73
  // Services
74
+ licenseValidationService = null;
57
75
  serviceContainer;
58
76
  eventBus;
59
77
  stateManager;
60
78
  cameraService;
61
79
  detectionService;
80
+ tracingService;
81
+ metricsService;
62
82
  // UI References
63
83
  videoRef;
64
84
  detectionContainer;
@@ -71,6 +91,7 @@ export class JaakStamps {
71
91
  alignmentStartTime;
72
92
  alignmentTimer;
73
93
  backDocumentTimer;
94
+ orientationTimer;
74
95
  // Performance monitoring
75
96
  performanceMetrics = {
76
97
  fps: 0,
@@ -92,6 +113,7 @@ export class JaakStamps {
92
113
  MAX_FAILURES = 30;
93
114
  lastInferenceTime = 0;
94
115
  MIN_INFERENCE_INTERVAL = 50;
116
+ MOBILE_MIN_INFERENCE_INTERVAL = 100; // Higher interval for mobile to prevent slow motion
95
117
  performanceHistory = [];
96
118
  PERFORMANCE_HISTORY_SIZE = 10;
97
119
  // Sistema simplificado de monitoreo de rendimiento
@@ -103,8 +125,23 @@ export class JaakStamps {
103
125
  // Canvas pool for optimized screenshot capture
104
126
  canvasPool = [];
105
127
  MAX_CANVAS_POOL_SIZE = 3;
128
+ async componentWillLoad() {
129
+ // License is required - always validate
130
+ // Wait a bit to ensure props are set from attributes
131
+ await new Promise(resolve => setTimeout(resolve, 50));
132
+ this.updateStatus('Validando licencia...', 'Verificando autorización de uso', 'initializing');
133
+ await this.validateLicense();
134
+ // If license validation failed, stop initialization
135
+ if (!this.licenseValid) {
136
+ return;
137
+ }
138
+ }
106
139
  async componentDidLoad() {
107
140
  try {
141
+ // If license validation failed, stop initialization
142
+ if (!this.licenseValid) {
143
+ return;
144
+ }
108
145
  this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
109
146
  await this.initializeServices();
110
147
  this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
@@ -120,6 +157,10 @@ export class JaakStamps {
120
157
  }
121
158
  }
122
159
  async initializeServices() {
160
+ // Parse propagateTraceHeaderCorsUrls from comma-separated string
161
+ const propagateUrls = this.propagateTraceHeaderCorsUrls
162
+ ? this.propagateTraceHeaderCorsUrls.split(',').map(url => url.trim())
163
+ : [];
123
164
  const config = {
124
165
  debug: this.debug,
125
166
  alignmentTolerance: this.alignmentTolerance,
@@ -128,13 +169,76 @@ export class JaakStamps {
128
169
  useDocumentClassification: this.useDocumentClassification,
129
170
  useDocumentDetector: this.useDocumentDetector,
130
171
  preferredCamera: this.preferredCamera,
131
- captureDelay: this.captureDelay
172
+ captureDelay: this.captureDelay,
173
+ telemetryCollectorUrl: this.telemetryCollectorUrl,
174
+ metricsCollectorUrl: this.metricsCollectorUrl,
175
+ enableTelemetry: this.enableTelemetry,
176
+ enableMetrics: this.enableMetrics,
177
+ customerId: this.customerId,
178
+ tenantId: this.tenantId,
179
+ environment: this.environment,
180
+ propagateTraceHeaderCorsUrls: propagateUrls,
181
+ metricsExportIntervalMillis: this.metricsExportIntervalMillis
132
182
  };
133
183
  this.serviceContainer = new ServiceContainer(config);
134
184
  this.eventBus = this.serviceContainer.getEventBus();
135
185
  this.stateManager = this.serviceContainer.getStateManager();
136
186
  this.cameraService = this.serviceContainer.getCameraService();
137
187
  this.detectionService = this.serviceContainer.getDetectionService();
188
+ this.tracingService = this.serviceContainer.getTracingService();
189
+ this.metricsService = this.serviceContainer.getMetricsService();
190
+ // Set the custom trace ID if provided via prop
191
+ if (this.tracingService && this.traceId) {
192
+ this.tracingService.setSessionTraceId(this.traceId);
193
+ }
194
+ // Record component initialization
195
+ if (this.tracingService) {
196
+ const span = this.tracingService.startSpan('component.initialize', {
197
+ 'component.debug': this.debug,
198
+ 'component.useDocumentDetector': this.useDocumentDetector,
199
+ 'component.useDocumentClassification': this.useDocumentClassification,
200
+ });
201
+ this.tracingService.endSpan(span);
202
+ }
203
+ }
204
+ async validateLicense() {
205
+ if (!this.license) {
206
+ console.error('❌ No license provided for JAAK Stamps component');
207
+ this.licenseError = 'Este componente requiere una licencia válida. Por favor, proporcione una licencia usando el atributo "license".';
208
+ this.licenseValid = false;
209
+ this.updateStatus('Error de licencia', 'Licencia no proporcionada', 'error');
210
+ return;
211
+ }
212
+ try {
213
+ console.log('🔑 Validating license for JAAK Stamps...');
214
+ // Initialize license validation service
215
+ this.licenseValidationService = new LicenseValidationService(this.licenseEnvironment);
216
+ // Validate the license, passing traceId and appId if provided
217
+ const response = await this.licenseValidationService.validateLicense(this.license, this.traceId, this.appId);
218
+ if (response && response.access_token) {
219
+ this.licenseValid = true;
220
+ this.licenseError = null;
221
+ console.log('✅ License validated successfully for JAAK Stamps');
222
+ // Emit the traceId (either provided or generated)
223
+ if (response.traceId) {
224
+ this.traceIdGenerated.emit({ traceId: response.traceId });
225
+ console.log('📋 TraceId:', response.traceId);
226
+ }
227
+ }
228
+ else {
229
+ this.licenseValid = false;
230
+ this.licenseError = 'La licencia proporcionada no es válida. Por favor, verifique su licencia e intente nuevamente.';
231
+ this.updateStatus('Error de licencia', 'Licencia inválida', 'error');
232
+ console.error('❌ Invalid license for JAAK Stamps');
233
+ }
234
+ }
235
+ catch (error) {
236
+ this.licenseValid = false;
237
+ const licenseError = error;
238
+ this.licenseError = licenseError.error_description || 'Error al validar la licencia. Por favor, verifique su conexión e intente nuevamente.';
239
+ this.updateStatus('Error de licencia', 'Fallo en la validación', 'error');
240
+ console.error('❌ License validation failed for JAAK Stamps:', error);
241
+ }
138
242
  }
139
243
  async setupEventListeners() {
140
244
  this.eventBus.on('state-changed', () => {
@@ -174,7 +278,7 @@ export class JaakStamps {
174
278
  if (!window.ort) {
175
279
  this.updateStatus('Preparando herramientas...', 'Configurando funciones de captura', 'initializing');
176
280
  const script = document.createElement('script');
177
- script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
281
+ script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.23.0/dist/ort.min.js';
178
282
  document.head.appendChild(script);
179
283
  await new Promise((resolve) => {
180
284
  script.onload = () => {
@@ -255,6 +359,18 @@ export class JaakStamps {
255
359
  });
256
360
  resizeObserver.observe(this.detectionContainer.parentElement);
257
361
  }
362
+ // Handle mobile device orientation changes
363
+ window.addEventListener('orientationchange', () => {
364
+ // Clear any existing orientation timer
365
+ if (this.orientationTimer) {
366
+ clearTimeout(this.orientationTimer);
367
+ }
368
+ // Add delay to ensure video dimensions are updated after rotation
369
+ this.orientationTimer = window.setTimeout(() => {
370
+ this.handleResize();
371
+ this.orientationTimer = undefined;
372
+ }, 300);
373
+ });
258
374
  }
259
375
  handleResize() {
260
376
  if (this.detectionContainer) {
@@ -390,17 +506,31 @@ export class JaakStamps {
390
506
  return this.stateManager.isProcessCompleted();
391
507
  }
392
508
  async startCapture() {
509
+ const span = this.tracingService?.startSpan('capture.start');
393
510
  const readyCheck = this.isComponentReady();
394
511
  if (!readyCheck.ready) {
395
512
  this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
513
+ if (span) {
514
+ this.tracingService?.setSpanAttribute(span, 'error', true);
515
+ this.tracingService?.setSpanAttribute(span, 'error.message', readyCheck.message);
516
+ this.tracingService?.endSpan(span);
517
+ }
396
518
  return { success: false, error: readyCheck.message };
397
519
  }
398
520
  try {
399
521
  await this.startDetection();
522
+ if (span) {
523
+ this.tracingService?.setSpanAttribute(span, 'success', true);
524
+ this.tracingService?.endSpan(span);
525
+ }
400
526
  return { success: true };
401
527
  }
402
528
  catch (error) {
403
529
  this.updateStatus('Error al iniciar captura', error.message, 'error');
530
+ if (span) {
531
+ this.tracingService?.recordException(span, error);
532
+ this.tracingService?.endSpan(span);
533
+ }
404
534
  return { success: false, error: error.message };
405
535
  }
406
536
  }
@@ -411,13 +541,7 @@ export class JaakStamps {
411
541
  }
412
542
  try {
413
543
  this.exitSession();
414
- // Aggressive cleanup when stopping capture
415
- const cleanupResult = await this.aggressiveResourceCleanup();
416
- return {
417
- success: true,
418
- resourcesFreed: cleanupResult.freed,
419
- cleanupErrors: cleanupResult.errors
420
- };
544
+ return { success: true };
421
545
  }
422
546
  catch (error) {
423
547
  return { success: false, error: error.message };
@@ -504,13 +628,22 @@ export class JaakStamps {
504
628
  }
505
629
  }
506
630
  async preloadModel() {
631
+ const span = this.tracingService?.startSpan('model.preload');
507
632
  const readyCheck = this.isComponentReady();
508
633
  if (!readyCheck.ready) {
509
634
  this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
635
+ if (span) {
636
+ this.tracingService?.setSpanAttribute(span, 'error', true);
637
+ this.tracingService?.endSpan(span);
638
+ }
510
639
  return { success: false, error: readyCheck.message };
511
640
  }
512
641
  if (this.detectionService.isModelLoaded()) {
513
642
  this.updateStatus('Reconocimiento listo', '', 'ready');
643
+ if (span) {
644
+ this.tracingService?.setSpanAttribute(span, 'cached', true);
645
+ this.tracingService?.endSpan(span);
646
+ }
514
647
  return { success: true, message: 'Model already loaded' };
515
648
  }
516
649
  try {
@@ -520,17 +653,31 @@ export class JaakStamps {
520
653
  this.stateManager.updateCaptureState({ isLoading: true });
521
654
  }
522
655
  try {
656
+ const detectionStartTime = performance.now();
657
+ const modelSpan = this.tracingService?.startSpan('model.detection.load');
523
658
  await this.detectionService.loadModel();
659
+ if (modelSpan)
660
+ this.tracingService?.endSpan(modelSpan);
661
+ const detectionLoadTime = performance.now() - detectionStartTime;
662
+ this.metricsService?.recordModelLoad('detection', detectionLoadTime, false);
524
663
  }
525
664
  catch (modelError) {
665
+ this.metricsService?.recordError('model_load_failed', 'detection');
526
666
  this.switchToManualModeWithError('Error al cargar modelo de detección');
527
667
  throw modelError;
528
668
  }
529
669
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
530
670
  try {
671
+ const classificationStartTime = performance.now();
672
+ const classSpan = this.tracingService?.startSpan('model.classification.load');
531
673
  await this.detectionService.loadClassificationModel();
674
+ if (classSpan)
675
+ this.tracingService?.endSpan(classSpan);
676
+ const classificationLoadTime = performance.now() - classificationStartTime;
677
+ this.metricsService?.recordModelLoad('classification', classificationLoadTime, false);
532
678
  }
533
679
  catch (classificationError) {
680
+ this.metricsService?.recordError('model_load_failed', 'classification');
534
681
  this.switchToManualModeWithError('Error al cargar modelo de clasificación');
535
682
  throw classificationError;
536
683
  }
@@ -540,6 +687,9 @@ export class JaakStamps {
540
687
  if (this.debug) {
541
688
  this.recordOnnxPerformance(loadTime, 0);
542
689
  }
690
+ if (span) {
691
+ this.tracingService?.setSpanAttribute(span, 'loadTime', loadTime);
692
+ }
543
693
  this.updateStatus('Finalizando configuración...', 'Preparando herramientas para captura', 'loading');
544
694
  await new Promise(resolve => setTimeout(resolve, 300));
545
695
  this.updateStatus('Reconocimiento preparado', '', 'ready');
@@ -547,6 +697,10 @@ export class JaakStamps {
547
697
  this.stateManager.updateCaptureState({ isLoading: false });
548
698
  }
549
699
  this.emitReadyEvent();
700
+ if (span) {
701
+ this.tracingService?.setSpanAttribute(span, 'success', true);
702
+ this.tracingService?.endSpan(span);
703
+ }
550
704
  return { success: true, message: 'Models preloaded successfully' };
551
705
  }
552
706
  catch (error) {
@@ -554,6 +708,10 @@ export class JaakStamps {
554
708
  if (this.stateManager) {
555
709
  this.stateManager.updateCaptureState({ isLoading: false });
556
710
  }
711
+ if (span) {
712
+ this.tracingService?.recordException(span, error);
713
+ this.tracingService?.endSpan(span);
714
+ }
557
715
  return { success: false, error: error.message };
558
716
  }
559
717
  }
@@ -642,65 +800,11 @@ export class JaakStamps {
642
800
  // Always allow getting capture delay, even during initialization
643
801
  return this.captureDelay;
644
802
  }
645
- async getResourceReport() {
646
- // Public method to get resource usage report
647
- return this.getResourceReportInternal();
648
- }
649
- async forceResourceCleanup() {
650
- // Public method to force aggressive resource cleanup
651
- const readyCheck = this.isComponentReady();
652
- if (!readyCheck.ready) {
653
- return { success: false, error: readyCheck.message };
654
- }
655
- try {
656
- const cleanupResult = await this.aggressiveResourceCleanup();
657
- return {
658
- success: true,
659
- resourcesFreed: cleanupResult.freed,
660
- errors: cleanupResult.errors,
661
- timestamp: new Date().toISOString()
662
- };
663
- }
664
- catch (error) {
665
- return { success: false, error: error.message };
666
- }
667
- }
668
803
  // Memory and device capability detection
669
804
  checkDeviceCapabilities() {
670
805
  const deviceMemory = navigator.deviceMemory;
671
- const hardwareConcurrency = navigator.hardwareConcurrency || 1;
672
- const isLowEndDevice = hardwareConcurrency <= 2;
673
- const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
674
- const isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
675
- // Si tenemos info de memoria del dispositivo
676
- if (deviceMemory && deviceMemory < 4) {
677
- return {
678
- canUseML: false,
679
- reason: `Memoria del dispositivo (${deviceMemory}GB) insuficiente para detección automática. Modo manual activado para mejor rendimiento.`,
680
- memoryMB: deviceMemory * 1024
681
- };
682
- }
683
- // Dispositivos móviles de gama baja
684
- if (isMobile && isLowEndDevice) {
685
- return {
686
- canUseML: false,
687
- reason: 'Dispositivo móvil optimizado detectado. Modo manual activado para mejor rendimiento y duración de batería.',
688
- memoryMB: null
689
- };
690
- }
691
- // Safari moderno soporta WASM, solo bloquearlo en versiones muy antiguas
692
- if (isSafari) {
693
- const safariMatch = navigator.userAgent.match(/Version\/(\d+)/);
694
- const safariVersion = safariMatch ? parseInt(safariMatch[1]) : 0;
695
- // Solo bloquear Safari muy antiguo (< version 14)
696
- if (safariVersion > 0 && safariVersion < 14) {
697
- return {
698
- canUseML: false,
699
- reason: 'Safari antiguo detectado. Modo manual activado por compatibilidad.',
700
- memoryMB: null
701
- };
702
- }
703
- }
806
+ // Siempre intentar usar ML, independientemente del dispositivo
807
+ // Las restricciones anteriores no eran confiables
704
808
  return {
705
809
  canUseML: true,
706
810
  reason: 'Dispositivo compatible con detección automática.',
@@ -826,6 +930,12 @@ export class JaakStamps {
826
930
  this.videoRef.onloadedmetadata = async () => {
827
931
  await this.videoRef.play();
828
932
  this.stateManager.updateCaptureState({ isVideoActive: true });
933
+ // Recalculate mask dimensions when new camera stream loads
934
+ if (this.detectionContainer) {
935
+ const container = this.detectionContainer.parentElement;
936
+ const rect = container.getBoundingClientRect();
937
+ this.updateMaskDimensions(rect);
938
+ }
829
939
  resolve();
830
940
  };
831
941
  });
@@ -868,9 +978,14 @@ export class JaakStamps {
868
978
  return;
869
979
  }
870
980
  this.frameSkipCounter = 0;
871
- // Throttle inference
981
+ // Throttle inference - use higher interval for mobile devices to prevent slow motion effect
872
982
  const currentTime = Date.now();
873
- if (currentTime - this.lastInferenceTime < this.MIN_INFERENCE_INTERVAL) {
983
+ const isMobileDevice = this.cameraInfoWithAutofocus.deviceType === 'mobile' ||
984
+ /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
985
+ const isTablet = this.cameraInfoWithAutofocus.deviceType === 'tablet' ||
986
+ (/iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768);
987
+ const minInterval = (isMobileDevice && !isTablet) ? this.MOBILE_MIN_INFERENCE_INTERVAL : this.MIN_INFERENCE_INTERVAL;
988
+ if (currentTime - this.lastInferenceTime < minInterval) {
874
989
  if (captureState.step !== 'completed') {
875
990
  this.animationId = requestAnimationFrame(() => this.detectFrame());
876
991
  }
@@ -997,6 +1112,10 @@ export class JaakStamps {
997
1112
  clearTimeout(this.alignmentTimer);
998
1113
  this.alignmentTimer = undefined;
999
1114
  }
1115
+ if (this.orientationTimer) {
1116
+ clearTimeout(this.orientationTimer);
1117
+ this.orientationTimer = undefined;
1118
+ }
1000
1119
  // Liberar recursos de ONNX al limpiar el componente
1001
1120
  this.releaseOnnxResources();
1002
1121
  if (this.performanceUpdateInterval) {
@@ -1005,182 +1124,11 @@ export class JaakStamps {
1005
1124
  }
1006
1125
  // Clear canvas pool
1007
1126
  this.canvasPool = [];
1008
- // Clean DOM references and memory leaks
1009
- this.cleanupDOMReferences();
1010
1127
  this.detectionBoxes = [];
1011
1128
  this.alignmentStartTime = undefined;
1012
1129
  this.hasDocumentDetected = false;
1013
1130
  this.serviceContainer?.cleanup();
1014
1131
  }
1015
- // AGGRESSIVE RESOURCE CLEANUP METHODS
1016
- // Clean DOM references and memory leaks
1017
- cleanupDOMReferences() {
1018
- // Remove event listeners that could maintain references
1019
- if (this.videoRef) {
1020
- // Remove common event listeners (if any were added)
1021
- this.videoRef.srcObject = null;
1022
- this.videoRef = undefined;
1023
- }
1024
- // Clean detection container
1025
- if (this.detectionContainer) {
1026
- this.detectionContainer = undefined;
1027
- }
1028
- // Clean references to images in the DOM and revoke blob URLs
1029
- const images = this.el.shadowRoot?.querySelectorAll('img');
1030
- images?.forEach(img => {
1031
- if (img.src.startsWith('blob:')) {
1032
- URL.revokeObjectURL(img.src);
1033
- }
1034
- img.src = '';
1035
- });
1036
- }
1037
- // Clear browser cache and storage
1038
- async clearBrowserCache() {
1039
- try {
1040
- // 1. Clear Service Worker cache if exists
1041
- if ('serviceWorker' in navigator) {
1042
- const registrations = await navigator.serviceWorker.getRegistrations();
1043
- await Promise.all(registrations.map(registration => registration.unregister()));
1044
- }
1045
- // 2. Clear Cache API specific to the component
1046
- const cacheNames = await caches.keys();
1047
- const componentCaches = cacheNames.filter(name => name.includes('jaak') || name.includes('stamps'));
1048
- await Promise.all(componentCaches.map(cacheName => caches.delete(cacheName)));
1049
- // 3. Force garbage collection if available (only in dev)
1050
- if (this.debug && window.gc) {
1051
- window.gc();
1052
- }
1053
- }
1054
- catch (error) {
1055
- console.warn('Error clearing browser cache:', error);
1056
- }
1057
- }
1058
- // Clear local storage specific to the component
1059
- async clearLocalStorage() {
1060
- try {
1061
- // Clear localStorage specific to the component
1062
- const keysToRemove = [];
1063
- for (let i = 0; i < localStorage.length; i++) {
1064
- const key = localStorage.key(i);
1065
- if (key && (key.includes('jaak') || key.includes('onnx') || key.includes('stamps'))) {
1066
- keysToRemove.push(key);
1067
- }
1068
- }
1069
- keysToRemove.forEach(key => localStorage.removeItem(key));
1070
- // Clear sessionStorage
1071
- const sessionKeysToRemove = [];
1072
- for (let i = 0; i < sessionStorage.length; i++) {
1073
- const key = sessionStorage.key(i);
1074
- if (key && (key.includes('jaak') || key.includes('onnx'))) {
1075
- sessionKeysToRemove.push(key);
1076
- }
1077
- }
1078
- sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
1079
- }
1080
- catch (error) {
1081
- console.warn('Error clearing local storage:', error);
1082
- }
1083
- }
1084
- // Aggressive resource cleanup combining all strategies
1085
- async aggressiveResourceCleanup() {
1086
- const freed = [];
1087
- const errors = [];
1088
- try {
1089
- // 1. Release ONNX resources
1090
- this.releaseOnnxResources();
1091
- freed.push('ONNX models');
1092
- // 2. Clear model cache in detection service
1093
- if (this.detectionService) {
1094
- await this.detectionService.clearModelCache();
1095
- freed.push('Model cache');
1096
- }
1097
- // 3. Clear browser cache
1098
- await this.clearBrowserCache();
1099
- freed.push('Browser cache');
1100
- // 4. Clear local storage
1101
- await this.clearLocalStorage();
1102
- freed.push('Local storage');
1103
- // 5. Clean DOM references
1104
- this.cleanupDOMReferences();
1105
- freed.push('DOM references');
1106
- // 6. Optimize canvas pools
1107
- if (this.detectionService) {
1108
- this.detectionService.optimizeCanvasPool(0);
1109
- freed.push('Canvas pools');
1110
- }
1111
- // 7. Clear component canvas pool
1112
- this.canvasPool = [];
1113
- freed.push('Component canvas pool');
1114
- // 8. Force garbage collection (if available)
1115
- if (this.debug && window.gc) {
1116
- window.gc();
1117
- freed.push('Garbage collection');
1118
- }
1119
- }
1120
- catch (error) {
1121
- errors.push(error.message);
1122
- }
1123
- return { freed, errors };
1124
- }
1125
- // Progressive resource release after front capture
1126
- async onFrontCaptureComplete() {
1127
- try {
1128
- // Release classification model if not needed for back
1129
- if (this.detectionService && !this.useDocumentClassification) {
1130
- await this.detectionService.releaseMobileNetResources();
1131
- }
1132
- // Optimize canvas pool to minimum needed for back capture
1133
- if (this.detectionService) {
1134
- this.detectionService.optimizeCanvasPool(1);
1135
- }
1136
- // Reduce component canvas pool
1137
- while (this.canvasPool.length > 1) {
1138
- const canvas = this.canvasPool.pop();
1139
- if (canvas) {
1140
- const ctx = canvas.getContext('2d');
1141
- if (ctx) {
1142
- ctx.clearRect(0, 0, canvas.width, canvas.height);
1143
- }
1144
- }
1145
- }
1146
- if (this.debug) {
1147
- console.log('🧹 Progressive cleanup after front capture completed');
1148
- }
1149
- }
1150
- catch (error) {
1151
- console.warn('Error in progressive cleanup:', error);
1152
- }
1153
- }
1154
- // Resource reporting for debugging
1155
- getResourceReportInternal() {
1156
- const report = {
1157
- timestamp: new Date().toISOString(),
1158
- component: {
1159
- hasVideoStream: !!this.videoStream,
1160
- hasAnimationFrame: !!this.animationId,
1161
- canvasPoolSize: this.canvasPool.length,
1162
- detectionBoxesCount: this.detectionBoxes.length,
1163
- performanceMonitorActive: !!this.performanceUpdateInterval
1164
- }
1165
- };
1166
- // Add detection service stats if available
1167
- if (this.detectionService) {
1168
- report.detectionService = {
1169
- isModelLoaded: this.detectionService.isModelLoaded(),
1170
- canvasPoolStats: this.detectionService.getPoolStats()
1171
- };
1172
- }
1173
- // Add memory info if available
1174
- if ('memory' in performance) {
1175
- const memInfo = performance.memory;
1176
- report.memory = {
1177
- usedJSHeapSize: Math.round(memInfo.usedJSHeapSize / 1048576), // MB
1178
- totalJSHeapSize: Math.round(memInfo.totalJSHeapSize / 1048576), // MB
1179
- jsHeapSizeLimit: Math.round(memInfo.jsHeapSizeLimit / 1048576) // MB
1180
- };
1181
- }
1182
- return report;
1183
- }
1184
1132
  render() {
1185
1133
  const captureState = this.stateManager?.getCaptureState() || {
1186
1134
  isVideoActive: false,
@@ -1191,7 +1139,7 @@ export class JaakStamps {
1191
1139
  isCapturing: false
1192
1140
  };
1193
1141
  const cameraInfo = this.cameraInfoWithAutofocus;
1194
- return (h("div", { key: 'd32941c10b3b7193b2113691dae712a80315b430', class: "detector-container" }, h("div", { key: '7704c993cb9e6e3d29edf6b0adfe341dc1cc8e04', class: "video-container" }, h("video", { key: '2d0d46f1973c86e91f9fe9ede0aa938326c6071f', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '9d62ea5221271a18938a9f80535ec7d960745ddb', 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: {
1142
+ return (h("div", { key: 'df70f4fa13b2997a19312125c8b236651cb2b81e', class: "detector-container" }, !this.licenseValid && this.licenseError && (h("div", { key: 'c4f7b51515843177bc3063dd7cf9383e3a4c6c73', class: "license-error-container" }, h("div", { key: 'a4a7e95a23690f9f59c971dd2af9bd096ae13ef0', class: "license-error-card" }, h("svg", { key: '6489a82c60fc9c1a7d89c2b3a537c04c1766cbd4', class: "license-error-icon", width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, h("path", { key: 'b90b16b0ee155c8baeee321237470de7b6fa1ff5', d: "M12 22C12 22 20 18 20 12V5L12 2L4 5V12C4 18 12 22 12 22Z", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { key: 'c90ec519befb073208dbc5e30e1237dda7f0a18f', d: "M12 8V12", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round" }), h("circle", { key: 'b926e97a83e19c7ca9b66472fedd53b12d22b7d8', cx: "12", cy: "16", r: "0.5", fill: "currentColor", stroke: "currentColor", "stroke-width": "1" })), h("h2", { key: '1f1fce48179331b0150e0c55a6d5efcd0bf95762', class: "license-error-title" }, "Licencia Requerida"), h("p", { key: '1f28787cea92c5160664bd8e445fb514ac55eff4', class: "license-error-message" }, this.licenseError), h("p", { key: '552aecf92fd34fbdd802fc80cdba46596edb115b', class: "license-error-help" }, "Contacte a soporte: ", h("a", { key: '8c44e1e33fbd22a2319acd6c6b27766bec4355f0', href: "mailto:support@jaak.ai" }, "support@jaak.ai")), h("div", { key: 'c2e139fdedcb8e8922f37e915caa7ae3a1b09cb9', class: "license-error-footer" }, "JAAK Stamps")))), this.licenseValid && (h("div", { key: 'c7876bbe35cd28b52433aab6e97de4a0282e25d5', class: "video-container" }, h("video", { key: 'a5fa99c086eb3730ceee7750ece0842539defa3d', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '2c7923dfa706f65cfc3fe71a6ec626cb2ccce7d4', 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: {
1195
1143
  position: 'absolute',
1196
1144
  left: `${box.x}px`,
1197
1145
  top: `${box.y}px`,
@@ -1200,9 +1148,9 @@ export class JaakStamps {
1200
1148
  border: '2px solid #32406C',
1201
1149
  pointerEvents: 'none',
1202
1150
  boxSizing: 'border-box'
1203
- } })))), this.isMaskReady && (h("div", { key: '38ebb5be5c55a184644411f06fe20221bdbdf693', class: "overlay-mask" }, h("div", { key: '6200c59e7a1212267e7335e1495cf9d9db2bca62', class: "card-outline" }, h("div", { key: 'c5321d2b5c09734cbca69fc8bb7849eef465a0ce', class: "side side-top" }), h("div", { key: 'd73b5afcb6781307460b8827c491673ebbfc40ef', class: "side side-right" }), h("div", { key: '787dfa0792c5a00f8a83feaa423ca22092e0a8bf', class: "side side-bottom" }), h("div", { key: '50736a6b9c7c8847d1dabaa22464e26f284da11e', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'ec8cc90cd589e4f713becb57b6787ce4f57318d4', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'd9d391c02855ac754607cbf77c119153d1f6e56f', class: "back-capture-section" }, h("div", { key: 'a3ebe694f74958b7910fc355a66cbf1394fb5a2a', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode) && (h("button", { key: 'bc6baf4e00199dfdd50bbfbf8abbcede0fb719e9', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '3a56f9132fce454c792818925c8bed5557e69eed', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: '2fe7e44bc91b73581d6333b7c404ec018a390372', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: 'ebb2d40cbcb646c9bd70ebe278b0643f9335779b', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
1151
+ } })))), this.isMaskReady && (h("div", { key: '3e1f77d89a00e4ad1519875cacd5c541c9b16f91', class: "overlay-mask" }, h("div", { key: '3c717afa809ef2fa102fdaab3418915602516d39', class: "card-outline" }, h("div", { key: 'd3dcd1a85290f3fb2216b1182dce9a4b62cc59c6', class: "side side-top" }), h("div", { key: '4280c1e0f0900ec69b905a6226c1afe9a0a993ad', class: "side side-right" }), h("div", { key: '9b88c21f5eb28d74872409335cd6ca83a284bd9b', class: "side side-bottom" }), h("div", { key: '46b4ef3fca60fcf807da8f574a43cba7f148165b', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '2222975293bebfd04fa2d4ac4878bce48fd35880', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '836fbeadb849cb9df66e03ef0cb791dd0e179df8', class: "back-capture-section" }, h("div", { key: 'f0e8119b79f30ffd40ae206783a83869196757ac', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (h("button", { key: 'e245e800e2378989bfa011524cca66026ace52d9', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: 'd98a692f9ff883d05e24122b54c4e36f5f96e82f', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: 'c32b7784e2bbb89947219f0d458d0a4d62b5352a', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: 'bb935e91f07c4c1052576c52aa2a82a726877217', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
1204
1152
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
1205
- : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '4373e4f9ab73d93ce32ae52fbdc14f7bcb69e68e', class: "camera-controls" }, h("button", { key: '5d2afe5b71b02d379fe2ec82bd57a86f614fbea6', 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: '4fe5e10260a1299a0dc8517646bed8134fe38d9a', class: "camera-selector-dropdown" }, h("div", { key: 'b041acd0b0292864d20ad256301e7be714854424', class: "camera-selector-header" }, h("span", { key: '2769170dc9ce382b420a719cad6f9bb1f5e4420f' }, "Seleccionar C\u00E1mara"), h("button", { key: '0c54f73bb29041ba88e1838c5b1c8a54eafdc67d', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'd72ae015c5e89cc3396c14406591308eda4d4c4a', 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: '5933bb5c8345debf2eb78fc2cd3900cb09a29335', class: "device-info" }, h("small", { key: 'a126d886feaede4f9657abee9b45d1eff1910e25' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '62ab98c6afa629583ff13f1792422ef1c5012883', class: "manual-capture-section" }, h("button", { key: '93a42f7b0626a63ab795652eb9eb81657f4fec13', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '77b5aaf62edab4fe8eb68f37083121d9f8493797', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '399242238fd4a2a4985af0f30faed504d73a0adc', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '06add02bc244511dc0dc82bf206f41d9ce3cdcd3', class: "flip-animation" }, h("div", { key: 'e3949d8e41bc496bf503d1dcd34df53d2bf876aa', class: "id-card-icon" }), h("div", { key: 'e8ebdd9d29e0b33dcfc506b40e67e0d4af8cb0bb', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'f72f6fc4d42c7b10d98c8aff7541596afe131883', class: "success-animation" }, h("div", { key: '8d0d422b37caa97f695f62c0094c52c3c12c7bda', class: "check-icon" }), h("div", { key: 'd9d673dbfd2af4a1b8ca1a775e5b318326d70446', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: 'fdfa1239ddea40bbbabf032542a7160fae3f3c96', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '34bb9d5b83e4383c4f640ccf11189484514e4efd', class: "status-spinner" })), h("div", { key: '131bcd252fc7e1f6eb27332d64fd249b5889aa9d', class: "status-content" }, h("div", { key: '911b7324456e84842fa3c14c9a6fd3ba475804cf', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'e938b9294ee3c6125b133bf737caa4a33a016872', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'cab385a12f6ccf8579d2d073c1ed987bf815efeb', class: "performance-monitor" }, h("div", { key: '903f8a7005a98c4b99eb7d4c91662911cee8beae', class: "performance-expanded" }, h("div", { key: 'f1ba17573d53442a23212e2c8fdd132bd3ddf4f0', class: "metrics-row" }, h("div", { key: '71357107dcc19749e5af2d5668df535db65cb986', class: "metric-compact" }, h("span", { key: '9ad542fb9445340c669c3d18fc293e2364fc5dcc', class: "metric-label" }, "FPS"), h("span", { key: 'ab0fb401df5dc792c0a641a574851aec48093862', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: 'a650ab2a1af27829d4a63a264f00907dfba363c6', class: "metric-compact" }, h("span", { key: '56e195b0fd468f2a457a66b2342ed6a80fbef0a0', class: "metric-label" }, "MEM"), h("span", { key: '265445131d4492c6e2f7571d367f52c93e00b711', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '56023a82a1978c7c2f8f08edac729f39efcafa63', class: "metrics-row" }, h("div", { key: '6f1ceb668fc246ec11bfeee3f14c745f21d4986e', class: "metric-compact" }, h("span", { key: '92efba3ee31c5b9a382f1e205584950217482722', class: "metric-label" }, "INF"), h("span", { key: 'fc2baec34fd2f0696a216c487753b94b8bacaf7e', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '683eeb875e96c94cf36c403385c2764239fb5a4d', class: "metric-compact" }, h("span", { key: '33ba3f357868f7feaaeefab35e3ecc35d3b5b726', class: "metric-label" }, "FRAME"), h("span", { key: 'f496be674b0328556d27a9a9b2f24ffa50ff76ea', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '521f275481d35c2132dbcf138e4bbda84a0b97b2', class: "metrics-row" }, h("div", { key: '17acc0238696816077b80b66aee317ae9ab20e75', class: "metric-compact" }, h("span", { key: 'cac1a4ee661de14739623a1a6f0149ba72fa918c', class: "metric-label" }, "DET"), h("span", { key: '70071c404c5e50001d813f1f3e0d1002776ed887', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '26a00ad1c95e6c2b8feaafc36103fb7fb869039f', class: "metric-compact" }, h("span", { key: 'a1c7fb80b6795f10111c1ed403de933aadcbe450', class: "metric-label" }, "RATE"), h("span", { key: '49471b7bfa2d6ca6357a39bf611738fd6344764a', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '3870b7399d2d5068e7f59177225476b00bef7fc7', class: "watermark" }, h("img", { key: '5030e9f14eebd4546207ced129d7d00d85abb189', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
1153
+ : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '471a1292e9d093cce0a0d3fa7f96f3665de749fb', class: "camera-controls" }, h("button", { key: '406911235a06723310ff577c27e3858912e522c4', 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: 'b8fd729b78a3c5cb52b7f17d031bb12b894aaf41', class: "camera-selector-dropdown" }, h("div", { key: '2b9b451440d6b6be4103419694ff6a53c92e68ad', class: "camera-selector-header" }, h("span", { key: '2271f4694aa6c0b32ab23a02a7a8d6d4c1bd4144' }, "Seleccionar C\u00E1mara"), h("button", { key: 'ccfc02eb031da17a4907cd835642785bc2ef5e77', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '073d6e9e09cde8523e926850dfa73704b579c899', 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: '8f914f855727a6ab02a2941fee91283bf3882df2', class: "device-info" }, h("small", { key: '295433a43a539034992f04a29355deed12082092' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'cc780583a636e7d4910851789710837c9e188d40', class: "manual-capture-section" }, h("button", { key: '40c5e212a3a4eab4d2edf44e29e6b4fe0e4c5e37', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '1245a13523250b5959a1c303ca20662c2d6750c7', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '10a0a26b346ecf0945688f82292720871f8dc0b7', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '68dc7effe82d2f043d778239e2aecb847d159818', class: "flip-animation" }, h("div", { key: '153e0c2229c39c5b9402b9610eaee586d98f84c1', class: "id-card-icon" }), h("div", { key: 'b60b249c43ac5f8417c15b8ce46f60d2cb3134d5', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'fefa7bf80546a463a54a275e425241545ace5c77', class: "success-animation" }, h("div", { key: 'cda9d64b3b30cc636d976c2c644a927a8ebe341e', class: "check-icon" }), h("div", { key: 'fd00554df841cdb6e1a167b7e6cc4a6b9b60957a', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: 'cd5fa1fbfa5e3a4e362281553dde2c37601b1078', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: 'cb0d76938d8ccab99632920ffc9d73a1fa7dec79', class: "status-spinner" })), h("div", { key: 'bcfdc2a24e12dc0bcc19ae0d647be5ce810994d5', class: "status-content" }, h("div", { key: 'a4b6dad22b40aed65a9c81e9581e61a9ca064e40', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '236084444d4d156b672f823349f95b462a8a9bba', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '7423d6c8c7e37b7baffc27fc243536e2659475cc', class: "performance-monitor" }, h("div", { key: '892736f4be3d1cd563ec919c5a504ca8e0d55db0', class: "performance-expanded" }, h("div", { key: 'aa0464f739f838ac4c9e553c79f4c7815f7f65cf', class: "metrics-row" }, h("div", { key: '5dca0c5fe0f91ecfaa25c5775c18a99d53eb8f73', class: "metric-compact" }, h("span", { key: '8e498b185cdefb97286356b56e45dc7b514f667e', class: "metric-label" }, "FPS"), h("span", { key: '3a90611c84b338314ae1503aef6208c67d1282ff', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '568e492ef7dad971f1613132028999a81f38f457', class: "metric-compact" }, h("span", { key: '222fd2b522dd9f7172ad9adb442b10c1ce4c6455', class: "metric-label" }, "MEM"), h("span", { key: '8cbe2cf9fd7dd9e22e9a0df6f214165a09e03045', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'e5d946afd1cc80a15a52c9ba1cb252e6e146e929', class: "metrics-row" }, h("div", { key: '65aaaa92e3ce7b5567560d94f98e16226ecf9fd5', class: "metric-compact" }, h("span", { key: '80c6ad0923e729912274a05fffca039e88aa5719', class: "metric-label" }, "INF"), h("span", { key: '4a87270ebdad3be718b86d1382b2a54092e53a16', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '41ddc923cdfab0012a2f5d3e19aee33dad6116fe', class: "metric-compact" }, h("span", { key: '84e9f4fb3d5bf60235d71d61dc1d6a8ad429293c', class: "metric-label" }, "FRAME"), h("span", { key: '88fd6964307399ac1547c7086f489af90a92a925', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '22409e8dfb349e66ac54c20c6043b414b7ddba34', class: "metrics-row" }, h("div", { key: 'bbe72cb477f4bc313e7978d98c217a4789765df1', class: "metric-compact" }, h("span", { key: '56df43d72df8d3549fa42b2bc32fa5491c3cdb41', class: "metric-label" }, "DET"), h("span", { key: '7ceedb73086eccaed0e25177d76218e9bae1262d', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '9407541b4f975b20843c19e7081d1c26bb3f4fd4', class: "metric-compact" }, h("span", { key: '511ffc66436ffe2f14300122213de11f50fbbc30', class: "metric-label" }, "RATE"), h("span", { key: '6a0c864b46314937043831be7646fbfa3d994c86', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '72f26e4248c87e428a10bbd7b67bd77a9a32f321', class: "watermark" }, h("img", { key: '32e3f23ca1ff81699dd2a4922f8f7d4aeaa3cc2b', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" }))))));
1206
1154
  }
1207
1155
  // Utility methods
1208
1156
  updateDetectionBoxes(boxes) {
@@ -1319,24 +1267,44 @@ export class JaakStamps {
1319
1267
  }
1320
1268
  async takeManualScreenshot() {
1321
1269
  console.log('🔵 Botón clicked: Capturar (Frente/Reverso)');
1270
+ const captureStartTime = performance.now();
1271
+ const span = this.tracingService?.startSpan('capture.screenshot.manual');
1322
1272
  // Set processing state immediately
1323
1273
  const captureState = this.stateManager?.getCaptureState();
1274
+ const side = captureState?.step === 'front' ? 'front' : 'back';
1324
1275
  if (captureState?.step === 'front') {
1325
1276
  this.processingButton = 'capture-front';
1277
+ if (span)
1278
+ this.tracingService?.setSpanAttribute(span, 'side', 'front');
1326
1279
  }
1327
1280
  else if (captureState?.step === 'back') {
1328
1281
  this.processingButton = 'capture-back';
1282
+ if (span)
1283
+ this.tracingService?.setSpanAttribute(span, 'side', 'back');
1329
1284
  }
1285
+ // Record user interaction
1286
+ this.metricsService?.recordUserInteraction('manual_capture_button');
1330
1287
  console.log('🔵 processingButton set to:', this.processingButton);
1331
1288
  // Add small delay to show spinner
1332
1289
  await new Promise(resolve => setTimeout(resolve, 100));
1333
1290
  if (!this.videoRef) {
1334
1291
  this.processingButton = null;
1292
+ if (span) {
1293
+ this.tracingService?.setSpanAttribute(span, 'error', 'no_video_ref');
1294
+ this.tracingService?.endSpan(span);
1295
+ }
1335
1296
  return;
1336
1297
  }
1337
1298
  // When using manual capture, use mask coordinates for cropping
1338
1299
  // Note: processingButton will be cleared inside takeScreenshotWithMaskCoordinates
1339
1300
  await this.takeScreenshotWithMaskCoordinates();
1301
+ // Record metrics
1302
+ const captureDuration = performance.now() - captureStartTime;
1303
+ this.metricsService?.recordCapture(side, 'manual', captureDuration);
1304
+ if (span) {
1305
+ this.tracingService?.setSpanAttribute(span, 'success', true);
1306
+ this.tracingService?.endSpan(span);
1307
+ }
1340
1308
  }
1341
1309
  async takeScreenshotWithMaskCoordinates() {
1342
1310
  if (!this.videoRef || !this.detectionContainer)
@@ -1399,8 +1367,8 @@ export class JaakStamps {
1399
1367
  if (captureState.step === 'front') {
1400
1368
  this.stateManager.setCapturedImages({
1401
1369
  front: {
1402
- fullFrame: captureCanvas.toDataURL('image/png'),
1403
- cropped: croppedCanvas.toDataURL('image/png')
1370
+ fullFrame: captureCanvas.toDataURL('image/jpeg'),
1371
+ cropped: croppedCanvas.toDataURL('image/jpeg')
1404
1372
  }
1405
1373
  });
1406
1374
  // Check if document classification is enabled (independent of detector)
@@ -1426,8 +1394,6 @@ export class JaakStamps {
1426
1394
  showFlipAnimation: true
1427
1395
  });
1428
1396
  this.triggerRerender();
1429
- // Progressive resource cleanup after front capture
1430
- await this.onFrontCaptureComplete();
1431
1397
  setTimeout(() => {
1432
1398
  this.stateManager.updateCaptureState({
1433
1399
  showFlipAnimation: false,
@@ -1442,8 +1408,8 @@ export class JaakStamps {
1442
1408
  else if (captureState.step === 'back') {
1443
1409
  this.stateManager.setCapturedImages({
1444
1410
  back: {
1445
- fullFrame: captureCanvas.toDataURL('image/png'),
1446
- cropped: croppedCanvas.toDataURL('image/png')
1411
+ fullFrame: captureCanvas.toDataURL('image/jpeg'),
1412
+ cropped: croppedCanvas.toDataURL('image/jpeg')
1447
1413
  }
1448
1414
  });
1449
1415
  this.completeProcess(false);
@@ -1457,6 +1423,8 @@ export class JaakStamps {
1457
1423
  async takeScreenshot() {
1458
1424
  if (!this.videoRef || !this.lastDetectedBox)
1459
1425
  return;
1426
+ const captureStartTime = performance.now();
1427
+ const span = this.tracingService?.startSpan('capture.screenshot.auto');
1460
1428
  this.stateManager.updateCaptureState({ isCapturing: true });
1461
1429
  this.triggerCaptureAnimation();
1462
1430
  // Use pooled canvas for optimization
@@ -1479,17 +1447,30 @@ export class JaakStamps {
1479
1447
  croppedCtx.drawImage(this.videoRef, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
1480
1448
  const captureState = this.stateManager.getCaptureState();
1481
1449
  if (captureState.step === 'front') {
1450
+ const frontImageData = captureCanvas.toDataURL('image/jpeg');
1451
+ const croppedImageData = croppedCanvas.toDataURL('image/jpeg');
1482
1452
  this.stateManager.setCapturedImages({
1483
1453
  front: {
1484
- fullFrame: captureCanvas.toDataURL('image/png'),
1485
- cropped: croppedCanvas.toDataURL('image/png')
1454
+ fullFrame: frontImageData,
1455
+ cropped: croppedImageData
1486
1456
  }
1487
1457
  });
1458
+ // Record metrics
1459
+ const captureDuration = performance.now() - captureStartTime;
1460
+ this.metricsService?.recordCapture('front', 'auto', captureDuration);
1461
+ this.metricsService?.recordImageSize('front', frontImageData.length);
1462
+ if (span) {
1463
+ this.tracingService?.setSpanAttribute(span, 'side', 'front');
1464
+ }
1488
1465
  // Check if document classification is enabled
1489
1466
  if (this.useDocumentClassification) {
1490
1467
  try {
1491
1468
  const classification = await this.detectionService.classifyDocument(croppedCanvas);
1492
1469
  if (classification && classification.class === 'passport') {
1470
+ if (span) {
1471
+ this.tracingService?.setSpanAttribute(span, 'document.type', 'passport');
1472
+ this.tracingService?.endSpan(span);
1473
+ }
1493
1474
  this.completeProcess(true);
1494
1475
  return;
1495
1476
  }
@@ -1503,8 +1484,9 @@ export class JaakStamps {
1503
1484
  isDetectionPaused: true,
1504
1485
  showFlipAnimation: true
1505
1486
  });
1506
- // Progressive resource cleanup after front capture
1507
- await this.onFrontCaptureComplete();
1487
+ if (span) {
1488
+ this.tracingService?.endSpan(span);
1489
+ }
1508
1490
  setTimeout(() => {
1509
1491
  this.stateManager.updateCaptureState({
1510
1492
  showFlipAnimation: false,
@@ -1514,12 +1496,22 @@ export class JaakStamps {
1514
1496
  }, 3000);
1515
1497
  }
1516
1498
  else if (captureState.step === 'back') {
1499
+ const backImageData = captureCanvas.toDataURL('image/jpeg');
1500
+ const croppedBackImageData = croppedCanvas.toDataURL('image/jpeg');
1517
1501
  this.stateManager.setCapturedImages({
1518
1502
  back: {
1519
- fullFrame: captureCanvas.toDataURL('image/png'),
1520
- cropped: croppedCanvas.toDataURL('image/png')
1503
+ fullFrame: backImageData,
1504
+ cropped: croppedBackImageData
1521
1505
  }
1522
1506
  });
1507
+ // Record metrics
1508
+ const captureDuration = performance.now() - captureStartTime;
1509
+ this.metricsService?.recordCapture('back', 'auto', captureDuration);
1510
+ this.metricsService?.recordImageSize('back', backImageData.length);
1511
+ if (span) {
1512
+ this.tracingService?.setSpanAttribute(span, 'side', 'back');
1513
+ this.tracingService?.endSpan(span);
1514
+ }
1523
1515
  this.completeProcess(false);
1524
1516
  }
1525
1517
  // Return canvases to pool after use
@@ -1643,10 +1635,13 @@ export class JaakStamps {
1643
1635
  this.useDocumentDetector = false; // Desactivar detector automático
1644
1636
  // Actualizar el estado con mensaje de error
1645
1637
  this.updateStatus('Modo manual activado', errorMessage, 'error');
1646
- // Ocultar mensaje después de 5 segundos
1647
- setTimeout(() => {
1648
- this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
1649
- }, 5000);
1638
+ // Continuar con la configuración de cámara para mostrar el video
1639
+ this.continueWithCameraSetup().catch(() => {
1640
+ // Si falla la configuración de cámara, mantener el mensaje de error pero cambiar el estado
1641
+ setTimeout(() => {
1642
+ this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
1643
+ }, 5000);
1644
+ });
1650
1645
  }
1651
1646
  stopPerformanceMonitoring() {
1652
1647
  // Nota: No necesitamos limpiar más variables porque ya no se usarán
@@ -1663,7 +1658,7 @@ export class JaakStamps {
1663
1658
  catch (error) {
1664
1659
  }
1665
1660
  }
1666
- completeProcess(skippedBack = false) {
1661
+ async completeProcess(skippedBack = false) {
1667
1662
  this.stateManager.updateCaptureState({
1668
1663
  step: 'completed',
1669
1664
  showSuccessAnimation: true
@@ -1678,20 +1673,19 @@ export class JaakStamps {
1678
1673
  ...capturedImages,
1679
1674
  timestamp: new Date().toISOString()
1680
1675
  };
1676
+ // Flush all traces to collector before emitting capture completed
1677
+ if (this.tracingService) {
1678
+ try {
1679
+ await this.tracingService.flush();
1680
+ }
1681
+ catch (error) {
1682
+ console.error('[my-component] Failed to flush traces:', error);
1683
+ }
1684
+ }
1681
1685
  this.captureCompleted.emit(finalImages);
1682
1686
  setTimeout(() => {
1683
1687
  this.stateManager.updateCaptureState({ showSuccessAnimation: false });
1684
1688
  }, 3000);
1685
- // Aggressive cleanup after process completion with delay
1686
- setTimeout(async () => {
1687
- const cleanupResult = await this.aggressiveResourceCleanup();
1688
- if (this.debug) {
1689
- console.log('🧹 Post-completion cleanup:', cleanupResult.freed);
1690
- if (cleanupResult.errors.length > 0) {
1691
- console.warn('⚠️ Cleanup errors:', cleanupResult.errors);
1692
- }
1693
- }
1694
- }, 1000); // Wait 1 second after completion
1695
1689
  }
1696
1690
  stopDetection() {
1697
1691
  if (this.animationId) {
@@ -1743,6 +1737,12 @@ export class JaakStamps {
1743
1737
  try {
1744
1738
  // Close the selector immediately when user selects a camera
1745
1739
  this.showCameraSelector = false;
1740
+ // Check if user selected the same camera that's already active
1741
+ const currentCameraId = this.cameraService.getSelectedCameraId();
1742
+ if (currentCameraId === cameraId) {
1743
+ // Same camera selected, just close the selector without switching
1744
+ return;
1745
+ }
1746
1746
  this.isSwitchingCamera = true;
1747
1747
  // Stop current video stream
1748
1748
  if (this.videoStream) {
@@ -1873,28 +1873,35 @@ export class JaakStamps {
1873
1873
  }
1874
1874
  // Adaptive frame skipping based on performance
1875
1875
  getAdaptiveFrameSkip() {
1876
+ // Check if running on mobile device for more aggressive frame skipping
1877
+ const isMobileDevice = this.cameraInfoWithAutofocus.deviceType === 'mobile' ||
1878
+ /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
1879
+ const isTablet = this.cameraInfoWithAutofocus.deviceType === 'tablet' ||
1880
+ (/iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768);
1881
+ // Mobile-specific base frame skip (more aggressive to prevent slow motion effect)
1882
+ const mobileBaseSkip = isMobileDevice && !isTablet ? 4 : this.BASE_FRAME_SKIP;
1876
1883
  // Base conditions for high frame skip
1877
1884
  if (this.consecutiveFailures > 20)
1878
1885
  return this.MAX_FRAME_SKIP;
1879
1886
  if (this.performanceMetrics.inferenceTime > 200)
1880
- return 6;
1887
+ return Math.max(6, mobileBaseSkip);
1881
1888
  if (this.performanceMetrics.memoryUsage > 200)
1882
- return 5;
1889
+ return Math.max(5, mobileBaseSkip);
1883
1890
  // Calculate average processing time
1884
1891
  if (this.performanceHistory.length > 0) {
1885
1892
  const avgProcessingTime = this.performanceHistory.reduce((a, b) => a + b, 0) / this.performanceHistory.length;
1886
1893
  if (avgProcessingTime > 100)
1887
- return 4;
1894
+ return Math.max(4, mobileBaseSkip);
1888
1895
  if (avgProcessingTime > 80)
1889
- return 3;
1896
+ return Math.max(3, mobileBaseSkip);
1890
1897
  if (avgProcessingTime > 60)
1891
- return this.BASE_FRAME_SKIP + 1;
1898
+ return Math.max(this.BASE_FRAME_SKIP + 1, mobileBaseSkip);
1892
1899
  }
1893
1900
  // Low memory devices get higher frame skip
1894
1901
  if (this.performanceMetrics.memoryUsage > 150)
1895
- return 4;
1896
- // Performance is good, use base frame skip
1897
- return this.BASE_FRAME_SKIP;
1902
+ return Math.max(4, mobileBaseSkip);
1903
+ // Mobile devices use higher base frame skip for smoother video
1904
+ return mobileBaseSkip;
1898
1905
  }
1899
1906
  // Método para resetear la máscara a color blanco
1900
1907
  resetMaskToWhite() {
@@ -2157,6 +2164,261 @@ export class JaakStamps {
2157
2164
  "setter": false,
2158
2165
  "reflect": false,
2159
2166
  "defaultValue": "20"
2167
+ },
2168
+ "telemetryCollectorUrl": {
2169
+ "type": "string",
2170
+ "attribute": "telemetry-collector-url",
2171
+ "mutable": false,
2172
+ "complexType": {
2173
+ "original": "string",
2174
+ "resolved": "string",
2175
+ "references": {}
2176
+ },
2177
+ "required": false,
2178
+ "optional": true,
2179
+ "docs": {
2180
+ "tags": [],
2181
+ "text": ""
2182
+ },
2183
+ "getter": false,
2184
+ "setter": false,
2185
+ "reflect": false,
2186
+ "defaultValue": "'https://collector.jaak.ai/v1/traces'"
2187
+ },
2188
+ "metricsCollectorUrl": {
2189
+ "type": "string",
2190
+ "attribute": "metrics-collector-url",
2191
+ "mutable": false,
2192
+ "complexType": {
2193
+ "original": "string",
2194
+ "resolved": "string",
2195
+ "references": {}
2196
+ },
2197
+ "required": false,
2198
+ "optional": true,
2199
+ "docs": {
2200
+ "tags": [],
2201
+ "text": ""
2202
+ },
2203
+ "getter": false,
2204
+ "setter": false,
2205
+ "reflect": false,
2206
+ "defaultValue": "'https://collector.jaak.ai/v1/metrics'"
2207
+ },
2208
+ "enableTelemetry": {
2209
+ "type": "boolean",
2210
+ "attribute": "enable-telemetry",
2211
+ "mutable": false,
2212
+ "complexType": {
2213
+ "original": "boolean",
2214
+ "resolved": "boolean",
2215
+ "references": {}
2216
+ },
2217
+ "required": false,
2218
+ "optional": false,
2219
+ "docs": {
2220
+ "tags": [],
2221
+ "text": ""
2222
+ },
2223
+ "getter": false,
2224
+ "setter": false,
2225
+ "reflect": false,
2226
+ "defaultValue": "true"
2227
+ },
2228
+ "enableMetrics": {
2229
+ "type": "boolean",
2230
+ "attribute": "enable-metrics",
2231
+ "mutable": false,
2232
+ "complexType": {
2233
+ "original": "boolean",
2234
+ "resolved": "boolean",
2235
+ "references": {}
2236
+ },
2237
+ "required": false,
2238
+ "optional": false,
2239
+ "docs": {
2240
+ "tags": [],
2241
+ "text": ""
2242
+ },
2243
+ "getter": false,
2244
+ "setter": false,
2245
+ "reflect": false,
2246
+ "defaultValue": "true"
2247
+ },
2248
+ "customerId": {
2249
+ "type": "string",
2250
+ "attribute": "customer-id",
2251
+ "mutable": false,
2252
+ "complexType": {
2253
+ "original": "string",
2254
+ "resolved": "string",
2255
+ "references": {}
2256
+ },
2257
+ "required": false,
2258
+ "optional": true,
2259
+ "docs": {
2260
+ "tags": [],
2261
+ "text": ""
2262
+ },
2263
+ "getter": false,
2264
+ "setter": false,
2265
+ "reflect": false
2266
+ },
2267
+ "tenantId": {
2268
+ "type": "string",
2269
+ "attribute": "tenant-id",
2270
+ "mutable": false,
2271
+ "complexType": {
2272
+ "original": "string",
2273
+ "resolved": "string",
2274
+ "references": {}
2275
+ },
2276
+ "required": false,
2277
+ "optional": true,
2278
+ "docs": {
2279
+ "tags": [],
2280
+ "text": ""
2281
+ },
2282
+ "getter": false,
2283
+ "setter": false,
2284
+ "reflect": false
2285
+ },
2286
+ "environment": {
2287
+ "type": "string",
2288
+ "attribute": "environment",
2289
+ "mutable": false,
2290
+ "complexType": {
2291
+ "original": "string",
2292
+ "resolved": "string",
2293
+ "references": {}
2294
+ },
2295
+ "required": false,
2296
+ "optional": true,
2297
+ "docs": {
2298
+ "tags": [],
2299
+ "text": ""
2300
+ },
2301
+ "getter": false,
2302
+ "setter": false,
2303
+ "reflect": false,
2304
+ "defaultValue": "'production'"
2305
+ },
2306
+ "propagateTraceHeaderCorsUrls": {
2307
+ "type": "string",
2308
+ "attribute": "propagate-trace-header-cors-urls",
2309
+ "mutable": false,
2310
+ "complexType": {
2311
+ "original": "string",
2312
+ "resolved": "string",
2313
+ "references": {}
2314
+ },
2315
+ "required": false,
2316
+ "optional": true,
2317
+ "docs": {
2318
+ "tags": [],
2319
+ "text": ""
2320
+ },
2321
+ "getter": false,
2322
+ "setter": false,
2323
+ "reflect": false
2324
+ },
2325
+ "metricsExportIntervalMillis": {
2326
+ "type": "number",
2327
+ "attribute": "metrics-export-interval-millis",
2328
+ "mutable": false,
2329
+ "complexType": {
2330
+ "original": "number",
2331
+ "resolved": "number",
2332
+ "references": {}
2333
+ },
2334
+ "required": false,
2335
+ "optional": true,
2336
+ "docs": {
2337
+ "tags": [],
2338
+ "text": ""
2339
+ },
2340
+ "getter": false,
2341
+ "setter": false,
2342
+ "reflect": false,
2343
+ "defaultValue": "60000"
2344
+ },
2345
+ "license": {
2346
+ "type": "string",
2347
+ "attribute": "license",
2348
+ "mutable": false,
2349
+ "complexType": {
2350
+ "original": "string",
2351
+ "resolved": "string",
2352
+ "references": {}
2353
+ },
2354
+ "required": false,
2355
+ "optional": true,
2356
+ "docs": {
2357
+ "tags": [],
2358
+ "text": ""
2359
+ },
2360
+ "getter": false,
2361
+ "setter": false,
2362
+ "reflect": false
2363
+ },
2364
+ "licenseEnvironment": {
2365
+ "type": "string",
2366
+ "attribute": "license-environment",
2367
+ "mutable": false,
2368
+ "complexType": {
2369
+ "original": "'dev' | 'qa' | 'sandbox' | 'prod'",
2370
+ "resolved": "\"dev\" | \"prod\" | \"qa\" | \"sandbox\"",
2371
+ "references": {}
2372
+ },
2373
+ "required": false,
2374
+ "optional": true,
2375
+ "docs": {
2376
+ "tags": [],
2377
+ "text": ""
2378
+ },
2379
+ "getter": false,
2380
+ "setter": false,
2381
+ "reflect": false,
2382
+ "defaultValue": "'prod'"
2383
+ },
2384
+ "traceId": {
2385
+ "type": "string",
2386
+ "attribute": "trace-id",
2387
+ "mutable": false,
2388
+ "complexType": {
2389
+ "original": "string",
2390
+ "resolved": "string",
2391
+ "references": {}
2392
+ },
2393
+ "required": false,
2394
+ "optional": true,
2395
+ "docs": {
2396
+ "tags": [],
2397
+ "text": ""
2398
+ },
2399
+ "getter": false,
2400
+ "setter": false,
2401
+ "reflect": false
2402
+ },
2403
+ "appId": {
2404
+ "type": "string",
2405
+ "attribute": "app-id",
2406
+ "mutable": false,
2407
+ "complexType": {
2408
+ "original": "string",
2409
+ "resolved": "string",
2410
+ "references": {}
2411
+ },
2412
+ "required": false,
2413
+ "optional": true,
2414
+ "docs": {
2415
+ "tags": [],
2416
+ "text": ""
2417
+ },
2418
+ "getter": false,
2419
+ "setter": false,
2420
+ "reflect": false,
2421
+ "defaultValue": "'jaak-stamps-web'"
2160
2422
  }
2161
2423
  };
2162
2424
  }
@@ -2177,7 +2439,9 @@ export class JaakStamps {
2177
2439
  "performanceDegradedMode": {},
2178
2440
  "showPerformanceMessage": {},
2179
2441
  "captureStateVersion": {},
2180
- "processingButton": {}
2442
+ "processingButton": {},
2443
+ "licenseValid": {},
2444
+ "licenseError": {}
2181
2445
  };
2182
2446
  }
2183
2447
  static get events() {
@@ -2211,6 +2475,21 @@ export class JaakStamps {
2211
2475
  "resolved": "boolean",
2212
2476
  "references": {}
2213
2477
  }
2478
+ }, {
2479
+ "method": "traceIdGenerated",
2480
+ "name": "traceIdGenerated",
2481
+ "bubbles": true,
2482
+ "cancelable": true,
2483
+ "composed": true,
2484
+ "docs": {
2485
+ "tags": [],
2486
+ "text": ""
2487
+ },
2488
+ "complexType": {
2489
+ "original": "{traceId: string}",
2490
+ "resolved": "{ traceId: string; }",
2491
+ "references": {}
2492
+ }
2214
2493
  }];
2215
2494
  }
2216
2495
  static get methods() {
@@ -2273,7 +2552,7 @@ export class JaakStamps {
2273
2552
  },
2274
2553
  "stopCapture": {
2275
2554
  "complexType": {
2276
- "signature": "() => Promise<{ success: boolean; resourcesFreed: string[]; cleanupErrors: string[]; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; cleanupErrors?: undefined; }>",
2555
+ "signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
2277
2556
  "parameters": [],
2278
2557
  "references": {
2279
2558
  "Promise": {
@@ -2281,7 +2560,7 @@ export class JaakStamps {
2281
2560
  "id": "global::Promise"
2282
2561
  }
2283
2562
  },
2284
- "return": "Promise<{ success: boolean; resourcesFreed: string[]; cleanupErrors: string[]; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; cleanupErrors?: undefined; }>"
2563
+ "return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
2285
2564
  },
2286
2565
  "docs": {
2287
2566
  "text": "",
@@ -2441,40 +2720,6 @@ export class JaakStamps {
2441
2720
  "text": "",
2442
2721
  "tags": []
2443
2722
  }
2444
- },
2445
- "getResourceReport": {
2446
- "complexType": {
2447
- "signature": "() => Promise<any>",
2448
- "parameters": [],
2449
- "references": {
2450
- "Promise": {
2451
- "location": "global",
2452
- "id": "global::Promise"
2453
- }
2454
- },
2455
- "return": "Promise<any>"
2456
- },
2457
- "docs": {
2458
- "text": "",
2459
- "tags": []
2460
- }
2461
- },
2462
- "forceResourceCleanup": {
2463
- "complexType": {
2464
- "signature": "() => Promise<{ success: boolean; resourcesFreed: string[]; errors: string[]; timestamp: string; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; errors?: undefined; timestamp?: undefined; }>",
2465
- "parameters": [],
2466
- "references": {
2467
- "Promise": {
2468
- "location": "global",
2469
- "id": "global::Promise"
2470
- }
2471
- },
2472
- "return": "Promise<{ success: boolean; resourcesFreed: string[]; errors: string[]; timestamp: string; error?: undefined; } | { success: boolean; error: any; resourcesFreed?: undefined; errors?: undefined; timestamp?: undefined; }>"
2473
- },
2474
- "docs": {
2475
- "text": "",
2476
- "tags": []
2477
- }
2478
2723
  }
2479
2724
  };
2480
2725
  }