@jaak.ai/stamps 2.0.0-beta.2 → 2.0.0-beta.4

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 (84) hide show
  1. package/README.md +216 -212
  2. package/dist/cjs/{index-DGM9-FNg.js → index-BfhtOB0D.js} +5 -2
  3. package/dist/cjs/index-BfhtOB0D.js.map +1 -0
  4. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +2 -2
  5. package/dist/cjs/jaak-stamps.cjs.entry.js +1860 -783
  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 +629 -72
  10. package/dist/collection/components/my-component/my-component.js +896 -781
  11. package/dist/collection/components/my-component/my-component.js.map +1 -1
  12. package/dist/collection/services/CameraService.js +453 -0
  13. package/dist/collection/services/CameraService.js.map +1 -0
  14. package/dist/collection/services/DetectionService.js +369 -0
  15. package/dist/collection/services/DetectionService.js.map +1 -0
  16. package/dist/collection/services/EventBusService.js +42 -0
  17. package/dist/collection/services/EventBusService.js.map +1 -0
  18. package/dist/collection/services/LoggerService.js +40 -0
  19. package/dist/collection/services/LoggerService.js.map +1 -0
  20. package/dist/collection/services/ServiceContainer.js +66 -0
  21. package/dist/collection/services/ServiceContainer.js.map +1 -0
  22. package/dist/collection/services/StateManagerService.js +109 -0
  23. package/dist/collection/services/StateManagerService.js.map +1 -0
  24. package/dist/collection/services/factories/DeviceStrategyFactory.js +16 -0
  25. package/dist/collection/services/factories/DeviceStrategyFactory.js.map +1 -0
  26. package/dist/collection/services/interfaces/ICameraService.js +2 -0
  27. package/dist/collection/services/interfaces/ICameraService.js.map +1 -0
  28. package/dist/collection/services/interfaces/IDetectionService.js +2 -0
  29. package/dist/collection/services/interfaces/IDetectionService.js.map +1 -0
  30. package/dist/collection/services/interfaces/IEventBus.js +2 -0
  31. package/dist/collection/services/interfaces/IEventBus.js.map +1 -0
  32. package/dist/collection/services/interfaces/ILogger.js +2 -0
  33. package/dist/collection/services/interfaces/ILogger.js.map +1 -0
  34. package/dist/collection/services/interfaces/IStateManager.js +2 -0
  35. package/dist/collection/services/interfaces/IStateManager.js.map +1 -0
  36. package/dist/collection/services/strategies/HighPerformanceDeviceStrategy.js +30 -0
  37. package/dist/collection/services/strategies/HighPerformanceDeviceStrategy.js.map +1 -0
  38. package/dist/collection/services/strategies/IDeviceStrategy.js +2 -0
  39. package/dist/collection/services/strategies/IDeviceStrategy.js.map +1 -0
  40. package/dist/collection/services/strategies/LowMemoryDeviceStrategy.js +30 -0
  41. package/dist/collection/services/strategies/LowMemoryDeviceStrategy.js.map +1 -0
  42. package/dist/collection/types/component-types.js +2 -0
  43. package/dist/collection/types/component-types.js.map +1 -0
  44. package/dist/components/index.js +3 -0
  45. package/dist/components/index.js.map +1 -1
  46. package/dist/components/jaak-stamps.js +1873 -797
  47. package/dist/components/jaak-stamps.js.map +1 -1
  48. package/dist/esm/{index-DqoVMnc7.js → index-BP1Q4KOg.js} +5 -2
  49. package/dist/esm/index-BP1Q4KOg.js.map +1 -0
  50. package/dist/esm/jaak-stamps-webcomponent.js +3 -3
  51. package/dist/esm/jaak-stamps.entry.js +1860 -783
  52. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  53. package/dist/esm/loader.js +3 -3
  54. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  55. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  56. package/dist/jaak-stamps-webcomponent/p-10ee2dab.entry.js +2 -0
  57. package/dist/jaak-stamps-webcomponent/p-10ee2dab.entry.js.map +1 -0
  58. package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js +3 -0
  59. package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js.map +1 -0
  60. package/dist/types/components/my-component/my-component.d.ts +90 -79
  61. package/dist/types/components.d.ts +32 -8
  62. package/dist/types/services/CameraService.d.ts +46 -0
  63. package/dist/types/services/DetectionService.d.ts +35 -0
  64. package/dist/types/services/EventBusService.d.ts +9 -0
  65. package/dist/types/services/LoggerService.d.ts +12 -0
  66. package/dist/types/services/ServiceContainer.d.ts +27 -0
  67. package/dist/types/services/StateManagerService.d.ts +15 -0
  68. package/dist/types/services/factories/DeviceStrategyFactory.d.ts +4 -0
  69. package/dist/types/services/interfaces/ICameraService.d.ts +34 -0
  70. package/dist/types/services/interfaces/IDetectionService.d.ts +31 -0
  71. package/dist/types/services/interfaces/IEventBus.d.ts +16 -0
  72. package/dist/types/services/interfaces/ILogger.d.ts +8 -0
  73. package/dist/types/services/interfaces/IStateManager.d.ts +35 -0
  74. package/dist/types/services/strategies/HighPerformanceDeviceStrategy.d.ts +6 -0
  75. package/dist/types/services/strategies/IDeviceStrategy.d.ts +21 -0
  76. package/dist/types/services/strategies/LowMemoryDeviceStrategy.d.ts +6 -0
  77. package/dist/types/types/component-types.d.ts +36 -0
  78. package/package.json +3 -3
  79. package/dist/cjs/index-DGM9-FNg.js.map +0 -1
  80. package/dist/esm/index-DqoVMnc7.js.map +0 -1
  81. package/dist/jaak-stamps-webcomponent/p-DqoVMnc7.js +0 -3
  82. package/dist/jaak-stamps-webcomponent/p-DqoVMnc7.js.map +0 -1
  83. package/dist/jaak-stamps-webcomponent/p-f1172b06.entry.js +0 -2
  84. package/dist/jaak-stamps-webcomponent/p-f1172b06.entry.js.map +0 -1
@@ -0,0 +1,453 @@
1
+ export class CameraService {
2
+ logger;
3
+ eventBus;
4
+ availableCameras = [];
5
+ selectedCameraId = null;
6
+ deviceType = 'desktop';
7
+ preferredCameraFacing = null;
8
+ preferredCamera = 'auto';
9
+ constructor(logger, eventBus, preferredCamera = 'auto') {
10
+ this.logger = logger;
11
+ this.eventBus = eventBus;
12
+ this.preferredCamera = preferredCamera;
13
+ }
14
+ async detectDeviceType() {
15
+ const userAgent = navigator.userAgent;
16
+ const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
17
+ const isTablet = /iPad|Android/i.test(userAgent) && window.innerWidth >= 768;
18
+ if (isTablet) {
19
+ this.deviceType = 'tablet';
20
+ }
21
+ else if (isMobile) {
22
+ this.deviceType = 'mobile';
23
+ }
24
+ else {
25
+ this.deviceType = 'desktop';
26
+ }
27
+ this.logger.state('DISPOSITIVO_DETECTADO', {
28
+ deviceType: this.deviceType,
29
+ userAgent: navigator.userAgent,
30
+ screenDimensions: { width: window.innerWidth, height: window.innerHeight }
31
+ });
32
+ return this.deviceType;
33
+ }
34
+ async enumerateDevices() {
35
+ try {
36
+ const permissionStatus = await this.checkCameraPermission();
37
+ if (permissionStatus === 'denied') {
38
+ this.logger.error('Permiso de cámara denegado por el usuario');
39
+ return [];
40
+ }
41
+ if (permissionStatus === 'prompt') {
42
+ const tempStream = await navigator.mediaDevices.getUserMedia({ video: true });
43
+ tempStream.getTracks().forEach(track => track.stop());
44
+ }
45
+ const devices = await navigator.mediaDevices.enumerateDevices();
46
+ this.availableCameras = devices.filter(device => device.kind === 'videoinput');
47
+ this.logger.state('CAMARAS_DETECTADAS', {
48
+ count: this.availableCameras.length,
49
+ cameras: this.availableCameras.map(cam => ({
50
+ id: cam.deviceId,
51
+ label: cam.label || 'Unknown Camera'
52
+ }))
53
+ });
54
+ this.setInitialCameraPreference();
55
+ this.eventBus.emit('camera-changed', this.selectedCameraId);
56
+ return this.availableCameras;
57
+ }
58
+ catch (error) {
59
+ this.logger.error('Error al enumerar cámaras disponibles:', error);
60
+ this.handleCameraPermissionError(error);
61
+ return [];
62
+ }
63
+ }
64
+ getAvailableCameras() {
65
+ return [...this.availableCameras];
66
+ }
67
+ isMultipleCamerasAvailable() {
68
+ return this.availableCameras.length > 1;
69
+ }
70
+ getSelectedCameraId() {
71
+ return this.selectedCameraId;
72
+ }
73
+ async setSelectedCamera(cameraId) {
74
+ const camera = this.availableCameras.find(cam => cam.deviceId === cameraId);
75
+ if (!camera) {
76
+ throw new Error(`Camera with ID ${cameraId} not found`);
77
+ }
78
+ this.selectedCameraId = cameraId;
79
+ this.updatePreferredFacing(camera);
80
+ this.savePreference();
81
+ this.eventBus.emit('camera-changed', cameraId);
82
+ }
83
+ getPreferredFacing() {
84
+ return this.preferredCameraFacing;
85
+ }
86
+ async setupCamera(constraints) {
87
+ const finalConstraints = constraints || await this.getMaxResolution();
88
+ const stream = await navigator.mediaDevices.getUserMedia({
89
+ video: finalConstraints,
90
+ audio: false
91
+ });
92
+ return stream;
93
+ }
94
+ async switchCamera(cameraId) {
95
+ const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);
96
+ if (!selectedCamera) {
97
+ this.logger.warn('Cámara seleccionada no encontrada, re-enumerando dispositivos...');
98
+ await this.enumerateDevices();
99
+ return;
100
+ }
101
+ await this.setSelectedCamera(cameraId);
102
+ this.logger.state('CAMARA_CAMBIADA', {
103
+ label: selectedCamera.label,
104
+ deviceId: selectedCamera.deviceId
105
+ });
106
+ }
107
+ async flipToNextCamera() {
108
+ if (!this.isMultipleCamerasAvailable())
109
+ return;
110
+ const currentIndex = this.availableCameras.findIndex(camera => camera.deviceId === this.selectedCameraId);
111
+ const nextIndex = (currentIndex + 1) % this.availableCameras.length;
112
+ const nextCamera = this.availableCameras[nextIndex];
113
+ await this.switchCamera(nextCamera.deviceId);
114
+ }
115
+ savePreference() {
116
+ try {
117
+ const preference = {
118
+ cameraId: this.selectedCameraId,
119
+ facing: this.preferredCameraFacing,
120
+ timestamp: Date.now()
121
+ };
122
+ localStorage.setItem('jaak-stamps-camera-preference', JSON.stringify(preference));
123
+ this.logger.state('PREFERENCIA_CAMARA_GUARDADA', preference);
124
+ }
125
+ catch (error) {
126
+ this.logger.warn('Error al guardar preferencia de cámara:', error);
127
+ }
128
+ }
129
+ loadPreference() {
130
+ try {
131
+ const saved = localStorage.getItem('jaak-stamps-camera-preference');
132
+ if (saved) {
133
+ const preference = JSON.parse(saved);
134
+ const isStillAvailable = this.availableCameras.some(camera => camera.deviceId === preference.cameraId);
135
+ if (isStillAvailable) {
136
+ this.selectedCameraId = preference.cameraId;
137
+ this.preferredCameraFacing = preference.facing;
138
+ this.logger.state('PREFERENCIA_CAMARA_CARGADA', preference);
139
+ }
140
+ }
141
+ }
142
+ catch (error) {
143
+ this.logger.warn('Error al cargar preferencia de cámara:', error);
144
+ }
145
+ }
146
+ isRearCamera(stream) {
147
+ const videoTrack = stream.getVideoTracks()[0];
148
+ if (!videoTrack)
149
+ return false;
150
+ const settings = videoTrack.getSettings();
151
+ return settings.facingMode === 'environment';
152
+ }
153
+ getCameraInfo() {
154
+ return {
155
+ availableCameras: this.availableCameras.map(camera => ({
156
+ id: camera.deviceId,
157
+ label: camera.label || 'Unknown Camera',
158
+ selected: camera.deviceId === this.selectedCameraId
159
+ })),
160
+ selectedCameraId: this.selectedCameraId,
161
+ deviceType: this.deviceType,
162
+ isMultipleCamerasAvailable: this.isMultipleCamerasAvailable(),
163
+ preferredFacing: this.preferredCameraFacing
164
+ };
165
+ }
166
+ async checkCameraPermission() {
167
+ try {
168
+ if (!navigator.permissions) {
169
+ return 'prompt';
170
+ }
171
+ const permission = await navigator.permissions.query({ name: 'camera' });
172
+ return permission.state;
173
+ }
174
+ catch (error) {
175
+ this.logger.warn('No se pudo verificar permisos de cámara:', error);
176
+ return 'prompt';
177
+ }
178
+ }
179
+ handleCameraPermissionError(error) {
180
+ this.availableCameras = [];
181
+ this.eventBus.emit('error', new Error(`Camera permission error: ${error.message}`));
182
+ }
183
+ setInitialCameraPreference() {
184
+ if (this.availableCameras.length === 0)
185
+ return;
186
+ if (this.preferredCamera === 'front') {
187
+ this.selectFrontCamera();
188
+ }
189
+ else if (this.preferredCamera === 'back') {
190
+ this.selectBackCamera();
191
+ }
192
+ else {
193
+ this.selectAutoCamera();
194
+ }
195
+ }
196
+ selectFrontCamera() {
197
+ this.preferredCameraFacing = 'user';
198
+ const frontCamera = this.availableCameras.find(camera => camera.label.toLowerCase().includes('front') ||
199
+ camera.label.toLowerCase().includes('user') ||
200
+ camera.label.toLowerCase().includes('selfie') ||
201
+ !camera.label.toLowerCase().includes('back') && !camera.label.toLowerCase().includes('rear'));
202
+ this.selectedCameraId = frontCamera ? frontCamera.deviceId : this.availableCameras[0].deviceId;
203
+ this.logger.state('CAMARA_FRONTAL_SELECCIONADA', {
204
+ label: frontCamera?.label || this.availableCameras[0].label,
205
+ deviceId: this.selectedCameraId
206
+ });
207
+ }
208
+ selectBackCamera() {
209
+ this.preferredCameraFacing = 'environment';
210
+ const backCamera = this.availableCameras.find(camera => camera.label.toLowerCase().includes('back') ||
211
+ camera.label.toLowerCase().includes('rear') ||
212
+ camera.label.toLowerCase().includes('environment'));
213
+ this.selectedCameraId = backCamera ? backCamera.deviceId : this.availableCameras[0].deviceId;
214
+ this.logger.state('CAMARA_TRASERA_SELECCIONADA', {
215
+ label: backCamera?.label || this.availableCameras[0].label,
216
+ deviceId: this.selectedCameraId
217
+ });
218
+ }
219
+ selectAutoCamera() {
220
+ if (this.deviceType === 'mobile' || this.deviceType === 'tablet') {
221
+ this.selectBackCamera();
222
+ this.logger.state('CAMARA_AUTO_SELECCIONADA_MOBILE', { type: 'rear' });
223
+ }
224
+ else {
225
+ this.selectedCameraId = this.availableCameras[0].deviceId;
226
+ this.logger.state('CAMARA_AUTO_SELECCIONADA_DESKTOP', {
227
+ label: this.availableCameras[0].label,
228
+ deviceId: this.selectedCameraId
229
+ });
230
+ }
231
+ }
232
+ updatePreferredFacing(camera) {
233
+ const isRearCamera = camera.label.toLowerCase().includes('back') ||
234
+ camera.label.toLowerCase().includes('rear') ||
235
+ camera.label.toLowerCase().includes('environment');
236
+ this.preferredCameraFacing = isRearCamera ? 'environment' : 'user';
237
+ }
238
+ async getMaxResolution() {
239
+ try {
240
+ const videoConstraints = {};
241
+ if (this.selectedCameraId) {
242
+ videoConstraints.deviceId = { exact: this.selectedCameraId };
243
+ }
244
+ else if (this.preferredCameraFacing) {
245
+ videoConstraints.facingMode = this.preferredCameraFacing;
246
+ }
247
+ else {
248
+ videoConstraints.facingMode = "environment";
249
+ }
250
+ const tempStream = await navigator.mediaDevices.getUserMedia({
251
+ video: videoConstraints
252
+ });
253
+ const videoTrack = tempStream.getVideoTracks()[0];
254
+ const capabilities = videoTrack.getCapabilities();
255
+ tempStream.getTracks().forEach(track => track.stop());
256
+ const constraints = { ...videoConstraints };
257
+ // Enhanced focus and image quality settings
258
+ this.applyAdvancedCameraSettings(constraints, capabilities);
259
+ if (capabilities.width && capabilities.height) {
260
+ const maxWidth = Math.min(capabilities.width.max, 1920);
261
+ const maxHeight = Math.min(capabilities.height.max, 1080);
262
+ const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
263
+ if (isTablet) {
264
+ constraints.width = { ideal: Math.min(maxWidth, 1280) };
265
+ constraints.height = { ideal: Math.min(maxHeight, 720) };
266
+ }
267
+ else {
268
+ constraints.width = { ideal: maxWidth };
269
+ constraints.height = { ideal: maxHeight };
270
+ }
271
+ }
272
+ this.logger.state('CONFIGURACION_CAMARA_OPTIMIZADA', {
273
+ constraints,
274
+ capabilities: this.getCapabilitiesSummary(capabilities)
275
+ });
276
+ return constraints;
277
+ }
278
+ catch (err) {
279
+ this.logger.warn('No se pudieron obtener capacidades de cámara, usando configuración de respaldo');
280
+ const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
281
+ const fallbackConstraints = {
282
+ width: { ideal: isTablet ? 1280 : 1920 },
283
+ height: { ideal: isTablet ? 720 : 1080 }
284
+ };
285
+ // Apply basic focus settings even for fallback
286
+ this.applyBasicFocusSettings(fallbackConstraints);
287
+ if (this.selectedCameraId) {
288
+ fallbackConstraints.deviceId = { exact: this.selectedCameraId };
289
+ }
290
+ else if (this.preferredCameraFacing) {
291
+ fallbackConstraints.facingMode = this.preferredCameraFacing;
292
+ }
293
+ else {
294
+ fallbackConstraints.facingMode = "environment";
295
+ }
296
+ return fallbackConstraints;
297
+ }
298
+ }
299
+ applyAdvancedCameraSettings(constraints, capabilities) {
300
+ // Apply standard camera settings that are widely supported
301
+ // Frame rate optimization
302
+ if (capabilities.frameRate) {
303
+ constraints.frameRate = {
304
+ ideal: 30,
305
+ min: 15,
306
+ max: 60
307
+ };
308
+ }
309
+ // Width and height constraints for optimal resolution
310
+ if (capabilities.width && capabilities.height) {
311
+ constraints.width = {
312
+ ideal: Math.min(1920, capabilities.width.max || 1920),
313
+ min: 640
314
+ };
315
+ constraints.height = {
316
+ ideal: Math.min(1080, capabilities.height.max || 1080),
317
+ min: 480
318
+ };
319
+ }
320
+ // Apply enhanced constraints through advanced array for better browser compatibility
321
+ const advancedConstraints = [];
322
+ // Try to set optimal settings through advanced constraints
323
+ const advanced = {};
324
+ // Focus mode - use 'any' type to bypass TypeScript limitations
325
+ if (capabilities.focusMode) {
326
+ if (capabilities.focusMode.includes('continuous')) {
327
+ advanced.focusMode = 'continuous';
328
+ }
329
+ else if (capabilities.focusMode.includes('single-shot')) {
330
+ advanced.focusMode = 'single-shot';
331
+ }
332
+ }
333
+ // Focus distance for close objects
334
+ if (capabilities.focusDistance) {
335
+ advanced.focusDistance = {
336
+ ideal: 0.4, // 40cm - optimal for document capture
337
+ min: 0.2,
338
+ max: 1.0
339
+ };
340
+ }
341
+ // Zoom control
342
+ if (capabilities.zoom) {
343
+ advanced.zoom = {
344
+ ideal: 1.0,
345
+ min: capabilities.zoom.min,
346
+ max: Math.min(capabilities.zoom.max, 3.0)
347
+ };
348
+ }
349
+ // Add advanced constraints if any were set
350
+ if (Object.keys(advanced).length > 0) {
351
+ advancedConstraints.push(advanced);
352
+ }
353
+ if (advancedConstraints.length > 0) {
354
+ constraints.advanced = advancedConstraints;
355
+ }
356
+ }
357
+ applyBasicFocusSettings(constraints) {
358
+ // Apply basic focus settings when capabilities are not available
359
+ // Use advanced constraints for non-standard properties
360
+ const advanced = {
361
+ focusMode: 'continuous',
362
+ exposureMode: 'continuous',
363
+ whiteBalanceMode: 'continuous'
364
+ };
365
+ if (!constraints.advanced) {
366
+ constraints.advanced = [];
367
+ }
368
+ constraints.advanced.push(advanced);
369
+ constraints.frameRate = { ideal: 30 };
370
+ }
371
+ getCapabilitiesSummary(capabilities) {
372
+ return {
373
+ hasAutoFocus: !!capabilities.focusMode,
374
+ hasFocusDistance: !!capabilities.focusDistance,
375
+ hasExposureControl: !!capabilities.exposureMode,
376
+ hasWhiteBalance: !!capabilities.whiteBalanceMode,
377
+ hasZoom: !!capabilities.zoom,
378
+ hasTorch: capabilities.torch !== undefined,
379
+ hasImageControls: !!(capabilities.brightness || capabilities.contrast || capabilities.saturation || capabilities.sharpness),
380
+ resolutionRange: capabilities.width && capabilities.height ?
381
+ `${capabilities.width.min}x${capabilities.height.min} - ${capabilities.width.max}x${capabilities.height.max}` : 'unknown'
382
+ };
383
+ }
384
+ // New method to enable torch/flash for better illumination
385
+ async setTorchEnabled(enabled, stream) {
386
+ try {
387
+ if (!stream) {
388
+ this.logger.warn('No hay stream disponible para controlar el flash');
389
+ return false;
390
+ }
391
+ const videoTrack = stream.getVideoTracks()[0];
392
+ if (!videoTrack) {
393
+ this.logger.warn('No hay track de video disponible');
394
+ return false;
395
+ }
396
+ const capabilities = videoTrack.getCapabilities();
397
+ if (capabilities.torch === undefined) {
398
+ this.logger.warn('El dispositivo no soporta control de flash');
399
+ return false;
400
+ }
401
+ await videoTrack.applyConstraints({
402
+ advanced: [{ torch: enabled }]
403
+ });
404
+ this.logger.state('FLASH_CONTROLADO', { enabled });
405
+ return true;
406
+ }
407
+ catch (error) {
408
+ this.logger.error('Error al controlar el flash:', error);
409
+ return false;
410
+ }
411
+ }
412
+ // Method to manually trigger focus on a specific point
413
+ async focusAtPoint(x, y, stream) {
414
+ try {
415
+ if (!stream) {
416
+ this.logger.warn('No hay stream disponible para enfocar');
417
+ return false;
418
+ }
419
+ const videoTrack = stream.getVideoTracks()[0];
420
+ if (!videoTrack) {
421
+ this.logger.warn('No hay track de video disponible');
422
+ return false;
423
+ }
424
+ const capabilities = videoTrack.getCapabilities();
425
+ if (!capabilities.focusMode || !capabilities.focusMode.includes('single-shot')) {
426
+ this.logger.warn('El dispositivo no soporta enfoque manual');
427
+ return false;
428
+ }
429
+ // Trigger single-shot focus
430
+ await videoTrack.applyConstraints({
431
+ advanced: [{ focusMode: 'single-shot' }]
432
+ });
433
+ // Return to continuous focus after a short delay
434
+ setTimeout(async () => {
435
+ try {
436
+ await videoTrack.applyConstraints({
437
+ advanced: [{ focusMode: 'continuous' }]
438
+ });
439
+ }
440
+ catch (error) {
441
+ this.logger.warn('Error al volver a enfoque continuo:', error);
442
+ }
443
+ }, 1000);
444
+ this.logger.state('ENFOQUE_MANUAL_ACTIVADO', { x, y });
445
+ return true;
446
+ }
447
+ catch (error) {
448
+ this.logger.error('Error al enfocar manualmente:', error);
449
+ return false;
450
+ }
451
+ }
452
+ }
453
+ //# sourceMappingURL=CameraService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CameraService.js","sourceRoot":"","sources":["../../src/services/CameraService.ts"],"names":[],"mappings":"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","sourcesContent":["import { ICameraService, CameraInfo, CameraPreference } from './interfaces/ICameraService';\nimport { ILogger } from './interfaces/ILogger';\nimport { IEventBus } from './interfaces/IEventBus';\n\nexport class CameraService implements ICameraService {\n private availableCameras: MediaDeviceInfo[] = [];\n private selectedCameraId: string | null = null;\n private deviceType: 'mobile' | 'desktop' | 'tablet' = 'desktop';\n private preferredCameraFacing: 'environment' | 'user' | null = null;\n private preferredCamera: 'auto' | 'front' | 'back' = 'auto';\n\n constructor(\n private logger: ILogger,\n private eventBus: IEventBus,\n preferredCamera: 'auto' | 'front' | 'back' = 'auto'\n ) {\n this.preferredCamera = preferredCamera;\n }\n\n async detectDeviceType(): Promise<'mobile' | 'desktop' | 'tablet'> {\n const userAgent = navigator.userAgent;\n const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);\n const isTablet = /iPad|Android/i.test(userAgent) && window.innerWidth >= 768;\n \n if (isTablet) {\n this.deviceType = 'tablet';\n } else if (isMobile) {\n this.deviceType = 'mobile';\n } else {\n this.deviceType = 'desktop';\n }\n \n this.logger.state('DISPOSITIVO_DETECTADO', {\n deviceType: this.deviceType,\n userAgent: navigator.userAgent,\n screenDimensions: { width: window.innerWidth, height: window.innerHeight }\n });\n \n return this.deviceType;\n }\n\n async enumerateDevices(): Promise<MediaDeviceInfo[]> {\n try {\n const permissionStatus = await this.checkCameraPermission();\n \n if (permissionStatus === 'denied') {\n this.logger.error('Permiso de cámara denegado por el usuario');\n return [];\n }\n \n if (permissionStatus === 'prompt') {\n const tempStream = await navigator.mediaDevices.getUserMedia({ video: true });\n tempStream.getTracks().forEach(track => track.stop());\n }\n \n const devices = await navigator.mediaDevices.enumerateDevices();\n this.availableCameras = devices.filter(device => device.kind === 'videoinput');\n \n this.logger.state('CAMARAS_DETECTADAS', {\n count: this.availableCameras.length,\n cameras: this.availableCameras.map(cam => ({\n id: cam.deviceId,\n label: cam.label || 'Unknown Camera'\n }))\n });\n \n this.setInitialCameraPreference();\n this.eventBus.emit('camera-changed', this.selectedCameraId);\n \n return this.availableCameras;\n } catch (error) {\n this.logger.error('Error al enumerar cámaras disponibles:', error);\n this.handleCameraPermissionError(error);\n return [];\n }\n }\n\n getAvailableCameras(): MediaDeviceInfo[] {\n return [...this.availableCameras];\n }\n\n isMultipleCamerasAvailable(): boolean {\n return this.availableCameras.length > 1;\n }\n\n getSelectedCameraId(): string | null {\n return this.selectedCameraId;\n }\n\n async setSelectedCamera(cameraId: string): Promise<void> {\n const camera = this.availableCameras.find(cam => cam.deviceId === cameraId);\n if (!camera) {\n throw new Error(`Camera with ID ${cameraId} not found`);\n }\n \n this.selectedCameraId = cameraId;\n this.updatePreferredFacing(camera);\n this.savePreference();\n this.eventBus.emit('camera-changed', cameraId);\n }\n\n getPreferredFacing(): 'environment' | 'user' | null {\n return this.preferredCameraFacing;\n }\n\n async setupCamera(constraints?: MediaTrackConstraints): Promise<MediaStream> {\n const finalConstraints = constraints || await this.getMaxResolution();\n \n const stream = await navigator.mediaDevices.getUserMedia({\n video: finalConstraints,\n audio: false\n });\n\n return stream;\n }\n\n async switchCamera(cameraId: string): Promise<void> {\n const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);\n if (!selectedCamera) {\n this.logger.warn('Cámara seleccionada no encontrada, re-enumerando dispositivos...');\n await this.enumerateDevices();\n return;\n }\n \n await this.setSelectedCamera(cameraId);\n this.logger.state('CAMARA_CAMBIADA', { \n label: selectedCamera.label, \n deviceId: selectedCamera.deviceId \n });\n }\n\n async flipToNextCamera(): Promise<void> {\n if (!this.isMultipleCamerasAvailable()) return;\n \n const currentIndex = this.availableCameras.findIndex(\n camera => camera.deviceId === this.selectedCameraId\n );\n \n const nextIndex = (currentIndex + 1) % this.availableCameras.length;\n const nextCamera = this.availableCameras[nextIndex];\n \n await this.switchCamera(nextCamera.deviceId);\n }\n\n savePreference(): void {\n try {\n const preference: CameraPreference = {\n cameraId: this.selectedCameraId!,\n facing: this.preferredCameraFacing,\n timestamp: Date.now()\n };\n \n localStorage.setItem('jaak-stamps-camera-preference', JSON.stringify(preference));\n this.logger.state('PREFERENCIA_CAMARA_GUARDADA', preference);\n } catch (error) {\n this.logger.warn('Error al guardar preferencia de cámara:', error);\n }\n }\n\n loadPreference(): void {\n try {\n const saved = localStorage.getItem('jaak-stamps-camera-preference');\n if (saved) {\n const preference: CameraPreference = JSON.parse(saved);\n \n const isStillAvailable = this.availableCameras.some(\n camera => camera.deviceId === preference.cameraId\n );\n \n if (isStillAvailable) {\n this.selectedCameraId = preference.cameraId;\n this.preferredCameraFacing = preference.facing;\n this.logger.state('PREFERENCIA_CAMARA_CARGADA', preference);\n }\n }\n } catch (error) {\n this.logger.warn('Error al cargar preferencia de cámara:', error);\n }\n }\n\n isRearCamera(stream: MediaStream): boolean {\n const videoTrack = stream.getVideoTracks()[0];\n if (!videoTrack) return false;\n \n const settings = videoTrack.getSettings();\n return settings.facingMode === 'environment';\n }\n\n getCameraInfo(): {\n availableCameras: CameraInfo[];\n selectedCameraId: string | null;\n deviceType: string;\n isMultipleCamerasAvailable: boolean;\n preferredFacing: 'environment' | 'user' | null;\n } {\n return {\n availableCameras: this.availableCameras.map(camera => ({\n id: camera.deviceId,\n label: camera.label || 'Unknown Camera',\n selected: camera.deviceId === this.selectedCameraId\n })),\n selectedCameraId: this.selectedCameraId,\n deviceType: this.deviceType,\n isMultipleCamerasAvailable: this.isMultipleCamerasAvailable(),\n preferredFacing: this.preferredCameraFacing\n };\n }\n\n private async checkCameraPermission(): Promise<'granted' | 'denied' | 'prompt'> {\n try {\n if (!navigator.permissions) {\n return 'prompt';\n }\n \n const permission = await navigator.permissions.query({ name: 'camera' as PermissionName });\n return permission.state;\n } catch (error) {\n this.logger.warn('No se pudo verificar permisos de cámara:', error);\n return 'prompt';\n }\n }\n\n private handleCameraPermissionError(error: any): void {\n this.availableCameras = [];\n this.eventBus.emit('error', new Error(`Camera permission error: ${error.message}`));\n }\n\n private setInitialCameraPreference(): void {\n if (this.availableCameras.length === 0) return;\n \n if (this.preferredCamera === 'front') {\n this.selectFrontCamera();\n } else if (this.preferredCamera === 'back') {\n this.selectBackCamera();\n } else {\n this.selectAutoCamera();\n }\n }\n\n private selectFrontCamera(): void {\n this.preferredCameraFacing = 'user';\n \n const frontCamera = this.availableCameras.find(camera => \n camera.label.toLowerCase().includes('front') || \n camera.label.toLowerCase().includes('user') ||\n camera.label.toLowerCase().includes('selfie') ||\n !camera.label.toLowerCase().includes('back') && !camera.label.toLowerCase().includes('rear')\n );\n \n this.selectedCameraId = frontCamera ? frontCamera.deviceId : this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_FRONTAL_SELECCIONADA', { \n label: frontCamera?.label || this.availableCameras[0].label,\n deviceId: this.selectedCameraId \n });\n }\n\n private selectBackCamera(): void {\n this.preferredCameraFacing = 'environment';\n \n const backCamera = this.availableCameras.find(camera => \n camera.label.toLowerCase().includes('back') || \n camera.label.toLowerCase().includes('rear') ||\n camera.label.toLowerCase().includes('environment')\n );\n \n this.selectedCameraId = backCamera ? backCamera.deviceId : this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_TRASERA_SELECCIONADA', { \n label: backCamera?.label || this.availableCameras[0].label,\n deviceId: this.selectedCameraId \n });\n }\n\n private selectAutoCamera(): void {\n if (this.deviceType === 'mobile' || this.deviceType === 'tablet') {\n this.selectBackCamera();\n this.logger.state('CAMARA_AUTO_SELECCIONADA_MOBILE', { type: 'rear' });\n } else {\n this.selectedCameraId = this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_AUTO_SELECCIONADA_DESKTOP', { \n label: this.availableCameras[0].label, \n deviceId: this.selectedCameraId \n });\n }\n }\n\n private updatePreferredFacing(camera: MediaDeviceInfo): void {\n const isRearCamera = camera.label.toLowerCase().includes('back') || \n camera.label.toLowerCase().includes('rear') ||\n camera.label.toLowerCase().includes('environment');\n this.preferredCameraFacing = isRearCamera ? 'environment' : 'user';\n }\n\n private async getMaxResolution(): Promise<MediaTrackConstraints> {\n try {\n const videoConstraints: MediaTrackConstraints = {};\n \n if (this.selectedCameraId) {\n videoConstraints.deviceId = { exact: this.selectedCameraId };\n } else if (this.preferredCameraFacing) {\n videoConstraints.facingMode = this.preferredCameraFacing;\n } else {\n videoConstraints.facingMode = \"environment\";\n }\n \n const tempStream = await navigator.mediaDevices.getUserMedia({\n video: videoConstraints\n });\n \n const videoTrack = tempStream.getVideoTracks()[0];\n const capabilities = videoTrack.getCapabilities();\n tempStream.getTracks().forEach(track => track.stop());\n \n const constraints: MediaTrackConstraints = { ...videoConstraints };\n \n // Enhanced focus and image quality settings\n this.applyAdvancedCameraSettings(constraints, capabilities);\n \n if (capabilities.width && capabilities.height) {\n const maxWidth = Math.min(capabilities.width.max, 1920);\n const maxHeight = Math.min(capabilities.height.max, 1080);\n \n const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;\n \n if (isTablet) {\n constraints.width = { ideal: Math.min(maxWidth, 1280) };\n constraints.height = { ideal: Math.min(maxHeight, 720) };\n } else {\n constraints.width = { ideal: maxWidth };\n constraints.height = { ideal: maxHeight };\n }\n }\n \n this.logger.state('CONFIGURACION_CAMARA_OPTIMIZADA', {\n constraints,\n capabilities: this.getCapabilitiesSummary(capabilities)\n });\n \n return constraints;\n } catch (err) {\n this.logger.warn('No se pudieron obtener capacidades de cámara, usando configuración de respaldo');\n \n const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;\n const fallbackConstraints: MediaTrackConstraints = {\n width: { ideal: isTablet ? 1280 : 1920 },\n height: { ideal: isTablet ? 720 : 1080 }\n };\n \n // Apply basic focus settings even for fallback\n this.applyBasicFocusSettings(fallbackConstraints);\n \n if (this.selectedCameraId) {\n fallbackConstraints.deviceId = { exact: this.selectedCameraId };\n } else if (this.preferredCameraFacing) {\n fallbackConstraints.facingMode = this.preferredCameraFacing;\n } else {\n fallbackConstraints.facingMode = \"environment\";\n }\n \n return fallbackConstraints;\n }\n }\n\n private applyAdvancedCameraSettings(constraints: MediaTrackConstraints, capabilities: MediaTrackCapabilities): void {\n // Apply standard camera settings that are widely supported\n \n // Frame rate optimization\n if (capabilities.frameRate) {\n constraints.frameRate = {\n ideal: 30,\n min: 15,\n max: 60\n };\n }\n\n // Width and height constraints for optimal resolution\n if (capabilities.width && capabilities.height) {\n constraints.width = {\n ideal: Math.min(1920, capabilities.width.max || 1920),\n min: 640\n };\n constraints.height = {\n ideal: Math.min(1080, capabilities.height.max || 1080),\n min: 480\n };\n }\n\n // Apply enhanced constraints through advanced array for better browser compatibility\n const advancedConstraints: MediaTrackConstraintSet[] = [];\n \n // Try to set optimal settings through advanced constraints\n const advanced: any = {};\n \n // Focus mode - use 'any' type to bypass TypeScript limitations\n if ((capabilities as any).focusMode) {\n if ((capabilities as any).focusMode.includes('continuous')) {\n advanced.focusMode = 'continuous';\n } else if ((capabilities as any).focusMode.includes('single-shot')) {\n advanced.focusMode = 'single-shot';\n }\n }\n\n // Focus distance for close objects\n if ((capabilities as any).focusDistance) {\n advanced.focusDistance = {\n ideal: 0.4, // 40cm - optimal for document capture\n min: 0.2,\n max: 1.0\n };\n }\n\n // Zoom control\n if ((capabilities as any).zoom) {\n advanced.zoom = {\n ideal: 1.0,\n min: (capabilities as any).zoom.min,\n max: Math.min((capabilities as any).zoom.max, 3.0)\n };\n }\n\n // Add advanced constraints if any were set\n if (Object.keys(advanced).length > 0) {\n advancedConstraints.push(advanced);\n }\n\n if (advancedConstraints.length > 0) {\n constraints.advanced = advancedConstraints;\n }\n }\n\n private applyBasicFocusSettings(constraints: MediaTrackConstraints): void {\n // Apply basic focus settings when capabilities are not available\n // Use advanced constraints for non-standard properties\n const advanced: any = {\n focusMode: 'continuous',\n exposureMode: 'continuous',\n whiteBalanceMode: 'continuous'\n };\n \n if (!constraints.advanced) {\n constraints.advanced = [];\n }\n constraints.advanced.push(advanced);\n \n constraints.frameRate = { ideal: 30 };\n }\n\n private getCapabilitiesSummary(capabilities: MediaTrackCapabilities): any {\n return {\n hasAutoFocus: !!(capabilities as any).focusMode,\n hasFocusDistance: !!(capabilities as any).focusDistance,\n hasExposureControl: !!(capabilities as any).exposureMode,\n hasWhiteBalance: !!(capabilities as any).whiteBalanceMode,\n hasZoom: !!(capabilities as any).zoom,\n hasTorch: (capabilities as any).torch !== undefined,\n hasImageControls: !!((capabilities as any).brightness || (capabilities as any).contrast || (capabilities as any).saturation || (capabilities as any).sharpness),\n resolutionRange: capabilities.width && capabilities.height ? \n `${capabilities.width.min}x${capabilities.height.min} - ${capabilities.width.max}x${capabilities.height.max}` : 'unknown'\n };\n }\n\n // New method to enable torch/flash for better illumination\n async setTorchEnabled(enabled: boolean, stream?: MediaStream): Promise<boolean> {\n try {\n if (!stream) {\n this.logger.warn('No hay stream disponible para controlar el flash');\n return false;\n }\n\n const videoTrack = stream.getVideoTracks()[0];\n if (!videoTrack) {\n this.logger.warn('No hay track de video disponible');\n return false;\n }\n\n const capabilities = videoTrack.getCapabilities();\n if ((capabilities as any).torch === undefined) {\n this.logger.warn('El dispositivo no soporta control de flash');\n return false;\n }\n\n await videoTrack.applyConstraints({\n advanced: [{ torch: enabled } as any]\n });\n\n this.logger.state('FLASH_CONTROLADO', { enabled });\n return true;\n } catch (error) {\n this.logger.error('Error al controlar el flash:', error);\n return false;\n }\n }\n\n // Method to manually trigger focus on a specific point\n async focusAtPoint(x: number, y: number, stream?: MediaStream): Promise<boolean> {\n try {\n if (!stream) {\n this.logger.warn('No hay stream disponible para enfocar');\n return false;\n }\n\n const videoTrack = stream.getVideoTracks()[0];\n if (!videoTrack) {\n this.logger.warn('No hay track de video disponible');\n return false;\n }\n\n const capabilities = videoTrack.getCapabilities();\n if (!(capabilities as any).focusMode || !(capabilities as any).focusMode.includes('single-shot')) {\n this.logger.warn('El dispositivo no soporta enfoque manual');\n return false;\n }\n\n // Trigger single-shot focus\n await videoTrack.applyConstraints({\n advanced: [{ focusMode: 'single-shot' } as any]\n });\n\n // Return to continuous focus after a short delay\n setTimeout(async () => {\n try {\n await videoTrack.applyConstraints({\n advanced: [{ focusMode: 'continuous' } as any]\n });\n } catch (error) {\n this.logger.warn('Error al volver a enfoque continuo:', error);\n }\n }, 1000);\n\n this.logger.state('ENFOQUE_MANUAL_ACTIVADO', { x, y });\n return true;\n } catch (error) {\n this.logger.error('Error al enfocar manualmente:', error);\n return false;\n }\n }\n}"]}