@jaak.ai/stamps 2.0.0-dev.28 → 2.0.0-dev.29

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.
@@ -78,34 +78,65 @@ const JaakStamps = class {
78
78
  CONFIDENCE_THRESHOLD = 0.6;
79
79
  // ISO/IEC 7810 ID-1 standard dimensions (85.60mm x 53.98mm)
80
80
  ID1_ASPECT_RATIO = 85.60 / 53.98; // 1.5863320574...
81
- debugLog(...args) {
82
- if (this.debug) {
83
- console.log(...args);
81
+ logger = {
82
+ info: (...args) => {
83
+ if (this.debug) {
84
+ console.log(`[JAAK-STAMPS] [INFO] [${new Date().toLocaleTimeString()}]`, ...args);
85
+ }
86
+ },
87
+ warn: (...args) => {
88
+ if (this.debug) {
89
+ console.warn(`[JAAK-STAMPS] [WARN] [${new Date().toLocaleTimeString()}]`, ...args);
90
+ }
91
+ },
92
+ error: (...args) => {
93
+ if (this.debug) {
94
+ console.error(`[JAAK-STAMPS] [ERROR] [${new Date().toLocaleTimeString()}]`, ...args);
95
+ }
96
+ },
97
+ debug: (...args) => {
98
+ if (this.debug) {
99
+ console.debug(`[JAAK-STAMPS] [DEBUG] [${new Date().toLocaleTimeString()}]`, ...args);
100
+ }
101
+ },
102
+ state: (state, data) => {
103
+ if (this.debug) {
104
+ console.log(`[JAAK-STAMPS] [STATE] [${new Date().toLocaleTimeString()}] ${state}`, data || '');
105
+ }
106
+ },
107
+ performance: (operation, duration) => {
108
+ if (this.debug) {
109
+ console.log(`[JAAK-STAMPS] [PERF] [${new Date().toLocaleTimeString()}] ${operation}: ${duration}ms`);
110
+ }
84
111
  }
85
- }
112
+ };
86
113
  validateMaskSize() {
87
114
  if (this.maskSize < 50 || this.maskSize > 100) {
88
- console.warn(`maskSize debe estar entre 50 y 100. Valor actual: ${this.maskSize}. Usando valor por defecto: 90`);
115
+ this.logger.warn(`Propiedad maskSize inválida. Valor: ${this.maskSize}, esperado: 50-100. Usando valor por defecto: 90`);
89
116
  this.maskSize = 90;
90
117
  }
91
118
  }
92
119
  validateCropMargin() {
93
120
  if (this.cropMargin < 0 || this.cropMargin > 100) {
94
- console.warn(`cropMargin debe estar entre 0 y 100. Valor actual: ${this.cropMargin}. Usando valor por defecto: 0`);
121
+ this.logger.warn(`Propiedad cropMargin inválida. Valor: ${this.cropMargin}, esperado: 0-100. Usando valor por defecto: 0`);
95
122
  this.cropMargin = 0;
96
123
  }
97
124
  }
98
125
  validatePreferredCamera() {
99
126
  const validOptions = ['auto', 'front', 'back'];
100
127
  if (!validOptions.includes(this.preferredCamera)) {
101
- console.warn(`preferredCamera debe ser uno de: ${validOptions.join(', ')}. Valor actual: ${this.preferredCamera}. Usando valor por defecto: 'auto'`);
128
+ this.logger.warn(`Propiedad preferredCamera inválida. Valor: ${this.preferredCamera}, esperado: ${validOptions.join(', ')}. Usando valor por defecto: 'auto'`);
102
129
  this.preferredCamera = 'auto';
103
130
  }
104
131
  }
105
132
  emitReadyEvent() {
106
133
  const isDocumentReady = !!window.ort && this.isModelPreloaded;
107
134
  this.isReady.emit(isDocumentReady);
108
- this.debugLog('🟢 isReady event emitted:', isDocumentReady);
135
+ this.logger.state('COMPONENTE_LISTO', {
136
+ ortLibraryLoaded: !!window.ort,
137
+ modelPreloaded: this.isModelPreloaded,
138
+ isReady: isDocumentReady
139
+ });
109
140
  }
110
141
  isRearCamera(stream) {
111
142
  const videoTrack = stream.getVideoTracks()[0];
@@ -128,7 +159,11 @@ const JaakStamps = class {
128
159
  else {
129
160
  this.deviceType = 'desktop';
130
161
  }
131
- this.debugLog('📱 Device type detected:', this.deviceType);
162
+ this.logger.state('DISPOSITIVO_DETECTADO', {
163
+ deviceType: this.deviceType,
164
+ userAgent: navigator.userAgent,
165
+ screenDimensions: { width: window.innerWidth, height: window.innerHeight }
166
+ });
132
167
  // Enumerate available cameras
133
168
  await this.enumerateAndDetectCameras();
134
169
  // Load user preference
@@ -139,7 +174,7 @@ const JaakStamps = class {
139
174
  // First, check if we have permission to enumerate devices
140
175
  const permissionStatus = await this.checkCameraPermission();
141
176
  if (permissionStatus === 'denied') {
142
- this.debugLog(' Camera permission denied');
177
+ this.logger.error('Permiso de cámara denegado por el usuario');
143
178
  this.statusMessage = "Permiso de cámara denegado";
144
179
  this.statusColor = "#ff6b6b";
145
180
  return;
@@ -153,7 +188,7 @@ const JaakStamps = class {
153
188
  const devices = await navigator.mediaDevices.enumerateDevices();
154
189
  this.availableCameras = devices.filter(device => device.kind === 'videoinput');
155
190
  this.isMultipleCamerasAvailable = this.availableCameras.length > 1;
156
- this.debugLog('📹 Available cameras:', {
191
+ this.logger.state('CAMARAS_DETECTADAS', {
157
192
  count: this.availableCameras.length,
158
193
  isMultipleCamerasAvailable: this.isMultipleCamerasAvailable,
159
194
  cameras: this.availableCameras.map(cam => ({
@@ -165,7 +200,7 @@ const JaakStamps = class {
165
200
  this.setInitialCameraPreference();
166
201
  }
167
202
  catch (error) {
168
- this.debugLog('Error enumerating cameras:', error);
203
+ this.logger.error('Error al enumerar cámaras disponibles:', error);
169
204
  this.handleCameraPermissionError(error);
170
205
  }
171
206
  }
@@ -178,7 +213,7 @@ const JaakStamps = class {
178
213
  return permission.state;
179
214
  }
180
215
  catch (error) {
181
- this.debugLog('⚠️ Could not check camera permission:', error);
216
+ this.logger.warn('No se pudo verificar permisos de cámara:', error);
182
217
  return 'prompt';
183
218
  }
184
219
  }
@@ -221,11 +256,11 @@ const JaakStamps = class {
221
256
  !camera.label.toLowerCase().includes('back') && !camera.label.toLowerCase().includes('rear'));
222
257
  if (frontCamera) {
223
258
  this.selectedCameraId = frontCamera.deviceId;
224
- this.debugLog('👤 User selected front camera:', frontCamera.label);
259
+ this.logger.state('CAMARA_FRONTAL_SELECCIONADA', { label: frontCamera.label, deviceId: frontCamera.deviceId });
225
260
  }
226
261
  else {
227
262
  this.selectedCameraId = this.availableCameras[0].deviceId;
228
- this.debugLog('⚠️ Front camera not found, using first available:', this.availableCameras[0].label);
263
+ this.logger.warn('Cámara frontal no encontrada, usando primera disponible:', this.availableCameras[0].label);
229
264
  }
230
265
  }
231
266
  else if (this.preferredCamera === 'back') {
@@ -236,11 +271,11 @@ const JaakStamps = class {
236
271
  camera.label.toLowerCase().includes('environment'));
237
272
  if (backCamera) {
238
273
  this.selectedCameraId = backCamera.deviceId;
239
- this.debugLog('📷 User selected back camera:', backCamera.label);
274
+ this.logger.state('CAMARA_TRASERA_SELECCIONADA', { label: backCamera.label, deviceId: backCamera.deviceId });
240
275
  }
241
276
  else {
242
277
  this.selectedCameraId = this.availableCameras[0].deviceId;
243
- this.debugLog('⚠️ Back camera not found, using first available:', this.availableCameras[0].label);
278
+ this.logger.warn('Cámara trasera no encontrada, usando primera disponible:', this.availableCameras[0].label);
244
279
  }
245
280
  }
246
281
  else {
@@ -253,17 +288,17 @@ const JaakStamps = class {
253
288
  camera.label.toLowerCase().includes('environment'));
254
289
  if (rearCamera) {
255
290
  this.selectedCameraId = rearCamera.deviceId;
256
- this.debugLog('📱 Auto-selected rear camera for mobile:', rearCamera.label);
291
+ this.logger.state('CAMARA_AUTO_SELECCIONADA_MOBILE', { type: 'rear', label: rearCamera.label, deviceId: rearCamera.deviceId });
257
292
  }
258
293
  else {
259
294
  this.selectedCameraId = this.availableCameras[0].deviceId;
260
- this.debugLog('📱 Rear camera not found, using first available:', this.availableCameras[0].label);
295
+ this.logger.warn('Cámara trasera no encontrada en mobile, usando primera disponible:', this.availableCameras[0].label);
261
296
  }
262
297
  }
263
298
  else {
264
299
  // For desktop, use first available camera (usually the only one)
265
300
  this.selectedCameraId = this.availableCameras[0].deviceId;
266
- this.debugLog('💻 Auto-selected desktop camera:', this.availableCameras[0].label);
301
+ this.logger.state('CAMARA_AUTO_SELECCIONADA_DESKTOP', { label: this.availableCameras[0].label, deviceId: this.availableCameras[0].deviceId });
267
302
  }
268
303
  }
269
304
  }
@@ -277,12 +312,12 @@ const JaakStamps = class {
277
312
  if (isStillAvailable) {
278
313
  this.selectedCameraId = preference.cameraId;
279
314
  this.preferredCameraFacing = preference.facing;
280
- this.debugLog('💾 Loaded camera preference:', preference);
315
+ this.logger.state('PREFERENCIA_CAMARA_CARGADA', preference);
281
316
  }
282
317
  }
283
318
  }
284
319
  catch (error) {
285
- this.debugLog('⚠️ Error loading camera preference:', error);
320
+ this.logger.warn('Error al cargar preferencia de cámara:', error);
286
321
  }
287
322
  }
288
323
  saveCameraPreference() {
@@ -293,10 +328,10 @@ const JaakStamps = class {
293
328
  timestamp: Date.now()
294
329
  };
295
330
  localStorage.setItem('jaak-stamps-camera-preference', JSON.stringify(preference));
296
- this.debugLog('💾 Saved camera preference:', preference);
331
+ this.logger.state('PREFERENCIA_CAMARA_GUARDADA', preference);
297
332
  }
298
333
  catch (error) {
299
- this.debugLog('⚠️ Error saving camera preference:', error);
334
+ this.logger.warn('Error al guardar preferencia de cámara:', error);
300
335
  }
301
336
  }
302
337
  async switchCamera(cameraId) {
@@ -306,7 +341,7 @@ const JaakStamps = class {
306
341
  // Check if the selected camera is still available
307
342
  const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);
308
343
  if (!selectedCamera) {
309
- this.debugLog(' Selected camera not found, re-enumerating...');
344
+ this.logger.warn('Cámara seleccionada no encontrada, re-enumerando dispositivos...');
310
345
  await this.enumerateAndDetectCameras();
311
346
  return;
312
347
  }
@@ -330,10 +365,10 @@ const JaakStamps = class {
330
365
  this.saveCameraPreference();
331
366
  // Setup new camera with error handling
332
367
  await this.setupCameraWithRetry();
333
- this.debugLog('🔄 Switched to camera:', selectedCamera.label);
368
+ this.logger.state('CAMARA_CAMBIADA', { label: selectedCamera.label, deviceId: selectedCamera.deviceId });
334
369
  }
335
370
  catch (error) {
336
- this.debugLog('Error switching camera:', error);
371
+ this.logger.error('Error al cambiar de cámara:', error);
337
372
  this.handleCameraPermissionError(error);
338
373
  }
339
374
  }
@@ -348,7 +383,7 @@ const JaakStamps = class {
348
383
  return; // Success
349
384
  }
350
385
  catch (error) {
351
- this.debugLog(`❌ Camera setup attempt ${attempt} failed:`, error);
386
+ this.logger.error(`Intento ${attempt} de configuración de cámara fallido:`, error);
352
387
  if (attempt === maxRetries) {
353
388
  // Last attempt failed, handle the error
354
389
  this.statusMessage = "Error al configurar la cámara";
@@ -368,7 +403,7 @@ const JaakStamps = class {
368
403
  }
369
404
  toggleCameraSelector() {
370
405
  this.showCameraSelector = !this.showCameraSelector;
371
- this.debugLog('📹 Camera selector toggled:', {
406
+ this.logger.state('SELECTOR_CAMARA_TOGGLE', {
372
407
  showCameraSelector: this.showCameraSelector,
373
408
  isMultipleCamerasAvailable: this.isMultipleCamerasAvailable,
374
409
  availableCameras: this.availableCameras.length,
@@ -384,6 +419,13 @@ const JaakStamps = class {
384
419
  await this.switchCamera(nextCamera.deviceId);
385
420
  }
386
421
  async componentDidLoad() {
422
+ this.logger.state('COMPONENTE_INICIALIZANDO', {
423
+ debug: this.debug,
424
+ maskSize: this.maskSize,
425
+ cropMargin: this.cropMargin,
426
+ useDocumentClassification: this.useDocumentClassification,
427
+ preferredCamera: this.preferredCamera
428
+ });
387
429
  if (this.debug) {
388
430
  // Show detailed initialization loading state only in debug mode
389
431
  this.isLoading = true;
@@ -448,7 +490,7 @@ const JaakStamps = class {
448
490
  this.canvasRef.height = rect.height;
449
491
  // Update mask positioning based on container and video dimensions
450
492
  this.updateMaskDimensions(rect);
451
- this.debugLog('📐 Canvas resized:', { width: rect.width, height: rect.height });
493
+ this.logger.debug('Canvas redimensionado:', { width: rect.width, height: rect.height });
452
494
  }
453
495
  }
454
496
  updateMaskDimensions(containerRect) {
@@ -508,7 +550,7 @@ const JaakStamps = class {
508
550
  this.el.style.setProperty('--mask-center-y', `${videoCenterYPercent}%`);
509
551
  // Mark mask as ready now that dimensions are calculated
510
552
  this.isMaskReady = true;
511
- this.debugLog('🎯 Mask dimensions updated:', {
553
+ this.logger.state('DIMENSIONES_MASCARA_ACTUALIZADAS', {
512
554
  video: { width: videoWidth, height: videoHeight },
513
555
  displayed: { width: displayedVideoWidth, height: displayedVideoHeight },
514
556
  mask: { widthPercent: maskWidthPercent, heightPercent: maskHeightPercent },
@@ -530,7 +572,7 @@ const JaakStamps = class {
530
572
  this.captureCtx = this.captureCanvas.getContext('2d', {
531
573
  alpha: false
532
574
  });
533
- this.debugLog('🎨 Canvas pool initialized for performance');
575
+ this.logger.state('CANVAS_POOL_INICIALIZADO', { preprocessCanvasSize: this.INPUT_SIZE });
534
576
  }
535
577
  disconnectedCallback() {
536
578
  this.cleanup();
@@ -578,7 +620,7 @@ const JaakStamps = class {
578
620
  }
579
621
  async preloadModel() {
580
622
  if (this.isModelPreloaded || this.session) {
581
- this.debugLog('🚀 Model already preloaded or session exists');
623
+ this.logger.state('MODELO_YA_PRECARGADO', { sessionExists: !!this.session, modelPreloaded: this.isModelPreloaded });
582
624
  return { success: true, message: 'Model already loaded' };
583
625
  }
584
626
  try {
@@ -586,7 +628,7 @@ const JaakStamps = class {
586
628
  this.statusMessage = "Precargando modelos...";
587
629
  this.statusColor = "#007bff";
588
630
  const modelPath = this.MODEL_PATH;
589
- this.debugLog('🤖 Preloading detection model:', modelPath);
631
+ this.logger.state('PRECARGANDO_MODELO_DETECCION', { modelPath });
590
632
  // Configure ONNX Runtime with device-specific optimizations
591
633
  const sessionOptions = this.getSessionOptions();
592
634
  const deviceInfo = this.getDeviceMemoryInfo();
@@ -595,7 +637,7 @@ const JaakStamps = class {
595
637
  }
596
638
  catch (error) {
597
639
  if (error.message.includes('failed to allocate a buffer')) {
598
- this.debugLog(' Buffer allocation failed during preload, trying with minimal settings');
640
+ this.logger.warn('Fallo en asignación de buffer durante precarga, intentando con configuración mínima');
599
641
  const fallbackOptions = {
600
642
  executionProviders: ['wasm'],
601
643
  graphOptimizationLevel: 'disabled',
@@ -616,7 +658,7 @@ const JaakStamps = class {
616
658
  // For low memory devices, load sequentially to avoid memory pressure
617
659
  if (this.useDocumentClassification) {
618
660
  if (deviceInfo.isLowMemory) {
619
- this.debugLog('🔄 Sequential model loading for low memory device');
661
+ this.logger.state('CARGA_SECUENCIAL_MODELOS', { reason: 'low memory device' });
620
662
  await new Promise(resolve => setTimeout(resolve, 1000));
621
663
  }
622
664
  await this.loadMobileNetModel();
@@ -626,11 +668,15 @@ const JaakStamps = class {
626
668
  this.statusMessage = "Modelos precargados. Listo para comenzar detección";
627
669
  this.statusColor = "#28a745";
628
670
  this.emitReadyEvent();
629
- this.debugLog(' Models preloaded successfully');
671
+ this.logger.state('MODELOS_PRECARGADOS_EXITOSAMENTE', {
672
+ detectionModel: !!this.session,
673
+ classificationModel: !!this.mobileNetSession,
674
+ useClassification: this.useDocumentClassification
675
+ });
630
676
  return { success: true, message: 'Models preloaded successfully' };
631
677
  }
632
678
  catch (error) {
633
- this.debugLog('Error preloading models:', error);
679
+ this.logger.error('Error al precargar modelos:', error);
634
680
  this.isLoading = false;
635
681
  this.statusMessage = "Error al precargar los modelos";
636
682
  this.statusColor = "#ff6b6b";
@@ -658,7 +704,7 @@ const JaakStamps = class {
658
704
  }
659
705
  async setPreferredCamera(camera) {
660
706
  this.preferredCamera = camera;
661
- this.debugLog('🎯 Camera preference changed to:', camera);
707
+ this.logger.state('PREFERENCIA_CAMARA_CAMBIADA', { newPreference: camera });
662
708
  // Re-detect and apply new camera preference
663
709
  await this.enumerateAndDetectCameras();
664
710
  // If video is active, switch to the new preferred camera
@@ -673,14 +719,14 @@ const JaakStamps = class {
673
719
  }
674
720
  async loadMobileNetModel() {
675
721
  try {
676
- this.debugLog('🤖 Loading MobileNet model...');
722
+ this.logger.state('CARGANDO_MODELO_MOBILENET', { path: this.MOBILENET_MODEL_PATH });
677
723
  // Load class map
678
724
  const classResponse = await fetch(this.MOBILENET_CLASSES_PATH);
679
725
  if (!classResponse.ok) {
680
726
  throw new Error(`Failed to load class map: ${this.MOBILENET_CLASSES_PATH}`);
681
727
  }
682
728
  this.mobileNetClassMap = await classResponse.json();
683
- this.debugLog('📋 MobileNet classes loaded:', this.mobileNetClassMap);
729
+ this.logger.state('CLASES_MOBILENET_CARGADAS', { classCount: Object.keys(this.mobileNetClassMap).length });
684
730
  // Load model
685
731
  const sessionOptions = this.getSessionOptions();
686
732
  try {
@@ -688,7 +734,7 @@ const JaakStamps = class {
688
734
  }
689
735
  catch (error) {
690
736
  if (error.message.includes('failed to allocate a buffer')) {
691
- this.debugLog(' MobileNet buffer allocation failed, trying with minimal settings');
737
+ this.logger.warn('Fallo en asignación de buffer de MobileNet, intentando con configuración mínima');
692
738
  const fallbackOptions = {
693
739
  executionProviders: ['wasm'],
694
740
  graphOptimizationLevel: 'disabled',
@@ -705,10 +751,10 @@ const JaakStamps = class {
705
751
  throw error;
706
752
  }
707
753
  }
708
- this.debugLog(' MobileNet model loaded successfully');
754
+ this.logger.state('MODELO_MOBILENET_CARGADO_EXITOSAMENTE', { sessionCreated: !!this.mobileNetSession });
709
755
  }
710
756
  catch (error) {
711
- this.debugLog('Error loading MobileNet model:', error);
757
+ this.logger.error('Error al cargar modelo MobileNet:', error);
712
758
  throw error;
713
759
  }
714
760
  }
@@ -735,11 +781,11 @@ const JaakStamps = class {
735
781
  }
736
782
  async classifyDocument(canvas) {
737
783
  if (!this.mobileNetSession || !this.mobileNetClassMap) {
738
- this.debugLog('⚠️ MobileNet model not loaded');
784
+ this.logger.warn('Modelo MobileNet no está cargado, saltando clasificación');
739
785
  return null;
740
786
  }
741
787
  try {
742
- this.debugLog('🔍 Classifying document...');
788
+ this.logger.state('CLASIFICANDO_DOCUMENTO', { timestamp: Date.now() });
743
789
  // Preprocess image for MobileNet
744
790
  const inputTensor = this.preprocessMobileNet(canvas);
745
791
  // Run inference
@@ -750,10 +796,11 @@ const JaakStamps = class {
750
796
  const maxIdx = output.reduce((bestIdx, val, idx, arr) => val > arr[bestIdx] ? idx : bestIdx, 0);
751
797
  const confidence = output[maxIdx];
752
798
  const className = this.mobileNetClassMap[maxIdx.toString()] || "unknown";
753
- this.debugLog('📄 Document classification result:', {
799
+ this.logger.state('DOCUMENTO_CLASIFICADO', {
754
800
  class: className,
755
801
  confidence: confidence.toFixed(3),
756
- classIndex: maxIdx
802
+ classIndex: maxIdx,
803
+ timestamp: Date.now()
757
804
  });
758
805
  return {
759
806
  class: className,
@@ -762,7 +809,7 @@ const JaakStamps = class {
762
809
  };
763
810
  }
764
811
  catch (error) {
765
- this.debugLog('Error classifying document:', error);
812
+ this.logger.error('Error al clasificar documento:', error);
766
813
  return null;
767
814
  }
768
815
  }
@@ -804,7 +851,7 @@ const JaakStamps = class {
804
851
  }
805
852
  this.mobileNetClassMap = undefined;
806
853
  this.isModelPreloaded = false;
807
- this.debugLog('🧹 Canvas pool cleaned up');
854
+ this.logger.state('CANVAS_POOL_LIMPIADO', { timestamp: Date.now() });
808
855
  }
809
856
  async getMaxResolution() {
810
857
  try {
@@ -853,18 +900,19 @@ const JaakStamps = class {
853
900
  constraints.width = { ideal: maxWidth };
854
901
  constraints.height = { ideal: maxHeight };
855
902
  }
856
- this.debugLog('📐 Resolution capabilities:', {
903
+ this.logger.state('CAPACIDADES_RESOLUCION_DETECTADAS', {
857
904
  maxWidth: capabilities.width.max,
858
905
  maxHeight: capabilities.height.max,
859
906
  selectedWidth: constraints.width.ideal,
860
907
  selectedHeight: constraints.height.ideal,
861
- isTablet
908
+ isTablet,
909
+ deviceType: this.deviceType
862
910
  });
863
911
  }
864
912
  return constraints;
865
913
  }
866
914
  catch (err) {
867
- this.debugLog('⚠️ Could not get capabilities, using fallback');
915
+ this.logger.warn('No se pudieron obtener capacidades de cámara, usando configuración de respaldo');
868
916
  // Optimized fallback for tablets
869
917
  const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
870
918
  const fallbackConstraints = {
@@ -897,8 +945,11 @@ const JaakStamps = class {
897
945
  // Determine if video should be mirrored
898
946
  const isRear = this.isRearCamera(stream);
899
947
  this.shouldMirrorVideo = !isRear;
900
- this.debugLog('📹 Rear camera:', isRear);
901
- this.debugLog('🪞 Should mirror video:', this.shouldMirrorVideo);
948
+ this.logger.state('CAMARA_CONFIGURADA', {
949
+ isRearCamera: isRear,
950
+ shouldMirrorVideo: this.shouldMirrorVideo,
951
+ videoActive: this.isVideoActive
952
+ });
902
953
  return new Promise((resolve) => {
903
954
  this.videoRef.onloadedmetadata = async () => {
904
955
  await this.videoRef.play();
@@ -915,7 +966,7 @@ const JaakStamps = class {
915
966
  }
916
967
  }
917
968
  catch (err) {
918
- this.debugLog("❌ No se pudo acceder a la cámara:", err);
969
+ this.logger.error('No se pudo acceder a la cámara:', err);
919
970
  this.handleCameraPermissionError(err);
920
971
  }
921
972
  }
@@ -981,6 +1032,12 @@ const JaakStamps = class {
981
1032
  };
982
1033
  }
983
1034
  async startDetection() {
1035
+ this.logger.state('INICIANDO_DETECCION', {
1036
+ sessionExists: !!this.session,
1037
+ modelPreloaded: this.isModelPreloaded,
1038
+ videoActive: this.isVideoActive,
1039
+ captureStep: this.captureStep
1040
+ });
984
1041
  try {
985
1042
  // Check if model is already preloaded
986
1043
  if (!this.session) {
@@ -994,14 +1051,14 @@ const JaakStamps = class {
994
1051
  this.statusColor = "#007bff";
995
1052
  }
996
1053
  const modelPath = this.MODEL_PATH;
997
- this.debugLog('🤖 Loading detection model:', modelPath);
1054
+ this.logger.state('CARGANDO_MODELO_DETECCION', { modelPath });
998
1055
  const sessionOptions = this.getSessionOptions();
999
1056
  try {
1000
1057
  this.session = await window.ort.InferenceSession.create(modelPath, sessionOptions);
1001
1058
  }
1002
1059
  catch (error) {
1003
1060
  if (error.message.includes('failed to allocate a buffer')) {
1004
- this.debugLog(' Buffer allocation failed, trying with minimal settings');
1061
+ this.logger.warn('Fallo en asignación de buffer, intentando con configuración mínima');
1005
1062
  const fallbackOptions = {
1006
1063
  executionProviders: ['wasm'],
1007
1064
  graphOptimizationLevel: 'disabled',
@@ -1032,7 +1089,7 @@ const JaakStamps = class {
1032
1089
  this.emitReadyEvent();
1033
1090
  }
1034
1091
  else {
1035
- this.debugLog('🚀 Using preloaded models');
1092
+ this.logger.state('USANDO_MODELOS_PRECARGADOS', { sessionExists: !!this.session, modelPreloaded: this.isModelPreloaded });
1036
1093
  if (this.debug) {
1037
1094
  this.statusMessage = "Usando modelos precargados...";
1038
1095
  this.statusColor = "#007bff";
@@ -1051,7 +1108,7 @@ const JaakStamps = class {
1051
1108
  this.detectFrame();
1052
1109
  }
1053
1110
  catch (err) {
1054
- this.debugLog("Error al inicializar:", err);
1111
+ this.logger.error('Error al inicializar detección:', err);
1055
1112
  this.statusMessage = "Error al inicializar el detector";
1056
1113
  this.statusColor = "#ff6b6b";
1057
1114
  this.isLoading = false;
@@ -1133,7 +1190,7 @@ const JaakStamps = class {
1133
1190
  }
1134
1191
  }
1135
1192
  catch (e) {
1136
- this.debugLog("Error en inferencia:", e);
1193
+ this.logger.error('Error en inferencia de modelo:', e);
1137
1194
  // Solo continuar si no hemos completado el proceso
1138
1195
  if (this.captureStep !== 'completed') {
1139
1196
  // On error, wait longer before retrying
@@ -1261,7 +1318,7 @@ const JaakStamps = class {
1261
1318
  if (!this.hasScreenshotTaken) {
1262
1319
  this.lastDetectedBox = bestBox;
1263
1320
  this.takeScreenshot().catch(error => {
1264
- this.debugLog('Error taking screenshot:', error);
1321
+ this.logger.error('Error al tomar captura de pantalla:', error);
1265
1322
  });
1266
1323
  this.hasScreenshotTaken = true;
1267
1324
  // Reset para permitir segunda captura
@@ -1279,6 +1336,7 @@ const JaakStamps = class {
1279
1336
  if (boxes.length === 0) {
1280
1337
  this.statusMessage = "Posicione la identificación dentro del marco";
1281
1338
  this.statusColor = "#ff6b6b";
1339
+ this.logger.debug('Sin detección de documento en el frame');
1282
1340
  }
1283
1341
  else {
1284
1342
  const bestBox = boxes.reduce((best, current) => current.score > best.score ? current : best);
@@ -1288,6 +1346,11 @@ const JaakStamps = class {
1288
1346
  if (allSidesAligned) {
1289
1347
  this.statusMessage = "Identificación perfectamente alineada. Mantenga inmóvil";
1290
1348
  this.statusColor = "#00ff00";
1349
+ this.logger.state('DOCUMENTO_PERFECTAMENTE_ALINEADO', {
1350
+ score: bestBox.score,
1351
+ boxDimensions: { width: bestBox.w, height: bestBox.h },
1352
+ alignedSides: 4
1353
+ });
1291
1354
  }
1292
1355
  else if (alignedSides > 0) {
1293
1356
  this.statusMessage = `Alinee los lados restantes (${alignedSides}/4 lados correctos)`;
@@ -1348,6 +1411,14 @@ const JaakStamps = class {
1348
1411
  async takeScreenshot() {
1349
1412
  if (!this.videoRef || !this.lastDetectedBox)
1350
1413
  return;
1414
+ this.logger.state('INICIANDO_CAPTURA', {
1415
+ captureStep: this.captureStep,
1416
+ detectedBox: this.lastDetectedBox,
1417
+ videoResolution: {
1418
+ width: this.videoRef.videoWidth,
1419
+ height: this.videoRef.videoHeight
1420
+ }
1421
+ });
1351
1422
  // Activar animación
1352
1423
  this.triggerCaptureAnimation();
1353
1424
  // OPTIMIZATION: Reuse capture canvas for full frame
@@ -1387,7 +1458,7 @@ const JaakStamps = class {
1387
1458
  await this.loadMobileNetModel();
1388
1459
  }
1389
1460
  catch (error) {
1390
- this.debugLog('⚠️ Failed to load classification model, continuing without classification:', error);
1461
+ this.logger.warn('Fallo al cargar modelo de clasificación, continuando sin clasificación:', error);
1391
1462
  }
1392
1463
  }
1393
1464
  // Classify the cropped document if model is available
@@ -1395,7 +1466,7 @@ const JaakStamps = class {
1395
1466
  const classification = await this.classifyDocument(croppedCanvas);
1396
1467
  if (classification && classification.class === 'passport') {
1397
1468
  // If it's a passport, skip back capture since passports don't have a back side
1398
- this.debugLog('📄 Passport detected - skipping back capture');
1469
+ this.logger.state('PASAPORTE_DETECTADO_SALTANDO_REVERSO', { classification: classification?.class });
1399
1470
  this.completeProcess(true);
1400
1471
  return;
1401
1472
  }
@@ -1416,14 +1487,23 @@ const JaakStamps = class {
1416
1487
  this.isDetectionPaused = false;
1417
1488
  }, 3000);
1418
1489
  }, 800);
1419
- this.debugLog('📸 FRENTE capturado. Esperando trasera...');
1490
+ this.logger.state('CAPTURA_FRENTE_COMPLETADA', {
1491
+ captureStep: this.captureStep,
1492
+ hasFullFrame: !!this.capturedFullFrame,
1493
+ hasCroppedId: !!this.capturedCroppedId
1494
+ });
1420
1495
  }
1421
1496
  else if (this.captureStep === 'back') {
1422
1497
  // Captura de la trasera usando canvas reutilizado
1423
1498
  this.capturedBackFullFrame = this.captureCanvas.toDataURL('image/png');
1424
1499
  this.capturedBackCroppedId = croppedCanvas.toDataURL('image/png');
1425
1500
  this.completeProcess(false);
1426
- this.debugLog('📸 TRASERA capturada. Proceso completado. Detector detenido. Imágenes emitidas.');
1501
+ this.logger.state('CAPTURA_TRASERA_COMPLETADA', {
1502
+ captureStep: this.captureStep,
1503
+ hasBackFullFrame: !!this.capturedBackFullFrame,
1504
+ hasBackCroppedId: !!this.capturedBackCroppedId,
1505
+ processCompleted: true
1506
+ });
1427
1507
  }
1428
1508
  }
1429
1509
  triggerCaptureAnimation() {
@@ -1447,7 +1527,7 @@ const JaakStamps = class {
1447
1527
  const ctx = this.canvasRef.getContext("2d");
1448
1528
  ctx.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height);
1449
1529
  }
1450
- this.debugLog('🛑 Detector de identificación detenido');
1530
+ this.logger.state('DETECTOR_DETENIDO', { timestamp: Date.now() });
1451
1531
  }
1452
1532
  resetDetection() {
1453
1533
  this.bestScore = 0;
@@ -1486,6 +1566,13 @@ const JaakStamps = class {
1486
1566
  "Proceso completado (solo frente capturado)" :
1487
1567
  "Proceso de captura completado exitosamente";
1488
1568
  this.statusColor = "#28a745";
1569
+ this.logger.state('PROCESO_COMPLETADO', {
1570
+ skippedBack,
1571
+ hasFrontImages: !!(this.capturedFullFrame && this.capturedCroppedId),
1572
+ hasBackImages: !!(this.capturedBackFullFrame && this.capturedBackCroppedId),
1573
+ totalImages: skippedBack ? 2 : 4,
1574
+ timestamp: new Date().toISOString()
1575
+ });
1489
1576
  // Detener el detector
1490
1577
  this.stopDetection();
1491
1578
  // Emitir evento con las imágenes capturadas
@@ -1537,7 +1624,7 @@ const JaakStamps = class {
1537
1624
  this.cleanup();
1538
1625
  }
1539
1626
  render() {
1540
- return (index.h("div", { key: 'bacf8b2ded1c5015d01dd4240dda6a93fbfb629e', class: "detector-container" }, index.h("div", { key: '235aefbd8916d6b53ab191c654ed00f303ca73b7', class: "video-container" }, index.h("video", { key: '9b641c7092c5ecfa601b1f80d6bf8c310539c436', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: this.isVideoActive ? 'block' : 'none' } }), index.h("canvas", { key: '2c3c384436b7dd91084dbdd27a34ac9192fa3f22', ref: el => this.canvasRef = el, class: this.shouldMirrorVideo ? 'mirror' : '' }), this.isMaskReady && (index.h("div", { key: '9e2ca398482ebae4da984854483af3159d908d2b', class: "overlay-mask" }, index.h("div", { key: 'ee14fd65d752351644a281fc4cbd7d8cf8765eec', class: "card-outline" }, index.h("div", { key: 'c5a924145ac54e7f545b0b6571f1d7d259fc1145', class: "side side-top" }), index.h("div", { key: 'a88e9ab5f1a4f7041edb44008697b619bf52a85b', class: "side side-right" }), index.h("div", { key: '08fbf94000951ea31d029469d8158b8bd3349bf8', class: "side side-bottom" }), index.h("div", { key: '3b9ead8e1d2c0f7fea2878a248241681c07bd0f6', class: "side side-left" }), index.h("div", { key: 'f96b9a5a0f00f016b11486a63ca984b368d18c8b', class: "corner corner-tl" }), index.h("div", { key: '30210f2ec60913466665dea50a1e8bc6cba34b41', class: "corner corner-tr" }), index.h("div", { key: 'cc4644c99957a9ddf68901ddd368037e01c56899', class: "corner corner-bl" }), index.h("div", { key: '95b08e9b36f880dda1d614f929ceb71259f5ec38', class: "corner corner-br" }), !this.showFlipAnimation && !this.showSuccessAnimation && (index.h("div", { key: '9bb545a793764b258381b0e3d665472c67d65b5b', class: "guide-text" }, this.statusMessage))), this.captureStep === 'back' && !this.showFlipAnimation && !this.showSuccessAnimation && (index.h("button", { key: '3b78e394067ea610021fd915215fca30ed0d654b', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, "Saltar reverso")), this.isVideoActive && (index.h("div", { key: '85fbcf97d151fbf1f37a2f6f90bbd93ef77afdba', class: "camera-controls" }, this.isMultipleCamerasAvailable && (index.h("button", { key: '56bab78e6ea660e42057b5e6bc322f6990116119', class: "flip-camera-button", onClick: () => this.flipCamera(), type: "button", title: "Cambiar c\u00E1mara" }, "Girar c\u00E1mara")), index.h("button", { key: 'c149303ed671d326efada743393e52b5a3710710', class: "camera-selector-button", onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara" }, "C\u00E1maras"), this.debug && (index.h("div", { key: 'ce7c0c1dbc2ba267dbd0181dd7268e2d9f7209c0', style: {
1627
+ return (index.h("div", { key: '87e2e2065012b5792b52b18349490492b5bdc80c', class: "detector-container" }, index.h("div", { key: '04b8bba38297d39c84ddc01f5a9bf72c65b9bef4', class: "video-container" }, index.h("video", { key: '905b99e1670fccb2ff5555818f91d0b0241fa29d', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: this.isVideoActive ? 'block' : 'none' } }), index.h("canvas", { key: '3f219b649b8d4f961b15256b8586451c2a737eb8', ref: el => this.canvasRef = el, class: this.shouldMirrorVideo ? 'mirror' : '' }), this.isMaskReady && (index.h("div", { key: 'f8d6742689d4446897ca48c2407cb3d3fab643e9', class: "overlay-mask" }, index.h("div", { key: '630a06c530f168cc0b71286adbf48fd922eb710d', class: "card-outline" }, index.h("div", { key: 'ce8c043ad41d5574cd0c4d737dec08b9f7fee927', class: "side side-top" }), index.h("div", { key: 'e90ead592f84e5df98143bc88714e70f32684204', class: "side side-right" }), index.h("div", { key: '31d6778c5036e42a3addb26c732149abc5a18ef7', class: "side side-bottom" }), index.h("div", { key: 'f85c31e77f024f9070c706b06b91d62a7fdbc357', class: "side side-left" }), index.h("div", { key: '27b362711bc53019483c0d36742d97e10ae9b9ce', class: "corner corner-tl" }), index.h("div", { key: '03b55893610684d690987c2eacf3c4e5d0c3bbe5', class: "corner corner-tr" }), index.h("div", { key: 'b6bf31327925c45a7fa8e1fd5ab2f25b824a03a2', class: "corner corner-bl" }), index.h("div", { key: '0737e0c82f8ad9703ce2546f3e594db6d50cafde', class: "corner corner-br" }), !this.showFlipAnimation && !this.showSuccessAnimation && (index.h("div", { key: '145f8383c496e151bd6ef4f5841292adeba083c4', class: "guide-text" }, this.statusMessage))), this.captureStep === 'back' && !this.showFlipAnimation && !this.showSuccessAnimation && (index.h("button", { key: 'fcdedebc31eddd21b978dd63e93c348d324b47da', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, "Saltar reverso")), this.isVideoActive && (index.h("div", { key: 'dc9157bf7bed3b6439db742055201e2c4eddec84', class: "camera-controls" }, this.isMultipleCamerasAvailable && (index.h("button", { key: '6b052562ccb203dbf3cc8b627f31ed2435559f80', class: "flip-camera-button", onClick: () => this.flipCamera(), type: "button", title: "Cambiar c\u00E1mara" }, "Girar c\u00E1mara")), index.h("button", { key: 'f9fad1757d01f0cb45f65bb2088bd1f0bc0e4e8d', class: "camera-selector-button", onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara" }, "C\u00E1maras"), this.debug && (index.h("div", { key: '5362fb33508c7f903b09f65fdb449b608804260d', style: {
1541
1628
  position: 'absolute',
1542
1629
  top: '50px',
1543
1630
  right: '0',
@@ -1547,10 +1634,10 @@ const JaakStamps = class {
1547
1634
  fontSize: '10px',
1548
1635
  borderRadius: '4px',
1549
1636
  whiteSpace: 'nowrap'
1550
- } }, "C\u00E1maras: ", this.availableCameras.length, index.h("br", { key: 'd62a6ddde7160f41075b3b1d00340515019a48ca' }), "M\u00FAltiples: ", this.isMultipleCamerasAvailable ? 'Sí' : 'No', index.h("br", { key: 'fe8c322421809a58f49150ef877ae0502d34d002' }), "Selector: ", this.showCameraSelector ? 'Visible' : 'Oculto', index.h("br", { key: '084d8c5c6c0193edcaf3bd7d64d0bce8e0bb6964' }), "Video: ", this.isVideoActive ? 'Activo' : 'Inactivo')))), this.showCameraSelector && this.availableCameras.length > 0 && (index.h("div", { key: '03b1a66188e65f1843c50aec096db840d60bb703', class: "camera-selector-dropdown" }, index.h("div", { key: 'fe7e08d25dd394d893a7f6269c80a2aaa4732134', class: "camera-selector-header" }, index.h("span", { key: '1b8f4892c3eef6dac8c682e152abb35e2331496c' }, "Seleccionar C\u00E1mara"), index.h("button", { key: '396c78a3753c707619e27ef7e5f3a412ba8480c6', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: '6d56d67efe54719f21978cc6cc44cf2f421469bb', class: "camera-list" }, this.availableCameras.map((camera) => (index.h("button", { key: camera.deviceId, class: `camera-option ${this.selectedCameraId === camera.deviceId ? 'selected' : ''}`, onClick: () => {
1637
+ } }, "C\u00E1maras: ", this.availableCameras.length, index.h("br", { key: '5ead451a54a59c885f62740d0a0ce43ba8b674e4' }), "M\u00FAltiples: ", this.isMultipleCamerasAvailable ? 'Sí' : 'No', index.h("br", { key: '9a92514d07576ed7052eaa3a418c00013ef7c479' }), "Selector: ", this.showCameraSelector ? 'Visible' : 'Oculto', index.h("br", { key: 'd06abebad3b2e3c19aa22e511e0c4cfd4425b8b2' }), "Video: ", this.isVideoActive ? 'Activo' : 'Inactivo')))), this.showCameraSelector && this.availableCameras.length > 0 && (index.h("div", { key: '93a09ce3bb108175b55944a71c21e86e58de55be', class: "camera-selector-dropdown" }, index.h("div", { key: '7ceb4c8826945ab2a9da106a88a44cbb5490e92a', class: "camera-selector-header" }, index.h("span", { key: 'fa98621700584d8021ff57eca6f804b6eb792c5b' }, "Seleccionar C\u00E1mara"), index.h("button", { key: 'ce6477a1494f58fb5974dd7c9e62bd7d041572c5', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: 'eff226b006c95fafce2566947155e13ee68f9a02', class: "camera-list" }, this.availableCameras.map((camera) => (index.h("button", { key: camera.deviceId, class: `camera-option ${this.selectedCameraId === camera.deviceId ? 'selected' : ''}`, onClick: () => {
1551
1638
  this.switchCamera(camera.deviceId);
1552
1639
  this.toggleCameraSelector();
1553
- }, type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${this.availableCameras.indexOf(camera) + 1}`), this.selectedCameraId === camera.deviceId && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: '7f5dda6214695d50c08d2ba4ccec236f3bd158cf', class: "device-info" }, index.h("small", { key: '50d2768f82b9b7f0a9c1729a5167493f731fb4f6' }, "Dispositivo: ", this.deviceType)))))), this.isCapturing && (index.h("div", { key: '7ed246b862639b2c6df54dbd32ebb883776bfbd7', class: "capture-animation" })), this.showFlipAnimation && (index.h("div", { key: 'f2d3a97dfcb9fd757b2f39ec184bce3f5dcb3595', class: "flip-animation" }, index.h("div", { key: '89a98223c60317d2b9449029ba393c75bec154b3', class: "id-card-icon" }), index.h("div", { key: '507f6f1b7f38082772bbac2fc28046618161c69b', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), this.showSuccessAnimation && (index.h("div", { key: '9010d5be6684759b5d99c58ee65b0a7ebe82b9cb', class: "success-animation" }, index.h("div", { key: '454c39a5c0d457418b8abdc6e63b1c8a249a118f', class: "check-icon" }), index.h("div", { key: '502e55f1496f7d8cde9178c416bd9db4cf0491eb', class: "success-text" }, "\u00A1Proceso completado!"))), this.isLoading && (index.h("div", { key: 'e4d6b7c5ebc8da42689150195dd837e77e152ea3', class: "loading-overlay" }, index.h("div", { key: '7cc39dbbe94e0730c47829291b83717476517f7f', class: "loading-spinner" }), index.h("div", { key: '4c1afe04fc7a627c9a57c2f7b851ce62f9305676', class: "loading-text" }, this.statusMessage))), this.debug && (index.h("div", { key: '8745c4603b35003bff859657c86c713b7f58e204', class: "status-bar" }, index.h("div", { key: 'e098f2402d58d1808052e3d213f851e66db69ee4', class: "status-message", style: { 'color': this.statusColor } }, this.statusMessage))), index.h("div", { key: 'af4e07b405af5b1786009b0e9ecf72551753de28', class: "watermark" }, index.h("img", { key: '25f4b80f13528617007aad160c855c4fff0c8af5', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
1640
+ }, type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${this.availableCameras.indexOf(camera) + 1}`), this.selectedCameraId === camera.deviceId && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: '617d33caa5d3b3e6dca56d479b27c0ac7ed19c2c', class: "device-info" }, index.h("small", { key: 'fba56ac1238c33864f8dba1d8bd9a5661e11d770' }, "Dispositivo: ", this.deviceType)))))), this.isCapturing && (index.h("div", { key: 'b46982a0b949ee9d50d0a819c59b24dceec9d7b2', class: "capture-animation" })), this.showFlipAnimation && (index.h("div", { key: 'c82773fffca8ec53949197a105e1e11bf0f9b294', class: "flip-animation" }, index.h("div", { key: 'ad52c9ec91132bf5ed94e36f621489a66b0bf680', class: "id-card-icon" }), index.h("div", { key: '2be407ee84beddc5ce968a9a94d9014f416cde54', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), this.showSuccessAnimation && (index.h("div", { key: 'f20b6be925178a123de99e535e893b7c09391dee', class: "success-animation" }, index.h("div", { key: 'd3ae65d40943f73bc92e9529764ded454adc5dbf', class: "check-icon" }), index.h("div", { key: 'f2b03082a106c69f09d384ba3b1cb4a636d3a189', class: "success-text" }, "\u00A1Proceso completado!"))), this.isLoading && (index.h("div", { key: '65736e303d9eb1c3f6f2dccb5cc0d27c571ac9c8', class: "loading-overlay" }, index.h("div", { key: '5863d855d1caea9202a9f3d0a29919985ddbb926', class: "loading-spinner" }), index.h("div", { key: 'aa5a16c96ac6bfa43846ba06346fad6dce39dfbb', class: "loading-text" }, this.statusMessage))), this.debug && (index.h("div", { key: '308c664b07d2c2427c38f766fc9eb78d27843f67', class: "status-bar" }, index.h("div", { key: '3884b311710d7f373a508eb492771e503e68f648', class: "status-message", style: { 'color': this.statusColor } }, this.statusMessage))), index.h("div", { key: '3125cfecda03d3ab9e80e0d9d5674f1cc167e2f8', class: "watermark" }, index.h("img", { key: '5ef43a18aea74f570db0d61f4fd86c3becf1d23e', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
1554
1641
  }
1555
1642
  };
1556
1643
  JaakStamps.style = myComponentCss;