@jaak.ai/stamps 2.1.0 → 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 (59) hide show
  1. package/README.md +227 -14
  2. package/dist/cjs/{index-Ga0t6BMe.js → index-D6NBn_qu.js} +30 -5
  3. package/dist/cjs/index-D6NBn_qu.js.map +1 -0
  4. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +2 -2
  5. package/dist/cjs/jaak-stamps.cjs.entry.js +17737 -127
  6. package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
  7. package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
  8. package/dist/cjs/loader.cjs.js +2 -2
  9. package/dist/collection/components/my-component/my-component.css +105 -4
  10. package/dist/collection/components/my-component/my-component.js +512 -20
  11. package/dist/collection/components/my-component/my-component.js.map +1 -1
  12. package/dist/collection/services/CameraService.js +41 -9
  13. package/dist/collection/services/CameraService.js.map +1 -1
  14. package/dist/collection/services/LicenseValidationService.js +111 -0
  15. package/dist/collection/services/LicenseValidationService.js.map +1 -0
  16. package/dist/collection/services/MetricsService.js +350 -0
  17. package/dist/collection/services/MetricsService.js.map +1 -0
  18. package/dist/collection/services/ServiceContainer.js +66 -1
  19. package/dist/collection/services/ServiceContainer.js.map +1 -1
  20. package/dist/collection/services/TracingService.js +494 -0
  21. package/dist/collection/services/TracingService.js.map +1 -0
  22. package/dist/collection/services/interfaces/ILicenseValidationService.js +2 -0
  23. package/dist/collection/services/interfaces/ILicenseValidationService.js.map +1 -0
  24. package/dist/collection/services/interfaces/IMetricsService.js +2 -0
  25. package/dist/collection/services/interfaces/IMetricsService.js.map +1 -0
  26. package/dist/collection/services/interfaces/ITracingService.js +2 -0
  27. package/dist/collection/services/interfaces/ITracingService.js.map +1 -0
  28. package/dist/components/index.js +28 -3
  29. package/dist/components/index.js.map +1 -1
  30. package/dist/components/jaak-stamps.js +17751 -126
  31. package/dist/components/jaak-stamps.js.map +1 -1
  32. package/dist/esm/{index-Drbzcuq-.js → index-BCfAsrzL.js} +30 -5
  33. package/dist/esm/index-BCfAsrzL.js.map +1 -0
  34. package/dist/esm/jaak-stamps-webcomponent.js +3 -3
  35. package/dist/esm/jaak-stamps.entry.js +17737 -127
  36. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  37. package/dist/esm/loader.js +3 -3
  38. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  39. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  40. package/dist/jaak-stamps-webcomponent/p-34bcebb1.entry.js +7 -0
  41. package/dist/jaak-stamps-webcomponent/p-34bcebb1.entry.js.map +1 -0
  42. package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js +3 -0
  43. package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js.map +1 -0
  44. package/dist/types/components/my-component/my-component.d.ts +24 -0
  45. package/dist/types/components.d.ts +76 -0
  46. package/dist/types/services/LicenseValidationService.d.ts +59 -0
  47. package/dist/types/services/MetricsService.d.ts +74 -0
  48. package/dist/types/services/ServiceContainer.d.ts +14 -1
  49. package/dist/types/services/TracingService.d.ts +72 -0
  50. package/dist/types/services/interfaces/ILicenseValidationService.d.ts +4 -0
  51. package/dist/types/services/interfaces/IMetricsService.d.ts +70 -0
  52. package/dist/types/services/interfaces/ITracingService.d.ts +64 -0
  53. package/package.json +18 -2
  54. package/dist/cjs/index-Ga0t6BMe.js.map +0 -1
  55. package/dist/esm/index-Drbzcuq-.js.map +0 -1
  56. package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js +0 -3
  57. package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js.map +0 -1
  58. package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js +0 -2
  59. package/dist/jaak-stamps-webcomponent/p-c06e5d7b.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;
@@ -93,6 +113,7 @@ export class JaakStamps {
93
113
  MAX_FAILURES = 30;
94
114
  lastInferenceTime = 0;
95
115
  MIN_INFERENCE_INTERVAL = 50;
116
+ MOBILE_MIN_INFERENCE_INTERVAL = 100; // Higher interval for mobile to prevent slow motion
96
117
  performanceHistory = [];
97
118
  PERFORMANCE_HISTORY_SIZE = 10;
98
119
  // Sistema simplificado de monitoreo de rendimiento
@@ -104,8 +125,23 @@ export class JaakStamps {
104
125
  // Canvas pool for optimized screenshot capture
105
126
  canvasPool = [];
106
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
+ }
107
139
  async componentDidLoad() {
108
140
  try {
141
+ // If license validation failed, stop initialization
142
+ if (!this.licenseValid) {
143
+ return;
144
+ }
109
145
  this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
110
146
  await this.initializeServices();
111
147
  this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
@@ -121,6 +157,10 @@ export class JaakStamps {
121
157
  }
122
158
  }
123
159
  async initializeServices() {
160
+ // Parse propagateTraceHeaderCorsUrls from comma-separated string
161
+ const propagateUrls = this.propagateTraceHeaderCorsUrls
162
+ ? this.propagateTraceHeaderCorsUrls.split(',').map(url => url.trim())
163
+ : [];
124
164
  const config = {
125
165
  debug: this.debug,
126
166
  alignmentTolerance: this.alignmentTolerance,
@@ -129,13 +169,76 @@ export class JaakStamps {
129
169
  useDocumentClassification: this.useDocumentClassification,
130
170
  useDocumentDetector: this.useDocumentDetector,
131
171
  preferredCamera: this.preferredCamera,
132
- 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
133
182
  };
134
183
  this.serviceContainer = new ServiceContainer(config);
135
184
  this.eventBus = this.serviceContainer.getEventBus();
136
185
  this.stateManager = this.serviceContainer.getStateManager();
137
186
  this.cameraService = this.serviceContainer.getCameraService();
138
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
+ }
139
242
  }
140
243
  async setupEventListeners() {
141
244
  this.eventBus.on('state-changed', () => {
@@ -403,17 +506,31 @@ export class JaakStamps {
403
506
  return this.stateManager.isProcessCompleted();
404
507
  }
405
508
  async startCapture() {
509
+ const span = this.tracingService?.startSpan('capture.start');
406
510
  const readyCheck = this.isComponentReady();
407
511
  if (!readyCheck.ready) {
408
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
+ }
409
518
  return { success: false, error: readyCheck.message };
410
519
  }
411
520
  try {
412
521
  await this.startDetection();
522
+ if (span) {
523
+ this.tracingService?.setSpanAttribute(span, 'success', true);
524
+ this.tracingService?.endSpan(span);
525
+ }
413
526
  return { success: true };
414
527
  }
415
528
  catch (error) {
416
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
+ }
417
534
  return { success: false, error: error.message };
418
535
  }
419
536
  }
@@ -511,13 +628,22 @@ export class JaakStamps {
511
628
  }
512
629
  }
513
630
  async preloadModel() {
631
+ const span = this.tracingService?.startSpan('model.preload');
514
632
  const readyCheck = this.isComponentReady();
515
633
  if (!readyCheck.ready) {
516
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
+ }
517
639
  return { success: false, error: readyCheck.message };
518
640
  }
519
641
  if (this.detectionService.isModelLoaded()) {
520
642
  this.updateStatus('Reconocimiento listo', '', 'ready');
643
+ if (span) {
644
+ this.tracingService?.setSpanAttribute(span, 'cached', true);
645
+ this.tracingService?.endSpan(span);
646
+ }
521
647
  return { success: true, message: 'Model already loaded' };
522
648
  }
523
649
  try {
@@ -527,17 +653,31 @@ export class JaakStamps {
527
653
  this.stateManager.updateCaptureState({ isLoading: true });
528
654
  }
529
655
  try {
656
+ const detectionStartTime = performance.now();
657
+ const modelSpan = this.tracingService?.startSpan('model.detection.load');
530
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);
531
663
  }
532
664
  catch (modelError) {
665
+ this.metricsService?.recordError('model_load_failed', 'detection');
533
666
  this.switchToManualModeWithError('Error al cargar modelo de detección');
534
667
  throw modelError;
535
668
  }
536
669
  this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
537
670
  try {
671
+ const classificationStartTime = performance.now();
672
+ const classSpan = this.tracingService?.startSpan('model.classification.load');
538
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);
539
678
  }
540
679
  catch (classificationError) {
680
+ this.metricsService?.recordError('model_load_failed', 'classification');
541
681
  this.switchToManualModeWithError('Error al cargar modelo de clasificación');
542
682
  throw classificationError;
543
683
  }
@@ -547,6 +687,9 @@ export class JaakStamps {
547
687
  if (this.debug) {
548
688
  this.recordOnnxPerformance(loadTime, 0);
549
689
  }
690
+ if (span) {
691
+ this.tracingService?.setSpanAttribute(span, 'loadTime', loadTime);
692
+ }
550
693
  this.updateStatus('Finalizando configuración...', 'Preparando herramientas para captura', 'loading');
551
694
  await new Promise(resolve => setTimeout(resolve, 300));
552
695
  this.updateStatus('Reconocimiento preparado', '', 'ready');
@@ -554,6 +697,10 @@ export class JaakStamps {
554
697
  this.stateManager.updateCaptureState({ isLoading: false });
555
698
  }
556
699
  this.emitReadyEvent();
700
+ if (span) {
701
+ this.tracingService?.setSpanAttribute(span, 'success', true);
702
+ this.tracingService?.endSpan(span);
703
+ }
557
704
  return { success: true, message: 'Models preloaded successfully' };
558
705
  }
559
706
  catch (error) {
@@ -561,6 +708,10 @@ export class JaakStamps {
561
708
  if (this.stateManager) {
562
709
  this.stateManager.updateCaptureState({ isLoading: false });
563
710
  }
711
+ if (span) {
712
+ this.tracingService?.recordException(span, error);
713
+ this.tracingService?.endSpan(span);
714
+ }
564
715
  return { success: false, error: error.message };
565
716
  }
566
717
  }
@@ -827,9 +978,14 @@ export class JaakStamps {
827
978
  return;
828
979
  }
829
980
  this.frameSkipCounter = 0;
830
- // Throttle inference
981
+ // Throttle inference - use higher interval for mobile devices to prevent slow motion effect
831
982
  const currentTime = Date.now();
832
- 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) {
833
989
  if (captureState.step !== 'completed') {
834
990
  this.animationId = requestAnimationFrame(() => this.detectFrame());
835
991
  }
@@ -983,7 +1139,7 @@ export class JaakStamps {
983
1139
  isCapturing: false
984
1140
  };
985
1141
  const cameraInfo = this.cameraInfoWithAutofocus;
986
- return (h("div", { key: 'f9cbca2fd6b8548a29ace7fcb972b53f402f61af', class: "detector-container" }, h("div", { key: '4f3cac734eb6bfd44a8de644eadca17e7893e11a', class: "video-container" }, h("video", { key: '5e15a38c989a72d5274281272d41ba064447ca6f', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '38c8ad8b691db60e9b5a2a8b641fe6febbeacfca', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
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: {
987
1143
  position: 'absolute',
988
1144
  left: `${box.x}px`,
989
1145
  top: `${box.y}px`,
@@ -992,9 +1148,9 @@ export class JaakStamps {
992
1148
  border: '2px solid #32406C',
993
1149
  pointerEvents: 'none',
994
1150
  boxSizing: 'border-box'
995
- } })))), this.isMaskReady && (h("div", { key: 'c56fde56b7a86a4e8a4cf558745291ea5cde12fd', class: "overlay-mask" }, h("div", { key: '04ed3f01f271dc84eed594cb4b60c2453645bd41', class: "card-outline" }, h("div", { key: '648bd423a5e886d4768115237de5896c7223b30b', class: "side side-top" }), h("div", { key: 'b8878059493ca61961b9b3c82ecc18b50b79ac85', class: "side side-right" }), h("div", { key: '09e5ad64ff33dbd5c112bf52b71e14409ab6aa65', class: "side side-bottom" }), h("div", { key: 'b8ba3d83c9bc1b4a0da859d7ed4b9a15eaddcc2a', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '3279701a240f77ac96e68a34ad95c099340c9a6a', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '2adf96d0a57e482372fd9586229dac2301b64fd4', class: "back-capture-section" }, h("div", { key: '305a147c9cfee854c60ac946a48a1d21b2792725', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (h("button", { key: '43270a0647f39307229c540dd5130fb58b1ff4fb', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: 'eb4666603774564eef95a120ac13faeb9e10c8b4', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: 'cac667d9548ae2314ad5a677c5ed90f25184f728', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '0af7aa4106ce189ae2d79bec78ede01abe25b1e7', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
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
996
1152
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
997
- : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'de6e5c4fad4d4d996b894457c9af52bc71e533fb', class: "camera-controls" }, h("button", { key: 'cfddcf2420d5f5923a6b02ca446bbe1b8e9fc5e4', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: '5fb73f958b9a5a6f9242032c4530b0bdab84f6c5', class: "camera-selector-dropdown" }, h("div", { key: '8ee2d26b1001e571e7b6c326e927926fa08287fd', class: "camera-selector-header" }, h("span", { key: '78350b0fb1bd933f7c8c59d8dab0b7122ffbe363' }, "Seleccionar C\u00E1mara"), h("button", { key: 'bb750e0ddd5a1ee5ff96bd57805a7f90b8821e9f', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'd0f6343cf1b7367f3da33ec8cf64891109d923b8', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: 'c9642fbfb0acea766d3f0542f4cc9f17f4af4d7b', class: "device-info" }, h("small", { key: 'b6e9507ff7addf011f339f6c22a88a96b40db68a' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '04ae9262409284782850b90945eb7038673e2097', class: "manual-capture-section" }, h("button", { key: '27c315e503ccba468b89ebd58d6b70f40ddd06ba', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '8f2dcd2df9a218b37eb0b8204a8724362cfeaa53', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '6fd6fe1d0213b01c8119a67fb8108f73227feee1', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'fba3816b48b99ca64510ecbefc0d41431569c9ae', class: "flip-animation" }, h("div", { key: 'beea1c8245b5d81f1008efc6d1b079a61c821e7f', class: "id-card-icon" }), h("div", { key: 'f0fbc34f619a653e2c0ade764ca99e7aa582d157', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'e4a0e6d31112e3a50024d1d0430b4ad997049fe7', class: "success-animation" }, h("div", { key: '5dd3f781f55630bc904c81207548ae35bc79b452', class: "check-icon" }), h("div", { key: 'a10ebc557582325091efb0abd12d62b27a43b98c', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '975865955dee1803b5215ecf049701c98b324855', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '84f6f67202e26ce80f7fc5d5f321e23b5bc55c57', class: "status-spinner" })), h("div", { key: 'a1ae56ed156e8feb279a1bf160f749c5d0b5ce45', class: "status-content" }, h("div", { key: 'd62932554cea257ce19763df26e0ba702e50eeca', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'c5506fa12dd0d37d26dc26c9f2381c843330451f', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '2097eda63185f336f7319014fae2ef6db1e41761', class: "performance-monitor" }, h("div", { key: 'd611c494c6b1a5930ef64aebdcba41e5d94c9ee6', class: "performance-expanded" }, h("div", { key: '800d46831e7b9bec260d9c5e67ea1a3eb9c2933e', class: "metrics-row" }, h("div", { key: 'dd0db1faa9050d98e71da4b5cd26a4a484c37c64', class: "metric-compact" }, h("span", { key: 'f0e6ea61a35ddb2ae5c2a6aff8c255a02c5c1219', class: "metric-label" }, "FPS"), h("span", { key: 'cbf4df4d142f6f01e45f566a8ede8fbbf29ad673', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '83861ad31fa46c8199a5583e4b3f8eefd44bacd6', class: "metric-compact" }, h("span", { key: '41b8de4cda47959dfc9df393e95fdadde534dd21', class: "metric-label" }, "MEM"), h("span", { key: 'b268f13cc78066e240ed158f1443113a94827aff', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '9f9a9d0690906b186492af23b900c5fa0f5cff89', class: "metrics-row" }, h("div", { key: '84fa6e453dc36e4a74e8b573447b17230766229e', class: "metric-compact" }, h("span", { key: 'f17d52899090c89f532d74c552dd8c5338fc2d83', class: "metric-label" }, "INF"), h("span", { key: '2beca0d7ce55ed574f6b01efc086c5846983e024', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '981e300cf076e81bfe946974f6cffd63619eacde', class: "metric-compact" }, h("span", { key: '3dfb0c996a69eb87b0f711322675cad69c471146', class: "metric-label" }, "FRAME"), h("span", { key: '3ae2ac6e90cda77f23e559f3197c3b761f9bdc40', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '2c51216838d87728d9d7764e5ba8a50fa02eb493', class: "metrics-row" }, h("div", { key: 'eda810ffb80ec38ab84583f59b26706f90a50fc9', class: "metric-compact" }, h("span", { key: '978ba9c0c67d6da6b29b3535af33a314bab9276a', class: "metric-label" }, "DET"), h("span", { key: '3e5e169fd4a3906283d8f918b5bbc7c5df6ccd0f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'b4b915c745e35c09470a4dc5edfc112d32cfb8b6', class: "metric-compact" }, h("span", { key: '3e2a7b88a4446813c955efb0dfa9e2958e8e1448', class: "metric-label" }, "RATE"), h("span", { key: 'f4f073f7ee622167a241c6a1c535ef580c13d9ab', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '76c556e92f6d7980539f1a30efe8751f665164fb', class: "watermark" }, h("img", { key: 'af23e115d6b4e92a72e55d6d98fde0e4b1d09e9b', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
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" }))))));
998
1154
  }
999
1155
  // Utility methods
1000
1156
  updateDetectionBoxes(boxes) {
@@ -1111,24 +1267,44 @@ export class JaakStamps {
1111
1267
  }
1112
1268
  async takeManualScreenshot() {
1113
1269
  console.log('🔵 Botón clicked: Capturar (Frente/Reverso)');
1270
+ const captureStartTime = performance.now();
1271
+ const span = this.tracingService?.startSpan('capture.screenshot.manual');
1114
1272
  // Set processing state immediately
1115
1273
  const captureState = this.stateManager?.getCaptureState();
1274
+ const side = captureState?.step === 'front' ? 'front' : 'back';
1116
1275
  if (captureState?.step === 'front') {
1117
1276
  this.processingButton = 'capture-front';
1277
+ if (span)
1278
+ this.tracingService?.setSpanAttribute(span, 'side', 'front');
1118
1279
  }
1119
1280
  else if (captureState?.step === 'back') {
1120
1281
  this.processingButton = 'capture-back';
1282
+ if (span)
1283
+ this.tracingService?.setSpanAttribute(span, 'side', 'back');
1121
1284
  }
1285
+ // Record user interaction
1286
+ this.metricsService?.recordUserInteraction('manual_capture_button');
1122
1287
  console.log('🔵 processingButton set to:', this.processingButton);
1123
1288
  // Add small delay to show spinner
1124
1289
  await new Promise(resolve => setTimeout(resolve, 100));
1125
1290
  if (!this.videoRef) {
1126
1291
  this.processingButton = null;
1292
+ if (span) {
1293
+ this.tracingService?.setSpanAttribute(span, 'error', 'no_video_ref');
1294
+ this.tracingService?.endSpan(span);
1295
+ }
1127
1296
  return;
1128
1297
  }
1129
1298
  // When using manual capture, use mask coordinates for cropping
1130
1299
  // Note: processingButton will be cleared inside takeScreenshotWithMaskCoordinates
1131
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
+ }
1132
1308
  }
1133
1309
  async takeScreenshotWithMaskCoordinates() {
1134
1310
  if (!this.videoRef || !this.detectionContainer)
@@ -1247,6 +1423,8 @@ export class JaakStamps {
1247
1423
  async takeScreenshot() {
1248
1424
  if (!this.videoRef || !this.lastDetectedBox)
1249
1425
  return;
1426
+ const captureStartTime = performance.now();
1427
+ const span = this.tracingService?.startSpan('capture.screenshot.auto');
1250
1428
  this.stateManager.updateCaptureState({ isCapturing: true });
1251
1429
  this.triggerCaptureAnimation();
1252
1430
  // Use pooled canvas for optimization
@@ -1269,17 +1447,30 @@ export class JaakStamps {
1269
1447
  croppedCtx.drawImage(this.videoRef, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
1270
1448
  const captureState = this.stateManager.getCaptureState();
1271
1449
  if (captureState.step === 'front') {
1450
+ const frontImageData = captureCanvas.toDataURL('image/jpeg');
1451
+ const croppedImageData = croppedCanvas.toDataURL('image/jpeg');
1272
1452
  this.stateManager.setCapturedImages({
1273
1453
  front: {
1274
- fullFrame: captureCanvas.toDataURL('image/jpeg'),
1275
- cropped: croppedCanvas.toDataURL('image/jpeg')
1454
+ fullFrame: frontImageData,
1455
+ cropped: croppedImageData
1276
1456
  }
1277
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
+ }
1278
1465
  // Check if document classification is enabled
1279
1466
  if (this.useDocumentClassification) {
1280
1467
  try {
1281
1468
  const classification = await this.detectionService.classifyDocument(croppedCanvas);
1282
1469
  if (classification && classification.class === 'passport') {
1470
+ if (span) {
1471
+ this.tracingService?.setSpanAttribute(span, 'document.type', 'passport');
1472
+ this.tracingService?.endSpan(span);
1473
+ }
1283
1474
  this.completeProcess(true);
1284
1475
  return;
1285
1476
  }
@@ -1293,6 +1484,9 @@ export class JaakStamps {
1293
1484
  isDetectionPaused: true,
1294
1485
  showFlipAnimation: true
1295
1486
  });
1487
+ if (span) {
1488
+ this.tracingService?.endSpan(span);
1489
+ }
1296
1490
  setTimeout(() => {
1297
1491
  this.stateManager.updateCaptureState({
1298
1492
  showFlipAnimation: false,
@@ -1302,12 +1496,22 @@ export class JaakStamps {
1302
1496
  }, 3000);
1303
1497
  }
1304
1498
  else if (captureState.step === 'back') {
1499
+ const backImageData = captureCanvas.toDataURL('image/jpeg');
1500
+ const croppedBackImageData = croppedCanvas.toDataURL('image/jpeg');
1305
1501
  this.stateManager.setCapturedImages({
1306
1502
  back: {
1307
- fullFrame: captureCanvas.toDataURL('image/jpeg'),
1308
- cropped: croppedCanvas.toDataURL('image/jpeg')
1503
+ fullFrame: backImageData,
1504
+ cropped: croppedBackImageData
1309
1505
  }
1310
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
+ }
1311
1515
  this.completeProcess(false);
1312
1516
  }
1313
1517
  // Return canvases to pool after use
@@ -1454,7 +1658,7 @@ export class JaakStamps {
1454
1658
  catch (error) {
1455
1659
  }
1456
1660
  }
1457
- completeProcess(skippedBack = false) {
1661
+ async completeProcess(skippedBack = false) {
1458
1662
  this.stateManager.updateCaptureState({
1459
1663
  step: 'completed',
1460
1664
  showSuccessAnimation: true
@@ -1469,6 +1673,15 @@ export class JaakStamps {
1469
1673
  ...capturedImages,
1470
1674
  timestamp: new Date().toISOString()
1471
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
+ }
1472
1685
  this.captureCompleted.emit(finalImages);
1473
1686
  setTimeout(() => {
1474
1687
  this.stateManager.updateCaptureState({ showSuccessAnimation: false });
@@ -1660,28 +1873,35 @@ export class JaakStamps {
1660
1873
  }
1661
1874
  // Adaptive frame skipping based on performance
1662
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;
1663
1883
  // Base conditions for high frame skip
1664
1884
  if (this.consecutiveFailures > 20)
1665
1885
  return this.MAX_FRAME_SKIP;
1666
1886
  if (this.performanceMetrics.inferenceTime > 200)
1667
- return 6;
1887
+ return Math.max(6, mobileBaseSkip);
1668
1888
  if (this.performanceMetrics.memoryUsage > 200)
1669
- return 5;
1889
+ return Math.max(5, mobileBaseSkip);
1670
1890
  // Calculate average processing time
1671
1891
  if (this.performanceHistory.length > 0) {
1672
1892
  const avgProcessingTime = this.performanceHistory.reduce((a, b) => a + b, 0) / this.performanceHistory.length;
1673
1893
  if (avgProcessingTime > 100)
1674
- return 4;
1894
+ return Math.max(4, mobileBaseSkip);
1675
1895
  if (avgProcessingTime > 80)
1676
- return 3;
1896
+ return Math.max(3, mobileBaseSkip);
1677
1897
  if (avgProcessingTime > 60)
1678
- return this.BASE_FRAME_SKIP + 1;
1898
+ return Math.max(this.BASE_FRAME_SKIP + 1, mobileBaseSkip);
1679
1899
  }
1680
1900
  // Low memory devices get higher frame skip
1681
1901
  if (this.performanceMetrics.memoryUsage > 150)
1682
- return 4;
1683
- // Performance is good, use base frame skip
1684
- 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;
1685
1905
  }
1686
1906
  // Método para resetear la máscara a color blanco
1687
1907
  resetMaskToWhite() {
@@ -1944,6 +2164,261 @@ export class JaakStamps {
1944
2164
  "setter": false,
1945
2165
  "reflect": false,
1946
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'"
1947
2422
  }
1948
2423
  };
1949
2424
  }
@@ -1964,7 +2439,9 @@ export class JaakStamps {
1964
2439
  "performanceDegradedMode": {},
1965
2440
  "showPerformanceMessage": {},
1966
2441
  "captureStateVersion": {},
1967
- "processingButton": {}
2442
+ "processingButton": {},
2443
+ "licenseValid": {},
2444
+ "licenseError": {}
1968
2445
  };
1969
2446
  }
1970
2447
  static get events() {
@@ -1998,6 +2475,21 @@ export class JaakStamps {
1998
2475
  "resolved": "boolean",
1999
2476
  "references": {}
2000
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
+ }
2001
2493
  }];
2002
2494
  }
2003
2495
  static get methods() {